You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2013/09/06 14:27:08 UTC
[16/18] temporarily added a patched version of javolution with fast
collections, because the released version has several bugs (see
https://java.net/jira/browse/JAVOLUTION-106 and
https://java.net/jira/browse/JAVOLUTION-105)
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/e43574ef/commons/marmotta-commons/src/ext/java/javolution/io/Struct.java
----------------------------------------------------------------------
diff --git a/commons/marmotta-commons/src/ext/java/javolution/io/Struct.java b/commons/marmotta-commons/src/ext/java/javolution/io/Struct.java
new file mode 100644
index 0000000..3f95451
--- /dev/null
+++ b/commons/marmotta-commons/src/ext/java/javolution/io/Struct.java
@@ -0,0 +1,1749 @@
+/*
+ * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
+ * Copyright (C) 2012 - 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 javolution.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import javolution.context.LocalContext;
+import javolution.lang.MathLib;
+import javolution.lang.Realtime;
+import javolution.text.TextBuilder;
+
+/**
+ * <p> Equivalent to a <code>C/C++ struct</code>; this class confers
+ * interoperability between Java classes and C/C++ struct.</p>
+ *
+ * <p> Unlike <code>C/C++</code>, 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 <code>C/C++</code> 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 wordSize
+ * rules as <code>C/C++ structs</code>.</p>
+ *
+ * <p> This class (as well as the {@link Union} sub-class) facilitates:
+ * <ul>
+ * <li> Memory sharing between Java applications and native libraries.</li>
+ * <li> Direct encoding/decoding of streams for which the structure
+ * is defined by legacy C/C++ code.</li>
+ * <li> Serialization/deserialization of Java objects (complete control,
+ * e.g. no class header)</li>
+ * <li> Mapping of Java objects to physical addresses (with JNI).</li>
+ * </ul></p>
+ *
+ * <p> 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:
+ * [code]
+ * enum Gender{MALE, FEMALE};
+ * struct Date {
+ * unsigned short year;
+ * unsigned byte month;
+ * unsigned byte day;
+ * };
+ * struct Student {
+ * enum Gender gender;
+ * char name[64];
+ * struct Date birth;
+ * float grades[10];
+ * Student* next;
+ * };[/code]</p>
+ * <p> and here is the Java equivalent using this class:
+ * [code]
+ * public enum Gender { MALE, FEMALE };
+ * 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 Enum32<Gender> gender = new Enum32<Gender>(Gender.values());
+ * 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<Student> next = new Reference32<Student>();
+ * }[/code]</p>
+ * <p> Struct's members are directly accessible:
+ * [code]
+ * Student student = new Student();
+ * student.gender.set(Gender.MALE);
+ * 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]</p>
+ *
+ * <p> Applications can 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]</p>
+ *
+ * <p> It is relatively easy to map instances of this class to any physical
+ * address using
+ * <a href="http://java.sun.com/docs/books/tutorial/native1.1/index.html">
+ * JNI</a>. 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]</p>
+ * <p> Below is the <code>nativeBuffer()</code> implementation
+ * (<code>Clock.c</code>):
+ * [code]
+ * #include <jni.h>
+ * #include "Clock.h" // Generated using javah
+ * JNIEXPORT jobject JNICALL Java_Clock_nativeBuffer (JNIEnv *env, jclass) {
+ * return (*env)->NewDirectByteBuffer(env, clock_address, buffer_size)
+ * }[/code]</p>
+ *
+ * <p> Bit-fields are supported (see <code>Clock</code> example above).
+ * Bit-fields allocation order is defined by the Struct {@link #byteOrder}
+ * return value. Leftmost bit to rightmost bit if
+ * <code>BIG_ENDIAN</code> and rightmost bit to leftmost bit if
+ * <code>LITTLE_ENDIAN</code> (same layout as Microsoft Visual C++).
+ * C/C++ 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).
+ * It is possible to avoid bit padding by using the {@link BitField}
+ * member (or a sub-class). In which case the allocation order is always
+ * from the leftmost to the rightmost bit (same as <code>BIG_ENDIAN</code>).
+ * </p>
+ *
+ * <p> Finally, it is possible to change the {@link #setByteBuffer ByteBuffer}
+ * and/or the Struct {@link #setByteBufferPosition position} in its
+ * <code>ByteBuffer</code> to allow for a single {@link Struct} object to
+ * encode/decode multiple memory mapped instances.</p>
+ *
+ * <p><i>Note: Because Struct/Union are basically wrappers around
+ * <code>java.nio.ByteBuffer</code>, tutorials/usages for the
+ * Java NIO package are directly applicable to Struct/Union.</i></p>
+ *
+ * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
+ * @version 5.5.1, April 1, 2010
+ */
+@SuppressWarnings("unchecked")
+@Realtime
+public class Struct {
+
+ /**
+ * Configurable holding the maximum wordSize in bytes
+ * (default <code>4</code>). Should be a value greater or equal to 1.
+ */
+ public static final LocalContext.Parameter<Integer> MAXIMUM_ALIGNMENT = new LocalContext.Parameter<Integer>() {
+ @Override
+ protected Integer getDefault() {
+ return 4;
+ }
+ };
+
+ /**
+ * Holds the outer struct if any.
+ */
+ Struct _outer;
+ /**
+ * Holds the byte buffer backing the struct (top struct).
+ */
+ ByteBuffer _byteBuffer;
+ /**
+ * Holds the offset of this struct relative to the outer struct or
+ * to the byte buffer if there is no outer.
+ */
+ int _outerOffset;
+ /**
+ * Holds this struct alignment in bytes (largest word size of its members).
+ */
+ int _alignment = 1;
+ /**
+ * Holds this struct's length.
+ */
+ int _length;
+ /**
+ * Holds the index position during construction.
+ * This is the index a the first unused byte available.
+ */
+ int _index;
+ /**
+ * Holds the word size during construction (for bit fields).
+ * This is the size of the last word used.
+ */
+ int _wordSize;
+ /**
+ * Holds the bits used in the word during construction (for bit fields).
+ * This is the number of bits used in the last word.
+ */
+ int _bitsUsed;
+ /**
+ * Indicates if the index has to be reset for each new field (
+ * <code>true</code> only for Union subclasses).
+ */
+ boolean _resetIndex;
+ /**
+ * Holds bytes array for Stream I/O when byteBuffer has no intrinsic array.
+ */
+ 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 word size requirement
+ * (defined by the largest word size of its {@link Member members}).
+ *
+ * @return the C/C++ <code>sizeof(this)</code>.
+ */
+ public final int size() {
+ return (_alignment <= 1) ? _length
+ : ((_length + _alignment - 1) / _alignment) * _alignment;
+ }
+
+ /**
+ * Returns the outer of this struct or <code>null</code> if this struct
+ * is not an inner struct.
+ *
+ * @return the outer struct or <code>null</code>.
+ */
+ public Struct outer() {
+ return _outer;
+ }
+
+ /**
+ * Returns the byte buffer for this struct. This method will allocate
+ * a new <b>direct</b> buffer if none has been set.
+ *
+ * <p> Changes to the buffer's content are visible in this struct,
+ * and vice versa.</p>
+ * <p> The buffer of an inner struct is the same as its parent struct.</p>
+ * <p> If no byte buffer has been {@link Struct#setByteBuffer set},
+ * a direct buffer is allocated with a capacity equals to this
+ * struct's {@link Struct#size() size}.</p>
+ *
+ * @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.
+ ByteBuffer bf = ByteBuffer.allocateDirect(size());
+ 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. <code>DatagramPacket</code>).
+ * The capacity of the specified byte buffer should be at least the
+ * {@link Struct#size() size} of this struct plus the offset position.
+ *
+ * @param byteBuffer the new byte buffer.
+ * @param position the position of this struct in the specified byte buffer.
+ * @return <code>this</code>
+ * @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 byte position of this struct within its byte buffer.
+ *
+ * @param position the position of this struct in its byte buffer.
+ * @return <code>this</code>
+ * @throws UnsupportedOperationException if this struct is an inner struct.
+ */
+ public final Struct setByteBufferPosition(int position) {
+ return setByteBuffer(this.getByteBuffer(), position);
+ }
+
+ /**
+ * Returns the absolute byte position of this struct within its associated
+ * {@link #getByteBuffer byte buffer}.
+ *
+ * @return the absolute position of this struct (can be an inner 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. <code>java.nio.channels.*</code>) is recommended.
+ * This method behaves appropriately when not all of the data is available
+ * from the input stream. Incomplete data is extremely common when the
+ * input stream is associated with something like a TCP connection.
+ * The typical usage pattern in those scenarios is to repeatedly call
+ * read() until the entire message is received.
+ *
+ * @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();
+ int size = size();
+ int remaining = size - buffer.position();
+ if (remaining == 0) remaining = size;// at end so move to beginning
+ int alreadyRead = size - remaining; // typically 0
+ if (buffer.hasArray()) {
+ int offset = buffer.arrayOffset() + getByteBufferPosition();
+ int bytesRead = in.read(buffer.array(), offset + alreadyRead,
+ remaining);
+ buffer.position(getByteBufferPosition() + alreadyRead + bytesRead
+ - offset);
+ return bytesRead;
+ } else {
+ synchronized (buffer) {
+ if (_bytes == null) {
+ _bytes = new byte[size()];
+ }
+ int bytesRead = in.read(_bytes, 0, remaining);
+ buffer.position(getByteBufferPosition() + alreadyRead);
+ buffer.put(_bytes, 0, bytesRead);
+ 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. <code>java.nio.channels.*</code>) 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 (if supported by the platform).
+ * This method allows for structs to be referenced (e.g. pointer)
+ * from other structs.
+ *
+ * @return the struct memory address.
+ * @throws UnsupportedOperationException if not supported by the platform.
+ * @see Reference32
+ * @see Reference64
+ */
+ public final long address() {
+ try {
+ Class<?> dbClass = Class.forName("sun.nio.ch.DirectBuffer");
+ java.lang.reflect.Method address = dbClass.getDeclaredMethod(
+ "address", new Class[0]);
+ return ((Long) address.invoke(this.getByteBuffer(),
+ (Object[]) null)).longValue();
+ } catch (Throwable error) {
+ error.printStackTrace();
+ throw new UnsupportedOperationException(
+ "Method Struct.address() not supported on this platform.");
+ }
+ }
+
+ /**
+ * Returns the <code>String</code> 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() {
+ TextBuilder tmp = new TextBuilder();
+ final int size = size();
+ final ByteBuffer buffer = getByteBuffer();
+ final int start = getByteBufferPosition();
+ for (int i = 0; i < size; i++) {
+ int b = buffer.get(start + i) & 0xFF;
+ tmp.append(HEXA[b >> 4]);
+ tmp.append(HEXA[b & 0xF]);
+ tmp.append(((i & 0xF) == 0xF) ? '\n' : ' ');
+ }
+ return tmp.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 <code>false</code>). 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 <code>true</code> if this struct's members are mapped to
+ * to the same location in memory; <code>false</code>
+ * 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]</p></p>
+ *
+ * @return the byte order when reading/writing multibyte values
+ * (default: network byte order, <code>BIG_ENDIAN</code>).
+ */
+ 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 <b>not</b> inherited by inner structs.
+ * Sub-classes may change the packing directive by overriding this method.
+ * For example:[code]
+ * public class MyStruct extends Struct {
+ * ... // Members initialization.
+ * public boolean isPacked() {
+ * return true; // MyStruct is packed.
+ * }
+ * }}[/code]
+ *
+ * @return <code>true</code> if word size requirements are ignored.
+ * <code>false</code> otherwise (default).
+ */
+ public boolean isPacked() {
+ return 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 extends Struct> S inner(S struct) {
+ if (struct._outer != null) throw new IllegalArgumentException(
+ "struct: Already an inner struct");
+ Member inner = new Member(struct.size() << 3, struct._alignment); // Update indexes.
+ struct._outer = this;
+ struct._outerOffset = inner.offset();
+ 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 extends Struct> S[] array(S[] structs) {
+ Class<?> structClass = null;
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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 = Class.forName(structName);
+ if (structClass == null) { throw new IllegalArgumentException(
+ "Struct class: " + structName + " not found"); }
+ }
+ struct = (S) structClass.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ 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 extends Struct> S[][] array(S[][] structs) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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 extends Struct> S[][][] array(S[][][] structs) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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 extends Member> M[] array(M[] arrayMember) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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<? extends Bool[]> BOOL = new Bool[0].getClass();
+ private static final Class<? extends Signed8[]> SIGNED_8 = new Signed8[0]
+ .getClass();
+ private static final Class<? extends Unsigned8[]> UNSIGNED_8 = new Unsigned8[0]
+ .getClass();
+ private static final Class<? extends Signed16[]> SIGNED_16 = new Signed16[0]
+ .getClass();
+ private static final Class<? extends Unsigned16[]> UNSIGNED_16 = new Unsigned16[0]
+ .getClass();
+ private static final Class<? extends Signed32[]> SIGNED_32 = new Signed32[0]
+ .getClass();
+ private static final Class<? extends Unsigned32[]> UNSIGNED_32 = new Unsigned32[0]
+ .getClass();
+ private static final Class<? extends Signed64[]> SIGNED_64 = new Signed64[0]
+ .getClass();
+ private static final Class<? extends Float32[]> FLOAT_32 = new Float32[0]
+ .getClass();
+ private static final Class<? extends Float64[]> 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 extends Member> M[][] array(M[][] arrayMember) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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 extends Member> M[][][] array(M[][][] arrayMember) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 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) {
+ boolean resetIndexSaved = _resetIndex;
+ if (_resetIndex) {
+ _index = 0;
+ _resetIndex = false; // Ensures the array elements are sequential.
+ }
+ for (int i = 0; i < array.length; i++) {
+ array[i] = new UTF8String(stringLength);
+ }
+ _resetIndex = resetIndexSaved;
+ return array;
+ }
+
+ /**
+ * Reads the specified bits from this Struct as an long (signed) integer
+ * value.
+ *
+ * @param bitOffset the bit start position in the Struct.
+ * @param bitSize the number of bits.
+ * @return the specified bits read as a signed long.
+ * @throws IllegalArgumentException if
+ * <code>(bitOffset + bitSize - 1) / 8 >= this.size()</code>
+ */
+ public long readBits(int bitOffset, int bitSize) {
+ if ((bitOffset + bitSize - 1) >> 3 >= this.size()) throw new IllegalArgumentException(
+ "Attempt to read outside the Struct");
+ int offset = bitOffset >> 3;
+ int bitStart = bitOffset - (offset << 3);
+ bitStart = (byteOrder() == ByteOrder.BIG_ENDIAN) ? bitStart : 64
+ - bitSize - bitStart;
+ int index = getByteBufferPosition() + offset;
+ long value = readByteBufferLong(index);
+ value <<= bitStart; // Clears preceding bits.
+ value >>= (64 - bitSize); // Signed shift.
+ return value;
+ }
+
+ private long readByteBufferLong(int index) {
+ ByteBuffer byteBuffer = getByteBuffer();
+ if (index + 8 < byteBuffer.limit()) return byteBuffer.getLong(index);
+ // Else possible buffer overflow.
+ if (byteBuffer.order() == ByteOrder.LITTLE_ENDIAN) {
+ return (readByte(index, byteBuffer) & 0xff)
+ + ((readByte(++index, byteBuffer) & 0xff) << 8)
+ + ((readByte(++index, byteBuffer) & 0xff) << 16)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 24)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 32)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 40)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 48)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 56);
+ } else {
+ return (((long) readByte(index, byteBuffer)) << 56)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 48)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 40)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 32)
+ + ((readByte(++index, byteBuffer) & 0xffL) << 24)
+ + ((readByte(++index, byteBuffer) & 0xff) << 16)
+ + ((readByte(++index, byteBuffer) & 0xff) << 8)
+ + (readByte(++index, byteBuffer) & 0xffL);
+ }
+ }
+
+ private static byte readByte(int index, ByteBuffer byteBuffer) {
+ return (index < byteBuffer.limit()) ? byteBuffer.get(index) : 0;
+ }
+
+ /**
+ * Writes the specified bits into this Struct.
+ *
+ * @param value the bits value as a signed long.
+ * @param bitOffset the bit start position in the Struct.
+ * @param bitSize the number of bits.
+ * @throws IllegalArgumentException if
+ * <code>(bitOffset + bitSize - 1) / 8 >= this.size()</code>
+ */
+ public void writeBits(long value, int bitOffset, int bitSize) {
+ if ((bitOffset + bitSize - 1) >> 3 >= this.size()) throw new IllegalArgumentException(
+ "Attempt to write outside the Struct");
+ int offset = bitOffset >> 3;
+ int bitStart = (byteOrder() == ByteOrder.BIG_ENDIAN) ? bitOffset
+ - (offset << 3) : 64 - bitSize - (bitOffset - (offset << 3));
+ long mask = -1L;
+ mask <<= bitStart; // Clears preceding bits
+ mask >>>= (64 - bitSize); // Unsigned shift.
+ mask <<= 64 - bitSize - bitStart;
+ value <<= (64 - bitSize - bitStart);
+ value &= mask; // Protects against out of range values.
+ int index = getByteBufferPosition() + offset;
+ long oldValue = readByteBufferLong(index);
+ long resetValue = oldValue & (~mask);
+ long newValue = resetValue | value;
+ writeByteBufferLong(index, newValue);
+ }
+
+ private void writeByteBufferLong(int index, long value) {
+ ByteBuffer byteBuffer = getByteBuffer();
+ if (index + 8 < byteBuffer.limit()) {
+ byteBuffer.putLong(index, value);
+ return;
+ }
+ // Else possible buffer overflow.
+ if (byteBuffer.order() == ByteOrder.LITTLE_ENDIAN) {
+ writeByte(index, byteBuffer, (byte) value);
+ writeByte(++index, byteBuffer, (byte) (value >> 8));
+ writeByte(++index, byteBuffer, (byte) (value >> 16));
+ writeByte(++index, byteBuffer, (byte) (value >> 24));
+ writeByte(++index, byteBuffer, (byte) (value >> 32));
+ writeByte(++index, byteBuffer, (byte) (value >> 40));
+ writeByte(++index, byteBuffer, (byte) (value >> 48));
+ writeByte(++index, byteBuffer, (byte) (value >> 56));
+ } else {
+ writeByte(index, byteBuffer, (byte) (value >> 56));
+ writeByte(++index, byteBuffer, (byte) (value >> 48));
+ writeByte(++index, byteBuffer, (byte) (value >> 40));
+ writeByte(++index, byteBuffer, (byte) (value >> 32));
+ writeByte(++index, byteBuffer, (byte) (value >> 24));
+ writeByte(++index, byteBuffer, (byte) (value >> 16));
+ writeByte(++index, byteBuffer, (byte) (value >> 8));
+ writeByte(++index, byteBuffer, (byte) value);
+ }
+ }
+
+ private static void writeByte(int index, ByteBuffer byteBuffer, byte value) {
+ if (index < byteBuffer.limit()) {
+ byteBuffer.put(index, value);
+ }
+ }
+
+ /////////////
+ // 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(nbrBits, 0); // Direct bit access.
+ * }
+ * public boolean get(int i) { ... }
+ * public void set(int i, boolean value) { ...}
+ * }
+ * }[/code]
+ */
+ protected class Member {
+
+ /**
+ * Holds the relative offset (in bytes) of this member within its struct.
+ */
+ private final int _offset;
+ /**
+ * Holds the relative bit offset of this member to its struct offset.
+ */
+ private final int _bitIndex;
+ /**
+ * Holds the bit length of this member.
+ */
+ private final int _bitLength;
+
+ /**
+ * Base constructor for custom member types.
+ *
+ * The word size can be zero, in which case the {@link #offset}
+ * of the member does not change, only {@link #bitIndex} is
+ * incremented.
+ *
+ * @param bitLength the number of bits or <code>0</code>
+ * to force next member on next word boundary.
+ * @param wordSize the word size in bytes used when accessing
+ * this member data or <code>0</code> if the data is accessed
+ * at the bit level.
+ */
+ protected Member(int bitLength, int wordSize) {
+ _bitLength = bitLength;
+
+ // Resets index if union.
+ if (_resetIndex) {
+ _index = 0;
+ }
+
+ // Check if we can merge bitfields (always true if no word boundary).
+ if ((wordSize == 0)
+ || ((bitLength != 0) && (wordSize == _wordSize) && ((_bitsUsed + bitLength) <= (wordSize << 3)))) {
+
+ _offset = _index - _wordSize;
+ _bitIndex = _bitsUsed;
+ _bitsUsed += bitLength;
+
+ // Straddling word boundary only possible if (wordSize == 0)
+ while (_bitsUsed > (_wordSize << 3)) {
+ _index++;
+ _wordSize++;
+ _length = MathLib.max(_length, _index);
+ }
+ return; // Bit field merge done.
+ }
+
+ // Check alignment.
+ if (!isPacked()) {
+
+ // Updates struct's alignment constraint, based on largest word size.
+ if ((_alignment < wordSize)) {
+ _alignment = wordSize;
+ }
+
+ // Adds padding if misaligned.
+ int misaligned = _index % wordSize;
+ if (misaligned != 0) {
+ _index += wordSize - misaligned;
+ }
+ }
+
+ // Sets member indices.
+ _offset = _index;
+ _bitIndex = 0;
+
+ // Update struct indices.
+ _index += MathLib.max(wordSize, (bitLength + 7) >> 3);
+ _wordSize = wordSize;
+ _bitsUsed = bitLength;
+ _length = MathLib.max(_length, _index);
+ // size and index may differ because of {@link Union}
+ }
+
+ /**
+ * Returns the outer {@link Struct struct} container.
+ *
+ * @return the outer struct.
+ */
+ public final Struct struct() {
+ return Struct.this;
+ }
+
+ /**
+ * Returns the byte offset of this member in its struct.
+ * Equivalent to C/C++ <code>offsetof(struct(), this)</code>
+ *
+ * @return the offset of this member in the Struct.
+ */
+ public final int offset() {
+ return _offset;
+ }
+
+ /**
+ * Holds the bit offset of this member (if any).
+ * The actual position of the bits data depends upon the endianess and
+ * the word size.
+ */
+ public final int bitIndex() {
+ return _bitIndex;
+ }
+
+ /**
+ * Returns the number of bits in this member. Can be zero if this
+ * member is used to force the next member to the next word boundary.
+ *
+ * @return the number of bits in the member.
+ */
+ public final int bitLength() {
+ return _bitLength;
+ }
+
+ // Returns the member int value.
+ final int get(int wordSize, int word) {
+ final int shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? (wordSize << 3)
+ - bitIndex() - bitLength()
+ : bitIndex();
+ word >>= shift;
+ int mask = 0xFFFFFFFF >>> (32 - bitLength());
+ return word & mask;
+ }
+
+ // Sets the member int value.
+ final int set(int value, int wordSize, int word) {
+ final int shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? (wordSize << 3)
+ - bitIndex() - bitLength()
+ : bitIndex();
+ int mask = 0xFFFFFFFF >>> (32 - bitLength());
+ mask <<= shift;
+ value <<= shift;
+ return (word & ~mask) | (value & mask);
+ }
+
+ // Returns the member long value.
+ final long get(int wordSize, long word) {
+ final int shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? (wordSize << 3)
+ - bitIndex() - bitLength()
+ : bitIndex();
+ word >>= shift;
+ long mask = 0xFFFFFFFFFFFFFFFFL >>> (64 - bitLength());
+ return word & mask;
+ }
+
+ // Sets the member long value.
+ final long set(long value, int wordSize, long word) {
+ final int shift = (byteOrder() == ByteOrder.BIG_ENDIAN) ? (wordSize << 3)
+ - bitIndex() - bitLength()
+ : bitIndex();
+ long mask = 0xFFFFFFFFFFFFFFFFL >>> (64 - bitLength());
+ mask <<= shift;
+ value <<= shift;
+ return (word & ~mask) | (value & mask);
+ }
+ }
+
+ ///////////////////////
+ // 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 int _length;
+
+ public UTF8String(int length) {
+ super(length << 3, 1);
+ _length = length; // Takes into account 0 terminator.
+ }
+
+ public void set(String string) {
+ final ByteBuffer buffer = getByteBuffer();
+ synchronized (buffer) {
+ try {
+ int index = getByteBufferPosition() + offset();
+ buffer.position(index);
+ _writer.setOutput(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) { // Should never happen.
+ throw new Error(e.getMessage());
+ } finally {
+ _writer.reset();
+ }
+ }
+ }
+
+ public String get() {
+ final ByteBuffer buffer = getByteBuffer();
+ synchronized (buffer) {
+ TextBuilder tmp = new TextBuilder();
+ try {
+ int index = getByteBufferPosition() + offset();
+ buffer.position(index);
+ _reader.setInput(buffer);
+ for (int i = 0; i < _length; i++) {
+ char c = (char) _reader.read();
+ if (c == 0) { // Null terminator.
+ return tmp.toString();
+ } else {
+ tmp.append(c);
+ }
+ }
+ return tmp.toString();
+ } catch (IOException e) { // Should never happen.
+ throw new Error(e.getMessage());
+ } finally {
+ _reader.reset();
+ }
+ }
+ }
+
+ public String toString() {
+ return this.get();
+ }
+ }
+
+ /**
+ * This class represents a 8 bits boolean with <code>true</code> represented
+ * by <code>1</code> and <code>false</code> represented by <code>0</code>.
+ */
+ public class Bool extends Member {
+
+ public Bool() {
+ super(8, 1);
+ }
+
+ public Bool(int nbrOfBits) {
+ super(nbrOfBits, 1);
+ }
+
+ public boolean get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().get(index);
+ word = (bitLength() == 8) ? word : get(1, word);
+ return word != 0;
+ }
+
+ public void set(boolean value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 8) {
+ getByteBuffer().put(index, (byte) (value ? -1 : 0));
+ } else {
+ getByteBuffer().put(
+ index,
+ (byte) set(value ? -1 : 0, 1, getByteBuffer()
+ .get(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 8 bits signed integer.
+ */
+ public class Signed8 extends Member {
+
+ public Signed8() {
+ super(8, 1);
+ }
+
+ public Signed8(int nbrOfBits) {
+ super(nbrOfBits, 1);
+ }
+
+ public byte get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().get(index);
+ return (byte) ((bitLength() == 8) ? word : get(1, word));
+ }
+
+ public void set(byte value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 8) {
+ getByteBuffer().put(index, value);
+ } else {
+ getByteBuffer().put(index,
+ (byte) set(value, 1, getByteBuffer().get(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 8 bits unsigned integer.
+ */
+ public class Unsigned8 extends Member {
+
+ public Unsigned8() {
+ super(8, 1);
+ }
+
+ public Unsigned8(int nbrOfBits) {
+ super(nbrOfBits, 1);
+ }
+
+ public short get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().get(index);
+ return (short) (0xFF & ((bitLength() == 8) ? word : get(1, word)));
+ }
+
+ public void set(short value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 8) {
+ getByteBuffer().put(index, (byte) value);
+ } else {
+ getByteBuffer().put(index,
+ (byte) set(value, 1, getByteBuffer().get(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 16 bits signed integer.
+ */
+ public class Signed16 extends Member {
+
+ public Signed16() {
+ super(16, 2);
+ }
+
+ public Signed16(int nbrOfBits) {
+ super(nbrOfBits, 2);
+ }
+
+ public short get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getShort(index);
+ return (short) ((bitLength() == 16) ? word : get(2, word));
+ }
+
+ public void set(short value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 16) {
+ getByteBuffer().putShort(index, value);
+ } else {
+ getByteBuffer().putShort(index,
+ (short) set(value, 2, getByteBuffer().getShort(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 16 bits unsigned integer.
+ */
+ public class Unsigned16 extends Member {
+
+ public Unsigned16() {
+ super(16, 2);
+ }
+
+ public Unsigned16(int nbrOfBits) {
+ super(nbrOfBits, 2);
+ }
+
+ public int get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getShort(index);
+ return 0xFFFF & ((bitLength() == 16) ? word : get(2, word));
+ }
+
+ public void set(int value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 16) {
+ getByteBuffer().putShort(index, (short) value);
+ } else {
+ getByteBuffer().putShort(index,
+ (short) set(value, 2, getByteBuffer().getShort(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 32 bits signed integer.
+ */
+ public class Signed32 extends Member {
+
+ public Signed32() {
+ super(32, 4);
+ }
+
+ public Signed32(int nbrOfBits) {
+ super(nbrOfBits, 4);
+ }
+
+ public int get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getInt(index);
+ return (bitLength() == 32) ? word : get(4, word);
+ }
+
+ public void set(int value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 32) {
+ getByteBuffer().putInt(index, value);
+ } else {
+ getByteBuffer().putInt(index,
+ set(value, 4, getByteBuffer().getInt(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 32 bits unsigned integer.
+ */
+ public class Unsigned32 extends Member {
+
+ public Unsigned32() {
+ super(32, 4);
+ }
+
+ public Unsigned32(int nbrOfBits) {
+ super(nbrOfBits, 4);
+ }
+
+ public long get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getInt(index);
+ return 0xFFFFFFFFL & ((bitLength() == 32) ? word : get(4, word));
+ }
+
+ public void set(long value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 32) {
+ getByteBuffer().putInt(index, (int) value);
+ } else {
+ getByteBuffer().putInt(index,
+ set((int) value, 4, getByteBuffer().getInt(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 64 bits signed integer.
+ */
+ public class Signed64 extends Member {
+
+ public Signed64() {
+ super(64, 8);
+ }
+
+ public Signed64(int nbrOfBits) {
+ super(nbrOfBits, 8);
+ }
+
+ public long get() {
+ final int index = getByteBufferPosition() + offset();
+ long word = getByteBuffer().getLong(index);
+ return (bitLength() == 64) ? word : get(8, word);
+ }
+
+ public void set(long value) {
+ final int index = getByteBufferPosition() + offset();
+ if (bitLength() == 64) {
+ getByteBuffer().putLong(index, value);
+ } else {
+ getByteBuffer().putLong(index,
+ set(value, 8, getByteBuffer().getLong(index)));
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents an arbitrary size (unsigned) bit field with
+ * no word size constraint (they can straddle words boundaries).
+ */
+ public class BitField extends Member {
+
+ public BitField(int nbrOfBits) {
+ super(nbrOfBits, 0);
+ }
+
+ public long longValue() {
+ long signedValue = readBits(bitIndex() + (offset() << 3),
+ bitLength());
+ return ~(-1L << bitLength()) & signedValue;
+ }
+
+ public int intValue() {
+ return (int) longValue();
+ }
+
+ public short shortValue() {
+ return (short) longValue();
+ }
+
+ public byte byteValue() {
+ return (byte) longValue();
+ }
+
+ public void set(long value) {
+ writeBits(value, bitIndex() + (offset() << 3), bitLength());
+ }
+
+ public String toString() {
+ return String.valueOf(longValue());
+ }
+ }
+
+ /**
+ * This class represents a 32 bits float (C/C++/Java <code>float</code>).
+ */
+ public class Float32 extends Member {
+
+ public Float32() {
+ super(32, 4);
+ }
+
+ public float get() {
+ final int index = getByteBufferPosition() + offset();
+ return getByteBuffer().getFloat(index);
+ }
+
+ public void set(float value) {
+ final int index = getByteBufferPosition() + offset();
+ getByteBuffer().putFloat(index, value);
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 64 bits float (C/C++/Java <code>double</code>).
+ */
+ public class Float64 extends Member {
+
+ public Float64() {
+ super(64, 8);
+ }
+
+ public double get() {
+ final int index = getByteBufferPosition() + offset();
+ return getByteBuffer().getDouble(index);
+ }
+
+ public void set(double value) {
+ final int index = getByteBufferPosition() + offset();
+ getByteBuffer().putDouble(index, value);
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * <p> This class represents a 32 bits reference (C/C++ pointer) to
+ * a {@link Struct} object (other types may require a {@link Struct}
+ * wrapper).</p>
+ * <p> 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 {@link Struct}
+ * can be created at the address specified by {@link #value}
+ * (using JNI) and the reference {@link #set set} accordingly.</p>
+ */
+ public class Reference32<S extends Struct> extends Member {
+
+ private S _struct;
+
+ public Reference32() {
+ super(32, 4);
+ }
+
+ public void set(S struct) {
+ final int index = getByteBufferPosition() + offset();
+ if (struct != null) {
+ getByteBuffer().putInt(index, (int) struct.address());
+ } else {
+ getByteBuffer().putInt(index, 0);
+ }
+ _struct = struct;
+ }
+
+ public S get() {
+ return _struct;
+ }
+
+ public int value() {
+ final int index = getByteBufferPosition() + offset();
+ return getByteBuffer().getInt(index);
+ }
+
+ public boolean isUpToDate() {
+ final int index = getByteBufferPosition() + offset();
+ if (_struct != null) {
+ return getByteBuffer().getInt(index) == (int) _struct.address();
+ } else {
+ return getByteBuffer().getInt(index) == 0;
+ }
+ }
+ }
+
+ /**
+ * <p> This class represents a 64 bits reference (C/C++ pointer) to
+ * a {@link Struct} object (other types may require a {@link Struct}
+ * wrapper).</p>
+ * <p> 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.</p>
+ */
+ public class Reference64<S extends Struct> extends Member {
+
+ private S _struct;
+
+ public Reference64() {
+ super(64, 8);
+ }
+
+ public void set(S struct) {
+ final int index = getByteBufferPosition() + offset();
+ if (struct != null) {
+ getByteBuffer().putLong(index, struct.address());
+ } else if (struct == null) {
+ getByteBuffer().putLong(index, 0L);
+ }
+ _struct = struct;
+ }
+
+ public S get() {
+ return _struct;
+ }
+
+ public long value() {
+ final int index = getByteBufferPosition() + offset();
+ return getByteBuffer().getLong(index);
+ }
+
+ public boolean isUpToDate() {
+ final int index = getByteBufferPosition() + offset();
+ if (_struct != null) {
+ return getByteBuffer().getLong(index) == _struct.address();
+ } else {
+ return getByteBuffer().getLong(index) == 0L;
+ }
+ }
+ }
+
+ /**
+ * This class represents a 8 bits {@link Enum}.
+ */
+ public class Enum8<T extends Enum<T>> extends Member {
+
+ private final T[] _values;
+
+ public Enum8(T[] values) {
+ super(8, 1);
+ _values = values;
+ }
+
+ public Enum8(T[] values, int nbrOfBits) {
+ super(nbrOfBits, 1);
+ _values = values;
+ }
+
+ public T get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().get(index);
+ return _values[0xFF & get(1, word)];
+ }
+
+ public void set(T e) {
+ int value = e.ordinal();
+ if (_values[value] != e) throw new IllegalArgumentException(
+ "enum: "
+ + e
+ + ", ordinal value does not reflect enum values position");
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().get(index);
+ getByteBuffer().put(index, (byte) set(value, 1, word));
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 16 bits {@link Enum}.
+ */
+ public class Enum16<T extends Enum<T>> extends Member {
+
+ private final T[] _values;
+
+ public Enum16(T[] values) {
+ super(16, 2);
+ _values = values;
+ }
+
+ public Enum16(T[] values, int nbrOfBits) {
+ super(nbrOfBits, 2);
+ _values = values;
+ }
+
+ public T get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getShort(index);
+ return _values[0xFFFF & get(2, word)];
+ }
+
+ public void set(T e) {
+ int value = e.ordinal();
+ if (_values[value] != e) throw new IllegalArgumentException(
+ "enum: "
+ + e
+ + ", ordinal value does not reflect enum values position");
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getShort(index);
+ getByteBuffer().putShort(index, (short) set(value, 2, word));
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 32 bits {@link Enum}.
+ */
+ public class Enum32<T extends Enum<T>> extends Member {
+
+ private final T[] _values;
+
+ public Enum32(T[] values) {
+ super(32, 4);
+ _values = values;
+ }
+
+ public Enum32(T[] values, int nbrOfBits) {
+ super(nbrOfBits, 4);
+ _values = values;
+ }
+
+ public T get() {
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getInt(index);
+ return _values[get(4, word)];
+ }
+
+ public void set(T e) {
+ int value = e.ordinal();
+ if (_values[value] != e) throw new IllegalArgumentException(
+ "enum: "
+ + e
+ + ", ordinal value does not reflect enum values position");
+ final int index = getByteBufferPosition() + offset();
+ int word = getByteBuffer().getInt(index);
+ getByteBuffer().putInt(index, set(value, 4, word));
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+
+ /**
+ * This class represents a 64 bits {@link Enum}.
+ */
+ public class Enum64<T extends Enum<T>> extends Member {
+
+ private final T[] _values;
+
+ public Enum64(T[] values) {
+ super(64, 8);
+ _values = values;
+ }
+
+ public Enum64(T[] values, int nbrOfBits) {
+ super(nbrOfBits, 8);
+ _values = values;
+ }
+
+ public T get() {
+ final int index = getByteBufferPosition() + offset();
+ long word = getByteBuffer().getLong(index);
+ return _values[(int) get(8, word)];
+ }
+
+ public void set(T e) {
+ long value = e.ordinal();
+ if (_values[(int) value] != e) throw new IllegalArgumentException(
+ "enum: "
+ + e
+ + ", ordinal value does not reflect enum values position");
+ final int index = getByteBufferPosition() + offset();
+ long word = getByteBuffer().getLong(index);
+ getByteBuffer().putLong(index, set(value, 8, word));
+ }
+
+ public String toString() {
+ return String.valueOf(this.get());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/e43574ef/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferReader.java
----------------------------------------------------------------------
diff --git a/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferReader.java b/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferReader.java
new file mode 100644
index 0000000..5defa8c
--- /dev/null
+++ b/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferReader.java
@@ -0,0 +1,267 @@
+/*
+ * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
+ * Copyright (C) 2012 - 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 javolution.io;
+
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * <p> A UTF-8 <code>java.nio.ByteBuffer</code> reader.
+ * </p>
+ *
+ * <p> This reader can be used for efficient decoding of native byte
+ * buffers (e.g. <code>MappedByteBuffer</code>), high-performance
+ * messaging (no intermediate buffer), etc.</p>
+ *
+ * <p> This reader supports surrogate <code>char</code> pairs (representing
+ * characters in the range [U+10000 .. U+10FFFF]). It can also be used
+ * to read characters unicodes (31 bits) directly
+ * (ref. {@link #read()}).</p>
+ *
+ * <p> Each invocation of one of the <code>read()</code> methods may cause one
+ * or more bytes to be read from the underlying byte buffer.
+ * The end of stream is reached when the byte buffer position and limit
+ * coincide.</p>
+ *
+ * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
+ * @version 2.0, December 9, 2004
+ * @see UTF8ByteBufferWriter
+ */
+public final class UTF8ByteBufferReader extends Reader {
+
+ /**
+ * Holds the byte buffer source.
+ */
+ private ByteBuffer _byteBuffer;
+
+ /**
+ * Default constructor.
+ */
+ public UTF8ByteBufferReader() {}
+
+ /**
+ * Sets the <code>ByteBuffer</code> to use for reading available bytes
+ * from current buffer position.
+ *
+ * @param byteBuffer the <code>ByteBuffer</code> source.
+ * @return this UTF-8 reader.
+ * @throws IllegalStateException if this reader is being reused and
+ * it has not been {@link #close closed} or {@link #reset reset}.
+ */
+ public UTF8ByteBufferReader setInput(ByteBuffer byteBuffer) {
+ if (_byteBuffer != null)
+ throw new IllegalStateException("Reader not closed or reset");
+ _byteBuffer = byteBuffer;
+ return this;
+ }
+
+ /**
+ * Indicates if this stream is ready to be read.
+ *
+ * @return <code>true</code> if the byte buffer has remaining bytes to
+ * read; <code>false</code> otherwise.
+ * @throws IOException if an I/O error occurs.
+ */
+ public boolean ready() throws IOException {
+ if (_byteBuffer != null) {
+ return _byteBuffer.hasRemaining();
+ } else {
+ throw new IOException("Reader closed");
+ }
+ }
+
+ /**
+ * Closes and {@link #reset resets} this reader for reuse.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ public void close() throws IOException {
+ if (_byteBuffer != null) {
+ reset();
+ }
+ }
+
+ /**
+ * Reads a single character. This method does not block, <code>-1</code>
+ * is returned if the buffer's limit has been reached.
+ *
+ * @return the 31-bits Unicode of the character read, or -1 if there is
+ * no more remaining bytes to be read.
+ * @throws IOException if an I/O error occurs (e.g. incomplete
+ * character sequence being read).
+ */
+ public int read() throws IOException {
+ if (_byteBuffer != null) {
+ if (_byteBuffer.hasRemaining()) {
+ byte b = _byteBuffer.get();
+ return (b >= 0) ? b : read2(b);
+ } else {
+ return -1;
+ }
+ } else {
+ throw new IOException("Reader closed");
+ }
+ }
+
+ // Reads one full character, throws CharConversionException if limit reached.
+ private int read2(byte b) throws IOException {
+ try {
+ // Decodes UTF-8.
+ if ((b >= 0) && (_moreBytes == 0)) {
+ // 0xxxxxxx
+ return b;
+ } else if (((b & 0xc0) == 0x80) && (_moreBytes != 0)) {
+ // 10xxxxxx (continuation byte)
+ _code = (_code << 6) | (b & 0x3f); // Adds 6 bits to code.
+ if (--_moreBytes == 0) {
+ return _code;
+ } else {
+ return read2(_byteBuffer.get());
+ }
+ } else if (((b & 0xe0) == 0xc0) && (_moreBytes == 0)) {
+ // 110xxxxx
+ _code = b & 0x1f;
+ _moreBytes = 1;
+ return read2(_byteBuffer.get());
+ } else if (((b & 0xf0) == 0xe0) && (_moreBytes == 0)) {
+ // 1110xxxx
+ _code = b & 0x0f;
+ _moreBytes = 2;
+ return read2(_byteBuffer.get());
+ } else if (((b & 0xf8) == 0xf0) && (_moreBytes == 0)) {
+ // 11110xxx
+ _code = b & 0x07;
+ _moreBytes = 3;
+ return read2(_byteBuffer.get());
+ } else if (((b & 0xfc) == 0xf8) && (_moreBytes == 0)) {
+ // 111110xx
+ _code = b & 0x03;
+ _moreBytes = 4;
+ return read2(_byteBuffer.get());
+ } else if (((b & 0xfe) == 0xfc) && (_moreBytes == 0)) {
+ // 1111110x
+ _code = b & 0x01;
+ _moreBytes = 5;
+ return read2(_byteBuffer.get());
+ } else {
+ throw new CharConversionException("Invalid UTF-8 Encoding");
+ }
+ } catch (BufferUnderflowException e) {
+ throw new CharConversionException("Incomplete Sequence");
+ }
+ }
+
+ private int _code;
+
+ private int _moreBytes;
+
+ /**
+ * Reads characters into a portion of an array. This method does not
+ * block.
+ *
+ * <p> Note: Characters between U+10000 and U+10FFFF are represented
+ * by surrogate pairs (two <code>char</code>).</p>
+ *
+ * @param cbuf the destination buffer.
+ * @param off the offset at which to start storing characters.
+ * @param len the maximum number of characters to read
+ * @return the number of characters read, or -1 if there is no more
+ * byte remaining.
+ * @throws IOException if an I/O error occurs.
+ */
+ public int read(char cbuf[], int off, int len) throws IOException {
+ if (_byteBuffer == null)
+ throw new IOException("Reader closed");
+ final int off_plus_len = off + len;
+ int remaining = _byteBuffer.remaining();
+ if (remaining <= 0)
+ return -1;
+ for (int i = off; i < off_plus_len;) {
+ if (remaining-- > 0) {
+ byte b = _byteBuffer.get();
+ if (b >= 0) {
+ cbuf[i++] = (char) b; // Most common case.
+ } else {
+ if (i < off_plus_len - 1) { // Up to two 'char' can be read.
+ int code = read2(b);
+ remaining = _byteBuffer.remaining(); // Recalculates.
+ if (code < 0x10000) {
+ cbuf[i++] = (char) code;
+ } else if (code <= 0x10ffff) { // Surrogates.
+ cbuf[i++] = (char) (((code - 0x10000) >> 10) + 0xd800);
+ cbuf[i++] = (char) (((code - 0x10000) & 0x3ff) + 0xdc00);
+ } else {
+ throw new CharConversionException(
+ "Cannot convert U+"
+ + Integer.toHexString(code)
+ + " to char (code greater than U+10FFFF)");
+ }
+ } else { // Not enough space in destination (go back).
+ _byteBuffer.position(_byteBuffer.position() - 1);
+ remaining++;
+ return i - off;
+ }
+ }
+ } else {
+ return i - off;
+ }
+ }
+ return len;
+ }
+
+ /**
+ * Reads characters into the specified appendable. This method does not
+ * block.
+ *
+ * <p> Note: Characters between U+10000 and U+10FFFF are represented
+ * by surrogate pairs (two <code>char</code>).</p>
+ *
+ * @param dest the destination buffer.
+ * @throws IOException if an I/O error occurs.
+ */
+ public void read(Appendable dest) throws IOException {
+ if (_byteBuffer == null)
+ throw new IOException("Reader closed");
+ while (_byteBuffer.hasRemaining()) {
+ byte b = _byteBuffer.get();
+ if (b >= 0) {
+ dest.append((char) b); // Most common case.
+ } else {
+ int code = read2(b);
+ if (code < 0x10000) {
+ dest.append((char) code);
+ } else if (code <= 0x10ffff) { // Surrogates.
+ dest.append((char) (((code - 0x10000) >> 10) + 0xd800));
+ dest.append((char) (((code - 0x10000) & 0x3ff) + 0xdc00));
+ } else {
+ throw new CharConversionException("Cannot convert U+"
+ + Integer.toHexString(code)
+ + " to char (code greater than U+10FFFF)");
+ }
+ }
+ }
+ }
+
+ public void reset() {
+ _byteBuffer = null;
+ _code = 0;
+ _moreBytes = 0;
+ }
+
+ /**
+ * @deprecated Replaced by {@link #setInput(ByteBuffer)}
+ */
+ public UTF8ByteBufferReader setByteBuffer(ByteBuffer byteBuffer) {
+ return this.setInput(byteBuffer);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/e43574ef/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferWriter.java
----------------------------------------------------------------------
diff --git a/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferWriter.java b/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferWriter.java
new file mode 100644
index 0000000..5765c41
--- /dev/null
+++ b/commons/marmotta-commons/src/ext/java/javolution/io/UTF8ByteBufferWriter.java
@@ -0,0 +1,224 @@
+/*
+ * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
+ * Copyright (C) 2012 - 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 javolution.io;
+
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+
+/**
+ * <p> A UTF-8 <code>java.nio.ByteBuffer</code> writer.</p>
+ *
+ * <p> This writer supports surrogate <code>char</code> pairs (representing
+ * characters in the range [U+10000 .. U+10FFFF]). It can also be used
+ * to write characters from their unicodes (31 bits) directly
+ * (ref. {@link #write(int)}).</p>
+ *
+ * <p> Instances of this class can be reused for different output streams
+ * and can be part of a higher level component (e.g. serializer) in order
+ * to avoid dynamic buffer allocation when the destination output changes.
+ * Also wrapping using a <code>java.io.BufferedWriter</code> is unnescessary
+ * as instances of this class embed their own data buffers.</p>
+ *
+ * <p> Note: This writer is unsynchronized and always produces well-formed
+ * UTF-8 sequences.</p>
+ *
+ * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
+ * @version 2.0, December 9, 2004
+ * @see UTF8ByteBufferReader
+ */
+public final class UTF8ByteBufferWriter extends Writer {
+
+ /**
+ * Holds the byte buffer destination.
+ */
+ private ByteBuffer _byteBuffer;
+
+ /**
+ * Default constructor.
+ */
+ public UTF8ByteBufferWriter() {}
+
+ /**
+ * Sets the byte buffer to use for writing until this writer is closed.
+ *
+ * @param byteBuffer the destination byte buffer.
+ * @return this UTF-8 writer.
+ * @throws IllegalStateException if this writer is being reused and
+ * it has not been {@link #close closed} or {@link #reset reset}.
+ */
+ public UTF8ByteBufferWriter setOutput(ByteBuffer byteBuffer) {
+ if (_byteBuffer != null)
+ throw new IllegalStateException("Writer not closed or reset");
+ _byteBuffer = byteBuffer;
+ return this;
+ }
+
+ /**
+ * Writes a single character. This method supports 16-bits
+ * character surrogates.
+ *
+ * @param c <code>char</code> the character to be written (possibly
+ * a surrogate).
+ * @throws IOException if an I/O error occurs.
+ */
+ public void write(char c) throws IOException {
+ if ((c < 0xd800) || (c > 0xdfff)) {
+ write((int) c);
+ } else if (c < 0xdc00) { // High surrogate.
+ _highSurrogate = c;
+ } else { // Low surrogate.
+ int code = ((_highSurrogate - 0xd800) << 10) + (c - 0xdc00)
+ + 0x10000;
+ write(code);
+ }
+ }
+
+ private char _highSurrogate;
+
+ /**
+ * Writes a character given its 31-bits Unicode.
+ *
+ * @param code the 31 bits Unicode of the character to be written.
+ * @throws IOException if an I/O error occurs.
+ */
+ public void write(int code) throws IOException {
+ if ((code & 0xffffff80) == 0) {
+ _byteBuffer.put((byte) code);
+ } else { // Writes more than one byte.
+ write2(code);
+ }
+ }
+
+ private void write2(int c) throws IOException {
+ if ((c & 0xfffff800) == 0) { // 2 bytes.
+ _byteBuffer.put((byte) (0xc0 | (c >> 6)));
+ _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
+ } else if ((c & 0xffff0000) == 0) { // 3 bytes.
+ _byteBuffer.put((byte) (0xe0 | (c >> 12)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
+ } else if ((c & 0xff200000) == 0) { // 4 bytes.
+ _byteBuffer.put((byte) (0xf0 | (c >> 18)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
+ } else if ((c & 0xf4000000) == 0) { // 5 bytes.
+ _byteBuffer.put((byte) (0xf8 | (c >> 24)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 18) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
+ } else if ((c & 0x80000000) == 0) { // 6 bytes.
+ _byteBuffer.put((byte) (0xfc | (c >> 30)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 24) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 18) & 0x3f)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3F)));
+ _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3F)));
+ _byteBuffer.put((byte) (0x80 | (c & 0x3F)));
+ } else {
+ throw new CharConversionException("Illegal character U+"
+ + Integer.toHexString(c));
+ }
+ }
+
+ /**
+ * Writes a portion of an array of characters.
+ *
+ * @param cbuf the array of characters.
+ * @param off the offset from which to start writing characters.
+ * @param len the number of characters to write.
+ * @throws IOException if an I/O error occurs.
+ */
+ public void write(char cbuf[], int off, int len) throws IOException {
+ final int off_plus_len = off + len;
+ for (int i = off; i < off_plus_len;) {
+ char c = cbuf[i++];
+ if (c < 0x80) {
+ _byteBuffer.put((byte) c);
+ } else {
+ write(c);
+ }
+ }
+ }
+
+ /**
+ * Writes a portion of a string.
+ *
+ * @param str a String.
+ * @param off the offset from which to start writing characters.
+ * @param len the number of characters to write.
+ * @throws IOException if an I/O error occurs
+ */
+ public void write(String str, int off, int len) throws IOException {
+ final int off_plus_len = off + len;
+ for (int i = off; i < off_plus_len;) {
+ char c = str.charAt(i++);
+ if (c < 0x80) {
+ _byteBuffer.put((byte) c);
+ } else {
+ write(c);
+ }
+ }
+ }
+
+ /**
+ * Writes the specified character sequence.
+ *
+ * @param csq the character sequence.
+ * @throws IOException if an I/O error occurs
+ */
+ public void write(CharSequence csq) throws IOException {
+ final int length = csq.length();
+ for (int i = 0; i < length;) {
+ char c = csq.charAt(i++);
+ if (c < 0x80) {
+ _byteBuffer.put((byte) c);
+ } else {
+ write(c);
+ }
+ }
+ }
+
+ /**
+ * Flushes the stream (this method has no effect, the data is
+ * always directly written to the <code>ByteBuffer</code>).
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ public void flush() throws IOException {
+ if (_byteBuffer == null) { throw new IOException("Writer closed"); }
+ }
+
+ /**
+ * Closes and {@link #reset resets} this writer for reuse.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ public void close() throws IOException {
+ if (_byteBuffer != null) {
+ reset();
+ }
+ }
+
+ // Implements Reusable.
+ public void reset() {
+ _byteBuffer = null;
+ _highSurrogate = 0;
+ }
+
+ /**
+ * @deprecated Replaced by {@link #setOutput(ByteBuffer)}
+ */
+ public UTF8ByteBufferWriter setByteBuffer(ByteBuffer byteBuffer) {
+ return this.setOutput(byteBuffer);
+ }
+
+}
\ No newline at end of file