2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.protocol.util;
10 import com.google.common.base.Preconditions;
11 import com.google.common.primitives.UnsignedInteger;
12 import io.netty.buffer.ByteBuf;
14 import java.io.FileInputStream;
15 import java.io.IOException;
16 import java.nio.ByteBuffer;
17 import java.nio.charset.CharacterCodingException;
18 import java.nio.charset.Charset;
19 import java.util.Arrays;
20 import java.util.BitSet;
21 import org.apache.commons.codec.binary.Hex;
24 * Util class for methods working with byte array.
26 public final class ByteArray {
29 throw new UnsupportedOperationException();
33 * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
34 * at reader index, with given length. Increases reader index of the buffer by 'length'.
36 * @param buffer ByteBuf from which the bytes are going to be taken
37 * @param length length of the returned byte array
40 public static byte[] readBytes(final ByteBuf buffer, final int length) {
41 Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
42 "Buffer cannot be read for %s bytes.", length);
43 final byte[] result = new byte[length];
44 buffer.readBytes(result);
49 * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
50 * Adjusts reader index of the buffer by length of readable bytes in the buffer.
52 * @param buffer byteBuf from which the bytes are going to be taken
55 public static byte[] readAllBytes(final ByteBuf buffer) {
56 return readBytes(buffer, buffer.readableBytes());
60 * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
61 * at reader index, with given length. Does not modify reader or writer index of the buffer.
63 * @param buffer ByteBuf from which the bytes are going to be taken
64 * @param length length of the returned byte array
67 public static byte[] getBytes(final ByteBuf buffer, final int length) {
68 Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
69 "Buffer cannot be read for %s bytes.", length);
70 final byte[] result = new byte[length];
71 buffer.getBytes(buffer.readerIndex(), result);
76 * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
77 * Does not modify writer or reader index of the buffer.
79 * @param buffer byteBuf from which the bytes are going to be taken
82 public static byte[] getAllBytes(final ByteBuf buffer) {
83 return getBytes(buffer, buffer.readableBytes());
87 * Returns a new byte array from given byte array, starting at start index with the size of the length parameter.
88 * Byte array given as parameter stays untouched.
90 * @param bytes original byte array
91 * @param startIndex beginning index, inclusive
92 * @param length how many bytes should be in the sub-array
93 * @return a new byte array that is a sub-array of the original
95 public static byte[] subByte(final byte[] bytes, final int startIndex, final int length) {
96 if (!checkLength(bytes, length) || !checkStartIndex(bytes, startIndex, length)) {
97 throw new IllegalArgumentException("Cannot create subByte, invalid arguments: Length: " + length + " startIndex: " + startIndex);
99 final byte[] res = new byte[length];
100 System.arraycopy(bytes, startIndex, res, 0, length);
104 private static boolean checkLength(final byte[] bytes, final int length) {
105 return length > 0 && bytes.length > 0 && length <= bytes.length;
108 private static boolean checkStartIndex(final byte[] bytes, final int startIndex, final int length) {
109 return startIndex >= 0 && startIndex < bytes.length && (startIndex + length <= bytes.length);
113 * Converts byte array to Integer. If there are less bytes in the array as required (4), the method will push
114 * adequate number of zero bytes prepending given byte array.
116 * @param bytes array to be converted to int
119 public static int bytesToInt(final byte[] bytes) {
120 if (bytes.length > Integer.SIZE / Byte.SIZE) {
121 throw new IllegalArgumentException("Cannot convert bytes to integer. Byte array too big.");
123 byte[] res = new byte[Integer.SIZE / Byte.SIZE];
124 if (bytes.length != Integer.SIZE / Byte.SIZE) {
125 System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
129 final ByteBuffer buff = ByteBuffer.wrap(res);
130 return buff.getInt();
134 * Converts byte array to long. If there are less bytes in the array as required (Long.Size), the method will push
135 * adequate number of zero bytes prepending given byte array.
137 * @param bytes array to be converted to long
140 public static long bytesToLong(final byte[] bytes) {
141 if (bytes.length > Long.SIZE / Byte.SIZE) {
142 throw new IllegalArgumentException("Cannot convert bytes to long.Byte array too big.");
144 byte[] res = new byte[Long.SIZE / Byte.SIZE];
145 if (bytes.length != Long.SIZE / Byte.SIZE) {
146 System.arraycopy(bytes, 0, res, Long.SIZE / Byte.SIZE - bytes.length, bytes.length);
150 final ByteBuffer buff = ByteBuffer.wrap(res);
151 return buff.getLong();
155 * Converts byte array to float IEEE 754 format. If there are less bytes in the array as required (Float.Size), the
156 * method will push adequate number of zero bytes prepending given byte array.
158 * @param bytes array to be converted to float
161 public static float bytesToFloat(final byte[] bytes) {
162 if (bytes.length > Float.SIZE / Byte.SIZE) {
163 throw new IllegalArgumentException("Cannot convert bytes to float.Byte array too big.");
165 byte[] res = new byte[Float.SIZE / Byte.SIZE];
166 if (bytes.length != Float.SIZE / Byte.SIZE) {
167 System.arraycopy(bytes, 0, res, Float.SIZE / Byte.SIZE - bytes.length, bytes.length);
171 final ByteBuffer buff = ByteBuffer.wrap(res);
172 return buff.getFloat();
176 * Cuts 'count' number of bytes from the beginning of given byte array.
178 * @param bytes array to be cut, cannot be null
179 * @param count how many bytes needed to be cut, needs to be > 0
180 * @return bytes array without first 'count' bytes
182 public static byte[] cutBytes(final byte[] bytes, final int count) {
183 if (bytes.length == 0 || count > bytes.length || count <= 0) {
184 throw new IllegalArgumentException("Cannot cut bytes, invalid arguments: Count: " + count + " bytes.length: " + bytes.length);
186 return Arrays.copyOfRange(bytes, count, bytes.length);
190 * Parse byte to bits, from the leftmost bit.
192 * @param b byte to be parsed
193 * @return array of booleans with size of 8
195 public static boolean[] parseBits(final byte b) {
196 final boolean[] bits = new boolean[Byte.SIZE];
198 for (int i = Byte.SIZE - 1; i >= 0; i--) {
199 bits[j] = ((b & (1 << i)) != 0);
206 * Parses array of bytes to BitSet, from left most bit.
208 * @param bytes array of bytes to be parsed
209 * @return BitSet with length = bytes.length * Byte.SIZE
211 public static BitSet bytesToBitSet(final byte[] bytes) {
212 final BitSet bitSet = new BitSet(bytes.length * Byte.SIZE);
213 for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
214 final int offset = bytesIter * Byte.SIZE;
215 for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
216 bitSet.set(offset + (Byte.SIZE - byteIter - 1), (bytes[bytesIter] & 1 << (byteIter)) != 0);
223 * Parses BitSet to bytes, from most left bit.
225 * @param bitSet BitSet to be parsed
226 * @param returnedLength Length of returned array. Overlapping flags are truncated.
227 * @return parsed array of bytes with length of bitSet.length / Byte.SIZE
229 public static byte[] bitSetToBytes(final BitSet bitSet, final int returnedLength) {
230 final byte[] bytes = new byte[returnedLength];
232 for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
233 final int offset = bytesIter * Byte.SIZE;
235 for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
236 bytes[bytesIter] |= (bitSet.get(offset + (Byte.SIZE - byteIter - 1)) ? 1 << byteIter : 0);
243 * Parses file to array of bytes
245 * @param name path to file to by parsed
246 * @return parsed array of bytes
248 public static byte[] fileToBytes(final String name) throws IOException {
249 final File file = new File(name);
253 if (file.length() > Integer.MAX_VALUE) {
254 throw new IOException("Too large file to load in byte array.");
256 final byte[] byteArray = new byte[(int) file.length()];
257 try (final FileInputStream fin = new FileInputStream(file)) {
258 while (offset < byteArray.length && (numRead = fin.read(byteArray, offset, byteArray.length - offset)) >= 0) {
267 * Parses integer to array of bytes
269 * @param num integer to be parsed
270 * @return parsed array of bytes with length of Integer.SIZE/Byte.SIZE
272 public static byte[] intToBytes(final int num) {
273 return intToBytes(num, Integer.SIZE / Byte.SIZE);
277 * Parses integer to array of bytes
279 * @param num integer to be parsed
280 * @param size desired byte array length
281 * @return parsed array of bytes with length of size
283 public static byte[] intToBytes(final int num, final int size) {
284 final int finalSize = Integer.SIZE / Byte.SIZE;
285 final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
286 bytesBuffer.putInt(num);
287 return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
291 * Parses long to array of bytes
293 * @param num long to be parsed
294 * @return parsed array of bytes with length of Long.SIZE/Byte.SIZE
296 public static byte[] longToBytes(final int num) {
297 return longToBytes(num, Long.SIZE / Byte.SIZE);
301 * Parses long to array of bytes
303 * @param num long to be parsed
304 * @param size desired byte array length
305 * @return parsed array of bytes with length of size
307 public static byte[] longToBytes(final long num, final int size) {
308 final int finalSize = Long.SIZE / Byte.SIZE;
309 final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
310 bytesBuffer.putLong(num);
311 return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
315 * Copies range of bits from passed byte and align to right.<br/>
317 * @param src source byte to copy from
318 * @param fromBit bit from which will copy (inclusive) - numbered from 0
319 * @param length of bits to by copied - <1,8>
320 * @return copied value aligned to right
322 public static byte copyBitsRange(final byte src, final int fromBit, final int length) {
323 if (fromBit < 0 || fromBit > Byte.SIZE - 1 || length < 1 || length > Byte.SIZE) {
324 throw new IllegalArgumentException("fromBit or toBit is out of range.");
326 if (fromBit + length > Byte.SIZE) {
327 throw new IllegalArgumentException("Out of range.");
333 for (int i = fromBit + length - 1; i >= fromBit; i--) {
335 if ((src & 1 << (Byte.SIZE - i - 1)) != 0) {
336 retByte |= 1 << retI;
346 * Copies whole source byte array to destination from offset.<br/>
347 * Length of src can't be bigger than dest length minus offset
353 public static void copyWhole(final byte[] src, final byte[] dest, final int offset) {
354 if (dest.length - offset < src.length) {
355 throw new ArrayIndexOutOfBoundsException("Can't copy whole array.");
358 System.arraycopy(src, 0, dest, offset, src.length);
362 * Convert array of bytes to java short.<br/>
363 * Size can't be bigger than size of short in bytes.
365 * @param bytes byte[]
366 * @return array of bytes
368 public static short bytesToShort(final byte[] bytes) {
369 if (bytes.length > Short.SIZE / Byte.SIZE) {
370 throw new IllegalArgumentException("Cannot convert bytes to short. Byte array too big.");
372 byte[] res = new byte[Short.SIZE / Byte.SIZE];
373 if (bytes.length != Short.SIZE / Byte.SIZE) {
374 System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
378 final ByteBuffer buff = ByteBuffer.wrap(res);
379 return buff.getShort();
383 * Convert short java representation to array of bytes.
386 * @return short represented as array of bytes
388 public static byte[] shortToBytes(final short num) {
389 final ByteBuffer bytesBuffer = ByteBuffer.allocate(Short.SIZE / Byte.SIZE);
390 bytesBuffer.putShort(num);
392 return bytesBuffer.array();
396 * Convert float java representation to array of bytes.
399 * @return float represented as array of bytes
401 public static byte[] floatToBytes(final float num) {
402 final ByteBuffer bytesBuffer = ByteBuffer.allocate(Float.SIZE / Byte.SIZE);
403 bytesBuffer.putFloat(num);
405 return bytesBuffer.array();
409 * Pretty print array of bytes as hex encoded string with 16 bytes per line. Each byte is separated by space, after
410 * first 8 bytes there are 2 spaces instead of one.
412 public static String bytesToHexString(final byte[] array) {
413 return bytesToHexString(array, 16, " ", 8, " ");
417 * Pretty-print an array of bytes as hex-encoded string. Separate them with specified separator.
419 public static String toHexString(final byte[] array, final String separator) {
420 final StringBuilder sb = new StringBuilder();
421 for (int i = 0; i < array.length; i++) {
422 sb.append(Hex.encodeHexString(new byte[] { array[i] }));
423 if (i + 1 != array.length) {
424 sb.append(separator);
427 return sb.toString();
431 * Convert array of bytes to hexadecimal String.
434 * @param bytesOnLine number of bytes that should by displayed in one line
435 * @param byteSeparator string that will be placed after each byte
436 * @param wordCount number of bytes that make a 'word' (group of bytes)
437 * @param wordSeparator string that will be placed after each word
438 * @return Hexadecimal string representation of given byte array
440 public static String bytesToHexString(final byte[] array, final int bytesOnLine, final String byteSeparator, final int wordCount,
441 final String wordSeparator) {
442 final StringBuilder sb = new StringBuilder();
443 for (int i = 0; i < array.length; i++) {
444 sb.append(Hex.encodeHexString(new byte[] { array[i] }));
445 if ((i + 1) % bytesOnLine == 0) {
448 sb.append(byteSeparator);
449 if ((i + 1) % wordCount == 0) {
450 sb.append(wordSeparator);
455 return sb.toString();
459 * Decodes bytes to human readable UTF-8 string. If bytes are not valid UTF-8, they are represented as raw binary.
461 * @param bytes bytes to be decoded to string
462 * @return String representation of passed bytes
464 public static String bytesToHRString(final byte[] bytes) {
466 return Charset.forName("UTF-8").newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
467 } catch (final CharacterCodingException e) {
468 return Arrays.toString(bytes);
473 * Searches for byte sequence in given array. Returns the index of first occurrence of this sequence (where it
476 * @param bytes byte array where to search for sequence
477 * @param sequence to be searched in given byte array
478 * @return -1 if the sequence could not be found in given byte array int index of first occurrence of the sequence
481 public static int findByteSequence(final byte[] bytes, final byte[] sequence) {
482 if (bytes.length < sequence.length) {
483 throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
485 if (bytes.length == sequence.length) {
486 if (Arrays.equals(bytes, sequence)) {
493 for (int i = 0; i < bytes.length; i++) {
494 if (bytes[i] == sequence[j]) {
496 if (j == sequence.length) {
506 private static final byte MASK_BITS[] = new byte[] { 0, -128, -64, -32, -16, -8, -4, -2 };
508 public static byte[] maskBytes(final byte[] original, final int bits) {
509 if (original.length * Byte.SIZE < bits) {
510 throw new IllegalArgumentException("Attempted to apply invalid mask (too long)");
513 final int needbytes = (bits + 7) / Byte.SIZE;
514 // We need to have a new copy of the underlying byte array, so that
515 // the original bytes stay untouched
516 final byte[] bytes = Arrays.copyOf(original, original.length);
518 final int needmask = bits % Byte.SIZE;
520 bytes[needbytes - 1] &= MASK_BITS[needmask];
523 // zero-out the rest of the bytes
524 for (int i = needbytes; i < bytes.length; i++) {
531 * Trims zeros from the beginning of the byte array.
534 * @return byte array without leading zeros.
536 public static byte[] trim(final byte[] bytes) {
537 int i = bytes.length - 1;
538 while (i >= 0 && bytes[i] == 0) {
541 return Arrays.copyOf(bytes, i + 1);
545 * Converts given byte array to unsigned Integer.
547 * @param bytes byte array to be converted to unsigned Integer.
550 public static UnsignedInteger bytesToUint32(final byte[] bytes) {
551 Preconditions.checkArgument(bytes.length == Integer.SIZE / Byte.SIZE);
552 return UnsignedInteger.fromIntBits(bytesToInt(bytes));
556 * Converts uint to byte array.
558 * @param uint to be converted to byte array
561 public static byte[] uint32ToBytes(final UnsignedInteger uint) {
562 return intToBytes(uint.intValue());
566 * Converts uint as long to byte array.
568 * @param uint to be converted to byte array
571 public static byte[] uint32ToBytes(final long uint) {
572 return uint32ToBytes(UnsignedInteger.valueOf(uint));