/* * ByteUtils.java * * Created on April 17, 2007, 6:53 PM * * Description: Byte array utilities adapted from org.apache.commons.id.uuid.Bytes. * * Copyright (C) 2007 Stephen L. Reed. * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.Comparator; import java.util.UUID; /** * * @author reed */ public final class ByteUtils { /** the hex characters */ private static final byte[] HEX_CHARACTERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** <p>Hide constructor in utility class.</p> */ private ByteUtils() { } /** Returns the lower 8 bits of the given int as a byte. * * @param i the given int * @return the lower 8 bits of the given int as a byte */ public static byte toUnsignedByte(final int i) { return (byte) (i & 0x000000ff); } /** Serializes the given object into a byte array. * * @param obj the given object * @return the serialized byte array */ public static byte[] serialize(final Serializable obj) { //Preconditions assert obj != null : "obj must not be null"; ObjectOutputStream objectOutputStream = null; try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); objectOutputStream.close(); return byteArrayOutputStream.toByteArray(); } catch (IOException ex) { throw new TexaiException(ex); } finally { try { if (objectOutputStream != null) { objectOutputStream.close(); } } catch (IOException ex) { throw new TexaiException(ex); } } } /** Deserializes the given byte array into an object. * * @param bytes the given byte array * @return the deserialized object */ public static Serializable deserialize(final byte[] bytes) { //Preconditions assert bytes != null : "obj must not be null"; final InputStream inputStream = new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream = null; try { objectInputStream = new ObjectInputStream(inputStream); return (Serializable) objectInputStream.readObject(); } catch (IOException | ClassNotFoundException ex) { throw new TexaiException(ex); } finally { try { if (objectInputStream != null) { objectInputStream.close(); } } catch (IOException ex) { throw new TexaiException(ex); } } } /** Returns the array of bytes resulting from a new UUID. * * @return the array of bytes resulting from a new UUID */ public static byte[] makeUUIDBytes() { final UUID uuid = UUID.randomUUID(); return append(toBytes(uuid.getMostSignificantBits()), toBytes(uuid.getLeastSignificantBits())); } /** Returns true if the given byte array is all zeros. * * @param bytes the given byte array * @return true if the given byte array is all zeros */ public static boolean isZero(final Byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; for (byte myByte : bytes) { if (myByte != 0) { return false; } } return true; } /** Returns true if the given byte array is all zeros. * * @param bytes the given byte array * @return true if the given byte array is all zeros */ public static boolean isZero(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; for (byte myByte : bytes) { if (myByte != 0) { return false; } } return true; } /** Returns true if the given byte array is not all zeros. * * @param bytes the given byte array * @return true if the given byte array is not all zeros */ public static boolean isNonZero(final Byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; for (byte myByte : bytes) { if (myByte == 0) { return false; } } return true; } /** Returns true if the given byte array is not all zeros. * * @param bytes the given byte array * @return true if the given byte array is not all zeros */ public static boolean isNonZero(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; for (byte myByte : bytes) { if (myByte == 0) { return false; } } return true; } /** Returns an array of Byte objects for the given UUID. * * @param uuid the UUID * @return an array of Byte objects for the given UUID */ public static Byte[] toByteObjectArray(final UUID uuid) { //Preconditions assert uuid != null : "uuid must not be null"; final byte[] byteArray = toBytes(uuid); final Byte[] bytes = new Byte[byteArray.length]; for (int i = 0; i < byteArray.length; i++) { bytes[i] = byteArray[i]; } return bytes; } /** Returns an array of Byte objects for the given byte array. * * @param bytes the byte array * @return an array of Byte objects for the given byte array */ public static Byte[] toByteObjectArray(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; final Byte[] byteObjectArray = new Byte[bytes.length]; for (int i = 0; i < bytes.length; i++) { byteObjectArray[i] = bytes[i]; } return byteObjectArray; } /** Returns an array of bytes for the given Byte object array. * * @param byteObjectArray the given Byte object array * @return an array of bytes for the given Byte object array */ public static byte[] toByteArray(final Byte[] byteObjectArray) { //Preconditions assert byteObjectArray != null : "byteObjectArray must not be null"; final byte[] bytes = new byte[byteObjectArray.length]; for (int i = 0; i < byteObjectArray.length; i++) { bytes[i] = byteObjectArray[i]; } return bytes; } /** Appends two bytes array into one. * * @param bytes1 the first given byte array * @param bytes2 the second given byte array * @return the byte array resulting from appending the two given byte arrays */ public static byte[] append(final byte[] bytes1, final byte[] bytes2) { //Preconditions assert bytes1 != null : "bytes1 must not be null"; assert bytes2 != null : "bytes2 must not be null"; final byte[] bytes3 = new byte[bytes1.length + bytes2.length]; System.arraycopy(bytes1, 0, bytes3, 0, bytes1.length); System.arraycopy(bytes2, 0, bytes3, bytes1.length, bytes2.length); return bytes3; } /** Returns a 8-byte array built from a long. * * @param number the long number to convert * @return a byte array */ public static byte[] toBytes(final long number) { final ByteBuffer byteBuffer = ByteBuffer.allocate(8); byteBuffer.putLong(number); return byteBuffer.array(); } /** Builds a long from first 8 bytes of the array. * * @param bytes the byte array to convert * @return a long */ public static long toLong(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; assert bytes.length == 8 : "the byte array must be length 8"; return ((((long) bytes[7]) & 0xFF) + ((((long) bytes[6]) & 0xFF) << 8) + ((((long) bytes[5]) & 0xFF) << 16) + ((((long) bytes[4]) & 0xFF) << 24) + ((((long) bytes[3]) & 0xFF) << 32) + ((((long) bytes[2]) & 0xFF) << 40) + ((((long) bytes[1]) & 0xFF) << 48) + ((((long) bytes[0]) & 0xFF) << 56)); } /** Returns a 4-byte array built from an int. * * @param number the int number to convert * @return a byte array */ public static byte[] toBytes(final int number) { final ByteBuffer byteBuffer = ByteBuffer.allocate(4); byteBuffer.putInt(number); return byteBuffer.array(); } /** Returns the 16-byte array from the given UUID. * * @param uuid the given UUID * @return the 16-byte array from the given UUID */ public static byte[] toBytes(final UUID uuid) { //Preconditions assert uuid != null : "uuid must not be null"; return append(toBytes(uuid.getMostSignificantBits()), toBytes(uuid.getLeastSignificantBits())); } /** Returns a hexadecimal string representation of the given byte array. * * @param bytes the given byte array * @return a hexadecimal string representation of the given byte array */ public static String toHex(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; final int byteArray_len = bytes.length; final StringBuilder stringBuilder = new StringBuilder(2 * byteArray_len); for (int i = 0; i < byteArray_len; i++) { final int value = bytes[i] & 0xff; stringBuilder.append((char) HEX_CHARACTERS[value >> 4]); stringBuilder.append((char) HEX_CHARACTERS[value & 0xf]); } return stringBuilder.toString(); } /** Returns a string representation of the given byte. * *@param byte1 the given byte * @return a string representation of the given byte */ public static String toHex(final Byte byte1) { final int value = byte1 & 0xff; final StringBuilder stringBuilder = new StringBuilder(2); stringBuilder.append((char) HEX_CHARACTERS[value >> 4]); stringBuilder.append((char) HEX_CHARACTERS[value & 0xf]); return stringBuilder.toString(); } /** Returns the integer represented by the given hex digit character. * * @param hexCharacter the given hex digit character * @return the integer represented by the given hex digit character */ public static int fromHex(final char hexCharacter) { final char lowerCaseHexCharacter = Character.toLowerCase(hexCharacter); for (int i = 0; i < 16; i++) { if (HEX_CHARACTERS[i] == lowerCaseHexCharacter) { return i; } } assert false : "invalid hex digit"; return -1; } /** Returns the byte represented by the two hex digits. * * @param string two hex digits * @return the byte represented by the two hex digits */ public static Byte fromHex(final String string) { //Preconditions assert string != null && string.length() == 2 : "string must have length 2"; final char c0 = string.charAt(0); final char c1 = string.charAt(1); return (byte) (16 * fromHex(c0) + fromHex(c1)); } /** Convert the byte array to an int. The array must be four or less in length. * * @param bytes The byte array * @return the integer */ public static int byteArrayToInt(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; if (bytes.length > 4) { throw new IllegalArgumentException("must be four or less bytes"); } return byteArrayToInt(bytes, 0); } /** Convert the byte array to an int starting from the given offset, and continuing for up to four bytes. * * @param bytes The byte array * @param offset The array offset * @return the integer */ public static int byteArrayToInt(final byte[] bytes, final int offset) { //Preconditions assert bytes != null : "bytes must not be null"; assert offset >= 0 : "offset must not be negative"; final int length; if ((bytes.length - offset) > 4) { length = 4; } else { length = bytes.length - offset; } int value = 0; for (int i = 0; i < length; i++) { final int shift = (length - 1 - i) * 8; value += (bytes[i + offset] & 0x000000FF) << shift; } return value; } /** Compares two byte arrays for equality. * * @param bytes1 the first given byte array * @param bytes2 the second given byte array * @return True if the arrays have identical contents. */ public static boolean areEqual(final byte[] bytes1, final byte[] bytes2) { //Preconditions assert bytes1 != null : "bytes1 must not be null"; assert bytes2 != null : "bytes2 must not be null"; final int aLength = bytes1.length; if (aLength != bytes2.length) { return false; } for (int i = 0; i < aLength; i++) { if (bytes1[i] != bytes2[i]) { return false; } } return true; } /** Compares two byte arrays for equality. * * @param bytes1 the first given byte array * @param bytes2 the second given byte array * @return True if the arrays have identical contents. */ public static boolean areEqual(final Byte[] bytes1, final Byte[] bytes2) { //Preconditions assert bytes1 != null : "bytes1 must not be null"; assert bytes2 != null : "bytes2 must not be null"; final int aLength = bytes1.length; if (aLength != bytes2.length) { return false; } for (int i = 0; i < aLength; i++) { if (!bytes1[i].equals(bytes2[i])) { return false; } } return true; } /** <p>Compares two Byte arrays as specified by <code>Comparable</code> with respect to each byte as a signed integer. * * @param lhs - left hand value in the comparison operation. * @param rhs - right hand value in the comparison operation. * @return a negative integer, zero, or a positive integer as <code>lhs</code> * is less than, equal to, or greater than <code>rhs</code>. */ public static int compareTo(final Byte[] lhs, final Byte[] rhs) { //Preconditions assert lhs != null : "lhs must not be null"; assert rhs != null : "rhs must not be null"; return compareTo(toByteArray(lhs), toByteArray(rhs)); } /** <p>Compares two byte arrays as specified by <code>Comparable</code> with respect to each byte as a signed integer. * * @param lhs - left hand value in the comparison operation. * @param rhs - right hand value in the comparison operation. * @return a negative integer, zero, or a positive integer as <code>lhs</code> * is less than, equal to, or greater than <code>rhs</code>. */ public static int compareTo(final byte[] lhs, final byte[] rhs) { //Preconditions assert lhs != null : "lhs must not be null"; assert rhs != null : "rhs must not be null"; if (lhs == rhs) { return 0; } if (lhs.length != rhs.length) { return ((lhs.length < rhs.length) ? -1 : +1); } for (int i = 0; i < lhs.length; i++) { if (lhs[i] < rhs[i]) { return -1; } else if (lhs[i] > rhs[i]) { return 1; } } return 0; } /** Provides a byte array comparator with respect to each byte as a signed integer. */ public static class ByteArrayComparator implements Comparator<Byte[]>, Serializable { /** the serialization version ID */ static final long serialVersionUID = 1; /** Constructs a new ByteArrayComparator instance. */ public ByteArrayComparator() { } /** Compares the two given byte arrays with respect to each byte as a signed integer. * * @param bytes1 the first given byte array * @param bytes2 the second given byte array * @return a negative integer, zero, or a positive integer as <code>lhs</code> * is less than, equal to, or greater than <code>rhs</code> */ @Override public int compare(final Byte[] bytes1, final Byte[] bytes2) { //Preconditions assert bytes1 != null : "bytes1 must not be null"; assert bytes2 != null : "bytes2 must not be null"; return compareTo(toByteArray(bytes1), toByteArray(bytes2)); } } }