All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tuweni.bytes.Bytes Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.apache.tuweni.bytes;

import static java.lang.String.format;
import static java.nio.ByteOrder.BIG_ENDIAN;
import static org.apache.tuweni.bytes.Checks.checkArgument;
import static org.apache.tuweni.bytes.Checks.checkElementIndex;
import static org.apache.tuweni.bytes.Checks.checkNotNull;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.math.BigInteger;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Random;

import io.netty.buffer.ByteBuf;
import io.vertx.core.buffer.Buffer;

/**
 * A value made of bytes.
 *
 * 

* This interface makes no thread-safety guarantee, and a {@link Bytes} value is generally not thread safe. However, * specific implementations may be thread-safe. For instance, the value returned by {@link #copy} is guaranteed to be * thread-safe as it is immutable. */ public interface Bytes extends Comparable { /** * The empty value (with 0 bytes). */ Bytes EMPTY = wrap(new byte[0]); /** * Wrap the provided byte array as a {@link Bytes} value. * *

* Note that value is not copied and thus any future update to {@code value} will be reflected in the returned value. * * @param value The value to wrap. * @return A {@link Bytes} value wrapping {@code value}. */ static Bytes wrap(byte[] value) { return wrap(value, 0, value.length); } /** * Wrap a slice of a byte array as a {@link Bytes} value. * *

* Note that value is not copied and thus any future update to {@code value} within the slice will be reflected in the * returned value. * * @param value The value to wrap. * @param offset The index (inclusive) in {@code value} of the first byte exposed by the returned value. In other * words, you will have {@code wrap(value, o, l).get(0) == value[o]}. * @param length The length of the resulting value. * @return A {@link Bytes} value that expose the bytes of {@code value} from {@code offset} (inclusive) to * {@code offset + length} (exclusive). * @throws IndexOutOfBoundsException if {@code offset < 0 || (value.length > 0 && offset >= * value.length)}. * @throws IllegalArgumentException if {@code length < 0 || offset + length > value.length}. */ static Bytes wrap(byte[] value, int offset, int length) { checkNotNull(value); if (length == 32) { return new ArrayWrappingBytes32(value, offset); } return new ArrayWrappingBytes(value, offset, length); } /** * Wrap the provided byte array as a {@link Bytes} value, encrypted in memory. * * * @param value The value to secure. * @return A {@link Bytes} value securing {@code value}. */ static Bytes secure(byte[] value) { return secure(value, 0, value.length); } /** * Wrap a slice of a byte array as a {@link Bytes} value, encrypted in memory. * * * @param value The value to secure. * @param offset The index (inclusive) in {@code value} of the first byte exposed by the returned value. In other * words, you will have {@code wrap(value, o, l).get(0) == value[o]}. * @param length The length of the resulting value. * @return A {@link Bytes} value that holds securely the bytes of {@code value} from {@code offset} (inclusive) to * {@code offset + length} (exclusive). * @throws IndexOutOfBoundsException if {@code offset < 0 || (value.length > 0 && offset >= * value.length)}. * @throws IllegalArgumentException if {@code length < 0 || offset + length > value.length}. */ static Bytes secure(byte[] value, int offset, int length) { checkNotNull(value); return new GuardedByteArrayBytes(value, offset, length); } /** * Wrap a list of other values into a concatenated view. * *

* Note that the values are not copied and thus any future update to the values will be reflected in the returned * value. If copying the inputs is desired, use {@link #concatenate(Bytes...)}. * * @param values The values to wrap. * @return A value representing a view over the concatenation of all {@code values}. * @throws IllegalArgumentException if the result overflows an int. */ static Bytes wrap(Bytes... values) { return ConcatenatedBytes.wrap(values); } /** * Wrap a list of other values into a concatenated view. * *

* Note that the values are not copied and thus any future update to the values will be reflected in the returned * value. If copying the inputs is desired, use {@link #concatenate(Bytes...)}. * * @param values The values to wrap. * @return A value representing a view over the concatenation of all {@code values}. * @throws IllegalArgumentException if the result overflows an int. */ static Bytes wrap(List values) { return ConcatenatedBytes.wrap(values); } /** * Create a value containing the concatenation of the values provided. * * @param values The values to copy and concatenate. * @return A value containing the result of concatenating the value from {@code values} in their provided order. * @throws IllegalArgumentException if the result overflows an int. */ static Bytes concatenate(List values) { if (values.size() == 0) { return EMPTY; } int size; try { size = values.stream().mapToInt(Bytes::size).reduce(0, Math::addExact); } catch (ArithmeticException e) { throw new IllegalArgumentException("Combined length of values is too long (> Integer.MAX_VALUE)"); } MutableBytes result = MutableBytes.create(size); int offset = 0; for (Bytes value : values) { value.copyTo(result, offset); offset += value.size(); } return result; } /** * Create a value containing the concatenation of the values provided. * * @param values The values to copy and concatenate. * @return A value containing the result of concatenating the value from {@code values} in their provided order. * @throws IllegalArgumentException if the result overflows an int. */ static Bytes concatenate(Bytes... values) { if (values.length == 0) { return EMPTY; } int size; try { size = Arrays.stream(values).mapToInt(Bytes::size).reduce(0, Math::addExact); } catch (ArithmeticException e) { throw new IllegalArgumentException("Combined length of values is too long (> Integer.MAX_VALUE)"); } MutableBytes result = MutableBytes.create(size); int offset = 0; for (Bytes value : values) { value.copyTo(result, offset); offset += value.size(); } return result; } /** * Wrap a full Vert.x {@link Buffer} as a {@link Bytes} value. * *

* Note that any change to the content of the buffer may be reflected in the returned value. * * @param buffer The buffer to wrap. * @return A {@link Bytes} value. */ static Bytes wrapBuffer(Buffer buffer) { checkNotNull(buffer); if (buffer.length() == 0) { return EMPTY; } return new BufferWrappingBytes(buffer); } /** * Wrap a slice of a Vert.x {@link Buffer} as a {@link Bytes} value. * *

* Note that any change to the content of the buffer may be reflected in the returned value. * * @param buffer The buffer to wrap. * @param offset The offset in {@code buffer} from which to expose the bytes in the returned value. That is, * {@code wrapBuffer(buffer, i, 1).get(0) == buffer.getByte(i)}. * @param size The size of the returned value. * @return A {@link Bytes} value. * @throws IndexOutOfBoundsException if {@code offset < 0 || (buffer.length() > 0 && offset >= * buffer.length())}. * @throws IllegalArgumentException if {@code length < 0 || offset + length > buffer.length()}. */ static Bytes wrapBuffer(Buffer buffer, int offset, int size) { checkNotNull(buffer); if (size == 0) { return EMPTY; } return new BufferWrappingBytes(buffer, offset, size); } /** * Wrap a full Netty {@link ByteBuf} as a {@link Bytes} value. * *

* Note that any change to the content of the byteBuf may be reflected in the returned value. * * @param byteBuf The {@link ByteBuf} to wrap. * @return A {@link Bytes} value. */ static Bytes wrapByteBuf(ByteBuf byteBuf) { checkNotNull(byteBuf); if (byteBuf.capacity() == 0) { return EMPTY; } return new ByteBufWrappingBytes(byteBuf); } /** * Wrap a slice of a Netty {@link ByteBuf} as a {@link Bytes} value. * *

* Note that any change to the content of the buffer may be reflected in the returned value. * * @param byteBuf The {@link ByteBuf} to wrap. * @param offset The offset in {@code byteBuf} from which to expose the bytes in the returned value. That is, * {@code wrapByteBuf(byteBuf, i, 1).get(0) == byteBuf.getByte(i)}. * @param size The size of the returned value. * @return A {@link Bytes} value. * @throws IndexOutOfBoundsException if {@code offset < 0 || (byteBuf.capacity() > 0 && offset >= * byteBuf.capacity())}. * @throws IllegalArgumentException if {@code length < 0 || offset + length > byteBuf.capacity()}. */ static Bytes wrapByteBuf(ByteBuf byteBuf, int offset, int size) { checkNotNull(byteBuf); if (size == 0) { return EMPTY; } return new ByteBufWrappingBytes(byteBuf, offset, size); } /** * Wrap a full Java NIO {@link ByteBuffer} as a {@link Bytes} value. * *

* Note that any change to the content of the byteBuf may be reflected in the returned value. * * @param byteBuffer The {@link ByteBuffer} to wrap. * @return A {@link Bytes} value. */ static Bytes wrapByteBuffer(ByteBuffer byteBuffer) { checkNotNull(byteBuffer); if (byteBuffer.limit() == 0) { return EMPTY; } return new ByteBufferWrappingBytes(byteBuffer); } /** * Wrap a slice of a Java NIO {@link ByteBuf} as a {@link Bytes} value. * *

* Note that any change to the content of the buffer may be reflected in the returned value. * * @param byteBuffer The {@link ByteBuffer} to wrap. * @param offset The offset in {@code byteBuffer} from which to expose the bytes in the returned value. That is, * {@code wrapByteBuffer(byteBuffer, i, 1).get(0) == byteBuffer.getByte(i)}. * @param size The size of the returned value. * @return A {@link Bytes} value. * @throws IndexOutOfBoundsException if {@code offset < 0 || (byteBuffer.limit() > 0 && offset >= * byteBuf.limit())}. * @throws IllegalArgumentException if {@code length < 0 || offset + length > byteBuffer.limit()}. */ static Bytes wrapByteBuffer(ByteBuffer byteBuffer, int offset, int size) { checkNotNull(byteBuffer); if (size == 0) { return EMPTY; } return new ByteBufferWrappingBytes(byteBuffer, offset, size); } /** * Create a value that contains the specified bytes in their specified order. * * @param bytes The bytes that must compose the returned value. * @return A value containing the specified bytes. */ static Bytes of(byte... bytes) { return wrap(bytes); } /** * Create a value that contains the specified bytes in their specified order. * * @param bytes The bytes. * @return A value containing bytes are the one from {@code bytes}. * @throws IllegalArgumentException if any of the specified would be truncated when storing as a byte. */ static Bytes of(int... bytes) { byte[] result = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) { int b = bytes[i]; checkArgument(b == (((byte) b) & 0xff), "%sth value %s does not fit a byte", i + 1, b); result[i] = (byte) b; } return Bytes.wrap(result); } /** * Return a 2-byte value corresponding to the provided value interpreted as an unsigned short. * * @param value The value, which must be no larger than an unsigned short. * @return A 2 bytes value corresponding to {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 2-byte short * (that is, if {@code value >= (1 << 16)}). */ static Bytes ofUnsignedShort(int value) { return ofUnsignedShort(value, BIG_ENDIAN); } /** * Return a 2-byte value corresponding to the provided value interpreted as an unsigned short. * * @param value The value, which must be no larger than an unsigned short. * @param order The byte-order for the integer encoding. * @return A 2 bytes value corresponding to {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 2-byte short * (that is, if {@code value >= (1 << 16)}). */ static Bytes ofUnsignedShort(int value, ByteOrder order) { checkArgument( value >= 0 && value <= BytesValues.MAX_UNSIGNED_SHORT, "Value %s cannot be represented as an unsigned short (it is negative or too big)", value); byte[] res = new byte[2]; if (order == BIG_ENDIAN) { res[0] = (byte) ((value >> 8) & 0xFF); res[1] = (byte) (value & 0xFF); } else { res[0] = (byte) (value & 0xFF); res[1] = (byte) ((value >> 8) & 0xFF); } return Bytes.wrap(res); } /** * Return a 4-byte value corresponding to the provided value interpreted as an unsigned int. * * @param value The value, which must be no larger than an unsigned int. * @return A 4 bytes value corresponding to {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 4-byte int * (that is, if {@code value >= (1L << 32)}). */ static Bytes ofUnsignedInt(long value) { return ofUnsignedInt(value, BIG_ENDIAN); } /** * Return a 4-byte value corresponding to the provided value interpreted as an unsigned int. * * @param value The value, which must be no larger than an unsigned int. * @param order The byte-order for the integer encoding. * @return A 4 bytes value corresponding to the encoded {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 4-byte int * (that is, if {@code value >= (1L << 32)}). */ static Bytes ofUnsignedInt(long value, ByteOrder order) { checkArgument( value >= 0 && value <= BytesValues.MAX_UNSIGNED_INT, "Value %s cannot be represented as an unsigned int (it is negative or too big)", value); byte[] res = new byte[4]; if (order == BIG_ENDIAN) { res[0] = (byte) ((value >> 24) & 0xFF); res[1] = (byte) ((value >> 16) & 0xFF); res[2] = (byte) ((value >> 8) & 0xFF); res[3] = (byte) ((value) & 0xFF); } else { res[0] = (byte) ((value) & 0xFF); res[1] = (byte) ((value >> 8) & 0xFF); res[2] = (byte) ((value >> 16) & 0xFF); res[3] = (byte) ((value >> 24) & 0xFF); } return Bytes.wrap(res); } /** * Return an 8-byte value corresponding to the provided value interpreted as an unsigned long. * * @param value The value, which will be interpreted as an unsigned long. * @return A 8 bytes value corresponding to {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 8-byte int * (that is, if {@code value >= (1L << 64)}). */ static Bytes ofUnsignedLong(long value) { return ofUnsignedLong(value, BIG_ENDIAN); } /** * Return an 8-byte value corresponding to the provided value interpreted as an unsigned long. * * @param value The value, which will be interpreted as an unsigned long. * @param order The byte-order for the integer encoding. * @return A 8 bytes value corresponding to {@code value}. * @throws IllegalArgumentException if {@code value < 0} or {@code value} is too big to fit an unsigned 8-byte int * (that is, if {@code value >= (1L << 64)}). */ static Bytes ofUnsignedLong(long value, ByteOrder order) { byte[] res = new byte[8]; if (order == BIG_ENDIAN) { res[0] = (byte) ((value >> 56) & 0xFF); res[1] = (byte) ((value >> 48) & 0xFF); res[2] = (byte) ((value >> 40) & 0xFF); res[3] = (byte) ((value >> 32) & 0xFF); res[4] = (byte) ((value >> 24) & 0xFF); res[5] = (byte) ((value >> 16) & 0xFF); res[6] = (byte) ((value >> 8) & 0xFF); res[7] = (byte) (value & 0xFF); } else { res[0] = (byte) ((value) & 0xFF); res[1] = (byte) ((value >> 8) & 0xFF); res[2] = (byte) ((value >> 16) & 0xFF); res[3] = (byte) ((value >> 24) & 0xFF); res[4] = (byte) ((value >> 32) & 0xFF); res[5] = (byte) ((value >> 40) & 0xFF); res[6] = (byte) ((value >> 48) & 0xFF); res[7] = (byte) ((value >> 56) & 0xFF); } return Bytes.wrap(res); } /** * Return the smallest bytes value whose bytes correspond to the provided long. That is, the returned value may be of * size less than 8 if the provided long has leading zero bytes. * * @param value The long from which to create the bytes value. * @return The minimal bytes representation corresponding to {@code l}. */ static Bytes minimalBytes(long value) { if (value == 0) { return Bytes.EMPTY; } int zeros = Long.numberOfLeadingZeros(value); int resultBytes = 8 - (zeros / 8); byte[] result = new byte[resultBytes]; int shift = 0; for (int i = 0; i < resultBytes; i++) { result[resultBytes - i - 1] = (byte) ((value >> shift) & 0xFF); shift += 8; } return Bytes.wrap(result); } /** * Parse a hexadecimal string into a {@link Bytes} value. * *

* This method is lenient in that {@code str} may of an odd length, in which case it will behave exactly as if it had * an additional 0 in front. * * @param str The hexadecimal string to parse, which may or may not start with "0x". * @return The value corresponding to {@code str}. * @throws IllegalArgumentException if {@code str} does not correspond to a valid hexadecimal representation. */ static Bytes fromHexStringLenient(CharSequence str) { checkNotNull(str); return BytesValues.fromHexString(str, -1, true); } /** * Parse a hexadecimal string into a {@link Bytes} value of the provided size. * *

* This method allows for {@code str} to have an odd length, in which case it will behave exactly as if it had an * additional 0 in front. * * @param str The hexadecimal string to parse, which may or may not start with "0x". * @param destinationSize The size of the returned value, which must be big enough to hold the bytes represented by * {@code str}. If it is strictly bigger those bytes from {@code str}, the returned value will be left padded * with zeros. * @return A value of size {@code destinationSize} corresponding to {@code str} potentially left-padded. * @throws IllegalArgumentException if {@code str} does not correspond to a valid hexadecimal representation, * represents more bytes than {@code destinationSize} or {@code destinationSize < 0}. */ static Bytes fromHexStringLenient(CharSequence str, int destinationSize) { checkNotNull(str); checkArgument(destinationSize >= 0, "Invalid negative destination size %s", destinationSize); return BytesValues.fromHexString(str, destinationSize, true); } /** * Parse a hexadecimal string into a {@link Bytes} value. * *

* This method requires that {@code str} have an even length. * * @param str The hexadecimal string to parse, which may or may not start with "0x". * @return The value corresponding to {@code str}. * @throws IllegalArgumentException if {@code str} does not correspond to a valid hexadecimal representation, or is of * an odd length. */ static Bytes fromHexString(CharSequence str) { checkNotNull(str); return BytesValues.fromHexString(str, -1, false); } /** * Parse a hexadecimal string into a {@link Bytes} value. * *

* This method requires that {@code str} have an even length. * * @param str The hexadecimal string to parse, which may or may not start with "0x". * @param destinationSize The size of the returned value, which must be big enough to hold the bytes represented by * {@code str}. If it is strictly bigger those bytes from {@code str}, the returned value will be left padded * with zeros. * @return A value of size {@code destinationSize} corresponding to {@code str} potentially left-padded. * @throws IllegalArgumentException if {@code str} does correspond to a valid hexadecimal representation, or is of an * odd length. * @throws IllegalArgumentException if {@code str} does not correspond to a valid hexadecimal representation, or is of * an odd length, or represents more bytes than {@code destinationSize} or {@code destinationSize < 0}. */ static Bytes fromHexString(CharSequence str, int destinationSize) { checkNotNull(str); checkArgument(destinationSize >= 0, "Invalid negative destination size %s", destinationSize); return BytesValues.fromHexString(str, destinationSize, false); } /** * Parse a base 64 string into a {@link Bytes} value. * * @param str The base 64 string to parse. * @return The value corresponding to {@code str}. */ static Bytes fromBase64String(CharSequence str) { return Bytes.wrap(Base64.getDecoder().decode(str.toString())); } /** * Generate random bytes. * * @param size The number of bytes to generate. * @return A value containing the desired number of random bytes. */ static Bytes random(int size) { return random(size, new SecureRandom()); } /** * Generate random bytes. * * @param size The number of bytes to generate. * @param generator The generator for random bytes. * @return A value containing the desired number of random bytes. */ static Bytes random(int size, Random generator) { byte[] array = new byte[size]; generator.nextBytes(array); return Bytes.wrap(array); } /** * Generate a bytes object filled with the same byte. * * @param b the byte to fill the Bytes with * @param size the size of the object * @return a value filled with a fixed byte */ static Bytes repeat(byte b, int size) { return new ConstantBytesValue(b, size); } /** * Splits a Bytes object into Bytes32 objects. If the last element is not exactly 32 bytes, it is right padded with * zeros. * * @param bytes the bytes object to segment * @return an array of Bytes32 objects */ static Bytes32[] segment(Bytes bytes) { int segments = (int) Math.ceil(bytes.size() / 32.0); Bytes32[] result = new Bytes32[segments]; for (int i = 0; i < segments; i++) { result[i] = Bytes32.rightPad(bytes.slice(i * 32, Math.min(32, bytes.size() - i * 32))); } return result; } /** * * Provides the number of bytes this value represents. * * @return The number of bytes this value represents. */ int size(); /** * Retrieve a byte in this value. * * @param i The index of the byte to fetch within the value (0-indexed). * @return The byte at index {@code i} in this value. * @throws IndexOutOfBoundsException if {@code i < 0} or {i >= size()}. */ byte get(int i); /** * Retrieve the 4 bytes starting at the provided index in this value as an integer. * * @param i The index from which to get the int, which must less than or equal to {@code size() - 4}. * @return An integer whose value is the 4 bytes from this value starting at index {@code i}. * @throws IndexOutOfBoundsException if {@code i < 0} or {@code i > size() - 4}. */ default int getInt(int i) { return getInt(i, BIG_ENDIAN); } /** * Retrieve the 4 bytes starting at the provided index in this value as an integer. * * @param i The index from which to get the int, which must less than or equal to {@code size() - 4}. * @param order The byte-order for decoding the integer. * @return An integer whose value is the 4 bytes from this value starting at index {@code i}. * @throws IndexOutOfBoundsException if {@code i < 0} or {@code i > size() - 4}. */ default int getInt(int i, ByteOrder order) { int size = size(); checkElementIndex(i, size); if (i > (size - 4)) { throw new IndexOutOfBoundsException( format("Value of size %s has not enough bytes to read a 4 bytes int from index %s", size, i)); } int value = 0; if (order == BIG_ENDIAN) { value |= ((int) get(i) & 0xFF) << 24; value |= ((int) get(i + 1) & 0xFF) << 16; value |= ((int) get(i + 2) & 0xFF) << 8; value |= ((int) get(i + 3) & 0xFF); } else { value |= ((int) get(i + 3) & 0xFF) << 24; value |= ((int) get(i + 2) & 0xFF) << 16; value |= ((int) get(i + 1) & 0xFF) << 8; value |= ((int) get(i) & 0xFF); } return value; } /** * The value corresponding to interpreting these bytes as an integer. * * @return An value corresponding to this value interpreted as an integer. * @throws IllegalArgumentException if {@code size() > 4}. */ default int toInt() { return toInt(BIG_ENDIAN); } /** * The value corresponding to interpreting these bytes as an integer. * * @param order The byte-order for decoding the integer. * @return An value corresponding to this value interpreted as an integer. * @throws IllegalArgumentException if {@code size() > 4}. */ default int toInt(ByteOrder order) { int size = size(); checkArgument(size <= 4, "Value of size %s has more than 4 bytes", size()); if (size == 0) { return 0; } if (order == BIG_ENDIAN) { int i = size; int value = ((int) get(--i) & 0xFF); if (i == 0) { return value; } value |= ((int) get(--i) & 0xFF) << 8; if (i == 0) { return value; } value |= ((int) get(--i) & 0xFF) << 16; if (i == 0) { return value; } return value | ((int) get(--i) & 0xFF) << 24; } else { int i = 0; int value = ((int) get(i) & 0xFF); if (++i == size) { return value; } value |= ((int) get(i++) & 0xFF) << 8; if (i == size) { return value; } value |= ((int) get(i++) & 0xFF) << 16; if (i == size) { return value; } return value | ((int) get(i) & 0xFF) << 24; } } /** * Whether this value contains no bytes. * * @return true if the value contains no bytes */ default boolean isEmpty() { return size() == 0; } /** * Retrieves the 8 bytes starting at the provided index in this value as a long. * * @param i The index from which to get the long, which must less than or equal to {@code size() - 8}. * @return A long whose value is the 8 bytes from this value starting at index {@code i}. * @throws IndexOutOfBoundsException if {@code i < 0} or {@code i > size() - 8}. */ default long getLong(int i) { return getLong(i, BIG_ENDIAN); } /** * Retrieves the 8 bytes starting at the provided index in this value as a long. * * @param i The index from which to get the long, which must less than or equal to {@code size() - 8}. * @param order The byte-order for decoding the integer. * @return A long whose value is the 8 bytes from this value starting at index {@code i}. * @throws IndexOutOfBoundsException if {@code i < 0} or {@code i > size() - 8}. */ default long getLong(int i, ByteOrder order) { int size = size(); checkElementIndex(i, size); if (i > (size - 8)) { throw new IndexOutOfBoundsException( format("Value of size %s has not enough bytes to read a 8 bytes long from index %s", size, i)); } long value = 0; if (order == BIG_ENDIAN) { value |= ((long) get(i) & 0xFF) << 56; value |= ((long) get(i + 1) & 0xFF) << 48; value |= ((long) get(i + 2) & 0xFF) << 40; value |= ((long) get(i + 3) & 0xFF) << 32; value |= ((long) get(i + 4) & 0xFF) << 24; value |= ((long) get(i + 5) & 0xFF) << 16; value |= ((long) get(i + 6) & 0xFF) << 8; value |= ((long) get(i + 7) & 0xFF); } else { value |= ((long) get(i + 7) & 0xFF) << 56; value |= ((long) get(i + 6) & 0xFF) << 48; value |= ((long) get(i + 5) & 0xFF) << 40; value |= ((long) get(i + 4) & 0xFF) << 32; value |= ((long) get(i + 3) & 0xFF) << 24; value |= ((long) get(i + 2) & 0xFF) << 16; value |= ((long) get(i + 1) & 0xFF) << 8; value |= ((long) get(i) & 0xFF); } return value; } /** * The value corresponding to interpreting these bytes as a long. * * @return An value corresponding to this value interpreted as a long. * @throws IllegalArgumentException if {@code size() > 8}. */ default long toLong() { return toLong(BIG_ENDIAN); } /** * The value corresponding to interpreting these bytes as a long. * * @param order The byte-order for decoding the integer. * @return An value corresponding to this value interpreted as a long. * @throws IllegalArgumentException if {@code size() > 8}. */ default long toLong(ByteOrder order) { int size = size(); checkArgument(size <= 8, "Value of size %s has more than 8 bytes", size()); if (size == 0) { return 0; } if (order == BIG_ENDIAN) { int i = size; long value = ((long) get(--i) & 0xFF); if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 8; if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 16; if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 24; if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 32; if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 40; if (i == 0) { return value; } value |= ((long) get(--i) & 0xFF) << 48; if (i == 0) { return value; } return value | ((long) get(--i) & 0xFF) << 56; } else { int i = 0; long value = ((long) get(i) & 0xFF); if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 8; if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 16; if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 24; if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 32; if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 40; if (++i == size) { return value; } value |= ((long) get(i) & 0xFF) << 48; if (++i == size) { return value; } return value | ((long) get(i) & 0xFF) << 56; } } /** * The BigInteger corresponding to interpreting these bytes as a two's-complement signed integer. * * @return A {@link BigInteger} corresponding to interpreting these bytes as a two's-complement signed integer. */ default BigInteger toBigInteger() { return toBigInteger(BIG_ENDIAN); } /** * The BigInteger corresponding to interpreting these bytes as a two's-complement signed integer. * * @param order The byte-order for decoding the integer. * @return A {@link BigInteger} corresponding to interpreting these bytes as a two's-complement signed integer. */ default BigInteger toBigInteger(ByteOrder order) { if (size() == 0) { return BigInteger.ZERO; } return new BigInteger((order == BIG_ENDIAN) ? toArrayUnsafe() : reverse().toArrayUnsafe()); } /** * The BigInteger corresponding to interpreting these bytes as an unsigned integer. * * @return A positive (or zero) {@link BigInteger} corresponding to interpreting these bytes as an unsigned integer. */ default BigInteger toUnsignedBigInteger() { return toUnsignedBigInteger(BIG_ENDIAN); } /** * The BigInteger corresponding to interpreting these bytes as an unsigned integer. * * @param order The byte-order for decoding the integer. * @return A positive (or zero) {@link BigInteger} corresponding to interpreting these bytes as an unsigned integer. */ default BigInteger toUnsignedBigInteger(ByteOrder order) { return new BigInteger(1, (order == BIG_ENDIAN) ? toArrayUnsafe() : reverse().toArrayUnsafe()); } /** * Whether this value has only zero bytes. * * @return {@code true} if all the bits of this value are zeros. */ default boolean isZero() { for (int i = size() - 1; i >= 0; --i) { if (get(i) != 0) return false; } return true; } /** * Whether the bytes start with a zero bit value. * * @return true if the first bit equals zero */ default boolean hasLeadingZero() { return size() > 0 && (get(0) & 0x80) == 0; } /** * Provides the number of zero bits preceding the highest-order ("leftmost") one-bit, or {@code size() * 8} if all * bits * are zero. * * @return The number of zero bits preceding the highest-order ("leftmost") one-bit, or {@code size() * 8} if all bits * are zero. */ default int numberOfLeadingZeros() { int size = size(); for (int i = 0; i < size; i++) { byte b = get(i); if (b == 0) { continue; } return (i * 8) + Integer.numberOfLeadingZeros(b & 0xFF) - 3 * 8; } return size * 8; } /** * Whether the bytes start with a zero byte value. * * @return true if the first byte equals zero */ default boolean hasLeadingZeroByte() { return size() > 0 && get(0) == 0; } /** * Provides the number of leading zero bytes of the value * * @return The number of leading zero bytes of the value. */ default int numberOfLeadingZeroBytes() { int size = size(); for (int i = 0; i < size; i++) { if (get(i) != 0) { return i; } } return size; } /** * Provides the number of trailing zero bytes of the value. * * @return The number of trailing zero bytes of the value. */ default int numberOfTrailingZeroBytes() { int size = size(); for (int i = size; i >= 1; i--) { if (get(i - 1) != 0) { return size - i; } } return size; } /** * Provides the number of bits following and including the highest-order ("leftmost") one-bit, or zero if all bits are * zero. * * @return The number of bits following and including the highest-order ("leftmost") one-bit, or zero if all bits are * zero. */ default int bitLength() { int size = size(); for (int i = 0; i < size; i++) { byte b = get(i); if (b == 0) continue; return (size * 8) - (i * 8) - (Integer.numberOfLeadingZeros(b & 0xFF) - 3 * 8); } return 0; } /** * Return a bit-wise AND of these bytes and the supplied bytes. * * If this value and the supplied value are different lengths, then the shorter will be zero-padded to the left. * * @param other The bytes to perform the operation with. * @return The result of a bit-wise AND. */ default Bytes and(Bytes other) { return and(other, MutableBytes.create(Math.max(size(), other.size()))); } /** * Calculate a bit-wise AND of these bytes and the supplied bytes. * *

* If this value or the supplied value are shorter in length than the output vector, then they will be zero-padded to * the left. Likewise, if either this value or the supplied valid is longer in length than the output vector, then * they will be truncated to the left. * * @param other The bytes to perform the operation with. * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T and(Bytes other, T result) { checkNotNull(other); checkNotNull(result); int rSize = result.size(); int offsetSelf = rSize - size(); int offsetOther = rSize - other.size(); for (int i = 0; i < rSize; i++) { byte b1 = (i < offsetSelf) ? 0x00 : get(i - offsetSelf); byte b2 = (i < offsetOther) ? 0x00 : other.get(i - offsetOther); result.set(i, (byte) (b1 & b2)); } return result; } /** * Return a bit-wise OR of these bytes and the supplied bytes. * *

* If this value and the supplied value are different lengths, then the shorter will be zero-padded to the left. * * @param other The bytes to perform the operation with. * @return The result of a bit-wise OR. */ default Bytes or(Bytes other) { return or(other, MutableBytes.create(Math.max(size(), other.size()))); } /** * Calculate a bit-wise OR of these bytes and the supplied bytes. * *

* If this value or the supplied value are shorter in length than the output vector, then they will be zero-padded to * the left. Likewise, if either this value or the supplied valid is longer in length than the output vector, then * they will be truncated to the left. * * @param other The bytes to perform the operation with. * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T or(Bytes other, T result) { checkNotNull(other); checkNotNull(result); int rSize = result.size(); int offsetSelf = rSize - size(); int offsetOther = rSize - other.size(); for (int i = 0; i < rSize; i++) { byte b1 = (i < offsetSelf) ? 0x00 : get(i - offsetSelf); byte b2 = (i < offsetOther) ? 0x00 : other.get(i - offsetOther); result.set(i, (byte) (b1 | b2)); } return result; } /** * Return a bit-wise XOR of these bytes and the supplied bytes. * *

* If this value and the supplied value are different lengths, then the shorter will be zero-padded to the left. * * @param other The bytes to perform the operation with. * @return The result of a bit-wise XOR. */ default Bytes xor(Bytes other) { return xor(other, MutableBytes.create(Math.max(size(), other.size()))); } /** * Calculate a bit-wise XOR of these bytes and the supplied bytes. * *

* If this value or the supplied value are shorter in length than the output vector, then they will be zero-padded to * the left. Likewise, if either this value or the supplied valid is longer in length than the output vector, then * they will be truncated to the left. * * @param other The bytes to perform the operation with. * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T xor(Bytes other, T result) { checkNotNull(other); checkNotNull(result); int rSize = result.size(); int offsetSelf = rSize - size(); int offsetOther = rSize - other.size(); for (int i = 0; i < rSize; i++) { byte b1 = (i < offsetSelf) ? 0x00 : get(i - offsetSelf); byte b2 = (i < offsetOther) ? 0x00 : other.get(i - offsetOther); result.set(i, (byte) (b1 ^ b2)); } return result; } /** * Return a bit-wise NOT of these bytes. * * @return The result of a bit-wise NOT. */ default Bytes not() { return not(MutableBytes.create(size())); } /** * Calculate a bit-wise NOT of these bytes. * *

* If this value is shorter in length than the output vector, then it will be zero-padded to the left. Likewise, if * this value is longer in length than the output vector, then it will be truncated to the left. * * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T not(T result) { checkNotNull(result); int rSize = result.size(); int offsetSelf = rSize - size(); for (int i = 0; i < rSize; i++) { byte b1 = (i < offsetSelf) ? 0x00 : get(i - offsetSelf); result.set(i, (byte) ~b1); } return result; } /** * Shift all bits in this value to the right. * * @param distance The number of bits to shift by. * @return A value containing the shifted bits. */ default Bytes shiftRight(int distance) { return shiftRight(distance, MutableBytes.create(size())); } /** * Shift all bits in this value to the right. * *

* If this value is shorter in length than the output vector, then it will be zero-padded to the left. Likewise, if * this value is longer in length than the output vector, then it will be truncated to the left (after shifting). * * @param distance The number of bits to shift by. * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T shiftRight(int distance, T result) { checkNotNull(result); int rSize = result.size(); int offsetSelf = rSize - size(); int d = distance / 8; int s = distance % 8; int resIdx = rSize - 1; for (int i = rSize - 1 - d; i >= 0; i--) { byte res; if (i < offsetSelf) { res = 0; } else { int selfIdx = i - offsetSelf; int leftSide = (get(selfIdx) & 0xFF) >>> s; int rightSide = (selfIdx == 0) ? 0 : get(selfIdx - 1) << (8 - s); res = (byte) (leftSide | rightSide); } result.set(resIdx--, res); } for (; resIdx >= 0; resIdx--) { result.set(resIdx, (byte) 0); } return result; } /** * Shift all bits in this value to the left. * * @param distance The number of bits to shift by. * @return A value containing the shifted bits. */ default Bytes shiftLeft(int distance) { return shiftLeft(distance, MutableBytes.create(size())); } /** * Shift all bits in this value to the left. * *

* If this value is shorter in length than the output vector, then it will be zero-padded to the left. Likewise, if * this value is longer in length than the output vector, then it will be truncated to the left. * * @param distance The number of bits to shift by. * @param result The mutable output vector for the result. * @param The {@link MutableBytes} value type. * @return The {@code result} output vector. */ default T shiftLeft(int distance, T result) { checkNotNull(result); int size = size(); int rSize = result.size(); int offsetSelf = rSize - size; int d = distance / 8; int s = distance % 8; int resIdx = 0; for (int i = d; i < rSize; i++) { byte res; if (i < offsetSelf) { res = 0; } else { int selfIdx = i - offsetSelf; int leftSide = get(selfIdx) << s; int rightSide = (selfIdx == size - 1) ? 0 : (get(selfIdx + 1) & 0xFF) >>> (8 - s); res = (byte) (leftSide | rightSide); } result.set(resIdx++, res); } for (; resIdx < rSize; resIdx++) { result.set(resIdx, (byte) 0); } return result; } /** * Create a new value representing (a view of) a slice of the bytes of this value. * *

* Please note that the resulting slice is only a view and as such maintains a link to the underlying full value. So * holding a reference to the returned slice may hold more memory than the slide represents. Use {@link #copy} on the * returned slice if that is not what you want. * * @param i The start index for the slice. * @return A new value providing a view over the bytes from index {@code i} (included) to the end. * @throws IndexOutOfBoundsException if {@code i < 0}. */ default Bytes slice(int i) { if (i == 0) { return this; } int size = size(); if (i >= size) { return EMPTY; } return slice(i, size - i); } /** * Create a new value representing (a view of) a slice of the bytes of this value. * *

* Please note that the resulting slice is only a view and as such maintains a link to the underlying full value. So * holding a reference to the returned slice may hold more memory than the slide represents. Use {@link #copy} on the * returned slice if that is not what you want. * * @param i The start index for the slice. * @param length The length of the resulting value. * @return A new value providing a view over the bytes from index {@code i} (included) to {@code i + length} * (excluded). * @throws IllegalArgumentException if {@code length < 0}. * @throws IndexOutOfBoundsException if {@code i < 0} or {i >= size()} or {i + length > size()} . */ Bytes slice(int i, int length); /** * Return a value equivalent to this one but guaranteed to 1) be deeply immutable (i.e. the underlying value will be * immutable) and 2) to not retain more bytes than exposed by the value. * * @return A value, equals to this one, but deeply immutable and that doesn't retain any "unreachable" bytes. For * performance reasons, this is allowed to return this value however if it already fit those constraints. */ Bytes copy(); /** * Return a new mutable value initialized with the content of this value. * * @return A mutable copy of this value. This will copy bytes, modifying the returned value will not modify * this value. */ MutableBytes mutableCopy(); /** * Copy the bytes of this value to the provided mutable one, which must have the same size. * * @param destination The mutable value to which to copy the bytes to, which must have the same size as this value. If * you want to copy value where size differs, you should use {@link #slice} and/or * {@link MutableBytes#mutableSlice} and apply the copy to the result. * @throws IllegalArgumentException if {@code this.size() != destination.size()}. */ default void copyTo(MutableBytes destination) { checkNotNull(destination); checkArgument( destination.size() == size(), "Cannot copy %s bytes to destination of non-equal size %s", size(), destination.size()); copyTo(destination, 0); } /** * Copy the bytes of this value to the provided mutable one from a particular offset. * *

* This is a (potentially slightly more efficient) shortcut for {@code * copyTo(destination.mutableSlice(destinationOffset, this.size()))}. * * @param destination The mutable value to which to copy the bytes to, which must have enough bytes from * {@code destinationOffset} for the copied value. * @param destinationOffset The offset in {@code destination} at which the copy starts. * @throws IllegalArgumentException if the destination doesn't have enough room, that is if {@code * this.size() > (destination.size() - destinationOffset)}. */ default void copyTo(MutableBytes destination, int destinationOffset) { checkNotNull(destination); // Special casing an empty source or the following checks might throw (even though we have // nothing to copy anyway) and this gets inconvenient for generic methods using copyTo() as // they may have to special case empty values because of this. As an example, // concatenate(EMPTY, EMPTY) would need to be special cased without this. int size = size(); if (size == 0) { return; } checkElementIndex(destinationOffset, destination.size()); checkArgument( destination.size() - destinationOffset >= size, "Cannot copy %s bytes, destination has only %s bytes from index %s", size, destination.size() - destinationOffset, destinationOffset); destination.set(destinationOffset, this); } /** * Append the bytes of this value to the {@link ByteBuffer}. * * @param byteBuffer The {@link ByteBuffer} to which to append this value. * @throws BufferOverflowException If the writer attempts to write more than the provided buffer can hold. * @throws ReadOnlyBufferException If the provided buffer is read-only. */ default void appendTo(ByteBuffer byteBuffer) { checkNotNull(byteBuffer); for (int i = 0; i < size(); i++) { byteBuffer.put(get(i)); } } /** * Append the bytes of this value to the provided Vert.x {@link Buffer}. * *

* Note that since a Vert.x {@link Buffer} will grow as necessary, this method never fails. * * @param buffer The {@link Buffer} to which to append this value. */ default void appendTo(Buffer buffer) { checkNotNull(buffer); for (int i = 0; i < size(); i++) { buffer.appendByte(get(i)); } } /** * Append this value as a sequence of hexadecimal characters. * * @param appendable The appendable * @param The appendable type. * @return The appendable. */ default T appendHexTo(T appendable) { try { appendable.append(toFastHex(false)); return appendable; } catch (IOException e) { throw new UncheckedIOException(e); } } default String toFastHex(boolean prefix) { int offset = prefix ? 2 : 0; int resultSize = (size() * 2) + offset; char[] result = new char[resultSize]; if (prefix) { result[0] = '0'; result[1] = 'x'; } for (int i = 0; i < size(); i++) { byte b = get(i); int pos = i * 2; result[pos + offset] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b >> 4 & 15); result[pos + offset + 1] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b & 15); } return new String(result); } /** * Return the number of bytes in common between this set of bytes and another. * * @param other The bytes to compare to. * @return The number of common bytes. */ default int commonPrefixLength(Bytes other) { checkNotNull(other); int ourSize = size(); int otherSize = other.size(); int i = 0; while (i < ourSize && i < otherSize && get(i) == other.get(i)) { i++; } return i; } /** * Return a slice over the common prefix between this set of bytes and another. * * @param other The bytes to compare to. * @return A slice covering the common prefix. */ default Bytes commonPrefix(Bytes other) { return slice(0, commonPrefixLength(other)); } /** * Return a slice of representing the same value but without any leading zero bytes. * * @return {@code value} if its left-most byte is non zero, or a slice that exclude any leading zero bytes. */ default Bytes trimLeadingZeros() { int size = size(); for (int i = 0; i < size; i++) { if (get(i) != 0) { return slice(i); } } return Bytes.EMPTY; } /** * Return a slice of representing the same value but without any trailing zero bytes. * * @return {@code value} if its right-most byte is non zero, or a slice that exclude any trailing zero bytes. */ default Bytes trimTrailingZeros() { int size = size(); for (int i = size - 1; i >= 0; i--) { if (get(i) != 0) { return slice(0, i + 1); } } return Bytes.EMPTY; } /** * Update the provided message digest with the bytes of this value. * * @param digest The digest to update. */ default void update(MessageDigest digest) { checkNotNull(digest); digest.update(toArrayUnsafe()); } /** * Computes the reverse array of bytes of the current bytes. * * @return a new Bytes value, containing the bytes in reverse order */ default Bytes reverse() { byte[] reverse = new byte[size()]; for (int i = 0; i < size(); i++) { reverse[size() - i - 1] = get(i); } return Bytes.wrap(reverse); } /** * Extract the bytes of this value into a byte array. * * @return A byte array with the same content than this value. */ default byte[] toArray() { return toArray(BIG_ENDIAN); } /** * Extract the bytes of this value into a byte array. * * @param byteOrder the byte order to apply : big endian or little endian * @return A byte array with the same content than this value. */ default byte[] toArray(ByteOrder byteOrder) { int size = size(); byte[] array = new byte[size]; if (byteOrder == BIG_ENDIAN) { for (int i = 0; i < size; i++) { array[i] = get(i); } } else { for (int i = 0; i < size(); i++) { array[size() - i - 1] = get(i); } } return array; } /** * Get the bytes represented by this value as byte array. * *

* Contrarily to {@link #toArray()}, this may avoid allocating a new array and directly return the backing array of * this value if said value is array backed and doing so is possible. As such, modifications to the returned array may * or may not impact this value. As such, this method should be used with care and hence the "unsafe" moniker. * * @return A byte array with the same content than this value, which may or may not be the direct backing of this * value. */ default byte[] toArrayUnsafe() { return toArray(); } /** * Return the hexadecimal string representation of this value. * * @return The hexadecimal representation of this value, starting with "0x". */ @Override String toString(); /** * Provides this value represented as hexadecimal, starting with "0x". * * @return This value represented as hexadecimal, starting with "0x". */ default String toHexString() { return toFastHex(true); } /** * Provides this value represented as hexadecimal, with no prefix * * @return This value represented as hexadecimal, with no prefix. */ default String toUnprefixedHexString() { return toFastHex(false); } default String toEllipsisHexString() { int size = size(); if (size < 6) { return toHexString(); } char[] result = new char[12]; result[0] = '0'; result[1] = 'x'; for (int i = 0; i < 2; i++) { byte b = get(i); int pos = (i * 2) + 2; result[pos] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b >> 4 & 15); result[pos + 1] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b & 15); } result[6] = '.'; result[7] = '.'; for (int i = 0; i < 2; i++) { byte b = get(i + size - 2); int pos = (i * 2) + 8; result[pos] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b >> 4 & 15); result[pos + 1] = AbstractBytes.HEX_CODE_AS_STRING.charAt(b & 15); } return new String(result); } /** * Provides this value represented as a minimal hexadecimal string (without any leading zero) * * @return This value represented as a minimal hexadecimal string (without any leading zero). */ default String toShortHexString() { String hex = toFastHex(false); int i = 0; while (i < hex.length() && hex.charAt(i) == '0') { i++; } return "0x" + hex.substring(i); } /** * Provides this 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). * * @return This 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). */ default String toQuantityHexString() { if (Bytes.EMPTY.equals(this)) { return "0x0"; } String hex = toFastHex(false); int i = 0; while (i < hex.length() - 1 && hex.charAt(i) == '0') { i++; } return "0x" + hex.substring(i); } /** * Provides this value represented as base 64 * * @return This value represented as base 64. */ default String toBase64String() { return Base64.getEncoder().encodeToString(toArrayUnsafe()); } @Override default int compareTo(Bytes b) { checkNotNull(b); int bitLength = bitLength(); int sizeCmp = Integer.compare(bitLength, b.bitLength()); if (sizeCmp != 0) { return sizeCmp; } // same bitlength and is zeroes only, return 0. if (bitLength == 0) { return 0; } for (int i = 0; i < size(); i++) { int cmp = Integer.compare(get(i) & 0xff, b.get(i) & 0xff); if (cmp != 0) { return cmp; } } return 0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy