Byte Order
Byte order (endianness) determines how multi-byte values are stored in memory.
Big-Endian vs Little-Endian
Consider the integer 0x12345678 stored in a 4-byte buffer:
Specifying Byte Order
The default byte order is ByteOrder.BIG_ENDIAN. Note that ByteOrder.NATIVE is not detected at runtime — it's a fixed alias for LITTLE_ENDIAN (chosen because most CPUs, including x86/x64 and ARM in its common configuration, are little-endian, so NATIVE avoids byte-swap overhead there). Specify a different byte order when needed:
import com.ditchoom.buffer.BufferFactory
import com.ditchoom.buffer.ByteOrder
// Big-endian (network byte order)
val bigEndian = BufferFactory.Default.allocate(
size = 1024,
byteOrder = ByteOrder.BIG_ENDIAN
)
// Little-endian
val littleEndian = BufferFactory.Default.allocate(
size = 1024,
byteOrder = ByteOrder.LITTLE_ENDIAN
)
Checking Byte Order
val buffer = BufferFactory.Default.allocate(1024)
println(buffer.byteOrder) // BIG_ENDIAN (the default)
When to Use Each
Big-Endian (BIG_ENDIAN)
- Network protocols: TCP/IP, HTTP, TLS
- File formats: PNG, JPEG, PDF
// Network packet parsing
val buffer = BufferFactory.Default.allocate(1024, ByteOrder.BIG_ENDIAN)
buffer.writeShort(80) // Port number in network byte order
Little-Endian (LITTLE_ENDIAN)
- x86/x64 native: Intel/AMD processors
- Windows file formats: BMP, WAV
- Some protocols: USB, PCIe
// Windows BMP file parsing
val buffer = BufferFactory.Default.allocate(1024, ByteOrder.LITTLE_ENDIAN)
buffer.writeInt(fileSize)
Common Protocol Byte Orders
| Protocol/Format | Byte Order |
|---|---|
| TCP/IP | Big-Endian |
| HTTP | Big-Endian |
| MQTT | Big-Endian |
| WebSocket | Big-Endian |
| USB | Little-Endian |
| BMP | Little-Endian |
| WAV | Little-Endian |
| Protocol Buffers | Little-Endian (varint) |
Example: Protocol Parsing
// Network protocols use big-endian
val buffer = BufferFactory.Default.allocate(1024, ByteOrder.BIG_ENDIAN)
// Write a length-prefixed message
buffer.writeByte(0x01) // message type
buffer.writeShort(payloadLength.toShort())
buffer.writeString(payload)
Mixing Byte Orders
If you're defining a protocol message with @ProtocolMessage, the idiomatic way to mix byte orders within a single message is the @WireOrder(Endianness.Big) / @WireOrder(Endianness.Little) field annotation — it overrides the message-level byte order per field and is handled for you by the generated codec.
For raw-buffer code without a codec, read the largest primitive and swap bytes manually:
// Read a little-endian int from a big-endian buffer
// Uses one memory read (readInt) instead of 4 separate readByte() calls
fun ReadBuffer.readLittleEndianInt(): Int {
val bigEndian = readInt()
return ((bigEndian and 0xFF) shl 24) or
((bigEndian and 0xFF00) shl 8) or
((bigEndian and 0xFF0000) ushr 8) or
((bigEndian and 0xFF000000.toInt()) ushr 24)
}
// Similarly for Long (swap all 8 bytes)
fun ReadBuffer.readLittleEndianLong(): Long {
val bigEndian = readLong()
return ((bigEndian and 0xFF) shl 56) or
((bigEndian and 0xFF00) shl 40) or
((bigEndian and 0xFF0000) shl 24) or
((bigEndian and 0xFF000000) shl 8) or
((bigEndian and 0xFF00000000) ushr 8) or
((bigEndian and 0xFF0000000000) ushr 24) or
((bigEndian and 0xFF000000000000) ushr 40) or
((bigEndian ushr 56) and 0xFF)
}
Best Practices
- Use big-endian for network code - it's the standard
- Match the protocol spec - always check documentation
- Be explicit - specify byte order when it matters (default is
ByteOrder.BIG_ENDIAN) - Document byte order - especially at API boundaries
- Read largest primitive - swap bytes after rather than reading byte-by-byte