JADE v6.1

com.dautelle.util
Class Struct

java.lang.Object
  extended bycom.dautelle.util.Struct
Direct Known Subclasses:
Union

public abstract class Struct
extends java.lang.Object

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 Struct's members and follows the same alignment rules as C/C++ structs.

This class (as well as the 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 Struct/Union using simple text macros. Here is an example of C struct:

     struct Date {
         unsigned short year;
         unsigned char month;
         unsigned char day;
     };
     struct Student {
         char        name[64];
         struct Date birth;
         float       grades[10];
         Student*    next;
     };
and here is the Java equivalent using this class:
     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  = (Date) new StructMember(Date.class).get();
         public final Float32[]   grades = (Float32[]) new ArrayMember(Float32.class, 10).get();
         public final Reference32 next   =  new Reference32(Student.class);
     }
Struct's members are directly accessible:
     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) student.next.get();

Applications may also work with the raw bytes directly. The following illustrate how Struct can be used to decode/encode UDP messages directly:

     class MyUdpMessage extends Struct {
         ... // UDP message fields.
         public MyUdpMessage(byte[] bytes) {
             super(ByteBuffer.wrap(bytes));
         }
     }
     public void run() {
         byte[] bytes = new byte[1024];
         DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
         MyUdpMessage message = new MyUdpMessage(bytes);
         // packet and message are now two different views of the same data. 
         while (true) {
             _socket.receive(packet);
             ... // Process message fields directly.
             packet.setLength(bytes.length); // Reset length to buffer's length.
         }
     }

It is relatively easy to map instances of this class to any physical address using JNI. Here is an example:

     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() {
             super(Clock.nativeBuffer());
         }
         private static native ByteBuffer nativeBuffer();
     }
Below is the nativeBuffer() implementation (Clock.c):
     #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)
     }

Finally, bit-fields are supported (see Clock example above). Bit-fields allocation order is defined by the Struct byteOrder() return value (leftmost bit to rightmost bit if BIG_ENDIAN and rightmost bit to leftmost bit if LITTLE_ENDIAN). Unless the Struct 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).

This class is public domain (not copyrighted).

Version:
5.3, December 7, 2003
Author:
Jean-Marie Dautelle

Nested Class Summary
 class Struct.ArrayMember
          This class represents an array member.
 class Struct.Bool
          This class represents a 8 bits boolean with true represented by 1 and false represented by 0.
 class Struct.Enum16
          This class represents a 16 bits Enum.
 class Struct.Enum32
          This class represents a 32 bits Enum.
 class Struct.Enum64
          This class represents a 64 bits Enum.
 class Struct.Enum8
          This class represents a 8 bits Enum.
 class Struct.Float32
          This class represents a 32 bits float (C/C++/Java float).
 class Struct.Float64
          This class represents a 64 bits float (C/C++/Java double).
protected  class Struct.Member
          This inner class represents the base class for all Struct members.
 class Struct.Reference32
           This class represents a 32 bits reference (C/C++ pointer) to a Struct object (other types may require a Struct wrapper).
 class Struct.Reference64
           This class represents a 64 bits reference (C/C++ pointer) to a Struct object (other types may require a Struct wrapper).
 class Struct.Signed16
          This class represents a 16 bits signed integer.
 class Struct.Signed32
          This class represents a 32 bits signed integer.
 class Struct.Signed64
          This class represents a 64 bits signed integer.
 class Struct.Signed8
          This class represents a 8 bits signed integer.
 class Struct.StructMember
          This class represents an inner struct member.
 class Struct.Unsigned16
          This class represents a 16 bits unsigned integer.
 class Struct.Unsigned32
          This class represents a 32 bits unsigned integer.
 class Struct.Unsigned8
          This class represents a 8 bits unsigned integer.
 class Struct.UTF8String
          This class represents a UTF-8 character string, null terminated (for C/C++ compatibility)
 
Constructor Summary
Struct()
          Default constructor.
Struct(java.nio.ByteBuffer byteBuffer)
          Creates a Struct backed by the specified ByteBuffer.
 
Method Summary
 long address()
          Returns this Struct address.
 java.nio.ByteBuffer byteBuffer()
          Returns the ByteBuffer backing this Struct.
 java.nio.ByteOrder byteOrder()
          Returns the byte order for this Struct.
static Struct copy(Struct src, Struct dest)
          Copies the content of the specified source Struct to the specified destination Struct.
 boolean equals(java.lang.Object that)
          Indicates if this Struct is equal to the specified object.
 int hashCode()
          Returns a hash code value for this Struct.
 boolean isPacked()
          Indicates if this Struct is packed.
 int size()
          Returns the size in bytes of this Struct.
 java.lang.String toString()
          Returns the String representation of this Struct in the form of its constituing bytes (hexadecimal).
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Constructor Detail

Struct

public Struct()
Default constructor. Top-level Struct created using this constructor are backed by a direct buffer. Inner Struct are always backed by the buffer of their outer parent.


Struct

public Struct(java.nio.ByteBuffer byteBuffer)
Creates a Struct backed by the specified ByteBuffer. 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). The address of the first field is always at position 0 in the specified buffer.

Parameters:
byteBuffer - the byte buffer for this Struct.
Method Detail

size

public final int size()
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 members).

Returns:
the C/C++ sizeof(this).

byteBuffer

public final java.nio.ByteBuffer byteBuffer()
Returns the ByteBuffer backing this Struct.

Changes to the buffer's content are visible in the Struct, and vice versa.

The buffer limit is size() and its position index is undefined. Methods modifying the buffer position should either synchronize on the buffer or work with a duplicate.

The buffer of an inner Struct is a shared subsequence of the buffer's content of its outer parent.

The buffer is direct if, and only if, this Struct (or its outer Struct) is backed by a direct buffer (see Struct(ByteBuffer)).

The position index of a Struct.Member within the buffer is given by Struct.Member.offset() (the first field member starting at 0).

Returns:
the actual byte buffer for this Struct.

copy

public static Struct copy(Struct src,
                          Struct dest)
Copies the content of the specified source Struct to the specified destination Struct.

Parameters:
src - the source Struct.
dest - the destination Struct.
Returns:
dest
Throws:
java.lang.IllegalArgumentException - if dest is not an instance of src.getClass(),

equals

public boolean equals(java.lang.Object that)
Indicates if this Struct is equal to the specified object.

Parameters:
that - the object to compare for equality.
Returns:
true if this struct and the specified object are both struct of same class with same content; false otherwise.

hashCode

public final int hashCode()
Returns a hash code value for this Struct.

Note: Because Struct hash codes are content-dependent, it is inadvisable to use Struct as keys in hash maps or similar data structures unless it is known that their contents will not change.

Returns:
this struct hash code value.

address

public final long address()
Returns this Struct address. This method allows for Struct to be referenced (e.g. pointer) from other Struct.

Returns:
the struct memory address.
Throws:
java.lang.UnsupportedOperationException - if the struct's buffer is not a direct buffer.

toString

public java.lang.String toString()
Returns the String representation of this Struct in the form of its constituing bytes (hexadecimal). For example:
     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

Returns:
a hexadecimal representation of the bytes content for this Struct.

byteOrder

public java.nio.ByteOrder byteOrder()
Returns the byte order for this Struct. The byte order is inherited by inner structs. Sub-classes may change the byte order by overriding this method. For example:
 public class TopStruct extends Struct {
     ... // Members initialization.
     public ByteOrder byteOrder() {
         // TopStruct and its inner structs use hardware byte order.
         return ByteOrder.nativeOrder();
    }
 }}

Returns:
the byte order when reading/writing multibyte values (default: network byte order, BIG_ENDIAN).

isPacked

public boolean isPacked()
Indicates if this Struct is packed. By default, Struct.Member of a Struct are aligned on the boundary corresponding to the member's 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:
 public class TopStruct extends Struct {
     ... // Members initialization.
     public boolean isPacked() {
         // TopStruct and its inner structs are packed.
         return true;
     }
 }}

Returns:
true if alignment requirements are ignored. false otherwise (default).

JADE v6.1

Copyright © 2004 Jean-Marie Dautelle.