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