Status | |
---|---|
Stability | stable |
Component Type | library |
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.
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);
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");
You can create Bytes
objects from a base64-encoded string with the fromBase64String
method:
Bytes value = Bytes.fromBase64String("deadbeefISDAbest");
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);
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);
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);
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.
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);
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.
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);
The not
method returns a bit-wise NOT of the bytes.
Bytes value = Bytes.fromHexString("0x01000001").not();
assertEquals(Bytes.fromHexString("0xfefffffe"), value);
The commonPrefix
method returns the common bytes both the value and the argument start with.
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.
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.
The get(i)
method provides the byte at index i
.
The method getInt()
and the method getLong()
respectively return the next 4 or 8 bytes into an int or a long.
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.
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.
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()
.
By default, bytes objects are not mutable. You can use MutableBytes
objects instead.
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 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();
You can set values with different arguments:
- The
set(int i, byte b)
method sets the value of a byte at indexi
. - The
setInt(int i, int value)
method sets the value of the next four bytes at indexi
. - The
setLong(int i, long value)
method sets the value of the next eight bytes at indexi
. - The
set(int offset, Bytes bytes)
method sets the value of the next bytes at indexi
.
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.