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

io.amient.affinity.core.util.ByteUtils Maven / Gradle / Ivy

/**
 * Copyright (C) 2015 Michal Harish
 * 

* 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 3 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, see . */ package io.amient.affinity.core.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.UUID; public class ByteUtils { /** * Converts ByteBuffer to an array - if the buffer is backed by the array but doesn't * fully overlap it it performs an array copy. If the buffer is not backed by an array * it constructs a new array and reads the buffer content into it. * @param b byte buffer to be converted to an array * @return all remaining bytes from the bytebuffer as a byte array */ public static byte[] bufToArray(ByteBuffer b) { if (b.hasArray()) { if (b.position() == 0 && b.arrayOffset() == 0 && b.limit() == b.capacity()) { return b.array(); } else { return Arrays.copyOfRange(b.array(), b.arrayOffset(), b.arrayOffset() + b.remaining()); } } else { byte[] a = new byte[b.remaining()]; int bp = b.position(); b.get(a); b.position(bp); return a; } } /** * Reverse byte array contents * @param src array to be reversed * @return reversed byte array */ public static byte[] reverse(byte[] src) { byte[] dest = new byte[src.length]; reverse(src, dest, 0); return dest; } /** * Reverse byte array contents * @param src array to be reversed * @param dest destination array * @param destOffset position in the destination array */ public static void reverse(byte[] src, byte[] dest, int destOffset) { int i = destOffset; int j = Math.min(dest.length - 1, src.length - 1); while (j >= 0 && i < dest.length) { dest[i] = src[j]; j--; i++; } } /** * Parse an array of character digits with base of 10 * @param array containing the digits to be parsed * @param offset position where to start the parsing * @param limit position of the last digit to parse * @return int equivalent of the parsed number */ public static int parseIntRadix10(byte[] array, int offset, int limit) { int result = 0; boolean negative = false; if (array[offset] == '-') { negative = true; offset++; } for (int i = offset; i <= limit; i++) { if (array[i] < 48 || array[i] > 57) { throw new IllegalArgumentException("Invalid numeric character " + (char) array[i]); } result *= 10; result += (array[i] - 48); } return negative ? -result : result; } /** * Parse an array of character digits with base of 10 * @param array containing the digits to be parsed * @param offset position where to start the parsing * @param limit position of the last digit to parse * @return long equivalent of the parsed number */ public static long parseLongRadix10(byte[] array, int offset, int limit) { long result = 0; for (int i = offset; i <= limit; i++) { if (array[i] < 48 || array[i] > 57) { throw new IllegalArgumentException("Invalid numeric character " + (char) array[i]); } result *= 10; result += (array[i] - 48); } return result; } /** * Parse an array of hexadecimal characters as a long value * @param array containing the digits to be parsed * @param offset position where to start the parsing * @param limit position of the last digit to parse * @return a long value represented by the hexadecimal input */ public static long parseLongRadix16(byte[] array, int offset, int limit) { long result = 0; for (int i = offset; i <= limit; i++) { result *= 16; result += parseRadix16Byte(array[i]); } return result; } /** * Parse an array of hexadecimal characters * @param array containing the hexadecimal characters * @param offset position where to start the parsing * @param len number of digits to parse * @return byte array as an n-bit representation of the input hexadecimal number */ public static byte[] parseRadix16(byte[] array, int offset, int len) { byte[] result = new byte[len / 2]; parseRadix16(array, offset, len, result, 0); return result; } /** * Parse an array of hexadecimal characters * @param array containing the hexadecimal characters * @param offset position where to start the parsing * @param len number of digits to parse * @param dest array where the n-bit representation result will be stored * @param destOffset position in the dest array where the result will be written */ public static void parseRadix16(byte[] array, int offset, int len, byte[] dest, int destOffset) { int limit = Math.min(offset + len, array.length); int j = destOffset; for (int i = offset; i < limit; i += 2, j++) { dest[j] = (byte) (parseRadix16Byte(array[i]) * 16 + parseRadix16Byte(array[i + 1])); } } private static final String HEX = "0123456789abcdef"; /** * Convert an n-bit BIG-ENDIAN representation of a number into its hexadecimal form * @param source an array containing the n * @param srcOffset position in the source array where to start * @param len number of bytes to convert * @return string of hexadecimal digits */ public static String toRadix16(byte[] source, int srcOffset, int len) { char[] hex = new char[len * 2]; for (int j = 0; j < len; j++) { int b = source[j + srcOffset] & 0xFF; hex[j * 2] = HEX.charAt(b >>> 4); hex[j * 2 + 1] = HEX.charAt(b & 0x0F); } return new String(hex); } /** * Parse a digit character with a base of 16 into its integer equivalent * @param b byte representing the ordinal digit, e.g. one of 0123456789ABCDEFabcdef * @return int from 0 to 16 */ private static int parseRadix16Byte(byte b) { if (b >= 48 && b <= 57) { return (b - 48); } else if (b >= 'A' && b <= 'F') { return (b - 55); } else if (b >= 97 && b <= 102) { return (b - 87); } else { throw new IllegalArgumentException("Invalid hexadecimal character " + (char) b); } } /** * Copy a range of an array into a dest array * @param src source array * @param srcOffset source array start position * @param dest destination array * @param destOffset destination array start position * @param len maximum number of bytes to copy * @return number of bytes copied */ public static int copy(byte[] src, int srcOffset, byte[] dest, int destOffset, int len) { int srcLimit = srcOffset + len; for (int i = srcOffset, j = destOffset; i < srcLimit; i++, j++) { dest[j] = src[i]; } return len; } /** * Convert 32-bit BIG ENDIAN array into an integer * @param value array containing at least 4 bytes * @return int */ static public int asIntValue(byte[] value) { return asIntValue(value, 0); } /** * Convert 64-bit BIG ENDIAN array into a long * @param value array containing at least 8 bytes * @return long */ static public long asLongValue(byte[] value) { return asLongValue(value, 0); } /** * Convert 32-bit BIG ENDIAN array into an integer * @param value array containing at least 4 bytes * @param offset position of the first byte in the value array * @return int */ static public int asIntValue(byte[] value, int offset) { return ((((int) value[offset + 0]) << 24) + (((int) value[offset + 1] & 0xff) << 16) + (((int) value[offset + 2] & 0xff) << 8) + (((int) value[offset + 3] & 0xff) << 0)); } /** * Read 32-bit BIG ENDIAN number from an input stream * @param in inpust stream * @return int * @throws IOException if the inpust stream cannot be read */ static public int readIntValue(InputStream in) throws IOException { return (in.read() << 24) + ((in.read() & 0xff) << 16) + ((in.read() & 0xff) << 8) + (in.read() & 0xff); } /** * Convert 64-bit BIG ENDIAN array into a long * @param value array containing at least 8 bytes * @param o offset position of the first byte in the value array * @return long */ static public long asLongValue(byte[] value, int o) { return (((long) value[o + 0] << 56) + (((long) value[o + 1] & 0xff) << 48) + (((long) value[o + 2] & 0xff) << 40) + (((long) value[o + 3] & 0xff) << 32) + (((long) value[o + 4] & 0xff) << 24) + (((long) value[o + 5] & 0xff) << 16) + (((long) value[o + 6] & 0xff) << 8) + (((long) value[o + 7] & 0xff) << 0)); } /** * Generate 32-bit BIG ENDIAN number into a byte array * @param value int number to convert * @return a new 4-byte array with encoded integer */ public static byte[] intValue(int value) { byte[] result = new byte[4]; return putIntValue(value, result, 0); } /** * Write 32-bit BIG ENDIAN number into a byte array * @param value int number to convert * @param result destination array where to write the big endian * @param offset position in the destination array to start from * @return the modified array passed in the result parameterInfo */ public static byte[] putIntValue(int value, byte[] result, int offset) { result[offset + 0] = (byte) ((value >>> 24) & 0xFF); result[offset + 1] = (byte) ((value >>> 16) & 0xFF); result[offset + 2] = (byte) ((value >>> 8) & 0xFF); result[offset + 3] = (byte) ((value >>> 0) & 0xFF); return result; } /** * Write 32-bit BIG ENDIAN number into an output stream * @param value int number to write * @param out destination output stream where to write the big endian * @throws IOException if the output stream cannot be written to */ public static void writeIntValue(int value, OutputStream out) throws IOException { out.write((value >>> 24) & 0xFF); out.write((value >>> 16) & 0xFF); out.write((value >>> 8) & 0xFF); out.write((value >>> 0) & 0xFF); } /** * Write 64-bit BIG ENDIAN number into an output stream * @param value long number to write * @param out destination output stream where to write the big endian * @throws IOException if the output stream cannot be written to */ public static void writeLongValue(long value, OutputStream out) throws IOException { out.write((byte)((value >>> 56) & 0xFF)); out.write((byte)((value >>> 48) & 0xFF)); out.write((byte)((value >>> 40) & 0xFF)); out.write((byte)((value >>> 32) & 0xFF)); out.write((byte)((value >>> 24) & 0xFF)); out.write((byte)((value >>> 16) & 0xFF)); out.write((byte)((value >>> 8) & 0xFF)); out.write((byte)((value >>> 0) & 0xFF)); } /** * Generate 64-bit BIG ENDIAN number into a byte array * @param value long number to convert * @return a new 8-byte array with encoded integer */ public static byte[] longValue(long value) { byte[] result = new byte[8]; return putLongValue(value, result, 0); } /** * Write 64-bit BIG ENDIAN number into a byte array * @param value long number to convert * @param result destination array where to write the big endian * @param offset position in the destination array to start from * @return the modified array passed in the result parameterInfo */ public static byte[] putLongValue(long value, byte[] result, int offset) { result[offset + 0] = (byte) ((value >>> 56) & 0xFF); result[offset + 1] = (byte) ((value >>> 48) & 0xFF); result[offset + 2] = (byte) ((value >>> 40) & 0xFF); result[offset + 3] = (byte) ((value >>> 32) & 0xFF); result[offset + 4] = (byte) ((value >>> 24) & 0xFF); result[offset + 5] = (byte) ((value >>> 16) & 0xFF); result[offset + 6] = (byte) ((value >>> 8) & 0xFF); result[offset + 7] = (byte) ((value >>> 0) & 0xFF); return result; } /** * Equality check for 2 byte arrays * @param a first array * @param a2 second array * @return true if the arrays are identical, false otherwise */ public static boolean equals(byte[] a, byte[] a2) { if (a == a2) return true; if (a == null || a2 == null) return false; int length = a.length; if (a2.length != length) return false; for (int i = 0; i < length; i++) if (a[i] != a2[i]) return false; return true; } /** * BIG ENDIAN comparison of 2 byte arrays * @param lArray left array * @param rArray right array * @return the larger of the 2 arguments */ final public static byte[] max(byte[] lArray, byte[] rArray) { int cmp = compare(lArray, 0, lArray.length, rArray, 0, rArray.length); if (cmp >= 0) { return lArray; } else { return rArray; } } /** * BIG ENDIAN comparison of 2 ranges of byte arrays * @param lArray left array * @param leftOffset left array range start * @param lSize left array range size * @param rArray right array * @param rightOffset right array range start * @param rSize right array range size * @return true if the ranges are identical, false otherwise */ final public static boolean equals(byte[] lArray, int leftOffset, int lSize, byte[] rArray, int rightOffset, int rSize) { if (lSize != rSize) { return false; } else { return compare(lArray, leftOffset, lSize, rArray, rightOffset, rSize) == 0; } } /** * BIG ENDIAN comparison of 2 ranges of byte arrays. If the 2 arrays have different size * they are still compared. If for example the left array has a smaller size than the right array * but all the bytes are identical to the head of the right array, the result is -1. * @param lArray left array * @param leftOffset left array range start * @param lSize left array range size * @param rArray right array * @param rightOffset right array range start * @param rSize right array range size * @return -1 if left array is smaller, 1 if the left array is larger, 0 if the 2 ranges are identical */ final public static int compare(byte[] lArray, int leftOffset, int lSize, byte[] rArray, int rightOffset, int rSize) { int i = leftOffset; int j = rightOffset; int n = lSize; for (int k = 0; k < n; k++, i++, j++) { if (k >= rSize) { return 1; } int cmp = (lArray[i] & 0xFF) - (rArray[j] & 0xFF); if (cmp != 0) { return cmp; } } if (lSize < rSize) { return -1; } return 0; } /** * Check if one byte array range is contained in another * @param cArray the content array which is searched through * @param cOffset the content array range where the search starts * @param cSize the content array range num bytes * @param vArray the value array that is searched for * @param vOffset the value array range start * @param vSize the value array range num bytes * @return true if the cArray contains the vArray */ final public static boolean contains(byte[] cArray, int cOffset, int cSize, byte[] vArray, int vOffset, int vSize) { if (cSize == 0 || vSize == 0 || vSize > cSize) { return false; } int cLimit = cOffset + cSize - 1; int vLimit = vOffset + vSize - 1; int v = vOffset; for (int c = cOffset; c <= cLimit; c++) { if (vArray[v] != cArray[c]) { v = vOffset; if (c + vSize > cLimit) { return false; } } else if (++v >= vLimit) { return true; } } return false; } /** * Check if one byte array range starts with another * @param cArray the content array which is searched through * @param vArray the value array that is searched for * @return true if the cArray starts with the vArray */ final public static boolean startsWith(byte[] cArray, byte[] vArray) { if (cArray.length < vArray.length) return false; int cLimit = vArray.length - 1; for (int c = 0; c <= cLimit; c++) { if (vArray[c] != cArray[c]) { return false; } } return true; } /** * Check if one byte buffer starts with another * @param cBuf the content buffer which is searched through * @param vBuf the value buffer that is searched for * @return true if the cBuf starts with the vBug */ final public static boolean startsWith(ByteBuffer cBuf, ByteBuffer vBuf) { if (cBuf.limit() < vBuf.limit()) return false; int cLimit = vBuf.limit() - 1; for (int c = 0; c <= cLimit; c++) { if (vBuf.get(vBuf.position()+c) != cBuf.get(cBuf.position()+c)) { return false; } } return true; } /** * CRC32 checksum of a byte array range * @param array input array to perform the checksum over * @param offset start position * @param size number of bytes to checksum from the offset * @return int checksum */ public static int crc32(byte[] array, int offset, int size) { int crc = 0xFFFF; for (int pos = offset; pos < offset + size; pos++) { crc ^= (int) array[pos]; for (int i = 8; i != 0; i--) { if ((crc & 0x0001) != 0) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } /** * XOR checksum of a byte array range * @param array input array to checksum * @param offset start position in the input * @param size number of bytes to checksum * @return int checksum */ public static int sum(byte[] array, int offset, int size) { int sum = 0; for (int i = offset; i < offset + size; i++) sum ^= array[i]; return sum; } /** * convert java.util.UUID into 128-bit representation as a byte array * @param uuid input java.util.UUID * @return byte array */ public static byte[] uuid(UUID uuid) { byte[] result = new byte[16]; putLongValue(uuid.getMostSignificantBits(), result, 0); putLongValue(uuid.getLeastSignificantBits(), result, 8); return result; } /** * convert 128-bit representation into a java.util.UUID * @param uuid input byte array * @return java.util.UUID */ public static UUID uuid(byte[] uuid) { return new UUID(asLongValue(uuid, 0), asLongValue(uuid, 8)); } /** * parse a standard string representation of a UUID (hexadecimal components spaced with dash) * @param uuid input string * @return 128-bit UUID representation */ public static byte[] parseUUID(String uuid) { return parseUUID(uuid, new byte[16], 0); } /** * convert a standard string representation of a UUID (hexadecimal components spaced with dash) * from a byte string range into a 128-bit UUID representation * @param uuid input byte string * @param dest output byte array * @param destOffset output byte array start position * @return modified dest array */ public static byte[] parseUUID(String uuid, byte[] dest, int destOffset) { parseUUID(uuid.getBytes(), 1, 0, dest, destOffset); return dest; } /** * parse a pure hexadecimal string representation of a UUID * @param uuid input byte string * @return 128-bit UUID representation */ public static byte[] parseUUIDNoSpacing(String uuid) { byte[] result = new byte[16]; parseUUID(uuid.getBytes(), 0, 0, result, 0); return result; } /** * parse a pure hexadecimal string representation of a UUID into a specific position in the dest byte array * @param uuid input byte string * @param dest output byte array * @param destOffset output byte array start position * @return 128-bit UUID representation */ public static byte[] parseUUIDNoSpacing(String uuid, byte[] dest, int destOffset) { parseUUID(uuid.getBytes(), 0, 0, dest, destOffset); return dest; } private static void parseUUID(byte[] uuid, int spacing, int uuidOffset, byte[] dest, int destOffset) { long mostSigBits = parseLongRadix16(uuid, uuidOffset, uuidOffset + 7); mostSigBits <<= 16; mostSigBits |= parseLongRadix16(uuid, uuidOffset + 8 + 1 * spacing, uuidOffset + 11 + 1 * spacing); mostSigBits <<= 16; mostSigBits |= parseLongRadix16(uuid, uuidOffset + 12 + 2 * spacing, uuidOffset + 15 + 2 * spacing); long leastSigBits = parseLongRadix16(uuid, uuidOffset + 16 + 3 * spacing, uuidOffset + 19 + 3 * spacing); leastSigBits <<= 48; leastSigBits |= parseLongRadix16(uuid, uuidOffset + 20 + 4 * spacing, uuidOffset + 31 + 4 * spacing); putLongValue(mostSigBits, dest, destOffset); putLongValue(leastSigBits, dest, destOffset + 8); } /** * convert 128-bit UUID into its String representation with dashes separating the UUID components * @param src byte array containing 16 bytes of the UUID value * @param offset position in the src array where the UUID starts * @return standard UUID string representation */ public static String UUIDToString(byte[] src, int offset) { return UUIDToString(asLongValue(src, offset), asLongValue(src, offset + 8), "-"); } /** * convert 128-bit UUID into its String representation without dashes separating the UUID components * @param src byte array containing 16 bytes of the UUID value * @param offset position in the src array where the UUID starts * @return hexadecimal string representation of the UUID */ public static String UUIDToNumericString(byte[] src, int offset) { return UUIDToString(asLongValue(src, offset), asLongValue(src, offset + 8), ""); } private static String UUIDToString(long mostSigBits, long leastSigBits, String separator) { return (digits(mostSigBits >> 32, 8) + separator + digits(mostSigBits >> 16, 4) + separator + digits(mostSigBits, 4) + separator + digits(leastSigBits >> 48, 4) + separator + digits(leastSigBits, 12)); } private static String digits(long val, int digits) { long hi = 1L << (digits * 4); return Long.toHexString(hi | (val & (hi - 1))).substring(1); } //the murmur2 function is identical to that of kafka (0.9+) and this is also tested under each kafka module public static int murmur2(final byte[] data) { int length = data.length; int seed = 0x9747b28c; // 'm' and 'r' are mixing constants generated offline. // They're not really 'magic', they just happen to work well. final int m = 0x5bd1e995; final int r = 24; // Initialize the hash to a random value int h = seed ^ length; int length4 = length / 4; for (int i = 0; i < length4; i++) { final int i4 = i * 4; int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8) + ((data[i4 + 2] & 0xff) << 16) + ((data[i4 + 3] & 0xff) << 24); k *= m; k ^= k >>> r; k *= m; h *= m; h ^= k; } // Handle the last few bytes of the input array switch (length % 4) { case 3: h ^= (data[(length & ~3) + 2] & 0xff) << 16; case 2: h ^= (data[(length & ~3) + 1] & 0xff) << 8; case 1: h ^= (data[length & ~3] & 0xff); h *= m; } h ^= h >>> 13; h *= m; h ^= h >>> 15; return h; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy