All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.lightspeed7.mongofs.BufferedChunksOutputStream Maven / Gradle / Ivy

package me.lightspeed7.mongofs;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import me.lightspeed7.mongofs.util.ChunkSize;

/**
 * The class implements a buffered output stream. By setting up such an output stream, an application can write bytes to the underlying
 * output stream in an orderly chunked fashion
 * 
 * NOTE: I have removed the synchronized from the write and flush methods for performance reasons, thread safety is not required.
 * 
 * @author David Buschman
 * @author Arthur van Hoff - Original
 * 
 * @since JDK1.0
 */
public class BufferedChunksOutputStream extends FilterOutputStream {

    /**
     * The internal buffer where the chunk data is stored.
     */
    private byte[] myBuffer;

    /**
     * The number of valid bytes in the buffer. This value is always in the range 0 through buf.length; elements
     * buf[0] through buf[count-1] contain valid byte data.
     */
    private int currentPosition;

    /**
     * The size of the chunk to be written downstream
     */
    private int chunkSize;

    /**
     * Creates a new buffered output stream to write data to the specified underlying output stream.
     * 
     * @param out
     *            the underlying output stream.
     */
    public BufferedChunksOutputStream(final OutputStream out) {

        this(out, 8192);
    }

    /**
     * Creates a new buffered output stream to write data to the specified underlying output stream with the specified buffer size.
     * 
     * @param out
     * @param chunkSize
     * 
     * @exception IllegalArgumentException
     *                if size <= 0.
     */
    public BufferedChunksOutputStream(final OutputStream out, final ChunkSize chunkSize) {
        this(out, chunkSize.getChunkSize());
    }

    /**
     * Creates a new buffered output stream to write data to the specified underlying output stream with the specified buffer size.
     * 
     * @param out
     *            the underlying output stream.
     * @param chunkSize
     *            the buffer size.
     * @exception IllegalArgumentException
     *                if size <= 0.
     */
    public BufferedChunksOutputStream(final OutputStream out, final int chunkSize) {

        super(out);
        this.chunkSize = chunkSize;
        if (chunkSize <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        myBuffer = new byte[chunkSize];
    }

    /** Flush the internal buffer */
    private void flushBuffer() throws IOException {

        if (currentPosition > 0) {
            out.write(myBuffer, 0, currentPosition);
            currentPosition = 0;
        }
    }

    /**
     * Writes the specified byte to this buffered output stream.
     * 
     * @param b
     *            the byte to be written.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public void write(final int b) throws IOException {

        myBuffer[currentPosition++] = (byte) b;
        if (currentPosition >= this.chunkSize) {
            flushBuffer();
        }
    }

    /**
     * Writes len bytes from the specified byte array starting at offset off to this buffered output stream.
     * 
     * This method will recurse on itself until the buffer is exhausted to allow for orderly chunk writes to the underlying stream.
     * 
     * @param inBuffer
     *            the data.
     * @param offset
     *            the start offset in the data.
     * @param length
     *            the number of bytes to write.
     * @exception IOException
     *                if an I/O error occurs.
     */
    public void write(final byte[] inBuffer, final int offset, final int length) throws IOException {

        if (length <= 0) {
            return;
        }

        // if empty buffer and full chunk copy, then send straight to underlying
        // if not-empty buffer and enough to fill, then copy to buffer and flush
        // if not empty and not enough to fill, then copy to buffer and return
        int bytesToCopy = length;
        if (bytesToCopy >= this.chunkSize - currentPosition) {
            if (this.currentPosition == 0) {

                // pull the data here before call the underlying stream
                System.arraycopy(inBuffer, offset, myBuffer, currentPosition, chunkSize);
                currentPosition += chunkSize;
                flushBuffer();

                write(inBuffer, offset + chunkSize, length - chunkSize); // recurse
            }
            else {
                // fill the rest of myBuffer and flush
                bytesToCopy = this.chunkSize - currentPosition;
                System.arraycopy(inBuffer, offset, myBuffer, currentPosition, bytesToCopy);
                currentPosition += bytesToCopy;
                // test for full buffer
                assert currentPosition == this.chunkSize;
                flushBuffer();
                write(inBuffer, offset + bytesToCopy, length - bytesToCopy); // recurse
            }
        }
        else {
            // the last of the buffer
            if (bytesToCopy > 0) {
                System.arraycopy(inBuffer, offset, myBuffer, currentPosition, bytesToCopy);
                currentPosition += bytesToCopy;
            }
        }
    }

    /**
     * Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream.
     * 
     * @exception IOException
     *                if an I/O error occurs.
     * @see java.io.FilterOutputStream#out
     */
    public void flush() throws IOException {

        flushBuffer();
        out.flush();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy