Merge "Bug-730: Test Vendor-Information object/tlv in message/object"
[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
28     private ByteArray() {
29         throw new UnsupportedOperationException();
30     }
31
32     /**
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'.
35      *
36      * @param buffer ByteBuf from which the bytes are going to be taken
37      * @param length length of the returned byte array
38      * @return byte array
39      */
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);
45         return result;
46     }
47
48     /**
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.
51      *
52      * @param buffer byteBuf from which the bytes are going to be taken
53      * @return byte array
54      */
55     public static byte[] readAllBytes(final ByteBuf buffer) {
56         return readBytes(buffer, buffer.readableBytes());
57     }
58
59     /**
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.
62      *
63      * @param buffer ByteBuf from which the bytes are going to be taken
64      * @param length length of the returned byte array
65      * @return byte array
66      */
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);
72         return result;
73     }
74
75     /**
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.
78      *
79      * @param buffer byteBuf from which the bytes are going to be taken
80      * @return byte array
81      */
82     public static byte[] getAllBytes(final ByteBuf buffer) {
83         return getBytes(buffer, buffer.readableBytes());
84     }
85
86     /**
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.
89      *
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
94      */
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);
98         }
99         final byte[] res = new byte[length];
100         System.arraycopy(bytes, startIndex, res, 0, length);
101         return res;
102     }
103
104     private static boolean checkLength(final byte[] bytes, final int length) {
105         return length > 0 && bytes.length > 0 && length <= bytes.length;
106     }
107
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);
110     }
111
112     /**
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.
115      *
116      * @param bytes array to be converted to int
117      * @return int
118      */
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.");
122         }
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);
126         } else {
127             res = bytes;
128         }
129         final ByteBuffer buff = ByteBuffer.wrap(res);
130         return buff.getInt();
131     }
132
133     /**
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.
136      *
137      * @param bytes array to be converted to long
138      * @return long
139      */
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.");
143         }
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);
147         } else {
148             res = bytes;
149         }
150         final ByteBuffer buff = ByteBuffer.wrap(res);
151         return buff.getLong();
152     }
153
154     /**
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.
157      *
158      * @param bytes array to be converted to float
159      * @return float
160      */
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.");
164         }
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);
168         } else {
169             res = bytes;
170         }
171         final ByteBuffer buff = ByteBuffer.wrap(res);
172         return buff.getFloat();
173     }
174
175     /**
176      * Cuts 'count' number of bytes from the beginning of given byte array.
177      *
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
181      */
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);
185         }
186         return Arrays.copyOfRange(bytes, count, bytes.length);
187     }
188
189     /**
190      * Parse byte to bits, from the leftmost bit.
191      *
192      * @param b byte to be parsed
193      * @return array of booleans with size of 8
194      */
195     public static boolean[] parseBits(final byte b) {
196         final boolean[] bits = new boolean[Byte.SIZE];
197         int j = 0;
198         for (int i = Byte.SIZE - 1; i >= 0; i--) {
199             bits[j] = ((b & (1 << i)) != 0);
200             j++;
201         }
202         return bits;
203     }
204
205     /**
206      * Parses array of bytes to BitSet, from left most bit.
207      *
208      * @param bytes array of bytes to be parsed
209      * @return BitSet with length = bytes.length * Byte.SIZE
210      */
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);
217             }
218         }
219         return bitSet;
220     }
221
222     /**
223      * Parses BitSet to bytes, from most left bit.
224      *
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
228      */
229     public static byte[] bitSetToBytes(final BitSet bitSet, final int returnedLength) {
230         final byte[] bytes = new byte[returnedLength];
231
232         for (int bytesIter = 0; bytesIter < bytes.length; bytesIter++) {
233             final int offset = bytesIter * Byte.SIZE;
234
235             for (int byteIter = Byte.SIZE - 1; byteIter >= 0; byteIter--) {
236                 bytes[bytesIter] |= (bitSet.get(offset + (Byte.SIZE - byteIter - 1)) ? 1 << byteIter : 0);
237             }
238         }
239         return bytes;
240     }
241
242     /**
243      * Parses file to array of bytes
244      *
245      * @param name path to file to by parsed
246      * @return parsed array of bytes
247      */
248     public static byte[] fileToBytes(final String name) throws IOException {
249         final File file = new File(name);
250         int offset = 0;
251         int numRead = 0;
252
253         if (file.length() > Integer.MAX_VALUE) {
254             throw new IOException("Too large file to load in byte array.");
255         }
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) {
259                 offset += numRead;
260             }
261             fin.close();
262         }
263         return byteArray;
264     }
265
266     /**
267      * Parses integer to array of bytes
268      *
269      * @param num integer to be parsed
270      * @return parsed array of bytes with length of Integer.SIZE/Byte.SIZE
271      */
272     public static byte[] intToBytes(final int num) {
273         return intToBytes(num, Integer.SIZE / Byte.SIZE);
274     }
275
276     /**
277      * Parses integer to array of bytes
278      *
279      * @param num integer to be parsed
280      * @param size desired byte array length
281      * @return parsed array of bytes with length of size
282      */
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);
288     }
289
290     /**
291      * Parses long to array of bytes
292      *
293      * @param num long to be parsed
294      * @return parsed array of bytes with length of Long.SIZE/Byte.SIZE
295      */
296     public static byte[] longToBytes(final int num) {
297         return longToBytes(num, Long.SIZE / Byte.SIZE);
298     }
299
300     /**
301      * Parses long to array of bytes
302      *
303      * @param num long to be parsed
304      * @param size desired byte array length
305      * @return parsed array of bytes with length of size
306      */
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);
312     }
313
314     /**
315      * Copies range of bits from passed byte and align to right.<br/>
316      *
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
321      */
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.");
325         }
326         if (fromBit + length > Byte.SIZE) {
327             throw new IllegalArgumentException("Out of range.");
328         }
329
330         byte retByte = 0;
331         int retI = 0;
332
333         for (int i = fromBit + length - 1; i >= fromBit; i--) {
334
335             if ((src & 1 << (Byte.SIZE - i - 1)) != 0) {
336                 retByte |= 1 << retI;
337             }
338
339             retI++;
340         }
341
342         return retByte;
343     }
344
345     /**
346      * Copies whole source byte array to destination from offset.<br/>
347      * Length of src can't be bigger than dest length minus offset
348      *
349      * @param src byte[]
350      * @param dest byte[]
351      * @param offset int
352      */
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.");
356         }
357
358         System.arraycopy(src, 0, dest, offset, src.length);
359     }
360
361     /**
362      * Convert array of bytes to java short.<br/>
363      * Size can't be bigger than size of short in bytes.
364      *
365      * @param bytes byte[]
366      * @return array of bytes
367      */
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.");
371         }
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);
375         } else {
376             res = bytes;
377         }
378         final ByteBuffer buff = ByteBuffer.wrap(res);
379         return buff.getShort();
380     }
381
382     /**
383      * Convert short java representation to array of bytes.
384      *
385      * @param num short
386      * @return short represented as array of bytes
387      */
388     public static byte[] shortToBytes(final short num) {
389         final ByteBuffer bytesBuffer = ByteBuffer.allocate(Short.SIZE / Byte.SIZE);
390         bytesBuffer.putShort(num);
391
392         return bytesBuffer.array();
393     }
394
395     /**
396      * Convert float java representation to array of bytes.
397      *
398      * @param num float
399      * @return float represented as array of bytes
400      */
401     public static byte[] floatToBytes(final float num) {
402         final ByteBuffer bytesBuffer = ByteBuffer.allocate(Float.SIZE / Byte.SIZE);
403         bytesBuffer.putFloat(num);
404
405         return bytesBuffer.array();
406     }
407
408     /**
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.
411      */
412     public static String bytesToHexString(final byte[] array) {
413         return bytesToHexString(array, 16, " ", 8, " ");
414     }
415
416     /**
417      * Pretty-print an array of bytes as hex-encoded string. Separate them with specified separator.
418      */
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);
425             }
426         }
427         return sb.toString();
428     }
429
430     /**
431      * Convert array of bytes to hexadecimal String.
432      *
433      * @param array
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
439      */
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) {
446                 sb.append("\n");
447             } else {
448                 sb.append(byteSeparator);
449                 if ((i + 1) % wordCount == 0) {
450                     sb.append(wordSeparator);
451                 }
452             }
453
454         }
455         return sb.toString();
456     }
457
458     /**
459      * Decodes bytes to human readable UTF-8 string. If bytes are not valid UTF-8, they are represented as raw binary.
460      *
461      * @param bytes bytes to be decoded to string
462      * @return String representation of passed bytes
463      */
464     public static String bytesToHRString(final byte[] bytes) {
465         try {
466             return Charset.forName("UTF-8").newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
467         } catch (final CharacterCodingException e) {
468             return Arrays.toString(bytes);
469         }
470     }
471
472     /**
473      * Searches for byte sequence in given array. Returns the index of first occurrence of this sequence (where it
474      * starts).
475      *
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
479      *         in bytes
480      */
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.");
484         }
485         if (bytes.length == sequence.length) {
486             if (Arrays.equals(bytes, sequence)) {
487                 return 0;
488             } else {
489                 return -1;
490             }
491         }
492         int j = 0;
493         for (int i = 0; i < bytes.length; i++) {
494             if (bytes[i] == sequence[j]) {
495                 j++;
496                 if (j == sequence.length) {
497                     return i - j + 1;
498                 }
499             } else {
500                 j = 0;
501             }
502         }
503         return -1;
504     }
505
506     private static final byte MASK_BITS[] = new byte[] { 0, -128, -64, -32, -16, -8, -4, -2 };
507
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)");
511         }
512
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);
517
518         final int needmask = bits % Byte.SIZE;
519         if (needmask != 0) {
520             bytes[needbytes - 1] &= MASK_BITS[needmask];
521         }
522
523         // zero-out the rest of the bytes
524         for (int i = needbytes; i < bytes.length; i++) {
525             bytes[i] = 0;
526         }
527         return bytes;
528     }
529
530     /**
531      * Trims zeros from the beginning of the byte array.
532      *
533      * @param bytes
534      * @return byte array without leading zeros.
535      */
536     public static byte[] trim(final byte[] bytes) {
537         int i = bytes.length - 1;
538         while (i >= 0 && bytes[i] == 0) {
539             --i;
540         }
541         return Arrays.copyOf(bytes, i + 1);
542     }
543
544     /**
545      * Converts given byte array to unsigned Integer.
546      *
547      * @param bytes byte array to be converted to unsigned Integer.
548      * @return uint
549      */
550     public static UnsignedInteger bytesToUint32(final byte[] bytes) {
551         Preconditions.checkArgument(bytes.length == Integer.SIZE / Byte.SIZE);
552         return UnsignedInteger.fromIntBits(bytesToInt(bytes));
553     }
554
555     /**
556      * Converts uint to byte array.
557      *
558      * @param uint to be converted to byte array
559      * @return byte array
560      */
561     public static byte[] uint32ToBytes(final UnsignedInteger uint) {
562         return intToBytes(uint.intValue());
563     }
564
565     /**
566      * Converts uint as long to byte array.
567      *
568      * @param uint to be converted to byte array
569      * @return byte array
570      */
571     public static byte[] uint32ToBytes(final long uint) {
572         return uint32ToBytes(UnsignedInteger.valueOf(uint));
573     }
574 }