Skip to content

Utility functions for getting values from and setting values into collections of bytes, taking into account byte order and word order.

License

Notifications You must be signed in to change notification settings

digitalpetri/byteops

Repository files navigation

ByteOps

License

Utility functions for reading and writing primitive values from byte collections with explicit byte order and word order control.

Features

  • Multiple byte sources: byte[], java.nio.ByteBuffer, and Netty ByteBuf
  • Four byte ordering variations: Big-endian, little-endian, and word-swapped variants
  • Unsigned type support: Optional modules for jOOU and Eclipse Milo unsigned types
  • Zero dependencies: Core module has no runtime dependencies (besides JSpecify annotations)
  • Java 11+: Compatible with Java 11 and later

Installation

Add the dependency to your pom.xml:

<dependency>
  <groupId>com.digitalpetri.util</groupId>
  <artifactId>byteops</artifactId>
  <version>0.2.1</version>
</dependency>

Available Modules

Module Artifact ID Description
Core byteops byte[] and ByteBuffer support
Netty byteops-netty Netty ByteBuf support
Unsigned (jOOU) byteops-unsigned UByte, UShort, UInteger, ULong via jOOU
Unsigned (Milo) byteops-milo Unsigned types from Eclipse Milo

Quick Start

Reading Values

import com.digitalpetri.util.byteops.ByteArrayByteOps;
import com.digitalpetri.util.byteops.ByteOps;

// Get a ByteOps instance for your preferred byte ordering
ByteOps<byte[]> ops = ByteArrayByteOps.BIG_ENDIAN;

byte[] data = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};

short s = ops.getShort(data, 0);   // 0x0001
int i = ops.getInt(data, 0);       // 0x00010203
long l = ops.getLong(data, 0);     // 0x0001020304050607L
float f = ops.getFloat(data, 0);   // Float.intBitsToFloat(0x00010203)
double d = ops.getDouble(data, 0); // Double.longBitsToDouble(0x0001020304050607L)

Writing Values

ByteOps<byte[]> ops = ByteArrayByteOps.LITTLE_ENDIAN;

byte[] buffer = new byte[8];
ops.setInt(buffer, 0, 0x01020304);
ops.setInt(buffer, 4, 0x05060708);
// buffer: [0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05]

Working with Arrays

ByteOps<byte[]> ops = ByteArrayByteOps.BIG_ENDIAN;

// Read an array of ints (2 ints = 8 bytes)
byte[] data = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
int[] ints = ops.getIntArray(data, 0, 2); // [0x00010203, 0x04050607]

// Write an array of shorts
byte[] buffer = new byte[4];
ops.setShortArray(buffer, 0, new short[] {0x0102, 0x0304});
// buffer: [0x01, 0x02, 0x03, 0x04]

With ByteBuffer

import com.digitalpetri.util.byteops.ByteBufferByteOps;
import java.nio.ByteBuffer;

ByteOps<ByteBuffer> ops = ByteBufferByteOps.BIG_ENDIAN;

ByteBuffer buffer = ByteBuffer.allocate(8);
ops.setLong(buffer, 0, 0x0102030405060708L);

long value = ops.getLong(buffer, 0); // 0x0102030405060708L

With Netty ByteBuf

import com.digitalpetri.util.byteops.netty.ByteBufByteOps;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

ByteOps<ByteBuf> ops = ByteBufByteOps.LITTLE_ENDIAN;

ByteBuf buf = Unpooled.buffer(8);
buf.writeZero(8); // ensure writable bytes
ops.setLong(buf, 0, 0x0102030405060708L);

long value = ops.getLong(buf, 0); // 0x0102030405060708L

Unsigned Types (jOOU)

import com.digitalpetri.util.byteops.unsigned.UnsignedByteOps;
import org.joou.UInteger;

ByteOps<byte[]> baseOps = ByteArrayByteOps.BIG_ENDIAN;
UnsignedByteOps<byte[]> ops = UnsignedByteOps.of(baseOps);

byte[] data = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
UInteger value = ops.getUInt(data, 0); // 4294967295 (not -1)

Byte Ordering

Four byte ordering variations are available for each implementation:

Constant Byte Order Word Order
BIG_ENDIAN Big High-Low
LITTLE_ENDIAN Little High-Low
BIG_ENDIAN_LOW_HIGH Big Low-High
LITTLE_ENDIAN_LOW_HIGH Little Low-High

Visual Example

For a 32-bit integer 0x01020304:

BIG_ENDIAN:             [0x01, 0x02, 0x03, 0x04]  (bytes: 1,2,3,4)
LITTLE_ENDIAN:          [0x04, 0x03, 0x02, 0x01]  (bytes: 4,3,2,1)
BIG_ENDIAN_LOW_HIGH:    [0x03, 0x04, 0x01, 0x02]  (words swapped: 3,4,1,2)
LITTLE_ENDIAN_LOW_HIGH: [0x02, 0x01, 0x04, 0x03]  (words swapped: 2,1,4,3)

API Overview

The ByteOps<T> interface provides:

Single Value Operations

Method Size Description
getBoolean / setBoolean 1 byte false = 0, true = non-zero
getByte / setByte 1 byte Signed byte
getShort / setShort 2 bytes Signed 16-bit integer
getInt / setInt 4 bytes Signed 32-bit integer
getLong / setLong 8 bytes Signed 64-bit integer
getFloat / setFloat 4 bytes IEEE 754 single-precision
getDouble / setDouble 8 bytes IEEE 754 double-precision

Array Operations

  • get*Array / set*Array - Primitive arrays (boolean[], byte[], short[], etc.)
  • getBoxed*Array / setBoxed*Array - Boxed arrays (Boolean[], Byte[], Short[], etc.)

Development

Requirements

  • JDK 17 is required to build (via Maven Toolchains)
  • Target: Java 11 bytecode

Maven Toolchains Setup

Create or edit ~/.m2/toolchains.xml:

<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>17</version>
    </provides>
    <configuration>
      <jdkHome>/path/to/your/jdk-17</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

Build Commands

mvn clean package    # Build all modules
mvn test             # Run tests
mvn spotless:apply   # Format code (Google Java Format)
mvn spotless:check   # Check code formatting

Project Structure

byteops/
├── byteops/           # Core module (byte[], ByteBuffer)
├── byteops-netty/     # Netty ByteBuf support
├── byteops-unsigned/  # jOOU unsigned types (UByte, UShort, UInteger, ULong)
└── byteops-milo/      # Eclipse Milo unsigned types

License

This project is licensed under the Eclipse Public License 2.0.

About

Utility functions for getting values from and setting values into collections of bytes, taking into account byte order and word order.

Resources

License

Stars

Watchers

Forks