Bug-731: Fixed few major Sonar warnings
[bgpcep.git] / util / src / main / java / org / opendaylight / protocol / util / ByteArray.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.util;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.primitives.UnsignedInteger;
12 import io.netty.buffer.ByteBuf;
13 import java.io.File;
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;
22
23 /**
24  * Util class for methods working with byte array.
25  */
26 public final class ByteArray {
27     private ByteArray() {
28     }
29
30     /**
31      * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
32      * at reader index, with given length. Increases reader index of the buffer by 'length'.
33      *
34      * @param buffer ByteBuf from which the bytes are going to be taken
35      * @param length length of the returned byte array
36      * @return byte array
37      */
38     public static byte[] readBytes(final ByteBuf buffer, final int length) {
39         Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
40             "Buffer cannot be read for %s bytes.", length);
41         final byte[] result = new byte[length];
42         buffer.readBytes(result);
43         return result;
44     }
45
46     /**
47      * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
48      * Adjusts reader index of the buffer by length of readable bytes in the buffer.
49      *
50      * @param buffer byteBuf from which the bytes are going to be taken
51      * @return byte array
52      */
53     public static byte[] readAllBytes(final ByteBuf buffer) {
54         return readBytes(buffer, buffer.readableBytes());
55     }
56
57     /**
58      * Helper method missing from netty ByteBuf methods. Directly returns byte array part of the given buffer, starting
59      * at reader index, with given length. Does not modify reader or writer index of the buffer.
60      *
61      * @param buffer ByteBuf from which the bytes are going to be taken
62      * @param length length of the returned byte array
63      * @return byte array
64      */
65     public static byte[] getBytes(final ByteBuf buffer, final int length) {
66         Preconditions.checkArgument(buffer != null && buffer.readableBytes() >= length,
67             "Buffer cannot be read for %s bytes.", length);
68         final byte[] result = new byte[length];
69         buffer.getBytes(buffer.readerIndex(), result);
70         return result;
71     }
72
73     /**
74      * Helper method missing from netty ByteBuf methods. Directly returns all readable bytes from buffer as byte array.
75      * Does not modify writer or reader index of the buffer.
76      *
77      * @param buffer byteBuf from which the bytes are going to be taken
78      * @return byte array
79      */
80     public static byte[] getAllBytes(final ByteBuf buffer) {
81         return getBytes(buffer, buffer.readableBytes());
82     }
83
84     /**
85      * Returns a new byte array from given byte array, starting at start index with the size of the length parameter.
86      * Byte array given as parameter stays untouched.
87      *
88      * @param bytes original byte array
89      * @param startIndex beginning index, inclusive
90      * @param length how many bytes should be in the sub-array
91      * @return a new byte array that is a sub-array of the original
92      */
93     public static byte[] subByte(final byte[] bytes, final int startIndex, final int length) {
94         if (!checkLength(bytes, length) || !checkStartIndex(bytes, startIndex, length)) {
95             throw new IllegalArgumentException("Cannot create subByte, invalid arguments: Length: " + length + " startIndex: " + startIndex);
96         }
97         final byte[] res = new byte[length];
98         System.arraycopy(bytes, startIndex, res, 0, length);
99         return res;
100     }
101
102     private static boolean checkLength(final byte[] bytes, final int length) {
103         return length > 0 && bytes.length > 0 && length <= bytes.length;
104     }
105
106     private static boolean checkStartIndex(final byte[] bytes, final int startIndex, final int length) {
107         return startIndex >= 0 && startIndex < bytes.length && (startIndex + length <= bytes.length);
108     }
109
110     /**
111      * Converts byte array to Integer. If there are less bytes in the array as required (4), the method will push
112      * adequate number of zero bytes prepending given byte array.
113      *
114      * @param bytes array to be converted to int
115      * @return int
116      */
117     public static int bytesToInt(final byte[] bytes) {
118         if (bytes.length > Integer.SIZE / Byte.SIZE) {
119             throw new IllegalArgumentException("Cannot convert bytes to integer. Byte array too big.");
120         }
121         byte[] res = new byte[Integer.SIZE / Byte.SIZE];
122         if (bytes.length != Integer.SIZE / Byte.SIZE) {
123             System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
124         } else {
125             res = bytes;
126         }
127         final ByteBuffer buff = ByteBuffer.wrap(res);
128         return buff.getInt();
129     }
130
131     /**
132      * Converts byte array to long. If there are less bytes in the array as required (Long.Size), the method will push
133      * adequate number of zero bytes prepending given byte array.
134      *
135      * @param bytes array to be converted to long
136      * @return long
137      */
138     public static long bytesToLong(final byte[] bytes) {
139         if (bytes.length > Long.SIZE / Byte.SIZE) {
140             throw new IllegalArgumentException("Cannot convert bytes to long.Byte array too big.");
141         }
142         byte[] res = new byte[Long.SIZE / Byte.SIZE];
143         if (bytes.length != Long.SIZE / Byte.SIZE) {
144             System.arraycopy(bytes, 0, res, Long.SIZE / Byte.SIZE - bytes.length, bytes.length);
145         } else {
146             res = bytes;
147         }
148         final ByteBuffer buff = ByteBuffer.wrap(res);
149         return buff.getLong();
150     }
151
152     /**
153      * Converts byte array to float IEEE 754 format. If there are less bytes in the array as required (Float.Size), the
154      * method will push adequate number of zero bytes prepending given byte array.
155      *
156      * @param bytes array to be converted to float
157      * @return float
158      */
159     public static float bytesToFloat(final byte[] bytes) {
160         if (bytes.length > Float.SIZE / Byte.SIZE) {
161             throw new IllegalArgumentException("Cannot convert bytes to float.Byte array too big.");
162         }
163         byte[] res = new byte[Float.SIZE / Byte.SIZE];
164         if (bytes.length != Float.SIZE / Byte.SIZE) {
165             System.arraycopy(bytes, 0, res, Float.SIZE / Byte.SIZE - bytes.length, bytes.length);
166         } else {
167             res = bytes;
168         }
169         final ByteBuffer buff = ByteBuffer.wrap(res);
170         return buff.getFloat();
171     }
172
173     /**
174      * Cuts 'count' number of bytes from the beginning of given byte array.
175      *
176      * @param bytes array to be cut, cannot be null
177      * @param count how many bytes needed to be cut, needs to be > 0
178      * @return bytes array without first 'count' bytes
179      */
180     public static byte[] cutBytes(final byte[] bytes, final int count) {
181         if (bytes.length == 0 || count > bytes.length || count <= 0) {
182             throw new IllegalArgumentException("Cannot cut bytes, invalid arguments: Count: " + count + " bytes.length: " + bytes.length);
183         }
184         return Arrays.copyOfRange(bytes, count, bytes.length);
185     }
186
187     /**
188      * Parse byte to bits, from the leftmost bit.
189      *
190      * @param b byte to be parsed
191      * @return array of booleans with size of 8
192      */
193     public static boolean[] parseBits(final byte b) {
194         final boolean[] bits = new boolean[Byte.SIZE];
195         int j = 0;
196         for (int i = Byte.SIZE - 1; i >= 0; i--) {
197             bits[j] = ((b & (1 << i)) != 0);
198             j++;
199         }
200         return bits;
201     }
202
203     /**
204      * Parses array of bytes to BitSet, from left most bit.
205      *
206      * @param bytes array of bytes to be parsed
207      * @return BitSet with length = bytes.length * Byte.SIZE
208      */
209     public static BitSet bytesToBitSet(final byte[] bytes) {
210         final BitSet bitSet = new BitSet(bytes.length * Byte.SIZE);
211         for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
212             final int offset = bytesIter * Byte.SIZE;
213             for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
214                 bitSet.set(offset + (Byte.SIZE - byteIter - 1), (bytes[bytesIter] & 1 << (byteIter)) != 0);
215             }
216         }
217         return bitSet;
218     }
219
220     /**
221      * Parses BitSet to bytes, from most left bit.
222      *
223      * @param bitSet BitSet to be parsed
224      * @param returnedLength Length of returned array. Overlapping flags are truncated.
225      * @return parsed array of bytes with length of bitSet.length / Byte.SIZE
226      */
227     public static byte[] bitSetToBytes(final BitSet bitSet, final int returnedLength) {
228         final byte[] bytes = new byte[returnedLength];
229
230         for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
231             final int offset = bytesIter * Byte.SIZE;
232
233             for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
234                 bytes[bytesIter] |= (bitSet.get(offset + (Byte.SIZE - byteIter - 1)) ? 1 << byteIter : 0);
235             }
236         }
237         return bytes;
238     }
239
240     /**
241      * Parses file to array of bytes
242      *
243      * @param name path to file to by parsed
244      * @return parsed array of bytes
245      */
246     public static byte[] fileToBytes(final String name) throws IOException {
247         final File file = new File(name);
248         int offset = 0;
249         int numRead = 0;
250
251         if (file.length() > Integer.MAX_VALUE) {
252             throw new IOException("Too large file to load in byte array.");
253         }
254         final byte[] byteArray = new byte[(int) file.length()];
255         try (final FileInputStream fin = new FileInputStream(file)) {
256             while (offset < byteArray.length && (numRead = fin.read(byteArray, offset, byteArray.length - offset)) >= 0) {
257                 offset += numRead;
258             }
259             fin.close();
260         }
261         return byteArray;
262     }
263
264     /**
265      * Parses integer to array of bytes
266      *
267      * @param num integer to be parsed
268      * @return parsed array of bytes with length of Integer.SIZE/Byte.SIZE
269      */
270     public static byte[] intToBytes(final int num) {
271         return intToBytes(num, Integer.SIZE / Byte.SIZE);
272     }
273
274     /**
275      * Parses integer to array of bytes
276      *
277      * @param num integer to be parsed
278      * @param size desired byte array length
279      * @return parsed array of bytes with length of size
280      */
281     public static byte[] intToBytes(final int num, final int size) {
282         final int finalSize = Integer.SIZE / Byte.SIZE;
283         final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
284         bytesBuffer.putInt(num);
285         return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
286     }
287
288     /**
289      * Parses long to array of bytes
290      *
291      * @param num long to be parsed
292      * @return parsed array of bytes with length of Long.SIZE/Byte.SIZE
293      */
294     public static byte[] longToBytes(final int num) {
295         return longToBytes(num, Long.SIZE / Byte.SIZE);
296     }
297
298     /**
299      * Parses long to array of bytes
300      *
301      * @param num long to be parsed
302      * @param size desired byte array length
303      * @return parsed array of bytes with length of size
304      */
305     public static byte[] longToBytes(final long num, final int size) {
306         final int finalSize = Long.SIZE / Byte.SIZE;
307         final ByteBuffer bytesBuffer = ByteBuffer.allocate(finalSize);
308         bytesBuffer.putLong(num);
309         return ByteArray.subByte(bytesBuffer.array(), finalSize - size, size);
310     }
311
312     /**
313      * Copies range of bits from passed byte and align to right.<br/>
314      *
315      * @param src source byte to copy from
316      * @param fromBit bit from which will copy (inclusive) - numbered from 0
317      * @param length of bits to by copied - <1,8>
318      * @return copied value aligned to right
319      */
320     public static byte copyBitsRange(final byte src, final int fromBit, final int length) {
321         if (fromBit < 0 || fromBit > Byte.SIZE - 1 || length < 1 || length > Byte.SIZE) {
322             throw new IllegalArgumentException("fromBit or toBit is out of range.");
323         }
324         if (fromBit + length > Byte.SIZE) {
325             throw new IllegalArgumentException("Out of range.");
326         }
327
328         byte retByte = 0;
329         int retI = 0;
330
331         for (int i = fromBit + length - 1; i >= fromBit; i--) {
332
333             if ((src & 1 << (Byte.SIZE - i - 1)) != 0) {
334                 retByte |= 1 << retI;
335             }
336
337             retI++;
338         }
339
340         return retByte;
341     }
342
343     /**
344      * Copies whole source byte array to destination from offset.<br/>
345      * Length of src can't be bigger than dest length minus offset
346      *
347      * @param src byte[]
348      * @param dest byte[]
349      * @param offset int
350      */
351     public static void copyWhole(final byte[] src, final byte[] dest, final int offset) {
352         if (dest.length - offset < src.length) {
353             throw new ArrayIndexOutOfBoundsException("Can't copy whole array.");
354         }
355
356         System.arraycopy(src, 0, dest, offset, src.length);
357     }
358
359     /**
360      * Convert array of bytes to java short.<br/>
361      * Size can't be bigger than size of short in bytes.
362      *
363      * @param bytes byte[]
364      * @return array of bytes
365      */
366     public static short bytesToShort(final byte[] bytes) {
367         if (bytes.length > Short.SIZE / Byte.SIZE) {
368             throw new IllegalArgumentException("Cannot convert bytes to short. Byte array too big.");
369         }
370         byte[] res = new byte[Short.SIZE / Byte.SIZE];
371         if (bytes.length != Short.SIZE / Byte.SIZE) {
372             System.arraycopy(bytes, 0, res, Integer.SIZE / Byte.SIZE - bytes.length, bytes.length);
373         } else {
374             res = bytes;
375         }
376         final ByteBuffer buff = ByteBuffer.wrap(res);
377         return buff.getShort();
378     }
379
380     /**
381      * Convert short java representation to array of bytes.
382      *
383      * @param num short
384      * @return short represented as array of bytes
385      */
386     public static byte[] shortToBytes(final short num) {
387         final ByteBuffer bytesBuffer = ByteBuffer.allocate(Short.SIZE / Byte.SIZE);
388         bytesBuffer.putShort(num);
389
390         return bytesBuffer.array();
391     }
392
393     /**
394      * Convert float java representation to array of bytes.
395      *
396      * @param num float
397      * @return float represented as array of bytes
398      */
399     public static byte[] floatToBytes(final float num) {
400         final ByteBuffer bytesBuffer = ByteBuffer.allocate(Float.SIZE / Byte.SIZE);
401         bytesBuffer.putFloat(num);
402
403         return bytesBuffer.array();
404     }
405
406     /**
407      * Pretty print array of bytes as hex encoded string with 16 bytes per line. Each byte is separated by space, after
408      * first 8 bytes there are 2 spaces instead of one.
409      */
410     public static String bytesToHexString(final byte[] array) {
411         return bytesToHexString(array, 16, " ", 8, " ");
412     }
413
414     /**
415      * Pretty-print an array of bytes as hex-encoded string. Separate them with specified separator.
416      */
417     public static String toHexString(final byte[] array, final String separator) {
418         final StringBuilder sb = new StringBuilder();
419         for (int i = 0; i < array.length; i++) {
420             sb.append(Hex.encodeHexString(new byte[] { array[i] }));
421             if (i + 1 != array.length) {
422                 sb.append(separator);
423             }
424         }
425         return sb.toString();
426     }
427
428     /**
429      * Convert array of bytes to hexadecimal String.
430      *
431      * @param array
432      * @param bytesOnLine number of bytes that should by displayed in one line
433      * @param byteSeparator string that will be placed after each byte
434      * @param wordCount number of bytes that make a 'word' (group of bytes)
435      * @param wordSeparator string that will be placed after each word
436      * @return Hexadecimal string representation of given byte array
437      */
438     public static String bytesToHexString(final byte[] array, final int bytesOnLine, final String byteSeparator, final int wordCount,
439         final String wordSeparator) {
440         final StringBuilder sb = new StringBuilder();
441         for (int i = 0; i < array.length; i++) {
442             sb.append(Hex.encodeHexString(new byte[] { array[i] }));
443             if ((i + 1) % bytesOnLine == 0) {
444                 sb.append("\n");
445             } else {
446                 sb.append(byteSeparator);
447                 if ((i + 1) % wordCount == 0) {
448                     sb.append(wordSeparator);
449                 }
450             }
451
452         }
453         return sb.toString();
454     }
455
456     /**
457      * Decodes bytes to human readable UTF-8 string. If bytes are not valid UTF-8, they are represented as raw binary.
458      *
459      * @param bytes bytes to be decoded to string
460      * @return String representation of passed bytes
461      */
462     public static String bytesToHRString(final byte[] bytes) {
463         try {
464             return Charset.forName("UTF-8").newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
465         } catch (final CharacterCodingException e) {
466             return Arrays.toString(bytes);
467         }
468     }
469
470     /**
471      * Searches for byte sequence in given array. Returns the index of first occurrence of this sequence (where it
472      * starts).
473      *
474      * @param bytes byte array where to search for sequence
475      * @param sequence to be searched in given byte array
476      * @return -1 if the sequence could not be found in given byte array int index of first occurrence of the sequence
477      *         in bytes
478      */
479     public static int findByteSequence(final byte[] bytes, final byte[] sequence) {
480         if (bytes.length < sequence.length) {
481             throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
482         }
483         if (bytes.length == sequence.length) {
484             if (Arrays.equals(bytes, sequence)) {
485                 return 0;
486             } else {
487                 return -1;
488             }
489         }
490         int j = 0;
491         for (int i = 0; i < bytes.length; i++) {
492             if (bytes[i] == sequence[j]) {
493                 j++;
494                 if (j == sequence.length) {
495                     return i - j + 1;
496                 }
497             } else {
498                 j = 0;
499             }
500         }
501         return -1;
502     }
503
504     private static final byte MASK_BITS[] = new byte[] { 0, -128, -64, -32, -16, -8, -4, -2 };
505
506     public static byte[] maskBytes(final byte[] original, final int bits) {
507         if (original.length * Byte.SIZE < bits) {
508             throw new IllegalArgumentException("Attempted to apply invalid mask (too long)");
509         }
510
511         final int needbytes = (bits + 7) / Byte.SIZE;
512         // We need to have a new copy of the underlying byte array, so that
513         // the original bytes stay untouched
514         final byte[] bytes = Arrays.copyOf(original, original.length);
515
516         final int needmask = bits % Byte.SIZE;
517         if (needmask != 0) {
518             bytes[needbytes - 1] &= MASK_BITS[needmask];
519         }
520
521         // zero-out the rest of the bytes
522         for (int i = needbytes; i < bytes.length; i++) {
523             bytes[i] = 0;
524         }
525         return bytes;
526     }
527
528     /**
529      * Trims zeros from the beginning of the byte array.
530      *
531      * @param bytes
532      * @return byte array without leading zeros.
533      */
534     public static byte[] trim(final byte[] bytes) {
535         int i = bytes.length - 1;
536         while (i >= 0 && bytes[i] == 0) {
537             --i;
538         }
539         return Arrays.copyOf(bytes, i + 1);
540     }
541
542     /**
543      * Converts given byte array to unsigned Integer.
544      *
545      * @param bytes byte array to be converted to unsigned Integer.
546      * @return uint
547      */
548     public static UnsignedInteger bytesToUint32(final byte[] bytes) {
549         Preconditions.checkArgument(bytes.length == Integer.SIZE / Byte.SIZE);
550         return UnsignedInteger.fromIntBits(bytesToInt(bytes));
551     }
552
553     /**
554      * Converts uint to byte array.
555      *
556      * @param uint to be converted to byte array
557      * @return byte array
558      */
559     public static byte[] uint32ToBytes(final UnsignedInteger uint) {
560         return intToBytes(uint.intValue());
561     }
562
563     /**
564      * Converts uint as long to byte array.
565      *
566      * @param uint to be converted to byte array
567      * @return byte array
568      */
569     public static byte[] uint32ToBytes(final long uint) {
570         return uint32ToBytes(UnsignedInteger.valueOf(uint));
571     }
572 }