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:
Most significant byte first (network byte order)
BIG-ENDIAN
[0]
12
[1]
34
[2]
56
[3]
78
Least significant byte first (x86/x64 native)
LITTLE-ENDIAN
[0]
78
[1]
56
[2]
34
[3]
12
Specifying Byte Order
The default byte order is ByteOrder.NATIVE (matches the CPU's native endianness). 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) // NATIVE (matches CPU endianness)
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 need to read/write different byte orders in the same buffer, read the largest primitive and swap bytes:
// 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.NATIVE) - Document byte order - especially at API boundaries
- Read largest primitive - swap bytes after rather than reading byte-by-byte