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