/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2005 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution37.javolution.io;

import java.lang.UnsupportedOperationException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.lang.Enum;

import javolution37.javolution.JavolutionError;
import javolution37.javolution.lang.Reflection;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 

This class represents a C/C++ struct; it confers * interoperability between Java classes and C/C++ struct.

*

Unlike C/C++, the storage layout of Java objects is not * determined by the compiler. The layout of objects in memory is deferred * to run time and determined by the interpreter (or just-in-time compiler). * This approach allows for dynamic loading and binding; but also makes * interfacing with C/C++ code difficult. Hence, this class for * which the memory layout is defined by the initialization order of the * {@link Struct}'s {@link Member members} and follows the same alignment * rules as C/C++ structs.

*

This class (as well as the {@link Union} sub-class) facilitates: *

*

Because of its one-to-one mapping, it is relatively easy to convert C * header files (e.g. OpenGL bindings) to Java {@link Struct}/{@link Union} * using simple text macros. Here is an example of C struct:

 *     struct Date {
 *         unsigned short year;
 *         unsigned byte month;
 *         unsigned byte day;
 *     };
 *     struct Student {
 *         char        name[64];
 *         struct Date birth;
 *         float       grades[10];
 *         Student*    next;
 *     };
* and here is the Java equivalent using this class:[code] * public static class Date extends Struct { * public final Unsigned16 year = new Unsigned16(); * public final Unsigned8 month = new Unsigned8(); * public final Unsigned8 day = new Unsigned8(); * } * public static class Student extends Struct { * public final Utf8String name = new Utf8String(64); * public final Date birth = inner(new Date()); * public final Float32[] grades = array(new Float32[10]); * public final Reference32 next = new Reference32(); * }[/code] * Struct's members are directly accessible:[code] * Student student = new Student(); * student.name.set("John Doe"); // Null terminated (C compatible) * int age = 2003 - student.birth.year.get(); * student.grades[2].set(12.5f); * student = student.next.get();[/code]

*

Applications may also work with the raw {@link #getByteBuffer() bytes} * directly. The following illustrate how {@link Struct} can be used to * decode/encode UDP messages directly:[code] * class UdpMessage extends Struct { * Unsigned16 xxx = new Unsigned16(); * ... * } * public void run() { * byte[] bytes = new byte[1024]; * DatagramPacket packet = new DatagramPacket(bytes, bytes.length); * UdpMessage message = new UdpMessage(); * message.setByteBuffer(ByteBuffer.wrap(bytes), 0); * // packet and message are now two different views of the same data. * while (isListening) { * multicastSocket.receive(packet); * int xxx = message.xxx.get(); * ... // Process message fields directly. * } * }[/code]

*

It is relatively easy to map instances of this class to any physical * address using * * JNI. Here is an example:[code] * import java.nio.ByteBuffer; * class Clock extends Struct { // Hardware clock mapped to memory. * Unsigned16 seconds = new Unsigned16(5); // unsigned short seconds:5 * Unsigned16 minutes = new Unsigned16(5); // unsigned short minutes:5 * Unsigned16 hours = new Unsigned16(4); // unsigned short hours:4 * Clock() { * setByteBuffer(Clock.nativeBuffer(), 0); * } * private static native ByteBuffer nativeBuffer(); * }[/code] * Below is the nativeBuffer() implementation * (Clock.c):

 *     #include 
 *     #include "Clock.h" // Generated using javah
 *     JNIEXPORT jobject JNICALL Java_Clock_nativeBuffer (JNIEnv *env, jclass) {
 *         return (*env)->NewDirectByteBuffer(env, clock_address, buffer_size)
 *     }

*

Bit-fields are supported (see Clock example above). * Bit-fields allocation order is defined by the Struct {@link #byteOrder} * return value (leftmost bit to rightmost bit if * BIG_ENDIAN and rightmost bit to leftmost bit if * LITTLE_ENDIAN). * Unless the Struct {@link #isPacked packing} directive is overriden, * bit-fields cannot straddle the storage-unit boundary as defined by their * base type (padding is inserted at the end of the first bit-field * and the second bit-field is put into the next storage unit).

*

Finally, it is possible to change the {@link #setByteBuffer ByteBuffer} * and/or the Struct {@link #setByteBufferPosition position} in its * ByteBuffer to allow for a single {@link Struct} object to * encode/decode multiple memory mapped instances.

* * @author Jean-Marie Dautelle * @version 3.3, June 7, 2005 */ public class Struct { /** * Holds the outer struct if any. */ protected Struct _outer; /** * Holds the byte buffer backing the struct (top struct). */ private ByteBuffer _byteBuffer; /** * Holds the offset of this struct relative to the outer struct or * to the byte buffer if there is no outer. */ protected int _outerOffset; /** * Holds the number of bits currently used (for size calculation). */ private int _bitsUsed; /** * Holds this struct alignment (largest alignment of its members). */ protected int _alignment = 1; /** * Holds the current bit index position (during construction). */ private int _bitIndex; /** * Indicates if the index has to be reset for each new field. */ private boolean _resetIndex; /** * Holds bytes array for Stream I/O when byteBuffer has no intrinsic array. */ private byte[] _bytes; /** * Default constructor. */ public Struct() { _resetIndex = isUnion(); } /** * Returns the size in bytes of this struct. The size includes * tail padding to satisfy the struct alignment requirement * (defined by the largest alignment of its {@link Member members}). * * @return the C/C++ sizeof(this). */ public final int size() { int nbrOfBytes = (_bitsUsed + 7) >> 3; return ((nbrOfBytes % _alignment) == 0) ? nbrOfBytes : // Already aligned or packed. nbrOfBytes + _alignment - (nbrOfBytes % _alignment); // Tail padding. } /** * Returns the byte buffer for this struct. This method will allocate * a new direct buffer if none has been set. * *

Changes to the buffer's content are visible in this struct, * and vice versa.

*

The buffer of an inner struct is the same as its parent struct.

*

The position of a {@link Struct.Member struct's member} within the * byte buffer is given by {@link Struct.Member#position * member.position()}

* * @return the current byte buffer or a new direct buffer if none set. * @see #setByteBuffer */ public final ByteBuffer getByteBuffer() { if (_outer != null) return _outer.getByteBuffer(); return (_byteBuffer != null) ? _byteBuffer : newBuffer(); } private synchronized ByteBuffer newBuffer() { if (_byteBuffer != null) return _byteBuffer; // Synchronized check. int size = size(); // Covers misaligned 64 bits access when packed. int capacity = isPacked() ? (((size & 0x7) == 0) ? size : size + 8 - (size & 0x7)) : size; ByteBuffer bf = ByteBuffer.allocateDirect(capacity); bf.order(byteOrder()); setByteBuffer(bf, 0); return _byteBuffer; } /** * Sets the current byte buffer for this struct. * The specified byte buffer can be mapped to memory for direct memory * access or can wrap a shared byte array for I/O purpose * (e.g. DatagramPacket). * * @param byteBuffer the new byte buffer. * @param position the position of this struct in the specified byte buffer. * @return this * @throws IllegalArgumentException if the specified byteBuffer has a * different byte order than this struct. * @throws UnsupportedOperationException if this struct is an inner struct. * @see #byteOrder() */ public final Struct setByteBuffer(ByteBuffer byteBuffer, int position) { if (byteBuffer.order() != byteOrder()) throw new IllegalArgumentException( "The byte order of the specified byte buffer" + " is different from this struct byte order"); if (_outer != null) throw new UnsupportedOperationException( "Inner struct byte buffer is inherited from outer"); _byteBuffer = byteBuffer; _outerOffset = position; return this; } /** * Sets the position of this struct within its byte buffer. * * @param position the position of this struct in its byte buffer. * @return this * @throws UnsupportedOperationException if this struct is an inner struct. */ public final Struct setByteBufferPosition(int position) { return setByteBuffer(this.getByteBuffer(), position); } /** * Returns the absolute position of this struct within its associated * {@link #getByteBuffer byte buffer}. * * @return the absolute position of this struct in the byte buffer. */ public final int getByteBufferPosition() { return (_outer != null) ? _outer.getByteBufferPosition() + _outerOffset : _outerOffset; } /** * Reads this struct from the specified input stream * (convenience method when using Stream I/O). For better performance, * use of Block I/O (e.g. java.nio.channels.*) is recommended. * * @param in the input stream being read from. * @return the number of bytes read (typically the {@link #size() size} * of this struct. * @throws IOException if an I/O error occurs. */ public int read(InputStream in) throws IOException { ByteBuffer buffer = getByteBuffer(); if (buffer.hasArray()) { int offset = buffer.arrayOffset() + getByteBufferPosition(); return in.read(buffer.array(), offset, size()); } else { synchronized (buffer) { if (_bytes == null) { _bytes = new byte[size()]; } int bytesRead = in.read(_bytes); buffer.position(getByteBufferPosition()); buffer.put(_bytes); return bytesRead; } } } /** * Writes this struct to the specified output stream * (convenience method when using Stream I/O). For better performance, * use of Block I/O (e.g. java.nio.channels.*) is recommended. * * @param out the output stream to write to. * @throws IOException if an I/O error occurs. */ public void write(OutputStream out) throws IOException { ByteBuffer buffer = getByteBuffer(); if (buffer.hasArray()) { int offset = buffer.arrayOffset() + getByteBufferPosition(); out.write(buffer.array(), offset, size()); } else { synchronized (buffer) { if (_bytes == null) { _bytes = new byte[size()]; } buffer.position(getByteBufferPosition()); buffer.get(_bytes); out.write(_bytes); } } } /** * Returns this struct address. This method allows for structs * to be referenced (e.g. pointer) from other structs. * * @return the struct memory address. * @throws UnsupportedOperationException if the struct buffer is not * a direct buffer. * @see Reference32 * @see Reference64 */ public final long address() { ByteBuffer thisBuffer = this.getByteBuffer(); if (ADDRESS_METHOD != null) { Long start = (Long) ADDRESS_METHOD.invoke(thisBuffer); return start.longValue() + getByteBufferPosition(); } else { throw new UnsupportedOperationException( "Operation not supported for " + thisBuffer.getClass()); } } private static final Reflection.Method ADDRESS_METHOD = Reflection .getMethod("sun.nio.ch.DirectBuffer.address()"); /** * Returns the String representation of this struct * in the form of its constituing bytes (hexadecimal). For example:[code] * public static class Student extends Struct { * Utf8String name = new Utf8String(16); * Unsigned16 year = new Unsigned16(); * Float32 grade = new Float32(); * } * Student student = new Student(); * student.name.set("John Doe"); * student.year.set(2003); * student.grade.set(12.5f); * System.out.println(student); * * 4A 6F 68 6E 20 44 6F 65 00 00 00 00 00 00 00 00 * 07 D3 00 00 41 48 00 00[/code] * * @return a hexadecimal representation of the bytes content for this * struct. */ public String toString() { final int size = size(); StringBuffer sb = new StringBuffer(size * 3); final ByteBuffer buffer = getByteBuffer(); final int start = getByteBufferPosition(); for (int i = 0; i < size; i++) { int b = buffer.get(start + i) & 0xFF; sb.append(HEXA[b >> 4]); sb.append(HEXA[b & 0xF]); sb.append(((i & 0xF) == 0xF) ? '\n' : ' '); } return sb.toString(); } private static final char[] HEXA = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /////////////////// // CONFIGURATION // /////////////////// /** * Indicates if this struct's members are mapped to the same location * in memory (default false). This method is useful for * applications extending {@link Struct} with new member types in order to * create unions from these new structs. For example:[code] * public abstract class FortranStruct extends Struct { * public class FortranString extends Member {...} * protected FortranString[] array(FortranString[] array, int stringLength) { ... } * } * public abstract class FortranUnion extends FortranStruct { * // Inherits new members and methods. * public final isUnion() { * return true; * } * }[/code] * * @return true if this struct's members are mapped to * to the same location in memory; false * otherwise. * @see Union */ public boolean isUnion() { return false; } /** * Returns the byte order for this struct (configurable). * The byte order is inherited by inner structs. Sub-classes may change * the byte order by overriding this method. For example:[code] * public class TopStruct extends Struct { * ... // Members initialization. * public ByteOrder byteOrder() { * // TopStruct and its inner structs use hardware byte order. * return ByteOrder.nativeOrder(); * } * }}[/code]

* * @return the byte order when reading/writing multibyte values * (default: network byte order, BIG_ENDIAN). */ public ByteOrder byteOrder() { return (_outer != null) ? _outer.byteOrder() : ByteOrder.BIG_ENDIAN; } /** * Indicates if this struct is packed (configurable). * By default, {@link Member members} of a struct are aligned on the * boundary corresponding to the member base type; padding is performed * if necessary. This directive is inherited by inner structs. * Sub-classes may change the packing directive by overriding this method. * For example:[code] * public class TopStruct extends Struct { * ... // Members initialization. * public boolean isPacked() { * // TopStruct and its inner structs are packed. * return true; * } * }}[/code] * * @return true if alignment requirements are ignored. * false otherwise (default). */ public boolean isPacked() { return (_outer != null) ? _outer.isPacked() : false; } /** * Defines the specified struct as inner of this struct. * * @param struct the inner struct. * @return the specified struct. * @throws IllegalArgumentException if the specified struct is already * an inner struct. */ protected S inner( S struct) { if (struct._outer != null) throw new IllegalArgumentException( "struct: Already an inner struct"); struct._outer = this; final int bitSize = struct.size() << 3; updateIndexes(struct._alignment, bitSize, bitSize); struct._outerOffset = (_bitIndex - bitSize) >> 3; return ( S ) struct; } /** * Defines the specified array of structs as inner structs. * The array is populated if necessary using the struct component * default constructor (which must be public). * * @param structs the struct array. * @return the specified struct array. * @throws IllegalArgumentException if the specified array contains * inner structs. */ protected S [] array( S [] structs) { Class structClass = null; boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } for (int i = 0; i < structs.length;) { S struct = structs[i]; if (struct == null) { try { if (structClass == null) { String arrayName = structs.getClass().getName(); String structName = arrayName.substring(2, arrayName .length() - 1); structClass = Reflection.getClass(structName); if (structClass == null) throw new JavolutionError("Struct class: " + structName + " not found"); } struct = ( S ) structClass.newInstance(); } catch (Exception e) { throw new JavolutionError(e); } } structs[i++] = inner(struct); } _resetIndex = resetIndexSaved; return ( S []) structs; } /** * Defines the specified two-dimensional array of structs as inner * structs. The array is populated if necessary using the struct component * default constructor (which must be public). * * @param structs the two dimensional struct array. * @return the specified struct array. * @throws IllegalArgumentException if the specified array contains * inner structs. */ protected S [][] array( S [][] structs) { boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } for (int i = 0; i < structs.length; i++) { array(structs[i]); } _resetIndex = resetIndexSaved; return ( S [][]) structs; } /** * Defines the specified three dimensional array of structs as inner * structs. The array is populated if necessary using the struct component * default constructor (which must be public). * * @param structs the three dimensional struct array. * @return the specified struct array. * @throws IllegalArgumentException if the specified array contains * inner structs. */ protected S [][][] array( S [][][] structs) { boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } for (int i = 0; i < structs.length; i++) { array(structs[i]); } _resetIndex = resetIndexSaved; return ( S [][][]) structs; } /** * Defines the specified array member. For predefined members, * the array is populated when empty; custom members should use * literal (populated) arrays. * * @param arrayMember the array member. * @return the specified array member. * @throws UnsupportedOperationException if the specified array * is empty and the member type is unknown. */ protected M [] array( M [] arrayMember) { boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } if (BOOL.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Bool(); } else if (SIGNED_8.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Signed8(); } else if (UNSIGNED_8.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Unsigned8(); } else if (SIGNED_16.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Signed16(); } else if (UNSIGNED_16.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Unsigned16(); } else if (SIGNED_32.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Signed32(); } else if (UNSIGNED_32.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Unsigned32(); } else if (SIGNED_64.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Signed64(); } else if (FLOAT_32.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Float32(); } else if (FLOAT_64.isInstance(arrayMember)) { for (int i = 0; i < arrayMember.length;) arrayMember[i++] = ( M ) this.new Float64(); } else { throw new UnsupportedOperationException( "Cannot create member elements, the arrayMember should " + "contain the member instances instead of null"); } _resetIndex = resetIndexSaved; return ( M []) arrayMember; } private static final Class BOOL = new Bool[0].getClass(); private static final Class SIGNED_8 = new Signed8[0].getClass(); private static final Class UNSIGNED_8 = new Unsigned8[0].getClass(); private static final Class SIGNED_16 = new Signed16[0].getClass(); private static final Class UNSIGNED_16 = new Unsigned16[0].getClass(); private static final Class SIGNED_32 = new Signed32[0].getClass(); private static final Class UNSIGNED_32 = new Unsigned32[0].getClass(); private static final Class SIGNED_64 = new Signed64[0].getClass(); private static final Class FLOAT_32 = new Float32[0].getClass(); private static final Class FLOAT_64 = new Float64[0].getClass(); /** * Defines the specified two-dimensional array member. For predefined * members, the array is populated when empty; custom members should use * literal (populated) arrays. * * @param arrayMember the two-dimensional array member. * @return the specified array member. * @throws UnsupportedOperationException if the specified array * is empty and the member type is unknown. */ protected M [][] array( M [][] arrayMember) { boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } for (int i = 0; i < arrayMember.length; i++) { array(arrayMember[i]); } _resetIndex = resetIndexSaved; return ( M [][]) arrayMember; } /** * Defines the specified three-dimensional array member. For predefined * members, the array is populated when empty; custom members should use * literal (populated) arrays. * * @param arrayMember the three-dimensional array member. * @return the specified array member. * @throws UnsupportedOperationException if the specified array * is empty and the member type is unknown. */ protected M [][][] array( M [][][] arrayMember) { boolean resetIndexSaved = _resetIndex; if (_resetIndex) { _bitIndex = 0; _resetIndex = false; // Ensures the array elements are sequential. } for (int i = 0; i < arrayMember.length; i++) { array(arrayMember[i]); } _resetIndex = resetIndexSaved; return ( M [][][]) arrayMember; } /** * Defines the specified array of UTF-8 strings, all strings having the * specified length (convenience method). * * @param array the string array. * @param stringLength the length of the string elements. * @return the specified string array. */ protected Utf8String[] array(Utf8String[] array, int stringLength) { for (int i = 0; i < array.length; i++) { array[i] = new Utf8String(stringLength); } return array; } /** * Updates this struct indexes after adding a member with the * specified constraints. * * @param alignment the desired alignment in bytes. * @param nbrOfBits the size in bits. * @param capacity the word size maximum capacity in bits * (equal to nbrOfBits for non-bitfields). * @return offset the offset of the member. * @throws IllegalArgumentException if * nbrOfBits > capacity */ private int updateIndexes(int alignment, int nbrOfBits, int capacity) { if (nbrOfBits > capacity) { throw new IllegalArgumentException("nbrOfBits: " + nbrOfBits + " exceeds capacity: " + capacity); } // Resets index if union. if (_resetIndex) { _bitIndex = 0; } // Caculates offset based on alignment constraints. alignment = isPacked() ? 1 : alignment; int offset = (_bitIndex / (alignment << 3)) * alignment; // Calculates bits already used from the offset position. int usedBits = _bitIndex - (offset << 3); // Checks if bits can be adjacents. // A bit size of 0 forces realignment, ref. C/C++ Standard. if ((capacity < usedBits + nbrOfBits) || ((nbrOfBits == 0) && (usedBits != 0))) { // Padding to next alignment boundary. offset += alignment; _bitIndex = (offset << 3) + nbrOfBits; } else { // Adjacent bits. _bitIndex += nbrOfBits; } // Updates bits used (for size calculation). if (_bitsUsed < _bitIndex) { _bitsUsed = _bitIndex; } // Updates Struct's alignment. if (_alignment < alignment) { _alignment = alignment; } return offset; } ///////////// // MEMBERS // ///////////// /** * This inner class represents the base class for all {@link Struct} * members. It allows applications to define additional member types. * For example:[code] * public class MyStruct extends Struct { * BitSet bits = new BitSet(256); * ... * public BitSet extends Member { * public BitSet(int nbrBits) { * super(1, (nbrBits+7)>>3); * } * public boolean get(int i) { ... } * public void set(int i, boolean value) { ...} * } * }[/code] */ protected class Member { /** * Holds the relative offset of this member within its struct. */ private final int _offset; /** * Base constructor for custom member types. * * @param alignment the desired alignment in bytes. * @param size the size of this member in bytes. */ protected Member(int alignment, int size) { final int nbrOfBits = size << 3; _offset = updateIndexes(alignment, nbrOfBits, nbrOfBits); } /** * Base constructor for predefined member types with bit fields. * * @param alignment the desired alignment in bytes. * @param nbrOfBits the size in bits. * @param capacity the word size maximum capacity in bits * (equal to nbrOfBits for non-bitfields). */ Member(int alignment, int nbrOfBits, int capacity) { _offset = updateIndexes(alignment, nbrOfBits, capacity); } /** * Returns the outer {@link Struct struct} container. * * @return the outer struct. */ public final Struct struct() { return Struct.this; } /** * Returns the relative offset of this member within its struct. * * @return the relative offset in bytes. */ public final int offset() { return _offset; } /** * Returns the absolute position of this member in the * byte buffer. * * @return the byte buffer position. */ public final int position() { return getByteBufferPosition() + _offset; } } /////////////////////// // PREDEFINED FIELDS // /////////////////////// /** * This class represents a UTF-8 character string, null terminated * (for C/C++ compatibility) */ public class Utf8String extends Member { private final Utf8ByteBufferWriter _writer = new Utf8ByteBufferWriter(); private final Utf8ByteBufferReader _reader = new Utf8ByteBufferReader(); private final char[] _chars; private final int _length; public Utf8String(int length) { super(1, length); _length = length; // Takes into account 0 terminator. _chars = new char[length]; } public void set(String string) { final ByteBuffer buffer = getByteBuffer(); synchronized (buffer) { try { buffer.position(position()); _writer.setByteBuffer(buffer); if (string.length() < _length) { _writer.write(string); _writer.write(0); // Marks end of string. } else if (string.length() > _length) { // Truncates. _writer.write(string.substring(0, _length)); } else { // Exact same length. _writer.write(string); } } catch (IOException e) { throw new JavolutionError(e); } finally { _writer.reset(); } } } public String get() { final ByteBuffer buffer = getByteBuffer(); synchronized (buffer) { try { buffer.position(position()); _reader.setByteBuffer(buffer); for (int i = 0; i < _length;) { char c = (char) _reader.read(); if (c == 0) { // Null terminator. return new String(_chars, 0, i); } else { _chars[i++] = c; } } return new String(_chars, 0, _length); } catch (IOException e) { throw new JavolutionError(e); } finally { _reader.reset(); } } } } /** * This class represents a 8 bits boolean with true represented * by 1 and false represented by 0. */ public class Bool extends Member { private final int _mask; private final int _shift; public Bool() { this(8); } public Bool(int nbrOfBits) { super(1, nbrOfBits, 8); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 8 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; } public boolean get() { return (getByteBuffer().get(position()) & _mask) != 0; } public void set(boolean value) { if (_mask == 0xFF) { // Non bit-field. getByteBuffer().put(position(), (byte) (value ? 1 : 0)); } else { // Bit-field. int prevCleared = getByteBuffer().get(position()) & (~_mask); if (value) { getByteBuffer().put(position(), (byte) (prevCleared | (1 << _shift))); } else { getByteBuffer().put(position(), (byte) (prevCleared)); } } } } /** * This class represents a 8 bits signed integer. */ public class Signed8 extends Member { private final int _mask; private final int _shift; private final int _signShift; public Signed8() { this(8); } public Signed8(int nbrOfBits) { super(1, nbrOfBits, 8); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 8 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public byte get() { if (_mask == 0xFF) { // Non bit-field. return getByteBuffer().get(position()); } else { // Bit-field. int value = getByteBuffer().get(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (byte) value; } } public void set(byte value) { if (_mask == 0xFF) { // Non bit-field. getByteBuffer().put(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().get(position()) & (~_mask); getByteBuffer().put(position(), (byte) (orMask | value)); } } } /** * This class represents a 8 bits unsigned integer. */ public class Unsigned8 extends Member { private final int _shift; private final int _mask; public Unsigned8() { this(8); } public Unsigned8(int nbrOfBits) { super(1, nbrOfBits, 8); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 8 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; } public short get() { int value = getByteBuffer().get(position()); return (short) ((value & _mask) >>> _shift); } public void set(short value) { if (_mask == 0xFF) { // Non bit-field. getByteBuffer().put(position(), (byte) value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().get(position()) & (~_mask); getByteBuffer().put(position(), (byte) (orMask | value)); } } } /** * This class represents a 16 bits signed integer. */ public class Signed16 extends Member { private final int _mask; private final int _shift; private final int _signShift; public Signed16() { this(16); } public Signed16(int nbrOfBits) { super(2, nbrOfBits, 16); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 16 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public short get() { if (_mask == 0xFFFF) { // Non bit-field. return getByteBuffer().getShort(position()); } else { // Bit-field. int value = getByteBuffer().getShort(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (short) value; } } public void set(short value) { if (_mask == 0xFFFF) { // Non bit-field. getByteBuffer().putShort(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getShort(position()) & (~_mask); getByteBuffer().putShort(position(), (short) (orMask | value)); } } } /** * This class represents a 16 bits unsigned integer. */ public class Unsigned16 extends Member { private final int _shift; private final int _mask; public Unsigned16() { this(16); } public Unsigned16(int nbrOfBits) { super(2, nbrOfBits, 16); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 16 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; } public int get() { int value = getByteBuffer().getShort(position()); return (value & _mask) >>> _shift; } public void set(int value) { if (_mask == 0xFFFF) { // Non bit-field. getByteBuffer().putShort(position(), (short) value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getShort(position()) & (~_mask); getByteBuffer().putShort(position(), (short) (orMask | value)); } } } /** * This class represents a 32 bits signed integer. */ public class Signed32 extends Member { private final int _mask; private final int _shift; private final int _signShift; public Signed32() { this(32); } public Signed32(int nbrOfBits) { super(4, nbrOfBits, 32); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 32 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = (nbrOfBits == 32) ? 0xFFFFFFFF : ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public int get() { if (_mask == 0xFFFFFFFF) { // Non bit-field. return getByteBuffer().getInt(position()); } else { // Bit-field. int value = getByteBuffer().getInt(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return value; } } public void set(int value) { if (_mask == 0xFFFFFFFF) { // Non bit-field. getByteBuffer().putInt(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getInt(position()) & (~_mask); getByteBuffer().putInt(position(), orMask | value); } } } /** * This class represents a 32 bits unsigned integer. */ public class Unsigned32 extends Member { private final int _shift; private final long _mask; public Unsigned32() { this(32); } public Unsigned32(int nbrOfBits) { super(4, nbrOfBits, 32); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 32 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = (nbrOfBits == 32) ? 0xFFFFFFFFl : ((1l << nbrOfBits) - 1l) << _shift; } public long get() { int value = getByteBuffer().getInt(position()); return (value & _mask) >>> _shift; } public void set(long value) { if (_mask == 0xFFFFFFFF) { // Non bit-field. getByteBuffer().putInt(position(), (int) value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getInt(position()) & (~(int) _mask); getByteBuffer().putInt(position(), (int) (orMask | value)); } } } /** * This class represents a 64 bits signed integer. */ public class Signed64 extends Member { private final long _mask; private final int _shift; private final int _signShift; public Signed64() { this(64); } public Signed64(int nbrOfBits) { super(8, nbrOfBits, 64); final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 64 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = (nbrOfBits == 64) ? 0xFFFFFFFFFFFFFFFFl : ((1l << nbrOfBits) - 1l) << _shift; _signShift = 64 - _shift - nbrOfBits; } public long get() { if (_mask == 0xFFFFFFFFFFFFFFFFl) { // Non bit-field. return getByteBuffer().getLong(position()); } else { // Bit-field. long value = getByteBuffer().getLong(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return value; } } public void set(long value) { if (_mask == 0xFFFFFFFFFFFFFFFFl) { // Non bit-field. getByteBuffer().putLong(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; long orMask = getByteBuffer().getLong(position()) & (~_mask); getByteBuffer().putLong(position(), orMask | value); } } } /** * This class represents a 32 bits float (C/C++/Java float). */ public class Float32 extends Member { public Float32() { super(4, 4); } /**/ public void set(float value) { getByteBuffer().putFloat(position(), value); } public float get() { return getByteBuffer().getFloat(position()); } /**/ } /** * This class represents a 64 bits float (C/C++/Java double). */ public class Float64 extends Member { public Float64() { super(8, 8); } /**/ public void set(double value) { getByteBuffer().putDouble(position(), value); } public double get() { return getByteBuffer().getDouble(position()); } /**/ } /** *

This class represents a 32 bits reference (C/C++ pointer) to * a {@link Struct} object (other types may require a {@link Struct} * wrapper).

*

Note: For references which can be externally modified, an application * may want to check the {@link #isUpToDate up-to-date} status of * the reference. For out-of-date references, a new {@link Struct} * can be created at the address specified by {@link #value} * (using JNI) and then {@link #set set} to the reference.

*/ public class Reference32 extends Member { private S _struct; public Reference32() { super(4, 4); } public void set( S struct) { if (struct != null) { getByteBuffer().putInt(position(), (int) struct.address()); } else { getByteBuffer().putInt(position(), 0); } _struct = struct; } public S get() { return _struct; } public int value() { return getByteBuffer().getInt(position()); } public boolean isUpToDate() { if (_struct != null) { return getByteBuffer().getInt(position()) == (int) _struct .address(); } else { return getByteBuffer().getInt(position()) == 0; } } } /** *

This class represents a 64 bits reference (C/C++ pointer) to * a {@link Struct} object (other types may require a {@link Struct} * wrapper).

*

Note: For references which can be externally modified, an application * may want to check the {@link #isUpToDate up-to-date} status of * the reference. For out-of-date references, a new {@link Struct} * can be created at the address specified by {@link #value} * (using JNI) and then {@link #set set} to the reference.

*/ public class Reference64 extends Member { private S _struct; public Reference64() { super(8, 8); } public void set( S struct) { if (struct != null) { getByteBuffer().putLong(position(), struct.address()); } else if (struct == null) { getByteBuffer().putLong(position(), 0L); } _struct = struct; } public S get() { return _struct; } public long value() { return getByteBuffer().getLong(position()); } public boolean isUpToDate() { if (_struct != null) { return getByteBuffer().getLong(position()) == _struct.address(); } else { return getByteBuffer().getLong(position()) == 0L; } } } /** * This class represents a 8 bits {@link Enum}. */ public class Enum8 extends Member { private final int _mask; private final int _shift; private final int _signShift; private final List _enumValues; public Enum8(List enumValues) { this(enumValues, 8); } public Enum8(List enumValues, int nbrOfBits) { super(1, nbrOfBits, 8); _enumValues = enumValues; final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 8 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public Enum get() { if (_mask == 0xFF) { // Non bit-field. return (Enum) _enumValues.get(getByteBuffer().get(position())); } else { // Bit-field. int value = getByteBuffer().get(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (Enum) _enumValues.get(value); } } public void set(Enum e) { int index = e.ordinal(); if (_enumValues.get(index) != e) { throw new IllegalArgumentException( "enum: " + e + ", ordinal value does not reflect enum values position"); } byte value = (byte) index; if (_mask == 0xFF) { // Non bit-field. getByteBuffer().put(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().get(position()) & (~_mask); getByteBuffer().put(position(), (byte) (orMask | value)); } } } /** * This class represents a 16 bits {@link Enum}. */ public class Enum16 extends Member { private final int _mask; private final int _shift; private final int _signShift; private final List _enumValues; public Enum16(List enumValues) { this(enumValues, 16); } public Enum16(List enumValues, int nbrOfBits) { super(2, nbrOfBits, 16); _enumValues = enumValues; final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 16 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public Enum get() { if (_mask == 0xFFFF) { // Non bit-field. return (Enum) _enumValues.get(getByteBuffer().getShort( position())); } else { // Bit-field. int value = getByteBuffer().getShort(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (Enum) _enumValues.get(value); } } public void set(Enum e) { int index = e.ordinal(); if (_enumValues.get(index) != e) { throw new IllegalArgumentException( "enum: " + e + ", ordinal value does not reflect enum values position"); } short value = (short) index; if (_mask == 0xFFFF) { // Non bit-field. getByteBuffer().putShort(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getShort(position()) & (~_mask); getByteBuffer().putShort(position(), (short) (orMask | value)); } } } /** * This class represents a 32 bits {@link Enum}. */ public class Enum32 extends Member { private final int _mask; private final int _shift; private final int _signShift; private final List _enumValues; public Enum32(List enumValues) { this(enumValues, 32); } public Enum32(List enumValues, int nbrOfBits) { super(4, nbrOfBits, 32); _enumValues = enumValues; final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 32 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = (nbrOfBits == 32) ? 0xFFFFFFFF : ((1 << nbrOfBits) - 1) << _shift; _signShift = 32 - _shift - nbrOfBits; } public Enum get() { if (_mask == 0xFFFFFFFF) { // Non bit-field. return (Enum) _enumValues.get(getByteBuffer() .getInt(position())); } else { // Bit-field. int value = getByteBuffer().getInt(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (Enum) _enumValues.get(value); } } public void set(Enum e) { int index = e.ordinal(); if (_enumValues.get(index) != e) { throw new IllegalArgumentException( "enum: " + e + ", ordinal value does not reflect enum values position"); } int value = index; if (_mask == 0xFFFFFFFF) { // Non bit-field. getByteBuffer().putInt(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; int orMask = getByteBuffer().getInt(position()) & (~_mask); getByteBuffer().putInt(position(), orMask | value); } } } /** * This class represents a 64 bits {@link Enum}. */ public class Enum64 extends Member { private final long _mask; private final int _shift; private final int _signShift; private final List _enumValues; public Enum64(List enumValues) { this(enumValues, 64); } public Enum64(List enumValues, int nbrOfBits) { super(8, nbrOfBits, 64); _enumValues = enumValues; final int startBit = offset() << 3; _shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? 64 - _bitIndex + startBit : _bitIndex - startBit - nbrOfBits; _mask = (nbrOfBits == 64) ? 0xFFFFFFFFFFFFFFFFl : ((1l << nbrOfBits) - 1l) << _shift; _signShift = 64 - _shift - nbrOfBits; } public Enum get() { if (_mask == 0xFFFFFFFFFFFFFFFFl) { // Non bit-field. return (Enum) _enumValues.get((int) getByteBuffer().getLong( position())); } else { // Bit-field. long value = getByteBuffer().getLong(position()); value &= _mask; value <<= _signShift; value >>= _signShift + _shift; // Keeps sign. return (Enum) _enumValues.get((int) value); } } public void set(Enum e) { int index = e.ordinal(); if (_enumValues.get(index) != e) { throw new IllegalArgumentException( "enum: " + e + ", ordinal value does not reflect enum values position"); } long value = index; if (_mask == 0xFFFFFFFFFFFFFFFFl) { // Non bit-field. getByteBuffer().putLong(position(), value); } else { // Bit-field. value <<= _shift; value &= _mask; long orMask = getByteBuffer().getLong(position()) & (~_mask); getByteBuffer().putLong(position(), orMask | value); } } } }

Ads help maintain this website.