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