Categories
Java

ByteBuffer.duplicate() Does Not Preserve Byte Order

I’ve just spent the last two hours pulling out my hair wondering why my ByteBuffer duplication code doesn’t work.

I’m having fun with a side project to create a lightweight CIFS server in Java. Since the CIFS protocol is in little endian byte order, I have to explicitly specify the byte order via the order API:

public static ByteBuffer allocate(int length) {
	ByteBuffer byteBuffer = ByteBuffer.allocate(length);
	byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
	return byteBuffer;
}

Having a little utility method such as the above makes it easy to stop worrying about the byte ordering.

Many CIFS requests contain two sections – one section for request parameters and one section for request data. This seemed like a good place to use the duplicate API to split the one ByteBuffer into two “views”. (Note, this still uses the underlying buffer so you’re not making extra copies.)

However, my first pass at the code contained a subtle, but nasty bug. Suddenly values in the duplicated buffer were coming out with crazy values but the original buffer worked fine. And apparently I’m not the only one who’s been tripped up by it. A bug was filed over five years ago against this very API call, noting that the ordering is not preserved from the original to the duplicate. My favorite comment from the Java Bug Parade entry is:

No rightminded developer wants his duplicated ByteBuffer to behave differently than the original.

I would agree 😉

Fortunately this is easy to fix, and creating another utility method for it makes it even clearer:

public static ByteBuffer duplicate(ByteBuffer buffer) {
	ByteBuffer duplicateBuffer = (ByteBuffer)buffer.duplicate();
	duplicateBuffer.order(buffer.order());       // duplicate() does not preserve ordering!
	return duplicateBuffer;
}

This little method then preserves my ByteOrder.LITTLE_ENDIAN setting to avoid future problems. Plus, I can hide away the pre-Generics cast from Buffer to ByteBuffer in one place 😉