package nl.tno.imb; // TODO: add Google protocol buffers implementation // TODO: starting Java 1.7 the DirectByteBuffer seems quick enough, for now implement own buffer (backwards compatible <= 1.6) import java.nio.charset.Charset; import java.util.Arrays; /** * General buffer for use within the IMB framework to standardize byte order and data exchange.
* IMB is always little-endian within the framework (and over the network). IMB defines the following types: byte, boolean, * int32, int64, single, double, string. string is defined as a byte array with UTF-8 characters prefixed with length as * int32.
* Class is to be extended with Google Protocol Buffers to store data compressed and compatible with Protocol Buffers.
* Standard Java ByteBuffers are not used because of performance issues.
* Java 1.7 defines DirectByteBuffer which looks promising but for now is not widely used.
* All reads advance the read cursor. All qreads advance the read cursor and read without any checking. All writes advance the * write cursor. All qwrites advance the write cursor and write without any checking. All prepares advance the prepare * cursor.
* Prepares are to be used to optimize the buffer memory allocations. prepareApply closes the prepares and allocates the newly * needed buffer size. * @author hans.cornelissen@tno.nl */ public class TByteBuffer { // constructors /** * Constructor: create byte buffer of the specified length * @param aLength int; length in bytes of the buffer to create */ public TByteBuffer(int aLength) { this.fBuffer = new byte[aLength]; } /** * Constructor: create empty byte buffer */ public TByteBuffer() { this.fBuffer = new byte[0]; } /** * Constructor: create byte buffer as copy of the specified byte array * @param aBuffer byte[]; */ public TByteBuffer(byte[] aBuffer) { this.fBuffer = aBuffer; } private byte[] fBuffer = null; private int fReadCursor = 0; private int fWriteCursor = 0; private int fPrepareCursor = 0; /** * length of the byte buffer in bytes * @return length of the allocated buffer in bytes */ public int getLength() { return this.fBuffer.length; } /** * adjust the length of the buffer retaining buffer contents (less when new size is less) * @param aLength int; new size of buffer in bytes */ private void setLength(int aLength) { if (aLength != this.fBuffer.length) { // create new buffer byte[] NewBuffer = new byte[aLength]; // check how much data to copy from old buffer int len = this.fBuffer.length; if (len > aLength) len = aLength; // copy old data to new buffer for (int i = 0; i < len; i++) NewBuffer[i] = this.fBuffer[i]; this.fBuffer = NewBuffer; } } // uncompressed size of variables within the framework /** uncompressed size of boolean within IMB framework */ public static final int SIZE_OF_BOOLEAN = Byte.SIZE / 8; /** uncompressed size of byte within IMB framework */ public static final int SIZE_OF_BYTE = Byte.SIZE / 8; /** uncompressed size of 32 bit integer within IMB framework */ public static final int SIZE_OF_INT32 = Integer.SIZE / 8; /** uncompressed size of 64 bit integer within IMB framework */ public static final int SIZE_OF_INT64 = Long.SIZE / 8; /** uncompressed size of single within IMB framework */ public static final int SIZE_OF_SINGLE = Float.SIZE / 8; /** uncompressed size of double within IMB framework */ public static final int SIZE_OF_DOUBLE = Double.SIZE / 8; /** * Retrieve reference to internal byte buffer * @return reference to internal byte buffer */ public byte[] getBuffer() { return this.fBuffer; } /** Clear the byte buffer to zero length and reset all cursors */ public void clear() { setLength(0); this.fReadCursor = 0; this.fPrepareCursor = 0; this.fWriteCursor = 0; } /** Clear the byte buffer to the specified length and reset all cursors. */ public void clear(int aLength) { setLength(aLength); this.fReadCursor = 0; this.fWriteCursor = 0; this.fPrepareCursor = 0; } /** * Check if the byte buffer is empty * @return true if the byte buffer is empty */ public boolean isEmpty() { return this.fBuffer.length == 0; } /** * Retrieve the current reading (cursor) position * @return cursor position for reading: index into byte buffer in bytes starting at 0 */ public int getReadCursor() { return this.fReadCursor; } /** * Retrieve the current writing (cursor) position * @return cursor position for writing: index into byte buffer in bytes starting at 0 */ public int getWriteCursor() { return this.fWriteCursor; } /** Reset the reading cursor */ public void ReadStart() { this.fReadCursor = 0; } /** * Retrieve the bytes that still can be read from the byte buffer * @return bytes available for reading */ public int getReadAvailable() { return this.fBuffer.length - this.fReadCursor; } /** * Read a boolean from the byte buffer * @return boolean read from byte buffer (default is false) */ public boolean readBoolean() { return readBoolean(false); } /** * Read a boolean from the byte buffer * @param aDefaultValue boolean; in case of a read error this value is returned * @return boolean read from byte buffer */ public boolean readBoolean(boolean aDefaultValue) { if (SIZE_OF_BOOLEAN <= getReadAvailable()) return this.fBuffer[this.fReadCursor++] != 0; else return aDefaultValue; } /** * Read a byte from the byte buffer * @return byte read from the byte buffer */ public byte readByte() { return readByte((byte) 0); } /** * Read a byte from the byte buffer * @param aDefaultValue byte; in case of a read error this value is returned * @return byte read from the byte buffer */ public byte readByte(byte aDefaultValue) { if (SIZE_OF_BYTE <= getReadAvailable()) return this.fBuffer[this.fReadCursor++]; else return aDefaultValue; } /** * Read an integer (32 bit) from the byte buffer * @return integer (32 bit) read from the byte buffer */ public int readInt32() { return readInt32(0); } /** * Read an integer (32 bit) from the byte buffer * @param aDefaultValue int; in case of a read error this value is returned * @return integer (32 bit) read from the byte buffer */ public int readInt32(int aDefaultValue) { if (SIZE_OF_INT32 <= getReadAvailable()) { this.fReadCursor += SIZE_OF_INT32; return (int) (this.fBuffer[this.fReadCursor - 4] & 0xFF) + ((int) (this.fBuffer[this.fReadCursor - 3] & 0xFF) << 8) + ((int) (this.fBuffer[this.fReadCursor - 2] & 0xFF) << 16) + ((int) (this.fBuffer[this.fReadCursor - 1] & 0xFF) << 24); } else return aDefaultValue; } /** * Read an integer (64 bit) from the byte buffer * @return integer (64 bit) read from the byte buffer */ public long readInt64() { return readInt64(0); } /** * Read an integer (64 bit) from the byte buffer * @param aDefaultValue long; in case of a read error this value is returned * @return integer (64 bit) read from the byte buffer */ public long readInt64(long aDefaultValue) { if (SIZE_OF_INT64 <= getReadAvailable()) { this.fReadCursor += SIZE_OF_INT64; return (long) (this.fBuffer[this.fReadCursor - 8] & 0xFF) + ((long) (this.fBuffer[this.fReadCursor - 7] & 0xFF) << 8) + ((long) (this.fBuffer[this.fReadCursor - 6] & 0xFF) << 16) + ((long) (this.fBuffer[this.fReadCursor - 5] & 0xFF) << 24) + ((long) (this.fBuffer[this.fReadCursor - 4] & 0xFF) << 32) + ((long) (this.fBuffer[this.fReadCursor - 3] & 0xFF) << 40) + ((long) (this.fBuffer[this.fReadCursor - 2] & 0xFF) << 48) + ((long) (this.fBuffer[this.fReadCursor - 1] & 0xFF) << 56); } else return aDefaultValue; } /** * Read a single float from the byte buffer * @return single float read from the byte buffer */ public float readSingle() { return readSingle(Float.NaN); } /** * Read a single float from the byte buffer * @param aDefaultValue float; in case of a read error this value is returned * @return single float read from the byte buffer */ public float readSingle(float aDefaultValue) { if (SIZE_OF_SINGLE <= getReadAvailable()) { return Float.intBitsToFloat(readInt32(0)); } else return aDefaultValue; } /** * Read a double float from the byte buffer * @return double float read from the byte buffer */ public double readDouble() { return readDouble(Double.NaN); } /** * Read a double float from the byte buffer * @param aDefaultValue double; in case of a read error this value is returned * @return double float read from the byte buffer */ public double readDouble(double aDefaultValue) { if (SIZE_OF_DOUBLE <= getReadAvailable()) { long res = readInt64(0); return Double.longBitsToDouble(res); } else return aDefaultValue; } /** * Read a string from the byte buffer. The string is converted from UTF-8 to a standard string. * @return the string read from the byte buffer */ public String readString() { return readString(""); } /** * Read a string from the byte buffer. The string is converted from UTF-8 to a standard string. * @param aDefaultValue String; in case of a read error this value is returned * @return the string read from the byte buffer */ public String readString(String aDefaultValue) { int len = readInt32(-1); if ((len != -1) && (len <= getReadAvailable())) { if (len > 0) { this.fReadCursor += len; return new String(this.fBuffer, this.fReadCursor - len, len, Charset.forName("UTF-8")); } else return ""; } else return aDefaultValue; } // read size and data and store as a whole WITHOUT size (size=length buffer) /** * Read new byte buffer contents from this byte buffer. Decode a size prefixed number of bytes from the byte buffer into a * newly created byte buffer. The size part is not transfered to the new byte buffer but used as the length. * @return a new byte buffer containing the data read from this byte buffer */ public TByteBuffer readByteBuffer() { int len = readInt32(-1); if ((len != -1) && (len <= getReadAvailable())) { this.fReadCursor += len; return new TByteBuffer(Arrays.copyOfRange(this.fBuffer, this.fReadCursor - len, this.fReadCursor)); } else return null; } // class version of simple read functions (auto type detect by overloading) /** * Read a Boolean object from the this byte buffer. * @param aValue the Boolean to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Boolean aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_BOOLEAN) // throw new IndexOutOfBoundsException(); // aValue = fBuffer[fReadCursor++] != 0; // } /** * Read a Byte object from the this byte buffer. * @param aValue the Byte to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Byte aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_BYTE) // throw new IndexOutOfBoundsException(); // aValue = fBuffer[fReadCursor++]; // } /** * Read an Integer object from the this byte buffer. * @param aValue the Integer to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Integer aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_INT32) // throw new IndexOutOfBoundsException(); // fReadCursor += SIZE_OF_INT32; // aValue = (fBuffer[fReadCursor - 4] & 0xFF) + ((fBuffer[fReadCursor - 3] & 0xFF) << 8) // + ((fBuffer[fReadCursor - 2] & 0xFF) << 16) + ((fBuffer[fReadCursor - 1] & 0xFF) << 24); // } /** * Read a Long object from the this byte buffer. * @param aValue the Long to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Long aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_INT64) // throw new IndexOutOfBoundsException(); // fReadCursor += SIZE_OF_INT64; // aValue = (long)(fBuffer[fReadCursor - 8] & 0xFF) + ((fBuffer[fReadCursor - 7] & 0xFF) << 8) // + ((fBuffer[fReadCursor - 6] & 0xFF) << 16) + ((fBuffer[fReadCursor - 5] & 0xFF) << 24) // + ((fBuffer[fReadCursor - 4] & 0xFF) << 32) + ((fBuffer[fReadCursor - 3] & 0xFF) << 40) // + ((fBuffer[fReadCursor - 2] & 0xFF) << 48) + ((fBuffer[fReadCursor - 1] & 0xFF) << 56); // } /** * Read a Float object from the this byte buffer. * @param aValue the Float to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Float aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_SINGLE) // throw new IndexOutOfBoundsException(); // aValue = Float.intBitsToFloat(readInt32(0)); // } /** * Read a Double object from the this byte buffer. * @param aValue the Double to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(Double aValue) throws IndexOutOfBoundsException { // if (getReadAvailable() < SIZE_OF_DOUBLE) // throw new IndexOutOfBoundsException(); // aValue = Double.longBitsToDouble(readInt64(0)); // } /** * Read a String object from the this byte buffer. * @param aValue the String to be read from this byte buffer * @throws IndexOutOfBoundsException when the value could not be read from this byte buffer */ // public void read(String aValue) throws IndexOutOfBoundsException { // Integer len = new Integer(0); // read(len); // if (len > 0) // { // if (getReadAvailable() < len) // throw new IndexOutOfBoundsException(); // fReadCursor += len; // aValue = new String(fBuffer, fReadCursor - len, len, Charset.forName("UTF-8")); // } // else // aValue = ""; // } /** * Read size and data and store as a whole WITHOUT size (size=length buffer) * @param aValue TByteBuffer; byte buffer to store the read data in */ public void readByteBuffer(TByteBuffer aValue) { Integer len = new Integer(0); len = readInt32(); if (len > 0) { aValue.setLength(len); this.fReadCursor += len; for (int i = 0; i < len; i++) aValue.fBuffer[i] = this.fBuffer[this.fReadCursor + i]; } else aValue.clear(); } // QRead (no checking) // Read a boolean from the byte buffer aDefaultValue in case of a read error this value is returned return boolean read from // byte buffer /** * Read a boolean from the byte buffer without any checks * @return the boolean read from the byte buffer */ public boolean qReadBoolean() { return this.fBuffer[this.fReadCursor++] != 0; } /** * Read a byte from the byte buffer without any checks * @return the byte read from the byte buffer */ public byte qReadByte() { return this.fBuffer[this.fReadCursor++]; } /** * Read an 32 bit integer from the byte buffer without any checks * @return the 32 bit integer read from the byte buffer */ public int qReadInt32() { this.fReadCursor += SIZE_OF_INT32; return (int) (this.fBuffer[this.fReadCursor - 4] & 0xFF) + ((int) (this.fBuffer[this.fReadCursor - 3] & 0xFF) << 8) + ((int) (this.fBuffer[this.fReadCursor - 1] & 0xFF) << 16) + ((int) (this.fBuffer[this.fReadCursor - 1] & 0xFF) << 24); } /** * Read an 64 bit integer from the byte buffer without any checks * @return the 64 bit integer read from the byte buffer */ public long qReadInt64() { this.fReadCursor += SIZE_OF_INT64; return (long) (this.fBuffer[this.fReadCursor - 8] & 0xFF) + ((long) (this.fBuffer[this.fReadCursor - 7] & 0xFF) << 8) + ((long) (this.fBuffer[this.fReadCursor - 6] & 0xFF) << 16) + ((long) (this.fBuffer[this.fReadCursor - 5] & 0xFF) << 24) + ((long) (this.fBuffer[this.fReadCursor - 4] & 0xFF) << 32) + ((long) (this.fBuffer[this.fReadCursor - 3] & 0xFF) << 40) + ((long) (this.fBuffer[this.fReadCursor - 2] & 0xFF) << 48) + ((long) (this.fBuffer[this.fReadCursor - 1] & 0xFF) << 56); } /** * Read a single (32 bit float) from the byte buffer without any checks * @return the single (32 bit float) read from the byte buffer */ public float qReadSingle() { // size of float = size of int (int32) return Float.intBitsToFloat(readInt32(0)); } /** * Read a double from the byte buffer without any checks * @return the double read from the byte buffer */ public double qReadDouble(double aDefaultValue) { // size of double = size of long (int64) return Double.longBitsToDouble(readInt64(0)); } /** * Read a string from the byte buffer without any checks * @return the string read from the byte buffer */ public String qReadString(String aDefaultValue) { int len = qReadInt32(); if (len > 0) { this.fReadCursor += len; return new String(this.fBuffer, this.fReadCursor - len, len, Charset.forName("UTF-8")); } else return ""; } /** * Read a byte buffer this byte buffer without any checks Read size and data and store as a whole WITHOUT size (size=length * buffer) * @return the new byte buffer read from the byte buffer */ public TByteBuffer qReadByteBuffer() { int len = qReadInt32(); this.fReadCursor += len; return new TByteBuffer(Arrays.copyOfRange(this.fBuffer, this.fReadCursor - len, this.fReadCursor)); } /** * Read all data available from the read cursor in this byte buffer to a newly created byte buffer * @return a newly created byte buffer that contains all data read */ public TByteBuffer readRestToByteBuffer() { return new TByteBuffer(readRest()); } /** * Read all data available from the read cursor * @return a byte array containing all data read */ public byte[] readRest() { return Arrays.copyOfRange(this.fBuffer, this.fReadCursor, this.fReadCursor + getReadAvailable()); } /** * Skip the specified amount of bytes for reading Advances the read cursor the specified amount of bytes * @param aValueSize int; number of bytes to skip for reading */ public void skipReading(int aValueSize) { this.fReadCursor += aValueSize; } // peek type result /** * Read a boolean from the byte buffer at an offset to the read cursor without advancing the read cursor. If the value could * not be read the default 'false' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the boolean * @return the value read from the byte buffer at the specified offset */ public boolean peekBoolean(int aOffset) { return peekBoolean(aOffset, false); } /** * Read a boolean from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the boolean * @param aDefaultValue boolean; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public boolean peekBoolean(int aOffset, boolean aDefaultValue) { if (SIZE_OF_BOOLEAN + aOffset <= getReadAvailable()) return this.fBuffer[this.fReadCursor + aOffset] != 0; else return aDefaultValue; } /** * Read a byte from the byte buffer at an offset to the read cursor without advancing the read cursor. If the value could * not be read the default '0' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the byte * @return the value read from the byte buffer at the specified offset */ public byte peekByte(int aOffset) { return peekByte(aOffset, (byte) 0); } /** * Read a byte from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the byte * @param aDefaultValue byte; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public byte peekByte(int aOffset, byte aDefaultValue) { if (SIZE_OF_BYTE + aOffset <= getReadAvailable()) return this.fBuffer[this.fReadCursor + aOffset]; else return aDefaultValue; } /** * Read an 32 bit integer from the byte buffer at an offset to the read cursor without advancing the read cursor. If the * value could not be read the default '0' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the integer * @return the value read from the byte buffer at the specified offset */ public int peekInt32(int aOffset) { return peekInt32(aOffset, 0); } /** * Read an 32 bit integer from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the integer * @param aDefaultValue int; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public int peekInt32(int aOffset, int aDefaultValue) { if (SIZE_OF_INT32 + aOffset <= getReadAvailable()) { return (int) (this.fBuffer[this.fReadCursor + aOffset] & 0xFF) + ((int) (this.fBuffer[this.fReadCursor + aOffset + 1] & 0xFF) << 8) + ((int) (this.fBuffer[this.fReadCursor + aOffset + 2] & 0xFF) << 16) + ((int) (this.fBuffer[this.fReadCursor + aOffset + 3] & 0xFF) << 24); } else return aDefaultValue; } /** * Read an 64 bit integer (long) from the byte buffer at an offset to the read cursor without advancing the read cursor. If * the value could not be read the default '0' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the long * @return the value read from the byte buffer at the specified offset */ public long peekInt64(int aOffset) { return peekInt64(aOffset, 0); } /** * Read an 64 bit integer (long) from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the long * @param aDefaultValue long; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public long peekInt64(int aOffset, long aDefaultValue) { if (SIZE_OF_INT64 + aOffset <= getReadAvailable()) return (long) (this.fBuffer[this.fReadCursor + aOffset] & 0xFF) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 1] & 0xFF) << 8) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 2] & 0xFF) << 16) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 3] & 0xFF) << 24) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 4] & 0xFF) << 32) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 5] & 0xFF) << 40) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 6] & 0xFF) << 48) + ((long) (this.fBuffer[this.fReadCursor + aOffset + 7] & 0xFF) << 56); else return aDefaultValue; } /** * Read a single (32 bit float) from the byte buffer at an offset to the read cursor without advancing the read cursor. If * the value could not be read the default 'NaN' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the float * @return the value read from the byte buffer at the specified offset */ public float peekSingle(int aOffset) { return peekSingle(aOffset, Float.NaN); } /** * Read a single (32 bit float) from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the float * @param aDefaultValue float; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public float peekSingle(int aOffset, float aDefaultValue) { if (SIZE_OF_SINGLE + aOffset <= getReadAvailable()) return Float.intBitsToFloat(peekInt32(aOffset, 0)); else return aDefaultValue; } /** * Read a double from the byte buffer at an offset to the read cursor without advancing the read cursor. If the value could * not be read the default 'NaN' is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the double * @return the value read from the byte buffer at the specified offset */ public double peekDouble(int aOffset) { return peekDouble(aOffset, Double.NaN); } /** * Read a double from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the double * @param aDefaultValue double; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public double peekDouble(int aOffset, double aDefaultValue) { if (SIZE_OF_DOUBLE + aOffset <= getReadAvailable()) return Double.longBitsToDouble(peekInt64(aOffset, 0)); else return aDefaultValue; } /** * Read a string from the byte buffer at an offset to the read cursor without advancing the read cursor. If the value could * not be read the default "" is returned. * @param aOffset int; 0-based offset to the read cursor to peek at for the string * @return the value read from the byte buffer at the specified offset */ public String peekString(int aOffset) { return peekString(aOffset, ""); } /** * Read a string from the byte buffer at an offset to the read cursor without advancing the read cursor. * @param aOffset int; 0-based offset to the read cursor to peek at for the string * @param aDefaultValue String; if the value could not be read this default is returned * @return the value read from the byte buffer at the specified offset */ public String peekString(int aOffset, String aDefaultValue) { int len = peekInt32(aOffset, -1); if (len >= 0) { if (len + aOffset <= getReadAvailable()) { return new String(this.fBuffer, this.fReadCursor + aOffset, len, Charset.forName("UTF-8")); } else return aDefaultValue; } else return aDefaultValue; } /** * Compare the contents of this byte buffer at the read cursor to a given byte array * @param aValue byte[]; byte array to compare with this byte buffer * @param aOffset int; offset to the read cursor to start comparing * @return if the byte buffer data equals the byte array contents true is returned */ public boolean compare(byte[] aValue, int aOffset) { if (aOffset + aValue.length <= getReadAvailable()) { for (int i = 0; i < aValue.length; i++) { if (this.fBuffer[aOffset + this.fReadCursor + i] != aValue[i]) return false; } return true; } else return false; } /** * Shift all bytes in the byte buffer to the left and insert a new byte to the right (end of byte buffer) * @param aRightByte byte; the byte to insert at the right side of the byte buffer after the shift */ public void shiftLeftOneByte(byte aRightByte) { for (int i = 0; i < this.fBuffer.length - 1; i++) this.fBuffer[i] = this.fBuffer[i + 1]; this.fBuffer[this.fBuffer.length - 1] = aRightByte; } /** * Start prepare sequence. This should be called before prepare calls. Resets the prepare cursor to the current writing * cursor position. * @return the new prepare cursor position. This can be used to calls of writeStart to reuse a byte buffer with partial * deviating data. */ public int prepareStart() { this.fPrepareCursor = this.fWriteCursor; return this.fPrepareCursor; } /** * Prepares the byte buffer for later writing of a boolean. Advances the prepare cursor for the correct amount of bytes to * write a boolean. * @param aValue boolean; the boolean to be written later in a call to qWrite */ public void prepare(boolean aValue) { this.fPrepareCursor += SIZE_OF_BOOLEAN; } /** * Prepares the byte buffer for later writing of a byte. Advances the prepare cursor for the correct amount of bytes to * write a byte. * @param aValue byte; the byte to be written later in a call to qWrite */ public void prepare(byte aValue) { this.fPrepareCursor += SIZE_OF_BYTE; } /** * Prepares the byte buffer for later writing of an 32 bit integer. Advances the prepare cursor for the correct amount of * bytes to write an 32 bit integer. * @param aValue int; the 32 bit integer to be written later in a call to qWrite */ public void prepare(int aValue) { this.fPrepareCursor += SIZE_OF_INT32; } /** * Prepares the byte buffer for later writing of an 64 bit integer (long). Advances the prepare cursor for the correct * amount of bytes to write an 64 bit integer (long). * @param aValue long; the 64 bit integer (long) to be written later in a call to qWrite */ public void prepare(long aValue) { this.fPrepareCursor += SIZE_OF_INT64; } /** * Prepares the byte buffer for later writing of a 32 bit single (float). Advances the prepare cursor for the correct amount * of bytes to write a 32 bit single (float). * @param aValue float; the 32 bit single (float) to be written later in a call to qWrite */ public void prepare(float aValue) { this.fPrepareCursor += SIZE_OF_SINGLE; } /** * Prepares the byte buffer for later writing of a double. Advances the prepare cursor for the correct amount of bytes to * write a double. * @param aValue double; the double to be written later in a call to qWrite */ public void prepare(double aValue) { this.fPrepareCursor += SIZE_OF_DOUBLE; } /** * Prepares the byte buffer for later writing of a string. Advances the prepare cursor for the correct amount of bytes to * write a string (including its size). * @param aValue String; the string to be written later in a call to qWrite */ public void prepare(String aValue) { this.fPrepareCursor += SIZE_OF_INT32 + aValue.getBytes(Charset.forName("UTF-8")).length; } /** * Prepares the byte buffer for later writing of a byte array (without size). Advances the prepare cursor for the correct * amount of bytes to write a byte array (without size). * @param aValue byte[]; the byte array to be written later in a call to qWrite */ public void prepare(byte[] aValue) { this.fPrepareCursor += aValue.length; } /** * Prepares the byte buffer for later writing of an other byte buffers readable data. Advances the prepare cursor for the * correct amount of bytes to write a byte buffers readable data. * @param aValue TByteBuffer; the byte buffers readable data to be written later in a call to qWrite */ public void prepare(TByteBuffer aValue) { this.fPrepareCursor += SIZE_OF_INT32 + aValue.getReadAvailable(); } /** * Prepares the byte buffer for later writing of the specified number of bytes. Advances the prepare cursor for the correct * amount of bytes to write the specified number of bytes. * @param aValueSize int; the number of bytes to be written later in a call to qWrite */ public int prepareSize(int aValueSize) { int res = this.fPrepareCursor; this.fPrepareCursor += aValueSize; return res; } /** * Adjusts the length of the byte buffer to accommodate at a minimum all prepared data */ public void prepareApply() { if (this.fBuffer.length < this.fPrepareCursor) setLength(this.fPrepareCursor); } /** * Adjusts the length of the byte buffer to accommodate exactly all prepared data */ public void prepareApplyAndTrim() { if (this.fBuffer.length != this.fPrepareCursor) setLength(this.fPrepareCursor); } /** * Start writing at the specified 0-based index. Resets the write cursor to the specified 0-based index * @param aIndex int; the new value for the write cursor */ public void writeStart(int aIndex) { this.fWriteCursor = aIndex; } /** * Returns the room still available in the byte buffer to write data to without reallocating memory. * @return the room still left in the byte buffer to write */ public int getwriteAvailable() { return this.fBuffer.length - this.fWriteCursor; } /** * Write the specified boolean to the byte buffer. Buffer space is allocated if needed. * @param aValue boolean; the boolean to be written to the byte buffer */ public void write(boolean aValue) { if (SIZE_OF_BOOLEAN > getwriteAvailable()) setLength(this.fWriteCursor + SIZE_OF_BOOLEAN); this.fBuffer[this.fWriteCursor] = (aValue) ? (byte) -1 : (byte) 0; this.fWriteCursor += SIZE_OF_BOOLEAN; } /** * Write the specified byte to the byte buffer. Buffer space is allocated if needed. * @param aValue byte; the byte to be written to the byte buffer */ public void write(byte aValue) { if (SIZE_OF_BYTE > getwriteAvailable()) setLength(this.fWriteCursor + SIZE_OF_BYTE); this.fBuffer[this.fWriteCursor] = aValue; this.fWriteCursor += SIZE_OF_BYTE; } /** * Write the specified 32 bit integer to the byte buffer. Buffer space is allocated if needed. * @param aValue int; the 32 bit integer to be written to the byte buffer */ public void write(int aValue) { if (SIZE_OF_INT32 > getwriteAvailable()) setLength(this.fWriteCursor + SIZE_OF_INT32); this.fBuffer[this.fWriteCursor] = (byte) (aValue & 0xFF); this.fBuffer[this.fWriteCursor + 1] = (byte) ((aValue >> 8) & 0xFF); this.fBuffer[this.fWriteCursor + 2] = (byte) ((aValue >> 16) & 0xFF); this.fBuffer[this.fWriteCursor + 3] = (byte) ((aValue >> 24) & 0xFF); this.fWriteCursor += SIZE_OF_INT32; } /** * Write the specified 64 bit integer (long) to the byte buffer. Buffer space is allocated if needed. * @param aValue long; the 64 bit integer (long) to be written to the byte buffer */ public void write(long aValue) { if (SIZE_OF_INT64 > getwriteAvailable()) setLength(this.fWriteCursor + SIZE_OF_INT64); this.fBuffer[this.fWriteCursor] = (byte) (aValue & 0xFF); this.fBuffer[this.fWriteCursor + 1] = (byte) ((aValue >> 8) & 0xFF); this.fBuffer[this.fWriteCursor + 2] = (byte) ((aValue >> 16) & 0xFF); this.fBuffer[this.fWriteCursor + 3] = (byte) ((aValue >> 24) & 0xFF); this.fBuffer[this.fWriteCursor + 4] = (byte) ((aValue >> 32) & 0xFF); this.fBuffer[this.fWriteCursor + 5] = (byte) ((aValue >> 40) & 0xFF); this.fBuffer[this.fWriteCursor + 6] = (byte) ((aValue >> 48) & 0xFF); this.fBuffer[this.fWriteCursor + 7] = (byte) ((aValue >> 56) & 0xFF); this.fWriteCursor += SIZE_OF_INT64; } /** * Write the specified single (float) to the byte buffer. Buffer space is allocated if needed. * @param aValue float; the single (float) to be written to the byte buffer */ public void write(float aValue) { write(Float.floatToIntBits(aValue)); } /** * Write the specified double to the byte buffer. Buffer space is allocated if needed. * @param aValue double; the double to be written to the byte buffer */ public void write(double aValue) { write(Double.doubleToLongBits(aValue)); } /** * Write the specified string to the byte buffer in UTF-8 format. Buffer space is allocated if needed. * @param aValue String; the string to be written to the byte buffer */ public void write(String aValue) { byte[] s = aValue.getBytes(Charset.forName("UTF-8")); int len = s.length; if (SIZE_OF_INT32 + len > getwriteAvailable()) setLength(this.fWriteCursor + SIZE_OF_INT32 + len); // first write size write(len); // write content for (int i = 0; i < len; i++) this.fBuffer[this.fWriteCursor + i] = s[i]; this.fWriteCursor += len; } /** * Write the specified byte array WITHOUT the size to the byte buffer. Buffer space is allocated if needed. * @param aValue byte[]; the byte buffer to be written to the byte buffer */ public void write(byte[] aValue) { if (aValue.length > getwriteAvailable()) setLength(this.fWriteCursor + aValue.length); for (int i = 0; i < aValue.length; i++) this.fBuffer[this.fWriteCursor + i] = aValue[i]; this.fWriteCursor += aValue.length; } // write all readable data WITH size /** * Write the readable data in the specified byte buffer to this byte buffer. Buffer space is allocated if needed. * @param aValue TByteBuffer; the byte buffer who's readable data is to be written to this byte buffer */ public void write(TByteBuffer aValue) { write(aValue.fBuffer.length); write(aValue.fBuffer); } // QWrite (no room checking) /** * write a boolean to the buffer; the QWrite methods do not check for room in the buffer * @param aValue boolean; the boolean value to be written to the buffer */ public void qWrite(boolean aValue) { this.fBuffer[this.fWriteCursor] = (aValue) ? (byte) -1 : (byte) 0; this.fWriteCursor += SIZE_OF_BOOLEAN; } /** * write a single byte to the buffer; the QWrite methods do not check for room in the buffer * @param aValue byte; the byte value to be written to the buffer */ public void qWrite(byte aValue) { this.fBuffer[this.fWriteCursor] = aValue; this.fWriteCursor += SIZE_OF_BYTE; } /** * write a single integer (32 bit) to the buffer; the QWrite methods do not check for room in the buffer * @param aValue int; the integer (32 bit) value to be written to the buffer */ public void qWrite(int aValue) { this.fBuffer[this.fWriteCursor] = (byte) (aValue & 0xFF); this.fBuffer[this.fWriteCursor + 1] = (byte) ((aValue >> 8) & 0xFF); this.fBuffer[this.fWriteCursor + 2] = (byte) ((aValue >> 16) & 0xFF); this.fBuffer[this.fWriteCursor + 3] = (byte) ((aValue >> 24) & 0xFF); this.fWriteCursor += SIZE_OF_INT32; } /** * write a single integer (64 bit) to the buffer; the QWrite methods do not check for room in the buffer * @param aValue long; the integer (64 bit) value to be written to the buffer */ public void qWrite(long aValue) { this.fBuffer[this.fWriteCursor] = (byte) (aValue & 0xFF); this.fBuffer[this.fWriteCursor + 1] = (byte) ((aValue >> 8) & 0xFF); this.fBuffer[this.fWriteCursor + 2] = (byte) ((aValue >> 16) & 0xFF); this.fBuffer[this.fWriteCursor + 3] = (byte) ((aValue >> 24) & 0xFF); this.fBuffer[this.fWriteCursor + 4] = (byte) ((aValue >> 32) & 0xFF); this.fBuffer[this.fWriteCursor + 5] = (byte) ((aValue >> 40) & 0xFF); this.fBuffer[this.fWriteCursor + 6] = (byte) ((aValue >> 48) & 0xFF); this.fBuffer[this.fWriteCursor + 7] = (byte) ((aValue >> 56) & 0xFF); this.fWriteCursor += SIZE_OF_INT64; } /** * write a single float (32 bit) to the buffer; the QWrite methods do not check for room in the buffer * @param aValue float; the float (32 bit) value to be written to the buffer */ public void qWrite(float aValue) { qWrite(Float.floatToIntBits(aValue)); } /** * write a single double (64 bit) to the buffer; the QWrite methods do not check for room in the buffer * @param aValue double; the float (64 bit) value to be written to the buffer */ public void qWrite(double aValue) { qWrite(Double.doubleToLongBits(aValue)); } /** * Write a string to the buffer, prefixed with the size as a 32 bit integer. The characters are UTF-8 encoded, every char is * 1 byte in size The QWrite methods do not check for room in the buffer * @param aValue String; the float (32 bit) value to be written to the buffer */ public void qWrite(String aValue) { byte[] s = aValue.getBytes(Charset.forName("UTF-8")); int len = s.length; // first write size qWrite(len); // write content for (int i = 0; i < len; i++) this.fBuffer[this.fWriteCursor + i] = s[i]; this.fWriteCursor += len; } /** * write array of byte WITHOUT size; the QWrite methods do not check for room in the buffer * @param aValue byte[]; the byte array written to the buffer (without prefixed size) */ public void qWrite(byte[] aValue) { for (int i = 0; i < aValue.length; i++) this.fBuffer[this.fWriteCursor + i] = aValue[i]; this.fWriteCursor += aValue.length; } /** * write, with no checking, all readable data from the given byte buffer to this prefixed WITH size * @param aValue TByteBuffer; readable data in byte buffer to be written to the buffer */ public void qWrite(TByteBuffer aValue) { qWrite(aValue.fBuffer.length); qWrite(aValue.fBuffer); } /** * signal number of bytes directly written to buffer without using class methods update write cursor and return if it fitted * into buffer (should trigger exception ?) * @param aValueSize int; number of bytes directly written into buffer * @return true if all written data fitted into buffer */ public boolean written(int aValueSize) { this.fWriteCursor += aValueSize; return getwriteAvailable() >= 0; } /** * apply written data (trim extra buffer space) */ public void writeApply() { if (this.fWriteCursor != this.fBuffer.length) setLength(this.fWriteCursor); } }