SSL defines a number of protocol objects in the spec. These objects are specified as “structs” in the text, that is, an ordered list of named, simpler objects. An example may be:
struct {
uint16 s;
enum { foo(1) bar(2), ..., (255) } e;
opaque o<1..256>;
} s;
This defines a layout of bytes, and attaches meaning to offsets into such a sequence of bytes, much like a C struct definition will. The difference is that the structure size isn’t constant: you don’t have pointers, so your variable-sized member data types really are variable sized, instead of being a pointer to variable-sized data.
In the previous version of Jessie, we did the obvious thing for blocking-IO users: we defined objects that matched structures, such as:
class s {
short s;
enum_type e;
byte[] o;
}
…and we’d fill these fields in just by reading each type from an InputStream. A better way to do that, though, is to just use offsets into real byte sequences. And that’s what we’re going to do in the new version of Jessie: instead of copying types from the input stream into native types, we’ll just hold on to a ByteBuffer representing the encoded structures, as read from the socket, and provide getters and setters that just find the right offset into that byte buffer to write the objects.
This is nice, mostly because it requires no unecessary copying: you get a copy of the object when you need to read it as a native type; otherwise, it’s stored as it was read. Also, if you have a mutable object, changes you make to that object are direct: you change a field foo in an object, and that changes the underlying buffer. There’s no need to separate storage from encoding.
A problem with this is that editing an object has to be done carefully. If one field b follows a variable-sized field a, you must write a first, because otherwise you have no idea where b will go. This is probably O.K., because you usually only write a type of object once in the SSL protocol, so you know where to be careful, at least, or the objects written often are simple enough that it doesn’t matter. You also don’t really know that converting-on-access is saving you anything, because you presumably have to decode everything exactly once anyway.
That’s the approach I’m going to use, anyway. It seems conceptually clean, and very “hands-off” w/r/t the byte buffers we’re going to be getting as input.