I have been experimenting with something that I think may come in handy. A C like structures defined via annotation. There are other libraries that do this kind of thing such as NPL in http://jnetstream project and google code Protocol Buffers. The difference here is that no external language is required and all is accomplished with a single annotation. The classes look very much like C structures and their behavior and limitations are nearly identical.
The advantage of such as a structure in jnetpcap pcap, would be if someone wanted to copy the values of a particular header and load them into a class. More specifically load values of a binary structure into fields of a class. This should, at least in theory, improve performance with working with such as structure since the code is simply accessing class fields, vs. using native methods to read a single primitive value out of a buffer. There should especially be noticeable performance increase if a structure has to be access many times.
The copy of data into the class fields is accomplished using JNI calls only once, thereafter the structure values are access via java fields (not methods). Theoretically increasing performance as java is greatly optimized for accessing values from class fields.
Here is the struct annotation javadoc comment that contains examples of how this API can be used:
The following C structure has corresponding java counter part:
struct Abc {
int a;
int b;
int c;
};
public class Abc {
public @struct(0) int a;
public @struct(1) int b;
public @struct(2) int c;
public final static JStruct<Abc> struct_abc = JStruct.load(Abc.class);
}
And this is how the above structure is used in java:
Abc abc = new Abc();
byte[] data = {}; // Some data buffer
abc.struct_abc.read(data, abc);
Only by convention, the struct_abc static information about a class, is
stored as a public final field within the class itself. This information can
be stored anywhere, but typically is stored within the class for convenience.
Alternatively you can define a structure for any class that has fields you
would like to have set and those fields correspond to some data structure.
For example the above C structure could also be defined in a more natural
way without the use of annotations:
public class Abc {
public int a;
public int b;
public int c;
}
Abc abc = new Abc();
byte[] data = {}; // Some data buffer
JStruct struct_abc = JStruct.load(Abc.class, "a", "b", "c");
struct_abc.read(data, abc);
The struct_abc is again singleton runtime information about the class objects
(which fields to set, signed/unsigned, how many bits, endianness, etc..). This
information can be stored anywhere, including the class itself, but this time
we did not use annotations. This allows any class with fields, to be treated
as a structure and those fields set.
Modifiers on the structure class have no bearings. The JStruct mechanism
bypasses normal private/protected/package local and static modifiers. It will
set the field value on any of the above fields.
Another nice thing about this API, is that it works outside the JPacket/JHeader realm. It works on several types of input data. The input can be a JBuffer mapping native memory to structure. A java.nio.ByteBuffer again, mapping native memory to structure. A byte[] allowing data to be mapped for a byte array and lastly on standard java IO streams or Channels.
So this is what a Ethernet structure might look like in practice. Typically with jnetpcap we use a Ethernet header definition object and we peer it (map it), with a native packet buffer over where ethernet header resides:
Ethernet ethernet = new Ethernet();
if (packet.hasHeader(ethernet)) {
System.out.printf("protocol=0x%X\n", ethernet.type());
}
If we needed to access these fields over and over for the same packet, it might be worth while to setup a structure and copy those values out of it, like so:
public class EthernetStruct implements JHeaderStruct {
public @struct(0) byte[] dst = new byte[6];
public @struct(1) byte[] src = new byte[6];
public @struct{1} short type;
public int getHeaderId() {
return JProtocol.ETHERNET_ID;
}
}
EthernetStruct ethernet = new EthernetStruct();
if (packet.hasHeader(ethernet)) {
System.out.printf("protocol=0x%X\n", ethernet.type);
}
Notice that our code looks almost identical to typical JHeader based calls. The difference is that we are instantiating a EthernetStruct class which does not extend JHeader. That means we don't have any of the header's runtime information within the packet. What we do get, is a copy of the values for all ethernet fields.
The method packet.hasHeader does some things on our behalf. First it expects objects that implements JHeaderStruct interface which has only 1 method. This method allows the packet API to lookup the right header within the packet. It also prevents just any type of object to be passed in as a parameter. Its upto the implementer to design and setup the structure class anyway desired. For example complementary accessor methods might be provided that synchronize on the object. But for the most part, fields can be used directly. Since reflection and JNI are used behind the scenes to actually set the values of these fields, the typical security mechanisms are bypassed allowing fields marked 'final' to be set, thus preventing any casual users from changing the values copied into the fields. The structure classes are reusable, so the actual runtime object needs to be created once, and reused for many packets and headers if desired.
Anyway, I think this is a neat construct to be possibly added to the API. I especially like it, since its not really tied to packets and headers. It can be used on raw JBuffers and many types of inputs.
It does lack the 'union' construct, although its possible to add it in the future. For simplicity reasons, I would not implement that right away. And unlike other libraries, there is no need for separate compilation of scripts. The jnetsoft libraries' JNI implementation can set those fields as efficiently as anything out there. Actually more efficiently since we are setting class fields directly and do not rely on accessor methods (java and native) for actual value retrieval.
I should add that struct annotation takes several parameters such as "endiannes", "signed" and "bits". These allow the field to be setup as unsigned, BIG or LITTLE endian, and sub-integer in length just like C structure bit-fields. The rules for bit-fields are exactly the same as in C structure. Where multiple adjacent bit-fields are accessed from a single primitive encompassing them. Adjacent full size primitive, is then read out of the next sequence of bytes, etc. Like I said the only thing missing are 'unions'.
Also, you can set defaults on these by setting the struct annotation on the class type itself. It works as class wide defaults for all of the fields defined within.
Both read and write operations are supported, so you can modify the structure fields and write them to desired output such as back to JBuffer, OutputStream, byte[], etc...
Lastly, JStruct can also define a structure without the use of any annotations at all. You can supply the class and list the names of the fields you want to use. There is an example of this usage int the javadoc above (between the HR-lines).
I think this might complement the rest of the API very nicely, since it adds great amount of efficiency. For example if an application does not need all of the runtime information about packets and headers, structures can be a very efficient way to retain the information need as pure java class file, allowing packets and their buffers to be reclaimed quickly.
I haven't decided whether to add this into the overall API or not. It does add extra complexity to the API. This is something that would need to be supported. On the other hand its a single java class and a single annotation we are talking about. Not a very big overhead at all.