BytesKit.java

package space.sunqian.common.base.bytes;

import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;

import java.nio.ByteBuffer;

/**
 * Utilities for {@code byte} and {@code byte array}.
 *
 * @author sunqian
 */
public class BytesKit {

    private static final byte @Nonnull [] EMPTY = new byte[0];
    private static final @Nonnull ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(EMPTY);

    /**
     * Returns whether the given buffer is null or empty.
     *
     * @param buffer the given buffer
     * @return whether the given buffer is null or empty
     */
    public static boolean isEmpty(@Nullable ByteBuffer buffer) {
        return buffer == null || !buffer.hasRemaining();
    }

    /**
     * Returns an empty byte array.
     *
     * @return an empty byte array
     */
    public static byte @Nonnull [] empty() {
        return EMPTY;
    }

    /**
     * Returns an empty byte buffer.
     *
     * @return an empty byte buffer
     */
    public static @Nonnull ByteBuffer emptyBuffer() {
        return EMPTY_BUFFER;
    }

    /**
     * Converts the given byte array to an int value in big endian, and the least significant byte is the last byte of
     * the array. Let {@code n} be {@code bytes.length - 1}, the int will be:
     * {@code byte[n-3], byte[n-2], byte[n-1], byte[n]}. If the bytes is not enough, padding 0 into the missing bytes.
     *
     * @param bytes the given byte array
     * @return the converted int value
     */
    public static int bytesToInt(byte @Nonnull [] bytes) {
        return bytesToInt(bytes, bytes.length - 1);
    }

    /**
     * Converts the given byte array to an int value in big endian, and the least significant byte is specified by the
     * {@code leastIndex}. Let {@code n} be the index of the least significant byte, the int will be:
     * {@code byte[n-3], byte[n-2], byte[n-1], byte[n]}. If the bytes is not enough, padding 0 into the missing bytes.
     *
     * @param bytes      the given byte array
     * @param leastIndex the index of the least significant byte
     * @return the converted int value
     * @throws IndexOutOfBoundsException if the {@code leastIndex} is out of bounds
     */
    public static int bytesToInt(byte @Nonnull [] bytes, int leastIndex) throws IndexOutOfBoundsException {
        int ret = 0;
        int off = Math.max(leastIndex - Integer.BYTES + 1, 0);
        int shift = 0;
        for (int i = leastIndex; i >= off; i--) {
            int b = (bytes[i] & 0xFF) << shift;
            ret |= b;
            shift += 8;
        }
        return ret;
    }

    /**
     * Converts the given byte array to a long value in big endian, and the least significant byte is the last byte of
     * the array. Let {@code n} be {@code bytes.length - 1}, the long will be:
     * {@code byte[n-7], byte[n-6], byte[n-5], byte[n-4], byte[n-3], byte[n-2], byte[n-1], byte[n]}. If the bytes is not
     * enough, padding 0 into the missing bytes.
     *
     * @param bytes the given byte array
     * @return the converted long value
     */
    public static long bytesToLong(byte @Nonnull [] bytes) {
        return bytesToLong(bytes, bytes.length - 1);
    }

    /**
     * Converts the given byte array to a long value in big endian, and the least significant byte is specified by the
     * {@code leastIndex}. Let {@code n} be the index of the least significant byte, the long will be:
     * {@code byte[n-7], byte[n-6], byte[n-5], byte[n-4], byte[n-3], byte[n-2], byte[n-1], byte[n]}. If the bytes is not
     * enough, padding 0 into the missing bytes.
     *
     * @param bytes      the given byte array
     * @param leastIndex the index of the least significant byte
     * @return the converted long value
     * @throws IndexOutOfBoundsException if the {@code leastIndex} is out of bounds
     */
    public static long bytesToLong(byte @Nonnull [] bytes, int leastIndex) throws IndexOutOfBoundsException {
        long ret = 0;
        int off = Math.max(leastIndex - Long.BYTES + 1, 0);
        int shift = 0;
        for (int i = leastIndex; i >= off; i--) {
            long b = (bytes[i] & 0xFFL) << shift;
            ret |= b;
            shift += 8;
        }
        return ret;
    }

    /**
     * Converts the given int value to a byte array in big endian like:
     * <pre>{@code
     * return new byte[]{
     *     (byte) (value >>> 24),
     *     (byte) (value >>> 16),
     *     (byte) (value >>> 8),
     *     (byte) value
     * };
     * }</pre>
     *
     * @param value the given int value
     * @return the converted byte array
     */
    public static byte @Nonnull [] intToBytes(int value) {
        return new byte[]{
            (byte) (value >>> 24),
            (byte) (value >>> 16),
            (byte) (value >>> 8),
            (byte) value
        };
    }

    /**
     * Converts the given long value to a byte array in big endian like:
     * <pre>{@code
     * return new byte[]{
     *     (byte) (value >>> 56),
     *     (byte) (value >>> 48),
     *     (byte) (value >>> 40),
     *     (byte) (value >>> 32),
     *     (byte) (value >>> 24),
     *     (byte) (value >>> 16),
     *     (byte) (value >>> 8),
     *     (byte) value
     * };
     * }</pre>
     *
     * @param value the given long value
     * @return the converted byte array
     */
    public static byte @Nonnull [] longToBytes(long value) {
        return new byte[]{
            (byte) (value >>> 56),
            (byte) (value >>> 48),
            (byte) (value >>> 40),
            (byte) (value >>> 32),
            (byte) (value >>> 24),
            (byte) (value >>> 16),
            (byte) (value >>> 8),
            (byte) value
        };
    }

    private BytesKit() {
    }
}