tree: b2dec873876b0ab00638b4c5d673d4b8f78d2469 [path history] [tgz]
  1. src/
  2. build.gradle
  3. README.md
bytes/README.md

Bytes

Status
Stabilitystable
Component Typelibrary

Getting started

Apache Tuweni provides support for manipulating bytes.

To get started, install the bytes library.

With Maven:

<dependency>
  <groupId>org.apache.tuweni</groupId>
  <artifactId>bytes</artifactId>
  <version>2.3.1</version> <!-- replace with latest release -->
</dependency>

Or using Gradle:

implementation("org.apache.tuweni:bytes:2.3.1") // replace with latest release

The bytes library revolves mainly around the Bytes interface.

This tutorial goes over the main uses of Bytes, from creating them to manipulating them.

Creating Bytes

From a bytes array:

You can create Bytes objects by wrapping a native byte array:

Bytes bytes = Bytes.wrap(new byte[] {1, 2, 3, 4});

Note the underlying array is not copied - any change to the byte array will change the Bytes object's behavior.

You can also wrap with an offset and a size to select a portion of the array:

// wrapping with an offset of 2 and a size of one byte
Bytes bytes = Bytes.wrap(new byte[] {1, 2, 3, 4}, 2, 1);

From a hex string:

You can create Bytes objects from a hexadecimal-encoded string with the fromHexString method:

Bytes bytes = Bytes.fromHexString("0xdeadbeef");

The "0x" prefix is optional.

However, this requires an even-sized string. For example, this succeeds:

Bytes bytes = Bytes.fromHexString("01FF2A");

This fails:

Bytes bytes = Bytes.fromHexString("1FF2A");

You can circumvent this with the fromHexStringLenient method:

Bytes bytes = Bytes.fromHexStringLenient("1FF2A");

From a base64-encoded string:

You can create Bytes objects from a base64-encoded string with the fromBase64String method:

Bytes value = Bytes.fromBase64String("deadbeefISDAbest");

From primitive types

We also have convenience methods to create Bytes objects from primitive types.

Bytes.of() takes a variadic argument of bytes:

Bytes value = Bytes.of(0x00, 0x01, 0xff, 0x2a);
Bytes value = Bytes.ofUnsignedInt(42);

More wrapping

Use Bytes.wrapByteBuf(buffer) to wrap a Netty ByteBuf object as a Bytes object.

ByteBuf buffer = Unpooled.buffer(42);
Bytes.wrapByteBuf(buffer);

You can apply an offset and size parameter:

Bytes value = Bytes.wrapByteBuf(buffer, 1, 1);

Use Bytes.wrapByteBuffer(buffer) to wrap a ByteBuffer object as a Bytes object.

Bytes.wrapByteBuffer(buffer);

You can apply an offset and size parameter:

Bytes value = Bytes.wrapByteBuffer(buffer, 1, 1);

Use Bytes.wrapBuffer(buffer) to wrap a Vert.x Buffer object as a Bytes object.

Bytes.wrapBuffer(buffer);

You can apply an offset and size parameter:

Bytes value = Bytes.wrapBuffer(buffer, 1, 1);

Random

You can create random bytes objects of a given length with the Bytes.random() method:

// create a Bytes object of 20 bytes:
Bytes.random(20);

Create a Bytes object with our own Random implementation:

Random random = new SecureRandom();
...
Bytes.random(20, random);

Manipulating bytes

Concatenating and wrapping

You can concatenate or wrap bytes objects.

When concatenating, the underlying objects are copied into a new bytes object.

When wrapping, you are creating a view made of the underlying bytes objects. If their values change, the wrapped view changes as well.

Of course, wrapping is preferrable to avoid copying bytes in memory.

Copying and slicing

In the same spirit as above, you can copy or slice bytes objects. When you slice a bytes object, you create a view of the original bytes object, and the slice will change if the underlying bytes object changes. If you copy instead, you create a new copy of the bytes.

// slice from the second byte:
bytes.slice(2);
// slice from the second byte to the fifth byte:
bytes.slice(2, 5);

Shifting bytes

You can shift right and left the bits of a bytes object by a given distance.

This is equivalent to the <<< or >>> operators in Java.

xor, or, and

You can apply boolean operations to Bytes objects.

Those methods take as argument the value to compare this value with, and return a new Bytes object that is the result of the boolean operation.

If the argument and the value are different lengths, then the shorter will be zero-padded to the left.

Bytes value = Bytes.fromHexString("0x01000001").xor(Bytes.fromHexString("0x01000000"));
assertEquals(Bytes.fromHexString("0x00000001"), value);

not

The not method returns a bit-wise NOT of the bytes.

Bytes value = Bytes.fromHexString("0x01000001").not();
assertEquals(Bytes.fromHexString("0xfefffffe"), value);

commonPrefix

The commonPrefix method returns the common bytes both the value and the argument start with.

Extracting values

You can extract values from a bytes object into native Java objects such as ints and longs, bytes, byte arrays and so on.

Note all the methods here take an optional ByteOrder argument, defaulting to big endian by default.

toInt() and toLong()

The method toInt() and the method toLong() respectively translate the bytes values into an int or a long, requiring respectively the value to be at most 4 or 8 bytes long.

get(i)

The get(i) method provides the byte at index i.

getInt(i) and getLong(i)

The method getInt() and the method getLong() respectively return the next 4 or 8 bytes into an int or a long.

toArray() and toArrayUnsafe()

The method toArray copies the bytes of the object into a new bytes array.

The method toArrayUnsafe makes available the underlying byte array of the object - modifying it changes the Bytes object. Note this is more performant as it doesn't allocate new memory.

To BigIntegers

The method toUnsignedBigInteger creates a new unsigned BigInteger object with the contents of the Bytes object. You can also use the method toBigInteger to represent Bytes as a signed integer, using the two's-complement representation.

Transforming Bytes into strings

There is a sleuth of options to turn bytes into strings, and they all have different use cases.

  • The method toHexString provides the value represented as hexadecimal, starting with “0x”.
  • The method toUnprefixedHexString provides the value represented as hexadecimal, no “0x” prefix though.
  • The method toShortHexString provides the value represented as a minimal hexadecimal string (without any leading zero).
  • The method toQuantityHexString provides the value represented as a minimal hexadecimal string (without any leading zero, except if it's valued zero or empty, in which case it returns 0x0).
  • The method toEllipsisHexString provides the first 3 bytes and last 3 bytes represented as hexadecimal strings, joined with an ellipsis (...).

By default, toString() calls toHexString().

Mutable Bytes

By default, bytes objects are not mutable. You can use MutableBytes objects instead.

Creating MutableBytes objects

The methods described in the tutorial “Creating Bytes” all work in the same way for MutableBytes.

You can call the method mutableCopy() on any Bytes object to get a copy of the Bytes object as mutable.

Finally, you can create fresh objects with the create() method.

Fill, Clear

Fill a MutableBytes with the same byte the fill method:

MutableBytes bytes = MutableBytes.create(2);
bytes.fill((byte) 34);
assertEquals(Bytes.fromHexString("0x2222"), bytes);

You can clear the contents with the clear method:

MutableBytes bytes = MutableBytes.fromHexString("0xdeadbeef");
bytes.clear();

Setting values

You can set values with different arguments:

Your own Bytes class

You can create your very own implementation of Bytes by extending the AbstractBytes class.

You will need to implement the following functions:

You can choose to simplify greatly by extending the DelegatingBytes class instead.