/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.protocol.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.BitSet;
import org.apache.commons.codec.binary.Hex;
/**
*
* Util class for methods working with byte array.
*
*/
public final class ByteArray {
private ByteArray() {
}
/**
* Returns a new byte array from given byte array, starting at start index with the size of the length parameter.
* Byte array given as parameter stays untouched.
*
* @param bytes original byte array
* @param startIndex beginning index, inclusive
* @param length how many bytes should be in the sub-array
* @return a new byte array that is a sub-array of the original
*/
public static byte[] subByte(final byte[] bytes, final int startIndex, final int length) {
if (bytes.length == 0 || length < 0 || length > bytes.length || startIndex < 0 || startIndex > bytes.length
|| startIndex + length > bytes.length) {
throw new IllegalArgumentException("Cannot create subByte, invalid arguments: Length: " + length + " startIndex: " + startIndex);
}
final byte[] res = new byte[length];
System.arraycopy(bytes, startIndex, res, 0, length);
return res;
}
/**
* Converts byte array to Integer. If there are less bytes in the array as required (4), the method will push
* adequate number of zero bytes prepending given byte array.
*
* @param bytes array to be converted to int
* @return int
*/
public static int bytesToInt(final byte[] bytes) {
if (bytes.length > Integer.SIZE / Byte.SIZE) {
throw new IllegalArgumentException("Cannot convert bytes to integer. Byte array too big.");
}
byte[] res = new byte[Integer.SIZE / Byte.SIZE];
if (bytes.length != Integer.SIZE / Byte.SIZE) {
System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
} else {
res = bytes;
}
final ByteBuffer buff = ByteBuffer.wrap(res);
return buff.getInt();
}
/**
* Converts byte array to long. If there are less bytes in the array as required (Long.Size), the method will push
* adequate number of zero bytes prepending given byte array.
*
* @param bytes array to be converted to long
* @return long
*/
public static long bytesToLong(final byte[] bytes) {
if (bytes.length > Long.SIZE / Byte.SIZE) {
throw new IllegalArgumentException("Cannot convert bytes to long.Byte array too big.");
}
byte[] res = new byte[Long.SIZE / Byte.SIZE];
if (bytes.length != Long.SIZE / Byte.SIZE) {
System.arraycopy(bytes, 0, res, Long.SIZE / Byte.SIZE - bytes.length, bytes.length);
} else {
res = bytes;
}
final ByteBuffer buff = ByteBuffer.wrap(res);
return buff.getLong();
}
/**
* Converts byte array to float IEEE 754 format. If there are less bytes in the array as required (Float.Size), the
* method will push adequate number of zero bytes prepending given byte array.
*
* @param bytes array to be converted to float
* @return float
*/
public static float bytesToFloat(final byte[] bytes) {
if (bytes.length > Float.SIZE / Byte.SIZE) {
throw new IllegalArgumentException("Cannot convert bytes to float.Byte array too big.");
}
byte[] res = new byte[Float.SIZE / Byte.SIZE];
if (bytes.length != Float.SIZE / Byte.SIZE) {
System.arraycopy(bytes, 0, res, Float.SIZE / Byte.SIZE - bytes.length, bytes.length);
} else {
res = bytes;
}
final ByteBuffer buff = ByteBuffer.wrap(res);
return buff.getFloat();
}
/**
* Cuts 'count' number of bytes from the beginning of given byte array.
*
* @param bytes array to be cut, cannot be null
* @param count how many bytes needed to be cut, needs to be > 0
* @return bytes array without first 'count' bytes
*/
public static byte[] cutBytes(final byte[] bytes, final int count) {
if (bytes.length == 0 || count > bytes.length || count <= 0) {
throw new IllegalArgumentException("Cannot cut bytes, invalid arguments: Count: " + count + " bytes.length: " + bytes.length);
}
return Arrays.copyOfRange(bytes, count, bytes.length);
}
/**
* Parse byte to bits, from the leftmost bit.
*
* @param b byte to be parsed
* @return array of booleans with size of 8
*/
public static boolean[] parseBits(final byte b) {
final boolean[] bits = new boolean[Byte.SIZE];
int j = 0;
for (int i = Byte.SIZE - 1; i >= 0; i--) {
bits[j] = ((b & (1 << i)) != 0);
j++;
}
return bits;
}
/**
* Parses array of bytes to BitSet, from left most bit.
*
* @param bytes array of bytes to be parsed
* @return BitSet with length = bytes.length * Byte.SIZE
*/
public static BitSet bytesToBitSet(final byte[] bytes) {
final BitSet bitSet = new BitSet(bytes.length * Byte.SIZE);
for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
final int offset = bytesIter * Byte.SIZE;
for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
bitSet.set(offset + (Byte.SIZE - byteIter - 1), (bytes[bytesIter] & 1 << (byteIter)) != 0);
}
}
return bitSet;
}
/**
* Parses BitSet to bytes, from most left bit.
*
* @param bitSet BitSet to be parsed
* @param returnedLength Length of returned array. Overlapping flags are truncated.
* @return parsed array of bytes with length of bitSet.length / Byte.SIZE
*/
public static byte[] bitSetToBytes(final BitSet bitSet, final int returnedLength) {
final byte[] bytes = new byte[returnedLength];
for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
final int offset = bytesIter * Byte.SIZE;
for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
bytes[bytesIter] |= (bitSet.get(offset + (Byte.SIZE - byteIter - 1)) ? 1 << byteIter : 0);
}
}
return bytes;
}
/**
* Parses file to array of bytes
*
* @param name path to file to by parsed
* @return parsed array of bytes
*/
public static byte[] fileToBytes(final String name) throws IOException {
final File file = new File(name);
int offset = 0;
int numRead = 0;
if (file.length() > Integer.MAX_VALUE) {
throw new IOException("Too large file to load in byte array.");
}
final FileInputStream fin = new FileInputStream(file);
final byte[] byteArray = new byte[(int) file.length()];
while (offset < byteArray.length && (numRead = fin.read(byteArray, offset, byteArray.length - offset)) >= 0) {
offset += numRead;
}
if (fin != null) {
fin.close();
}
return byteArray;
}
/**
* Parses integer to array of bytes
*
* @param num integer to be parsed
* @return parsed array of bytes with length of Integer.SIZE/Byte.SIZE
*/
public static byte[] intToBytes(final int num) {
return intToBytes(num, Integer.SIZE / Byte.SIZE);
}
/**
* Parses integer to array of bytes
*
* @param num integer to be parsed
* @param size desired byte array length
* @return parsed array of bytes with length of size
*/
public static byte[] intToBytes(final int num, final int size) {
final int finalSize = Integer.SIZE / Byte.SIZE;
final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
bytesBuffer.putInt(num);
return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
}
/**
* Parses long to array of bytes
*
* @param num long to be parsed
* @return parsed array of bytes with length of Long.SIZE/Byte.SIZE
*/
public static byte[] longToBytes(final int num) {
return longToBytes(num, Long.SIZE / Byte.SIZE);
}
/**
* Parses long to array of bytes
*
* @param num long to be parsed
* @param size desired byte array length
* @return parsed array of bytes with length of size
*/
public static byte[] longToBytes(final long num, final int size) {
final int finalSize = Long.SIZE / Byte.SIZE;
final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
bytesBuffer.putLong(num);
return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
}
/**
* Copies range of bits from passed byte and align to right.
*
* @param src source byte to copy from
* @param fromBit bit from which will copy (inclusive) - numbered from 0
* @param length of bits to by copied - <1,8>
* @return copied value aligned to right
*/
public static byte copyBitsRange(final byte src, final int fromBit, final int length) {
if (fromBit < 0 | fromBit > Byte.SIZE - 1 | length < 1 | length > Byte.SIZE) {
throw new IllegalArgumentException("fromBit or toBit is out of range.");
}
if (fromBit + length > Byte.SIZE) {
throw new IllegalArgumentException("Out of range.");
}
byte retByte = 0;
int retI = 0;
for (int i = fromBit + length - 1; i >= fromBit; i--) {
if ((src & 1 << (Byte.SIZE - i - 1)) != 0) {
retByte |= 1 << retI;
}
retI++;
}
return retByte;
}
/**
* Copies whole source byte array to destination from offset.
* Length of src can't be bigger than dest length minus offset
*
* @param src byte[]
* @param dest byte[]
* @param offset int
*/
public static void copyWhole(final byte[] src, final byte[] dest, final int offset) {
if (dest.length - offset < src.length) {
throw new ArrayIndexOutOfBoundsException("Can't copy whole array.");
}
System.arraycopy(src, 0, dest, offset, src.length);
}
/**
* Convert array of bytes to java short.
* Size can't be bigger than size of short in bytes.
*
* @param bytes byte[]
* @return array of bytes
*/
public static short bytesToShort(final byte[] bytes) {
if (bytes.length > Short.SIZE / Byte.SIZE) {
throw new IllegalArgumentException("Cannot convert bytes to short. Byte array too big.");
}
byte[] res = new byte[Short.SIZE / Byte.SIZE];
if (bytes.length != Short.SIZE / Byte.SIZE) {
System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
} else {
res = bytes;
}
final ByteBuffer buff = ByteBuffer.wrap(res);
return buff.getShort();
}
/**
* Convert short java representation to array of bytes.
*
* @param num short
* @return short represented as array of bytes
*/
public static byte[] shortToBytes(final short num) {
final ByteBuffer bytesBuffer = ByteBuffer.allocate(Short.SIZE / Byte.SIZE);
bytesBuffer.putShort(num);
return bytesBuffer.array();
}
/**
* Convert float java representation to array of bytes.
*
* @param num float
* @return float represented as array of bytes
*/
public static byte[] floatToBytes(final float num) {
final ByteBuffer bytesBuffer = ByteBuffer.allocate(Float.SIZE / Byte.SIZE);
bytesBuffer.putFloat(num);
return bytesBuffer.array();
}
/**
* Pretty print array of bytes as hex encoded string with 16 bytes per line. Each byte is separated by space, after
* first 8 bytes there are 2 spaces instead of one.
*/
public static String bytesToHexString(final byte[] array) {
return bytesToHexString(array, 16, " ", 8, " ");
}
/**
* Pretty-print an array of bytes as hex-encoded string. Separate them with specified separator.
*/
public static String toHexString(final byte[] array, final String separator) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) {
sb.append(Hex.encodeHexString(new byte[] { array[i] }));
if (i + 1 != array.length) {
sb.append(separator);
}
}
return sb.toString();
}
/**
* Convert array of bytes to hexadecimal String.
*
* @param array
* @param bytesOnLine number of bytes that should by displayed in one line
* @param byteSeparator string that will be placed after each byte
* @param wordCount number of bytes that make a 'word' (group of bytes)
* @param wordSeparator string that will be placed after each word
* @return Hexadecimal string representation of given byte array
*/
public static String bytesToHexString(final byte[] array, final int bytesOnLine, final String byteSeparator, final int wordCount,
final String wordSeparator) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) {
sb.append(Hex.encodeHexString(new byte[] { array[i] }));
if ((i + 1) % bytesOnLine == 0) {
sb.append("\n");
} else {
sb.append(byteSeparator);
if ((i + 1) % wordCount == 0) {
sb.append(wordSeparator);
}
}
}
return sb.toString();
}
/**
* Decodes bytes to human readable UTF-8 string. If bytes are not valid UTF-8, they are represented as raw binary.
*
* @param bytes bytes to be decoded to string
* @return String representation of passed bytes
*/
public static String bytesToHRString(final byte[] bytes) {
try {
return Charset.forName("UTF-8").newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
} catch (final CharacterCodingException e) {
return Arrays.toString(bytes);
}
}
/**
* Searches for byte sequence in given array. Returns the index of first occurrence of this sequence (where it
* starts).
*
* @param bytes byte array where to search for sequence
* @param sequence to be searched in given byte array
* @return -1 if the sequence could not be found in given byte array int index of first occurrence of the sequence
* in bytes
*/
public static int findByteSequence(final byte[] bytes, final byte[] sequence) {
if (bytes.length < sequence.length) {
throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
}
if (bytes.length == sequence.length) {
if (Arrays.equals(bytes, sequence)) {
return 0;
} else {
return -1;
}
}
int j = 0;
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] == sequence[j]) {
j++;
if (j == sequence.length) {
return i - j + 1;
}
} else {
j = 0;
}
}
return -1;
}
private static byte MASK_BITS[] = new byte[] { 0, -128, -64, -32, -16, -8, -4, -2 };
public static byte[] maskBytes(final byte[] original, final int bits) {
if (original.length * Byte.SIZE < bits) {
throw new IllegalArgumentException("Attempted to apply invalid mask (too long)");
}
final int needbytes = (bits + 7) / Byte.SIZE;
// We need to have a new copy of the underlying byte array, so that
// the original bytes stay untouched
final byte[] bytes = Arrays.copyOf(original, original.length);
final int needmask = bits % Byte.SIZE;
if (needmask != 0) {
bytes[needbytes - 1] &= MASK_BITS[needmask];
}
// zero-out the rest of the bytes
for (int i = needbytes; i < bytes.length; i++) {
bytes[i] = 0;
}
return bytes;
}
public static byte[] trim(final byte[] bytes) {
int i = bytes.length - 1;
while (i >= 0 && bytes[i] == 0) {
--i;
}
return Arrays.copyOf(bytes, i + 1);
}
}