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
Member members and follows the same alignment
rules as C/C++ structs .
This class (as well as the
Union sub-class) facilitates:
- Memory sharing between Java applications and native libraries.
- Direct encoding/decoding of streams for which the structure
is defined by legacy C/C++ code.
- Serialization/deserialization of Java objects (complete control,
e.g. no class header)
- Mapping of Java objects to physical addresses (with JNI).
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 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
Struct.getByteBuffer() bytes directly. The following illustrate how
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 ):[code]
#include
#include "Clock.h" // Generated using javah
JNIEXPORT jobject JNICALL Java_Clock_nativeBuffer (JNIEnv *env, jclass) {
return (*env)->NewDirectByteBuffer(env, clock_address, buffer_size)
}[/code]
Bit-fields are supported (see Clock example above).
Bit-fields allocation order is defined by the Struct
Struct.byteOrder return value (leftmost bit to rightmost bit if
BIG_ENDIAN and rightmost bit to leftmost bit if
LITTLE_ENDIAN ).
Unless the Struct
Struct.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
Struct.setByteBuffer ByteBuffer
and/or the Struct
Struct.setByteBufferPosition position in its
ByteBuffer to allow for a single
Struct object to
encode/decode multiple memory mapped instances.
Note: Because Struct/Union are basically wrappers around
java.nio.ByteBuffer , tutorials/usages for
the Java NIO package are directly applicable to Struct.
author: Jean-Marie Dautelle version: 5.1, July 17, 2007 |