// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.util;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
/**
* An array object which mimics to a certain degree the type casting behavior of pointers from
* other programming languages. The byte order is initially set to little endian.
* Features include:
* <ul>
* <li>Converting between integer types of different sizes</li>
* <li>Setting and modifying a base offset</li>
* <li>Reading/writing data in big endian or little endian byte order</li>
* <li>Static methods for quick array/value conversions</li>
* </ul>
*/
public class DynamicArray
{
/**
* Supported data type sizes.
*/
public static enum ElementType { BYTE, SHORT, INTEGER24, INTEGER, LONG }
private byte[] buffer;
private ElementType elementType;
private int elementSize;
private ByteOrder order;
private int baseOfs;
/**
* Creates a new Array with a buffer of specified size.
* @param size Number of logical elements in the buffer.
* @param type The logical data type of the array.
* @return An array object of the specified logical element type.
*/
public static DynamicArray allocate(int size, ElementType type)
{
return new DynamicArray(size, type);
}
/**
* Creates a new Array by using an existing array.
* @param b The array that will back this object.
* @param type The logical data type of the array.
* @return An array object of the specified logical element type.
*/
public static DynamicArray wrap(byte[] b, ElementType type)
{
return new DynamicArray(b, 0, type);
}
/**
* Creates a new Array by using an existing array.
* @param b The array that will back this object.
* @param ofs The start ofs of the array that backs this object.
* @param type The logical data type of the array.
* @return An array object of the specified logical element type.
*/
public static DynamicArray wrap(byte[] b, int ofs, ElementType type)
{
return new DynamicArray(b, ofs, type);
}
/**
* Reads a byte from the specified buffer
* @param buffer The buffer to read the value from.
* @param offset Buffer offset.
* @return Byte value from buffer or 0 on error.
*/
public static byte getByte(byte[] buffer, int offset)
{
if (buffer != null && offset >= 0 && offset < buffer.length) {
return buffer[offset];
} else
return 0;
}
/**
* Writes a byte into the specified buffer.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param value Value to write.
* @return true if successfull, false otherwise.
*/
public static boolean putByte(byte[] buffer, int offset, byte value)
{
if (buffer != null && offset >= 0 && offset < buffer.length) {
buffer[offset] = value;
return true;
} else
return false;
}
/**
* Returns a byte array representation of the specified byte value.
* @param value The value to convert into a byte array.
* @return The byte array of the specified value.
*/
public static byte[] convertByte(byte value)
{
return new byte[]{value};
}
/**
* Reads a short from the specified buffer in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset.
* @return Short value from buffer or 0 on error.
*/
public static short getShort(byte[] buffer, int offset)
{
if (buffer != null && offset >= 0 && offset + 1 < buffer.length) {
short v = (short)(buffer[offset] & 0xff);
v |= (buffer[offset+1] & 0xff) << 8;
return v;
} else
return 0;
}
/**
* Writes a short into the specified buffer, using little endian byte order.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param value Value to write.
* @return true if successfull, false otherwise.
*/
public static boolean putShort(byte[] buffer, int offset, short value)
{
if (buffer != null && offset >= 0 && offset + 1 < buffer.length) {
buffer[offset] = (byte)(value & 0xff);
buffer[offset+1] = (byte)((value >>> 8) & 0xff);
return true;
} else
return false;
}
/**
* Returns a byte array representation of the specified short value in little endian byte order.
* @param value The value to convert into a byte array.
* @return The byte array of the specified value.
*/
public static byte[] convertShort(short value)
{
return new byte[]{(byte)(value & 0xff),
(byte)((value >>> 8) & 0xff)};
}
/**
* Reads a 24-bit integer from the specified buffer in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset.
* @return 24-bit integer value from buffer or 0 on error.
*/
public static int getInt24(byte[] buffer, int offset)
{
if (buffer != null && offset >= 0 && offset + 2 < buffer.length) {
int v = buffer[offset] & 0xff;
v |= (buffer[offset+1] & 0xff) << 8;
v |= (buffer[offset+2] & 0xff) << 16;
if ((v & 0x800000) != 0) // sign-extending value
v |= 0xff000000;
return v;
} else
return 0;
}
/**
* Writes a 24-bit integer into the specified buffer, using little endian byte order.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param value Value to write.
* @return true if successfull, false otherwise.
*/
public static boolean putInt24(byte[] buffer, int offset, int value)
{
if (buffer != null && offset >= 0 && offset + 2 < buffer.length) {
buffer[offset] = (byte)(value & 0xff);
buffer[offset+1] = (byte)((value >>> 8) & 0xff);
buffer[offset+2] = (byte)((value >>> 16) & 0xff);
return true;
} else
return false;
}
/**
* Returns a byte array representation of the specified 24-bit integer value in little endian byte order.
* @param value The value to convert into a byte array.
* @return The byte array of the specified value.
*/
public static byte[] convertInt24(int value)
{
return new byte[]{(byte)(value & 0xff),
(byte)((value >>> 8) & 0xff),
(byte)((value >>> 16) & 0xff)};
}
/**
* Reads an integer from the specified buffer in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset.
* @return Integer value from buffer or 0 on error.
*/
public static int getInt(byte[] buffer, int offset)
{
if (buffer != null && offset >= 0 && offset + 3 < buffer.length) {
int v = buffer[offset] & 0xff;
v |= (buffer[offset+1] & 0xff) << 8;
v |= (buffer[offset+2] & 0xff) << 16;
v |= (buffer[offset+3] & 0xff) << 24;
return v;
} else
return 0;
}
/**
* Writes an integer into the specified buffer, using little endian byte order.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param value Value to write.
* @return true if successfull, false otherwise.
*/
public static boolean putInt(byte[] buffer, int offset, int value)
{
if (buffer != null && offset >= 0 && offset + 3 < buffer.length) {
buffer[offset] = (byte)(value & 0xff);
buffer[offset+1] = (byte)((value >>> 8) & 0xff);
buffer[offset+2] = (byte)((value >>> 16) & 0xff);
buffer[offset+3] = (byte)((value >>> 24) & 0xff);
return true;
} else
return false;
}
/**
* Returns a byte array representation of the specified integer value in little endian byte order.
* @param value The value to convert into a byte array.
* @return The byte array of the specified value.
*/
public static byte[] convertInt(int value)
{
return new byte[]{(byte)(value & 0xff),
(byte)((value >>> 8) & 0xff),
(byte)((value >>> 16) & 0xff),
(byte)((value >>> 24) & 0xff)};
}
/**
* Reads a long from the specified buffer in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset.
* @return Long value from buffer or 0 on error.
*/
public static long getLong(byte[] buffer, int offset)
{
if (buffer != null && offset >= 0 && offset + 7 < buffer.length) {
long v = buffer[offset] & 0xffL;
v |= (buffer[offset+1] & 0xffL) << 8;
v |= (buffer[offset+2] & 0xffL) << 16;
v |= (buffer[offset+3] & 0xffL) << 24;
v |= (buffer[offset+4] & 0xffL) << 32;
v |= (buffer[offset+5] & 0xffL) << 40;
v |= (buffer[offset+6] & 0xffL) << 48;
v |= (buffer[offset+7] & 0xffL) << 56;
return v;
} else
return 0;
}
/**
* Writes a long into the specified buffer, using little endian byte order.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param value Value to write.
* @return true if successfull, false otherwise.
*/
public static boolean putLong(byte[] buffer, int offset, long value)
{
if (buffer != null && offset >= 0 && offset + 7 < buffer.length) {
buffer[offset] = (byte)(value & 0xffL);
buffer[offset+1] = (byte)((value >>> 8) & 0xffL);
buffer[offset+2] = (byte)((value >>> 16) & 0xffL);
buffer[offset+3] = (byte)((value >>> 24) & 0xffL);
buffer[offset+4] = (byte)((value >>> 32) & 0xffL);
buffer[offset+5] = (byte)((value >>> 40) & 0xffL);
buffer[offset+6] = (byte)((value >>> 48) & 0xffL);
buffer[offset+7] = (byte)((value >>> 56) & 0xffL);
return true;
} else
return false;
}
/**
* Returns a byte array representation of the specified long value in little endian byte order.
* @param value The value to convert into a byte array.
* @return The byte array of the specified value.
*/
public static byte[] convertLong(long value)
{
return new byte[]{(byte)(value & 0xffL),
(byte)((value >>> 8) & 0xffL),
(byte)((value >>> 16) & 0xffL),
(byte)((value >>> 24) & 0xffL),
(byte)((value >>> 32) & 0xffL),
(byte)((value >>> 40) & 0xffL),
(byte)((value >>> 48) & 0xffL),
(byte)((value >>> 56) & 0xffL)};
}
/**
* Converts a byte sequence of a buffer into a string using the "windows-1252" charset.
* @param buffer The buffer to read the byte sequence from.
* @param offset Buffer offset.
* @param length The number of bytes to convert.
* @return A string representation of the byte sequence or an empty string on error.
*/
public static String getString(byte[] buffer, int offset, int length)
{
return getString(buffer, offset, length, Misc.CHARSET_DEFAULT);
}
/**
* Converts a byte sequence of a buffer into a string.
* @param buffer The buffer to read the byte sequence from.
* @param offset Buffer offset.
* @param length The number of bytes to convert.
* @param charset The charset to be used to decode the bytes.
* Specify {@code null} to use "windows-1252" charset.
* @return A string representation of the byte sequence or an empty string on error.
*/
public static String getString(byte[] buffer, int offset, int length, Charset charset)
{
if (buffer != null && offset >= 0 && offset < buffer.length && length >= 0) {
if (charset == null) {
charset = Misc.CHARSET_DEFAULT;
}
if (offset + length > buffer.length)
length = buffer.length - offset;
for (int i = 0; i < length; i++) {
if (buffer[offset+i] == 0x00) {
length = i;
break;
}
}
// XXX: Work around a bug in Java 6: String(buffer, ofs, len, charset) creates an internal copy of the whole buffer before proceeding
byte[] temp = new byte[length];
System.arraycopy(buffer, offset, temp, 0, length);
return new String(temp, charset);
}
return new String();
}
/**
* Writes a text string into the specified buffer using the "windows-1252" charset for conversion.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param length The max. length of the string to write.
* @param s The (null-terminated) text string to write.
* @return true if successfull, false otherwise.
*/
public static boolean putString(byte[] buffer, int offset, int length, String s)
{
return putString(buffer, offset, length, s, Misc.CHARSET_DEFAULT);
}
/**
* Writes a text string into the specified buffer, using the specified charset for conversion.
* @param buffer The buffer to write the value to.
* @param offset Buffer offset.
* @param length The max. length of the string to write.
* @param s The (null-terminated) text string to write.
* @param cs The charset used to convert characters into bytes.
* @return true if successfull, false otherwise.
*/
public static boolean putString(byte[] buffer, int offset, int length, String s, Charset cs)
{
if (buffer != null && offset >= 0 && length >= 0 && offset+length <= buffer.length && s != null) {
if (cs == null) {
cs = Misc.CHARSET_DEFAULT;
}
byte[] buf = s.getBytes(cs);
int len = Math.min(buffer.length - offset, buf.length);
System.arraycopy(buf, 0, buffer, offset, len);
if (offset+len < buffer.length) {
buffer[offset+len] = 0; // writing string termination byte
}
return true;
}
return false;
}
/**
* Convenience method to read an unsigned byte value from the specified buffer.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset
* @return An unsigned byte value.
*/
public static short getUnsignedByte(byte[] buffer, int offset)
{
return (short)(getByte(buffer, offset) & 0xff);
}
/**
* Convenience method to read an unsigned short value from the specified buffer
* in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset
* @return An unsigned short value.
*/
public static int getUnsignedShort(byte[] buffer, int offset)
{
return ((int)getShort(buffer, offset) & 0xffff);
}
/**
* Convenience method to read an unsigned 24-bit integer value from the specified buffer
* in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset
* @return An unsigned 24-bit integer value.
*/
public static int getUnsignedInt24(byte[] buffer, int offset)
{
return (getInt24(buffer, offset) & 0xffffff);
}
/**
* Convenience method to read an unsigned integer value from the specified buffer
* in little endian byte order.
* @param buffer The buffer to read the value from.
* @param offset Buffer offset
* @return An unsigned integer value.
*/
public static long getUnsignedInt(byte[] buffer, int offset)
{
return ((long)getInt(buffer, offset) & 0xffffffffL);
}
/**
* Creates and returns a shallow copy of the current object.
*/
@Override
public DynamicArray clone()
{
return new DynamicArray(buffer, baseOfs, elementType, order);
}
/**
* Takes only the underlying array and the current base offset into account.
*/
@Override
public int hashCode()
{
int hash = 0;
if (buffer != null)
hash += buffer.hashCode();
hash += new Integer(baseOfs).hashCode();
return hash;
}
/**
* Indicates whether this object is equal to another. The objects are equal if both point to
* the same array and are set to the same base offset.
* @return true if both objects refer to the same array, starting at the same base offset.
* false otherwise.
*/
@Override
public boolean equals(Object obj)
{
if (obj != null && obj instanceof DynamicArray) {
DynamicArray o = (DynamicArray)obj;
if (buffer == o.buffer && baseOfs == o.baseOfs)
return true;
}
return false;
}
/**
* Returns the number of logical array elements.
* @return Number of logical array elements.
*/
public int size()
{
return buffer.length / elementSize;
}
/**
* Returns the size of the underlying array in bytes.
* @return Array size in bytes.
*/
public int arraySize()
{
return buffer.length;
}
/**
* Returns the size of a single logical array element.
* @return Size of a single array element.
*/
public int elementSize()
{
return elementSize;
}
public ElementType elementType()
{
return elementType;
}
/**
* Modifies the array's byte order.
* @param order The new byte order
* @return This object
*/
public DynamicArray setOrder(ByteOrder order)
{
if (order != null)
this.order = order;
return this;
}
/**
* Returns the currently used byte order.
* @return The current byte order
*/
public ByteOrder getOrder()
{
return order;
}
/**
* Returns the current base offset of the underlying array.
* @return The current base offset.
*/
public int getBaseOffset()
{
return baseOfs;
}
/**
* Sets a new base offset for the underlying array. All read/write operations take this
* offset into account automatically.
* @param baseOffset The new base offset.
* @return This object
*/
public DynamicArray setBaseOffset(int baseOffset)
{
if (baseOffset >= 0 && baseOffset < buffer.length)
this.baseOfs = baseOffset;
return this;
}
/**
* Adds the specified index to the current base offset of the underlying array.
* @param index Adds an offset of {@code index} elements to the current base offset. Can be negative.
* @return This object
*/
public DynamicArray addToBaseOffset(int index)
{
int ofs = index * elementSize;
if (baseOfs + ofs < 0)
ofs = -baseOfs;
if (baseOfs + ofs > buffer.length)
ofs = buffer.length - baseOfs;
baseOfs += ofs;
return this;
}
/**
* Returns the complete underlying array without regard to the current base offset.
* @return The complete underlying array.
*/
public byte[] getArray()
{
return buffer;
}
/**
* Returns the array with a logical element type of byte.
* @return A dynamic array of the current buffer with a byte element size.
*/
public DynamicArray asByteArray()
{
return asByteArray(this.baseOfs);
}
/**
* Returns the array with a logical element type of byte.
* @param baseOfs The start offset of the underlying array (offset in bytes).
* @return A dynamic array of the current buffer with a byte element size.
*/
public DynamicArray asByteArray(int baseOfs)
{
return new DynamicArray(buffer, baseOfs, ElementType.BYTE, order);
}
/**
* Returns the array with a logical element type of short.
* @return A dynamic array of the current buffer with a short element size.
*/
public DynamicArray asShortArray()
{
return asShortArray(this.baseOfs);
}
/**
* Returns the array with a logical element type of short.
* @param baseOfs The start offset of the underlying array (offset in bytes).
* @return A dynamic array of the current buffer with a short element size.
*/
public DynamicArray asShortArray(int baseOfs)
{
return new DynamicArray(buffer, baseOfs, ElementType.SHORT, order);
}
/**
* Returns the array with a logical element type of 24-bit integer.
* @return A dynamic array of the current buffer with a 24-bit integer element size.
*/
public DynamicArray asInt24Array()
{
return asInt24Array(this.baseOfs);
}
/**
* Returns the array with a logical element type of 24-bit integer.
* @param baseOfs The start offset of the underlying array (offset in bytes).
* @return A dynamic array of the current buffer with a 24-bit integer element size.
*/
public DynamicArray asInt24Array(int baseOfs)
{
return new DynamicArray(buffer, baseOfs, ElementType.INTEGER24, order);
}
/**
* Returns the array with a logical element type of integer.
* @return A dynamic array of the current buffer with a integer element size.
*/
public DynamicArray asIntArray()
{
return asIntArray(this.baseOfs);
}
/**
* Returns the array with a logical element type of integer.
* @param baseOfs The start offset of the underlying array (offset in bytes).
* @return A dynamic array of the current buffer with a integer element size.
*/
public DynamicArray asIntArray(int baseOfs)
{
return new DynamicArray(buffer, baseOfs, ElementType.INTEGER, order);
}
/**
* Returns the array with a logical element type of long.
* @return A dynamic array of the current buffer with a long element size.
*/
public DynamicArray asLongArray()
{
return asLongArray(this.baseOfs);
}
/**
* Returns the array with a logical element type of long.
* @param baseOfs The start offset of the underlying array (offset in bytes).
* @return A dynamic array of the current buffer with a long element size.
*/
public DynamicArray asLongArray(int baseOfs)
{
return new DynamicArray(buffer, baseOfs, ElementType.LONG, order);
}
/**
* Returns the byte value at the specified index.
* @param index The logical index of the the value.
* @return The byte value at the specified position.
*/
public byte getByte(int index)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
byte v = 0;
if (ofs < buffer.length)
v = buffer[ofs];
return v;
}
/**
* Writes the specified byte value to the specified logical position.
* @param index The logical index within the array to write the value to.
* @param v The value to write
* @return This object
*/
public DynamicArray putByte(int index, byte v)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
if (ofs < buffer.length)
buffer[ofs] = v;
return this;
}
/**
* Returns the short value at the specified index.
* @param index The logical index of the the value.
* @return The short value at the specified position.
*/
public short getShort(int index)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
short v = 0;
if (ofs < buffer.length)
v |= buffer[ofs++] & 0xff;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 8;
return fixShortOrder(v);
}
/**
* Writes the specified short value to the specified logical position.
* @param index The logical index within the array to write the value to.
* @param v The value to write
* @return This object
*/
public DynamicArray putShort(int index, short v)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
v = fixShortOrder(v);
if (ofs < buffer.length)
buffer[ofs++] = (byte)(v & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 8) & 0xff);
return this;
}
/**
* Returns the 24-bit integer value at the specified index.
* @param index The logical index of the the value.
* @return The 24-bit integer value at the specified position.
*/
public int getInt24(int index)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
int v = 0;
if (ofs < buffer.length)
v |= buffer[ofs++] & 0xff;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 8;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 16;
v = fixInt24Order(v);
if ((v & 0x800000) != 0)
v |= 0xff000000;
return v;
}
/**
* Writes the specified 24-bit integer value to the specified logical position.
* @param index The logical index within the array to write the value to.
* @param v The value to write
* @return This object
*/
public DynamicArray putInt24(int index, int v)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
v = fixInt24Order(v);
if (ofs < buffer.length)
buffer[ofs++] = (byte)(v & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 8) & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 16) & 0xff);
return this;
}
/**
* Returns the integer value at the specified index.
* @param index The logical index of the the value.
* @return The integer value at the specified position.
*/
public int getInt(int index)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
int v = 0;
if (ofs < buffer.length)
v |= buffer[ofs++] & 0xff;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 8;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 16;
if (ofs < buffer.length)
v |= (buffer[ofs++] & 0xff) << 24;
return fixIntOrder(v);
}
/**
* Writes the specified integer value to the specified logical position.
* @param index The logical index within the array to write the value to.
* @param v The value to write
* @return This object
*/
public DynamicArray putInt(int index, int v)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
v = fixIntOrder(v);
if (ofs < buffer.length)
buffer[ofs++] = (byte)(v & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 8) & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 16) & 0xff);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 24) & 0xff);
return this;
}
/**
* Returns the long value at the specified index.
* @param index The logical index of the the value.
* @return The long value at the specified position.
*/
public long getLong(int index)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
long v = 0L;
if (ofs < buffer.length)
v |= (long)buffer[ofs++] & 0xffL;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 8;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 16;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 24;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 32;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 40;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 48;
if (ofs < buffer.length)
v |= ((long)buffer[ofs++] & 0xffL) << 56;
return fixLongOrder(v);
}
/**
* Writes the specified long value to the specified logical position.
* @param index The logical index within the array to write the value to.
* @param v The value to write
* @return This object
*/
public DynamicArray putLong(int index, long v)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0)
throw new NullPointerException();
v = fixLongOrder(v);
if (ofs < buffer.length)
buffer[ofs++] = (byte)(v & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 8) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 16) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 24) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 32) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 40) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 48) & 0xffL);
if (ofs < buffer.length)
buffer[ofs++] = (byte)((v >>> 56) & 0xffL);
return this;
}
/**
* Returns a subarray of specified size starting at the specified index.
* @param index The logical starting index of the the subarray.
* @param size The size of the the subarray in bytes.
* @return The extracted subarray.
*/
public byte[] get(int index, int size)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0 || size < 0)
throw new NullPointerException();
int remaining = (ofs + size > buffer.length) ? ofs + size - buffer.length : 0;
if (ofs + size > buffer.length)
size = buffer.length - ofs;
final byte[] b = new byte[size];
System.arraycopy(buffer, ofs, b, 0, size);
for (int i = 0; i < remaining; i++)
b[ofs + size + i] = 0;
return b;
}
/**
* Writes the specified subarray into the array, starting at the specified logical array position.
* @param index The logical starting index to write the subarray to.
* @param b The subarray to write
* @return This object
*/
public DynamicArray put(int index, byte[] b)
{
int ofs = baseOfs + index * elementSize;
if (ofs < 0 || b == null)
throw new NullPointerException();
int size = (ofs + b.length > buffer.length) ? buffer.length - ofs : b.length;
System.arraycopy(b, 0, buffer, ofs, size);
return this;
}
/**
* Writes the specified subarray into the array, starting at the specified logical array position.
* @param index The logical starting index to write the subarray to.
* @param b The subarray to write
* @param ofs The start offset of the subarray
* @param len The number of bytes to write
* @return This object
*/
public DynamicArray put(int index, byte[] b, int ofs, int len)
{
int bufferOfs = baseOfs + index * elementSize;
if (bufferOfs < 0 || b == null || ofs < 0 || ofs >= b.length || len < 0)
throw new NullPointerException();
if (ofs + len > b.length)
len = b.length - ofs;
if (len > 0) {
int size = (bufferOfs + len > buffer.length) ? buffer.length - bufferOfs : len;
System.arraycopy(b, ofs, buffer, bufferOfs, size);
}
return this;
}
/**
* Convenience method: Returns the unsigned byte value at the specified index.
* @param index The logical index of the the value.
* @return The unsigned byte value at the specified position.
*/
public short getUnsignedByte(int index)
{
return (short)(getByte(index) & 0xff);
}
/**
* Convenience method: Returns the unsigned short value at the specified index.
* @param index The logical index of the the value.
* @return The unsigned short value at the specified position.
*/
public int getUnsignedShort(int index)
{
return ((int)getShort(index) & 0xffff);
}
/**
* Convenience method: Returns the unsigned 24-bit integer value at the specified index.
* @param index The logical index of the the value.
* @return The unsigned 24-byte integer value at the specified position.
*/
public int getUnsignedInt24(int index)
{
return getInt24(index) & 0xffffff;
}
/**
* Convenience method: Returns the unsigned integer value at the specified index.
* @param index The logical index of the the value.
* @return The unsigned integer value at the specified position.
*/
public long getUnsignedInt(int index)
{
return ((long)getInt(index) & 0xffffffffL);
}
// Allocate a new buffer
private DynamicArray(int size, ElementType elemType)
{
this(size, elemType, ByteOrder.LITTLE_ENDIAN);
}
// Allocate a new buffer
private DynamicArray(int size, ElementType elemType, ByteOrder order)
{
createBuffer(size, elemType, order);
}
// Wraps an existing buffer
private DynamicArray(byte[] buf, int ofs, ElementType elemType)
{
this(buf, ofs, elemType, ByteOrder.LITTLE_ENDIAN);
}
// Wraps an existing buffer
private DynamicArray(byte[] buf, int ofs, ElementType elemType, ByteOrder order)
{
if (buf == null || elemType == null)
throw new NullPointerException();
if (ofs < 0)
ofs = 0;
if (ofs > buf.length)
ofs = buf.length;
setElementType(elemType);
this.buffer = buf;
this.baseOfs = ofs;
this.order = (order != null) ? order : ByteOrder.LITTLE_ENDIAN;
}
// Sets new element size
private void setElementType(ElementType type)
{
if (type != null) {
switch (type) {
case SHORT:
elementType = type;
elementSize = 2;
break;
case INTEGER24:
elementType = type;
elementSize = 3;
break;
case INTEGER:
elementType = type;
elementSize = 4;
break;
case LONG:
elementType = type;
elementSize = 8;
break;
case BYTE:
default:
elementType = ElementType.BYTE;
elementSize = 1;
break;
}
} else {
elementType = ElementType.BYTE;
elementSize = 1;
}
}
// Creates a new buffer of specified size multiplied with elemSize
private void createBuffer(int size, ElementType elemType, ByteOrder order)
{
if (size < 0 || elemType == null)
throw new NullPointerException();
setElementType(elemType);
buffer = new byte[size*elementSize()];
order = (order != null) ? order : ByteOrder.LITTLE_ENDIAN;
}
private short fixShortOrder(short v)
{
if (order == ByteOrder.BIG_ENDIAN) {
short tmp = (short)((v << 8) & 0xff00);
tmp |= (short)((v >>> 8) & 0xff);
v = tmp;
}
return v;
}
private int fixInt24Order(int v)
{
if (order == ByteOrder.BIG_ENDIAN) {
int tmp = (v << 16) & 0xff0000;
tmp |= v & 0xff00;
tmp |= (v >>> 16) & 0xff;
v = tmp;
}
return v;
}
private int fixIntOrder(int v)
{
if (order == ByteOrder.BIG_ENDIAN) {
int tmp = (v << 24) & 0xff000000;
tmp |= (v << 8) & 0xff0000;
tmp |= (v >>> 8) & 0xff00;
tmp |= (v >>> 24) & 0xff;
v = tmp;
}
return v;
}
private long fixLongOrder(long v)
{
if (order == ByteOrder.BIG_ENDIAN) {
long tmp = (v << 56) & 0xff00000000000000L;
tmp |= (v << 40) & 0xff000000000000L;
tmp |= (v << 24) & 0xff0000000000L;
tmp |= (v << 8) & 0xff00000000L;
tmp |= (v >>> 8) & 0xff000000L;
tmp |= (v >>> 24) & 0xff0000L;
tmp |= (v >>> 40) & 0xff00L;
tmp |= (v >>> 56) & 0xffL;
v = tmp;
}
return v;
}
}