![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.io.ByteArrayBuffer Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Dec 23, 2007
*/
package com.bigdata.io;
import it.unimi.dsi.fastutil.io.RepositionableStream;
import it.unimi.dsi.io.OutputBitStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import com.bigdata.util.BytesUtil;
/**
* A view on a mutable byte[] that may be extended.
*
* Note: The backing byte[] slice always has an {@link IDataRecord#off() offset}
* of ZERO (0) and a {@link IDataRecord#len() length} equal to the capacity of
* the backing byte[]. The {@link IDataRecord#len() length} is automatically
* extended iff the backing buffer is extended.
*
* Note: This class implements {@link OutputStream} so that it may be wrapped by
* an {@link OutputBitStream}. Likewise it implements
* {@link RepositionableStream} so that you can rewind the
* {@link OutputBitStream}.
*
* Note: The concept of {@link #limit()} and {@link #capacity()} are slightly
* different than for {@link ByteBuffer} since the limit and the capacity may be
* transparently extended on write. However, neither the limit nor the capacity
* will be extended on read (this prevents you from reading bad data). The
* relative put operations always set the limit to the position as a
* post-condition of the operation. The absolute get/put operations ignore the
* limit entirely.
*
* This class is NOT thread-safe for mutation. Not only is the underlying
* {@link FixedByteArrayBuffer} not thread-safe for mutation, but the operation
* which replaces the {@link #slice} when the capacity of the backing buffer
* must be extended is not atomic.
*
* @author Bryan Thompson
* @version $Id$
*/
public class ByteArrayBuffer extends OutputStream implements IByteArrayBuffer,
RepositionableStream, LongPacker.IByteBuffer {
private static final transient Logger log = Logger
.getLogger(ByteArrayBuffer.class);
/**
* The default capacity of the buffer.
*/
final public static int DEFAULT_INITIAL_CAPACITY = 128;//1024;
/**
* The backing byte[]. This is re-allocated whenever the capacity of the
* buffer is too small and reused otherwise.
*/
byte[] buf;
/**
* {@inheritDoc} This is re-allocated whenever the capacity of the buffer is
* too small and reused otherwise.
*/
final public byte[] array() {
return buf;
}
/**
* The offset of the slice into the backing byte[] is always zero.
*/
final public int off() {
return 0;
}
/**
* The length of the slice is always the capacity of the backing byte[].
*/
final public int len() {
return buf.length;
}
/**
* Throws exception unless the value is non-negative.
*
* @param msg
* The exception message.
* @param v
* The value.
*
* @return The value.
*
* @exception IllegalArgumentException
* unless the value is non-negative.
*/
protected static int assertNonNegative(final String msg, final int v) {
if (v < 0)
throw new IllegalArgumentException(msg);
return v;
}
/**
* Creates a buffer with an initial capacity of
* {@value #DEFAULT_INITIAL_CAPACITY} bytes. The position and the read limit
* will zero. The capacity of the buffer will be automatically extended as
* required.
*/
public ByteArrayBuffer() {
this(DEFAULT_INITIAL_CAPACITY);
}
/**
* Creates a buffer with the specified initial capacity. The position and
* the read limit will be zero. The capacity of the buffer will be
* automatically extended as required.
*
* @param initialCapacity
* The initial capacity.
*/
public ByteArrayBuffer(final int initialCapacity) {
this(//
0, // pos
0, // readLimit
new byte[assertNonNegative("initialCapacity", initialCapacity)]//
);
}
/**
* Ensure that at least len bytes are free in the buffer starting at
* pos. The {@link #buf buffer} may be grown by this operation but
* it will not be truncated.
*
* This operation is equivalent to
*
*
* ensureCapacity(pos + len)
*
*
* and the latter is often used as an optimization.
*
* @param pos
* The position in the buffer.
* @param len
* The minimum #of free bytes.
*/
final public void ensureFree(final int pos, final int len) {
ensureCapacity(pos + len);
}
/**
* {@inheritDoc}
*
* @todo this can be potentially overridden in a derived class to only copy
* those bytes up to the current position, which would be somewhat
* faster.
*/
/*final*/ public void ensureCapacity(final int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
if (buf == null) {
buf = new byte[capacity];
return;
}
final int overflow = capacity - buf.length;
if (overflow > 0) {
// Extend to at least the target capacity.
final byte[] tmp = new byte[extend(capacity)];
// copy all bytes to the new byte[].
System.arraycopy(buf, 0, tmp, 0, buf.length);
// update the reference to use the new byte[].
buf = tmp;
}
}
final public int capacity() {
return buf == null ? 0 : buf.length;
}
/**
* Return the new capacity for the buffer (default is always large enough
* and will normally double the buffer capacity each time it overflows).
*
* @param required
* The minimum required capacity.
*
* @return The new capacity.
*
* @todo this does not need to be final. also, caller's could set the policy
* including a policy that refuses to extend the capacity.
*/
protected int extend(final int required) {
final int capacity = Math.max(required, capacity() * 2);
if(log.isInfoEnabled())
log.info("Extending buffer to capacity=" + capacity + " bytes.");
return capacity;
}
/**
* Trims the backing byte[] to an exact fit by making a copy of the data in
* the buffer and returns the old byte[]. All bytes between {@link #off()}
* and {@link #limit()} are copied into the exact fit byte[].
*
* Note: A {@link #slice(int, int)} with a view of this
* {@link ByteArrayBuffer} will continue to have a view onto the backing
* buffer associated with this instance. This works because
* {@link SliceImpl#array()} delegates to the outer
* {@link ByteArrayBuffer#array()} method. This makes it possible to
* {@link #trim()} a {@link ByteArrayBuffer} on which you have
* {@link #slice(int, int)}s, while maintaining the validity of those
* slices.
*
* @return The old byte[].
*/
final public byte[] trim() {
final byte[] tmp = buf;
// assert not required - only tests RabaCoder assumptions.
assert limit == pos;
final byte[] a = new byte[limit];
// Note: assumes offset==0, otherwise must reset offset!
assert off() == 0;
// copy onto new buffer.
System.arraycopy(tmp, off(), a, 0, a.length);
// replace buffer.
this.buf = a;
// return the old buffer.
return tmp;
}
/**
* Clears the buffer reference to null
.
*/
final public void clear() {
this.buf = null;
}
/*
* Absolute put/get methods.
*/
final public void put(final int pos, //
final byte[] b) {
put(pos, b, 0, b.length);
}
final public void put(final int pos,//
final byte[] b, final int off, final int len) {
ensureCapacity(pos + len);
System.arraycopy(b, off, buf, pos, len);
}
final public void get(final int srcoff, final byte[] dst) {
get(srcoff, dst, 0/* dstoff */, dst.length);
}
final public void get(final int srcoff, final byte[] dst, final int dstoff,
final int dstlen) {
System.arraycopy(buf, srcoff, dst, dstoff, dstlen);
}
final public void putByte(int pos, final byte v) {
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos] = v;
}
final public byte getByte(int pos) {
return buf[pos];
}
final public void putShort(int pos, final short v) {
if (pos + 2 > buf.length)
ensureCapacity(pos + 2);
// big-endian
buf[pos++] = (byte) (v >>> 8);
buf[pos ] = (byte) (v >>> 0);
}
final public short getShort(int pos) {
short v = 0;
// big-endian.
v += (0xff & buf[pos++]) << 8;
v += (0xff & buf[pos ]) << 0;
return v;
}
final public void putInt(int pos, final int v){
if (pos + 4 > buf.length)
ensureCapacity(pos + 4);
buf[pos++] = (byte) (v >>> 24);
buf[pos++] = (byte) (v >>> 16);
buf[pos++] = (byte) (v >>> 8);
buf[pos ] = (byte) (v >>> 0);
}
final public int getInt( int pos ) {
int v = 0;
// big-endian.
v += (0xff & buf[pos++]) << 24;
v += (0xff & buf[pos++]) << 16;
v += (0xff & buf[pos++]) << 8;
v += (0xff & buf[pos ]) << 0;
return v;
}
final public void putFloat(int pos, final float f) {
putInt( pos, Float.floatToIntBits(f) );
}
final public float getFloat( int pos ) {
return Float.intBitsToFloat( getInt( pos ) );
}
final public void putLong( int pos, final long v) {
if (pos + 8 > buf.length)
ensureCapacity(pos + 8);
// big-endian.
buf[pos++] = (byte) (v >>> 56);
buf[pos++] = (byte) (v >>> 48);
buf[pos++] = (byte) (v >>> 40);
buf[pos++] = (byte) (v >>> 32);
buf[pos++] = (byte) (v >>> 24);
buf[pos++] = (byte) (v >>> 16);
buf[pos++] = (byte) (v >>> 8);
buf[pos ] = (byte) (v >>> 0);
}
final public void putDouble( int pos, final double d) {
putLong( pos, Double.doubleToLongBits(d) );
}
final public long getLong( int pos ) {
long v = 0L;
// big-endian.
v += (0xffL & buf[pos++]) << 56;
v += (0xffL & buf[pos++]) << 48;
v += (0xffL & buf[pos++]) << 40;
v += (0xffL & buf[pos++]) << 32;
v += (0xffL & buf[pos++]) << 24;
v += (0xffL & buf[pos++]) << 16;
v += (0xffL & buf[pos++]) << 8;
v += (0xffL & buf[pos ]) << 0;
return v;
}
final public double getDouble( int pos ) {
return Double.longBitsToDouble( getLong( pos ) );
}
/**
* Return a copy of the data written on the buffer (the bytes in [0:pos]).
*
* @return A new array containing data in the buffer.
*
* @see #asByteBuffer()
*
* @todo this returns the data in [0:pos], which is essentially the data
* written on the buffer using relative put operations but without
* requiring a {@link #flip()} to set the pos to zero while leaving
* the read limit alone. that is at odds with the rest of the relative
* api but there is a lot of use of this method already. perhaps it
* can be moved to the {@link DataOutputBuffer} where it would share
* semantics with {@link ByteArrayOutputStream#toByteArray()}?
*/
final public byte[] toByteArray() {
final byte[] tmp = new byte[this.pos];
System.arraycopy(buf, 0, tmp, 0, this.pos);
return tmp;
}
/*
* Sequential operations (position, limit, capacity)
*/
/**
* A non-negative integer specifying the #of bytes of data in the buffer
* that contain valid data starting from position zero(0).
*/
int pos = 0;
/**
* The read limit (there is no write limit on the buffer since the capacity
* will be automatically extended on overflow). The read limit is always
* incremented by an append on the end of the buffer.
*
* @todo review absolute writes on the buffer. they are the underlying write
* operation in all cases, right?
*/
int limit;
/**
* An optional mark to which the buffer can be rewound and 0
* if the mark has never been set.
*/
private int mark = 0;
/**
* Create a new buffer backed by the given array. The initial capacity will
* be the size of the given byte[]. The mark will be zero. The capacity of
* the buffer will be automatically extended as required.
*
* Note: The buffer reference is used directly rather than making a copy of
* the data.
*
* @param pos
* The initial {@link #pos() position}.
* @param limit
* The initial {@link #limit()}.
* @param buf
* The backing byte[].
*/
public ByteArrayBuffer(final int pos, final int limit, final byte[] buf) {
if (pos < 0)
throw new IllegalArgumentException("pos<0");
if (pos > limit)
throw new IllegalArgumentException("pos>limit");
if (buf == null)
throw new IllegalArgumentException("buf");
if (limit > buf.length)
throw new IllegalArgumentException("limit>buf.length");
this.buf = buf;
this.pos = pos;
this.limit = limit;
}
/**
* The #of bytes remaining in the buffer for relative read operations (limit -
* pos).
*/
final public int remaining() {
return limit - pos;
}
/**
* The current position in the buffer.
*/
final public int pos() {
return pos;
}
/**
* Set the position in the buffer (does not change the limit).
*
* @param pos
* The new position, must be in [0:{@link #capacity()}).
*
* @return The old position.
*
* @throws IllegalArgumentException
* if pos is less than ZERO (0).
* @throws IllegalArgumentException
* if pos is greater than or equal to the current
* capacity of the buffer.
*/
final public int pos(final int pos) {
if (pos < 0 || pos >= buf.length)
throw new IllegalArgumentException("pos=" + pos + ", capacity="
+ buf.length);
int v = this.pos;
this.pos = pos;
return v;
}
/**
* The read limit (there is no write limit on the buffer since the capacity
* will be automatically extended on overflow).
*/
final public int limit() {
return limit;
}
/**
* Sets the position to zero but leaves the read limit at the old position.
* After invoking this method you can use relative get methods to read all
* data up to the read limit.
*/
final public void flip() {
/*
* @todo remove this assertion -- it's here to be triggered if relative
* put methods fail to update the limit with the position, but it is not
* an absolute requirement since you can change the position
* independently of the limit using pos().
*/
assert limit == pos : "pos="+pos+", limit="+limit;
pos = 0;
}
/**
* Prepares the buffer for new data by resetting the position and limit to
* zero.
*
* @return This buffer.
*/
public ByteArrayBuffer reset() {
pos = limit = 0;
return this;
}
final public void ensureFree(final int len) {
ensureCapacity(this.pos + len);
}
/**
* Sets the mark.
*
* @return the old mark (initially zero).
*/
final public int mark() {
final int tmp = mark;
mark = pos;
return tmp;
}
/**
* Rewinds the buffer to the mark. Does not change the limit.
*
* @return The new {@link #pos()}.
*/
final public int rewind() {
pos = mark;
return pos;
}
/**
* Relative copy of data into this buffer.
*
* @param src
* The source.
*
* @return The #of bytes copied.
*/
final public int copy(final ByteBuffer src) {
final int n = src.remaining();
if (n > 0) {
ensureFree(n);
src.get(buf, pos, n);
this.pos += n;
this.limit = this.pos;
}
return n;
}
/**
* Relative copy data from the current position of the
* source buffer up to its read limit into this buffer.
*
* @param src
* The source buffer.
*
* @return The #of bytes copied.
*
* @see #copyAll(ByteArrayBuffer)
*/
final public int copyRest(final ByteArrayBuffer src) {
final int n = src.remaining();
if (n > 0) {
put(src.buf, src.pos, n);
}
return n;
}
/**
* Relative copy data from the origin (offset ZERO) of the
* source buffer up to its read limit into this buffer.
*
* @param src
* The source buffer.
*
* @return The #of bytes copied.
*
* @see #copyRest(ByteArrayBuffer)
*/
final public int copyAll(final ByteArrayBuffer src) {
final int n = src.limit;
if (n > 0) {
put(src.buf, 0/* offset */, n);
}
return n;
}
/**
* Wraps up a reference to the data in a {@link ByteBuffer} between the
* position and the limit.
*
* @return A {@link ByteBuffer} encapsulating a reference to the data in the
* current buffer. The data will be overwritten if {@link #reset()}
* is invoked followed by any operations that write on the buffer.
*/
final public ByteBuffer asByteBuffer() {
return ByteBuffer.wrap(buf, pos, limit);
}
/**
* Relative method advances the position and the limit by len bytes
* (this simulates a relative put method, but does not write any
* data).
*
* @param len
* The #of bytes to advance (non-negative).
*/
final public void advancePosAndLimit(final int len) {
if (len < 0)
throw new IllegalArgumentException();
ensureCapacity(pos + len);
this.pos += len;
this.limit = this.pos;
}
/**
* Relative put method for writing a byte[] on the buffer.
*
* @param b
* The byte[].
*/
final public void put(final byte[] b) {
put(this.pos/*pos*/, b, 0, b.length);
this.pos += b.length;
this.limit = this.pos;
}
/**
* Relative put method for writing a byte[] on the buffer.
*
* @param b
* The byte[].
* @param off
* The offset of the first byte in b to be written on the
* buffer.
* @param len
* The #of bytes in b to be written on the buffer.
*/
final public void put(final byte[] b, final int off, final int len) {
put(this.pos/*pos*/, b, off, len);
this.pos += len;
this.limit = this.pos;
}
/**
* Relative put method for writing a byte value.
*
* @param v
* The value.
*
* @todo rename as put(byte) to conform with {@link ByteBuffer}?
*/
final public void putByte(final byte v) {
putByte(pos, v);
limit = ++pos;
}
/**
* Relative get method for reading a byte value.
*
* @return The value.
*
* @exception IndexOutOfBoundsException
* if the position is greater than or equal to the limit.
*
* @todo rename as get(byte) to conform with {@link ByteBuffer}?
*/
final public byte getByte() {
if (pos >= limit)
throw new IndexOutOfBoundsException();
final byte v = getByte(pos);
pos++;
return v;
}
final public void putShort(final short v) {
putShort(pos, v);
pos += 2;
limit = pos;
}
final public short getShort() {
if (pos + 2 >= limit)
throw new IndexOutOfBoundsException();
final short v = getShort(pos);
pos += 2;
return v;
}
final public void putInt(final int v) {
putInt(pos, v);
pos += 4;
limit = pos;
}
final public int getInt() {
if (pos + 4 >= limit)
throw new IndexOutOfBoundsException();
final int v = getInt(pos);
pos += 4;
return v;
}
final public void putFloat(final float v) {
putFloat(pos, v);
pos += 4;
limit = pos;
}
final public float getFloat() {
if (pos + 4 >= limit)
throw new IndexOutOfBoundsException();
final float v = getFloat(pos);
pos += 4;
return v;
}
final public void putLong(final long v) {
putLong(pos, v);
pos += 8;
limit = pos;
}
final public long getLong() {
if (pos + 8 >= limit)
throw new IndexOutOfBoundsException();
final long v = getLong(pos);
pos += 8;
return v;
}
final public void putDouble(final double v) {
putDouble(pos, v);
pos += 8;
limit = pos;
}
final public double getDouble() {
if (pos + 8 >= limit)
throw new IndexOutOfBoundsException();
final double v = getDouble(pos);
pos += 8;
return v;
}
/*
* Pack unsigned long integer.
*/
/**
* Packs a non-negative long value into the minimum #of bytes in which the
* value can be represented and writes those bytes onto the buffer.
* The first byte determines whether or not the long value was packed and,
* if packed, how many bytes were required to represent the packed long
* value. When the high bit of the first byte is a one (1), then the long
* value could not be packed and the long value is found by clearing the
* high bit and interpreting the first byte plus the next seven (7) bytes as
* a long. Otherwise the next three (3) bits are interpreted as an unsigned
* integer giving the #of bytes (nbytes) required to represent the packed
* long value. To recover the long value the high nibble is cleared and the
* first byte together with the next nbytes are interpreted as an unsigned
* long value whose leading zero bytes were not written.
*
*
*
* [0|1|2|3|4|5|6|7]
* 1 - - - nbytes = 8, clear high bit and interpret this plus the next 7 bytes as a long.
* 0 1 1 1 nbytes = 7, clear high nibble and interpret this plus the next 6 bytes as a long.
* 0 1 1 0 nbytes = 6, clear high nibble and interpret this plus the next 5 bytes as a long.
* 0 1 0 1 nbytes = 5, clear high nibble and interpret this plus the next 4 bytes as a long.
* 0 1 0 0 nbytes = 4, clear high nibble and interpret this plus the next 3 bytes as a long.
* 0 0 1 1 nbytes = 3, clear high nibble and interpret this plus the next 3 bytes as a long.
* 0 0 1 0 nbytes = 2, clear high nibble and interpret this plus the next byte as a long.
* 0 0 0 1 nbytes = 1, clear high nibble. value is the low nibble.
*
*
*
* @param v The unsigned long value.
*
* @return The #of bytes onto which the unsigned long value was packed.
*/
final public int packLong(final long v) {
return LongPacker.packLong(v, pbuf, this);
}
/**
* Private buffer for packing long integers.
*/
final private byte[] pbuf = new byte[8];
// /**
// * Return the #of non-zero nibbles, counting from the first non-zero nibble
// * in the long value. A value of 0L
is considered to be one
// * nibble for our purposes.
// *
// * @param v
// * The long value.
// *
// * @return The #of nibbles in [1:16].
// */
// static protected final int getNibbleLength( final long v )
// {
// return LongPacker.getNibbleLength(v);
//
//// for( int i=56, j=16; i>=0; i-=8, j-=2 ) {
////
//// if( (0xf0 & (v >> i)) != 0 ) return j;
////
//// if( (0x0f & (v >> i)) != 0 ) return j-1;
////
//// }
////
//// if (v != 0)
//// throw new AssertionError("v=" + v);
////
//// /*
//// * value is zero, which is considered to be one nibble for our purposes.
//// */
////
//// return 1;
////
// }
/*
* Pack unsigned short integer.
*/
/**
* Packs a non-negative short value into one or two bytes and writes them on
* the buffer. A short in [0:127] is packed into one byte. Larger values are
* packed into two bytes. The high bit of the first byte is set if the value
* was packed into two bytes. If the bit is set, clear the high bit, read
* the next byte, and interpret the two bytes as a short value. Otherwise
* interpret the byte as a short value.
*
* @param v
* The unsigned short integer.
*
* @return The #of bytes into which the value was packed.
*/
final public int packShort(final short v) {
/*
* You can only pack non-negative values with this method.
*/
if( v < 0 ) {
throw new IllegalArgumentException( "negative value: v="+v );
}
if( v > 127 ) {
// the value requires two bytes.
if (pos + 2 > buf.length)
ensureCapacity(pos + 2);
buf[pos++] = ( (byte)((0xff & (v >> 8))|0x80) ); // note: set the high bit.
buf[pos++] = ( (byte)(0xff & v) );
limit = pos;
return 2;
} else {
// the value fits in one byte.
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos++] = ( (byte)(0xff & v) );
limit = pos;
return 1;
}
}
// /*
// * unpack unsigned long integer.
// */
//
// /**
// * Unpack a long value from the current buffer position, incrementing the
// * buffer position as a side-effect.
// *
// * @return The long value.
// *
// * @throws IOException
// *
// * @todo unit test for pack/unpack in this class.
// */
// final public long unpackLong() throws IOException {
// if (pos + 1 > limit)
// throw new EOFException();
// int b = buf[pos++];
// int nbytes;
// long l;
// if ((b & 0x80) != 0) {
// // high bit is set.
// nbytes = 8; // use 8 bytes (this one plus the next 7).
// l = b & 0x7f; // clear the high bit - the rest of the byte is the
// // start value.
// } else {
// // high bit is clear.
// nbytes = b >> 4; // nbytes is the upper nibble. (right shift one
// // nibble).
// l = b & 0x0f; // starting value is lower nibble (clear the upper
// // nibble).
// }
// if (pos + nbytes - 1 > limit)
// throw new EOFException();
// for (int i = 1; i < nbytes; i++) {
// // Read the next byte.
// b = buf[pos++];
// // Shift the existing value one byte left and add into the low
// // (unsigned) byte.
// l = (l << 8) + (0xff & b);
// }
// return l;
// }
//
// /*
// * unpack unsigned short integer.
// */
//
// /**
// * Unpack a non-negative short value from the input stream, incrementing
// * the buffer position as a side-effect.
// *
// * @param is
// * The input stream.
// *
// * @return The short value.
// *
// * @throws IOException
// *
// * @todo unit tests for pack/unpack in this class.
// */
// final public short unpackShort() throws IOException {
// if (pos + 1 > limit)
// throw new EOFException();
// short b = (short) buf[pos++];
// short v;
// if ((b & 0x80) != 0) { // high bit is set.
// /*
// * clear the high bit and shift over one byte.
// */
// v = (short) ((b & 0x7f) << 8);
// if (pos+ 1 > limit)
// throw new EOFException();
// b = buf[pos++]; // read the next byte.
// v |= (b & 0xff); // and combine it together with the high byte.
// } else {
// // high bit is clear.
// v = b; // interpret the byte as a short value.
// }
// return (short) v;
// }
/*
* OutputStream integration. Operations all set the read limit to the
* position as a post-condition (they are treated as relative puts).
*/
final public void write(final int b) {
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos++] = (byte) (b & 0xff);
limit = pos;
}
final public void write(final byte[] b) {
write(b, 0, b.length);
}
public ByteArrayBuffer append(final byte b) {
if (pos + 1 > buf.length)
ensureCapacity(pos + 1);
buf[pos++] = b;//(byte) (b & 0xff);
limit = pos;
return this;
}
public ByteArrayBuffer append(final byte[] b) {
return append(b, 0, b.length);
}
final public void write(final byte[] b, final int off, final int len) {
if (len == 0)
return;
ensureFree(len);
System.arraycopy(b, off, buf, this.pos, len);
this.pos += len;
this.limit = this.pos;
}
public ByteArrayBuffer append(final byte[] b, final int off, final int len) {
write(b, off, len);
return this;
}
/*
* RepositionableStream.
*
* Note: This interface allows us to use OutputBitStream and reset the
* position to zero (0L) so that we can reuse the buffer, e.g., to serialize
* nodes in the B+Tree.
*/
public long position() throws IOException {
return (int) pos;
}
public void position(long v) throws IOException {
if (v < 0 || v > buf.length) {
throw new IOException();
}
this.pos = (int) v;
}
final public boolean getBit(final long bitIndex) {
final int byteIndexForBit = BytesUtil.byteIndexForBit(bitIndex);
if (byteIndexForBit > buf.length)
ensureCapacity(byteIndexForBit);
return BytesUtil.getBit(buf, bitIndex);
}
final public boolean setBit(final long bitIndex, final boolean value) {
final int byteIndexForBit = BytesUtil.byteIndexForBit(bitIndex);
if (byteIndexForBit > buf.length)
ensureCapacity(byteIndexForBit);
return BytesUtil.setBit(buf, bitIndex, value);
}
/**
* Skip forward or backward the specified number of bytes.
*
* @param nbytes
* The #of bytes to skip (MAY be negative).
*
* @return The new position.
*/
public int skip(final int nbytes) {
if (pos + nbytes < 0)
throw new IllegalArgumentException();
if (pos + nbytes > capacity())
ensureCapacity(pos + nbytes);
pos += nbytes;
return pos;
}
/**
* Return a slice of the backing buffer. The slice will always reference the
* current backing {@link #array()}, even when the buffer is extended and
* the array reference is replaced. The {@link #pos()} and {@link #limit()}
* are ignored by this method.
*
* @param off
* The starting offset into the backing buffer of the slice.
* @param len
* The length of that slice.
*
* @return The slice.
*/
public AbstractFixedByteArrayBuffer slice(final int off, final int len) {
return new SliceImpl(off, len);
}
/**
* A slice of the outer {@link ByteArrayBuffer}. The slice will always
* reflect the backing {@link #array()} for the instance of the outer class.
*
* @author Bryan
* Thompson
* @version $Id$
*/
private class SliceImpl extends AbstractFixedByteArrayBuffer {
protected SliceImpl(final int off, final int len) {
super(off, len);
}
public String toString() {
return super.toString() + "{off=" + off() + ",len=" + len() + "}";
}
public byte[] array() {
return ByteArrayBuffer.this.array();
}
};
/*
* Note: These methods are not included here because the conflicting
* semantics of a pos()/limit() based buffer and a byte[] slice make it
* impossible to remember which semantics will be applied in any given
* context.
*/
// /**
// */
// public DataInput getInputData() {
//
// return new DataInputBuffer(buf, pos(), pos() + limit());
//
// }
//
// /**
// * Return a bit stream reading on the data between the {@link #pos()} and
// * the {@link #limit()}.
// */
// public InputBitStream getInputBitStream() {
//
// return new InputBitStream(new DataInputBuffer(this),
// 0/* unbuffered */, false/* reflectionTest */);
//
// }
//
// /**
// * Write the data between the {@link #pos()} and the {@link #limit()} onto
// * the stream.
// */
// final public void writeOn(final OutputStream os) throws IOException {
//
// os.write(array(), pos(), pos()+limit());
//
// }
//
// /**
// * Write the data between the {@link #pos()} and the {@link #limit()} onto
// * the stream.
// */
// final public void writeOn(final DataOutput out) throws IOException {
//
// out.write(array(), pos(), pos()+limit());
//
// }
/**
* Return a bit stream which will write on this buffer. The stream will
* begin at the current {@link #pos()}.
*
* @return The bit stream.
*
* @todo Add OBS(byte[],off,len) ctor for faster operations. However, you
* MUST pre-extend the buffer to have sufficient capacity since an
* {@link OutputBitStream} wrapping a byte[] will not auto-extend.
*/
public OutputBitStream getOutputBitStream() {
return new OutputBitStream(this, 0 /* unbuffered! */, false/* reflectionTest */);
}
}