package com.exxeleron.qjava; import java.io.IOException; import java.io.OutputStream; /** * {@link OutputStream} wrapping resizable byte buffer. Provides convenient methods for serializing data to IPC stream. */ public class ByteOutputStream extends OutputStream { public static final int BUFFER_SIZE = 65536; private static final double BUFFER_GROWTH_FACTOR = 1.5; protected byte buffer[]; protected int count; /** * Creates new {@link ByteOutputStream} with default size. */ public ByteOutputStream() { buffer = new byte[BUFFER_SIZE]; } /** * Creates new {@link ByteOutputStream} with specified size. * * @param bufferSize * initial size of the buffer */ public ByteOutputStream(final int bufferSize) { buffer = new byte[bufferSize]; } private void resizeBuffer( final int newcount ) { if ( newcount > buffer.length ) { final byte[] copy = new byte[Math.max(newcount + BUFFER_SIZE, (int) (buffer.length * BUFFER_GROWTH_FACTOR) + 1)]; System.arraycopy(buffer, 0, copy, 0, buffer.length); buffer = copy; } } /** * Resets the buffer writing position. */ public void reset() { count = 0; } /** * Writes the specified short to this output stream. * * @param value * the <code>short</code> */ public void writeShort( final short value ) { writeByte((byte) value); writeByte((byte) (value >> 8)); } /** * Writes the specified int to this output stream. * * @param value * the <code>int</code> */ public void writeInt( final int value ) { writeShort((short) value); writeShort((short) (value >> 16)); } /** * Writes the specified long to this output stream. * * @param value * the <code>long</code> */ public void writeLong( final long value ) { writeInt((int) value); writeInt((int) (value >> 32)); } /** * Writes the specified long to this output stream using big endian padding. * * @param value * the <code>long</code> */ public void writeLongBigEndian( final long value ) { final byte[] arr = new byte[] { (byte) ((value >> 56) & 0xff), (byte) ((value >> 48) & 0xff), (byte) ((value >> 40) & 0xff), (byte) ((value >> 32) & 0xff), (byte) ((value >> 24) & 0xff), (byte) ((value >> 16) & 0xff), (byte) ((value >> 8) & 0xff), (byte) ((value >> 0) & 0xff) }; for ( byte anArr : arr ) { writeByte(anArr); } } /** * Writes the specified float to this output stream. * * @param value * the <code>float</code> */ public void writeFloat( final float value ) { writeInt(Float.floatToIntBits(value)); } /** * Writes the specified double to this output stream. * * @param value * the <code>double</code> */ public void writeDouble( final double value ) { writeLong(Double.doubleToLongBits(value)); } /** * Writes the specified byte to this output stream. * * @param value * the <code>byte</code> */ public void writeByte( final byte value ) { final int newcount = count + 1; resizeBuffer(newcount); buffer[count] = value; count = newcount; } /** * Writes the specified byte to this output stream. * * @see java.io.OutputStream#write(int) */ @Override public void write( final int b ) { final int newcount = count + 1; resizeBuffer(newcount); buffer[count] = (byte) b; count = newcount; } @Override public void write( final byte b[], final int off, final int len ) { if ( (off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) ) { throw new IndexOutOfBoundsException("Attempt to write outside of the buffer. Offset: " + off + ", Size: " + len + ", Buffer: " + b.length + "."); } else if ( len == 0 ) { return; } final int newcount = count + len; resizeBuffer(newcount); System.arraycopy(b, off, buffer, count, len); count = newcount; } /** * Numbers of written bytes. * * @return number of bytes */ public int count() { return count; } @Override public void close() throws IOException { // } /** * Creates copy of internal buffer. * * @return copy of the internal buffer as byte[] */ public byte[] toByteArray() { final byte[] copy = new byte[count]; System.arraycopy(buffer, 0, copy, 0, Math.min(buffer.length, count)); return copy; } /** * Provides direct access to underlying buffer. * * @return buffer */ public byte[] buffer() { return buffer; } }