2 * Copyright (c) 2013, 2017 Cisco Systems, Inc. and others. All rights reserved.
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
12 package org.opendaylight.controller.liblldp;
14 import java.util.Arrays;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
20 * BitBufferHelper class that provides utility methods to - fetch specific bits
21 * from a serialized stream of bits - convert bits to primitive data type - like
22 * short, int, long - store bits in specified location in stream of bits -
23 * convert primitive data types to stream of bits.
25 public abstract class BitBufferHelper {
26 protected static final Logger LOG = LoggerFactory.getLogger(BitBufferHelper.class);
28 public static final long ByteMask = 0xFF;
31 // data: array where data are stored
32 // startOffset: bit from where to start reading
33 // numBits: number of bits to read
34 // All this function return an exception if overflow or underflow
37 * Returns the first byte from the byte array.
41 public static byte getByte(final byte[] data) {
42 if (data.length * NetUtils.NumBitsInAByte > Byte.SIZE) {
44 throw new BufferException("Container is too small for the number of requested bits");
45 } catch (final BufferException e) {
53 * Returns the short value for the byte array passed. Size of byte array is
54 * restricted to Short.SIZE
58 public static short getShort(final byte[] data) {
59 if (data.length > Short.SIZE) {
61 throw new BufferException("Container is too small for the number of requested bits");
62 } catch (final BufferException e) {
66 return (short) toNumber(data);
70 * Returns the int value for the byte array passed. Size of byte array is
71 * restricted to Integer.SIZE
73 * @return int - the integer value of byte array
75 public static int getInt(final byte[] data) {
76 if (data.length > Integer.SIZE) {
78 throw new BufferException("Container is too small for the number of requested bits");
79 } catch (final BufferException e) {
83 return (int) toNumber(data);
87 * Returns the long value for the byte array passed. Size of byte array is
88 * restricted to Long.SIZE
90 * @return long - the integer value of byte array
92 public static long getLong(final byte[] data) {
93 if (data.length > Long.SIZE) {
95 throw new BufferException("Container is too small for the number of requested bits");
96 } catch (final Exception e) {
100 return toNumber(data);
104 * Returns the short value for the last numBits of the byte array passed.
105 * Size of numBits is restricted to Short.SIZE
107 * @return short - the short value of byte array
109 public static short getShort(final byte[] data, final int numBits) {
110 if (numBits > Short.SIZE) {
112 throw new BufferException("Container is too small for the number of requested bits");
113 } catch (final BufferException e) {
117 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
120 bits = BitBufferHelper.getBits(data, startOffset, numBits);
121 } catch (final BufferException e) {
124 return (short) toNumber(bits, numBits);
128 * Returns the int value for the last numBits of the byte array passed. Size
129 * of numBits is restricted to Integer.SIZE
131 * @return int - the integer value of byte array
133 public static int getInt(final byte[] data, final int numBits) {
134 if (numBits > Integer.SIZE) {
136 throw new BufferException("Container is too small for the number of requested bits");
137 } catch (final BufferException e) {
141 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
144 bits = BitBufferHelper.getBits(data, startOffset, numBits);
145 } catch (final BufferException e) {
148 return (int) toNumber(bits, numBits);
152 * Returns the long value for the last numBits of the byte array passed.
153 * Size of numBits is restricted to Long.SIZE
155 * @return long - the integer value of byte array
157 public static long getLong(final byte[] data, final int numBits) {
158 if (numBits > Long.SIZE) {
160 throw new BufferException("Container is too small for the number of requested bits");
161 } catch (final BufferException e) {
165 if (numBits > data.length * NetUtils.NumBitsInAByte) {
167 throw new BufferException("Trying to read more bits than contained in the data buffer");
168 } catch (final BufferException e) {
172 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
175 bits = BitBufferHelper.getBits(data, startOffset, numBits);
176 } catch (final BufferException e) {
179 return toNumber(bits, numBits);
183 * Reads the specified number of bits from the passed byte array starting to
184 * read from the specified offset The bits read are stored in a byte array
185 * which size is dictated by the number of bits to be stored. The bits are
186 * stored in the byte array LSB aligned.
188 * Ex. Read 7 bits at offset 10 0 9 10 16 17 0101000010 | 0000101 |
189 * 1111001010010101011 will be returned as {0,0,0,0,0,1,0,1}
192 * - offset to start fetching bits from data from
194 * - number of bits to be fetched from data
195 * @return byte [] - LSB aligned bits
197 * @throws BufferException
198 * when the startOffset and numBits parameters are not congruent
199 * with the data buffer size
201 public static byte[] getBits(final byte[] data, final int startOffset, final int numBits) throws BufferException {
202 int startByteOffset = 0;
203 int valfromcurr, valfromnext;
204 int extranumBits = numBits % NetUtils.NumBitsInAByte;
205 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
206 int numBytes = numBits % NetUtils.NumBitsInAByte != 0 ? 1 + numBits / NetUtils.NumBitsInAByte
207 : numBits / NetUtils.NumBitsInAByte;
208 byte[] shiftedBytes = new byte[numBytes];
209 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
210 byte[] bytes = new byte[numBytes];
215 checkExceptions(data, startOffset, numBits);
217 if (extraOffsetBits == 0) {
218 if (extranumBits == 0) {
219 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
222 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
223 bytes[numBytes - 1] = (byte) (data[startByteOffset + numBytes - 1] & getMSBMask(extranumBits));
227 for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) {
228 // Reading numBytes starting from offset
229 valfromcurr = data[startByteOffset + i] & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
230 valfromnext = data[startByteOffset + i + 1] & getMSBMask(extraOffsetBits);
231 bytes[i] = (byte) (valfromcurr << extraOffsetBits
232 | valfromnext >> NetUtils.NumBitsInAByte - extraOffsetBits);
234 // Now adding the rest of the bits if any
235 if (extranumBits != 0) {
236 if (extranumBits < NetUtils.NumBitsInAByte - extraOffsetBits) {
237 valfromnext = (byte) (data[startByteOffset + i] & getMSBMask(extranumBits) >> extraOffsetBits);
238 bytes[i] = (byte) (valfromnext << extraOffsetBits);
239 } else if (extranumBits == NetUtils.NumBitsInAByte - extraOffsetBits) {
240 valfromcurr = data[startByteOffset + i] & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
241 bytes[i] = (byte) (valfromcurr << extraOffsetBits);
243 valfromcurr = data[startByteOffset + i] & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
244 valfromnext = data[startByteOffset + i + 1]
245 & getMSBMask(extranumBits - (NetUtils.NumBitsInAByte - extraOffsetBits));
246 bytes[i] = (byte) (valfromcurr << extraOffsetBits
247 | valfromnext >> NetUtils.NumBitsInAByte - extraOffsetBits);
252 // Aligns the bits to LSB
253 shiftedBytes = shiftBitsToLSB(bytes, numBits);
258 // data: array where data will be stored
259 // input: the data that need to be stored in the data array
260 // startOffset: bit from where to start writing
261 // numBits: number of bits to read
264 * Bits are expected to be stored in the input byte array from LSB
267 * to set the input byte
269 * byte to be inserted
271 * offset of data[] to start inserting byte from
273 * number of bits of input to be inserted into data[]
275 * @throws BufferException
276 * when the input, startOffset and numBits are not congruent
277 * with the data buffer size
279 public static void setByte(final byte[] data, final byte input, final int startOffset, final int numBits)
280 throws BufferException {
281 byte[] inputByteArray = new byte[1];
282 Arrays.fill(inputByteArray, 0, 1, input);
283 setBytes(data, inputByteArray, startOffset, numBits);
287 * Bits are expected to be stored in the input byte array from LSB
290 * to set the input byte
292 * bytes to be inserted
294 * offset of data[] to start inserting byte from
296 * number of bits of input to be inserted into data[]
297 * @throws BufferException
298 * when the startOffset and numBits parameters are not congruent
299 * with data and input buffers' size
301 public static void setBytes(final byte[] data, final byte[] input, final int startOffset, final int numBits)
302 throws BufferException {
303 checkExceptions(data, startOffset, numBits);
304 insertBits(data, input, startOffset, numBits);
308 * Returns numBits 1's in the MSB position
310 public static int getMSBMask(final int numBits) {
312 for (int i = 0; i < numBits; i++) {
313 mask = mask | 1 << 7 - i;
319 * Returns numBits 1's in the LSB position
321 public static int getLSBMask(final int numBits) {
323 for (int i = 0; i < numBits; i++) {
324 mask = mask | 1 << i;
330 * Returns the numerical value of the byte array passed
332 * @return long - numerical value of byte array passed
334 static public long toNumber(final byte[] array) {
336 long length = array.length;
338 for (int i = 0; i < length; i++) {
343 ret = ret | (long) value << (length - i - 1) * NetUtils.NumBitsInAByte;
349 * Returns the numerical value of the last numBits (LSB bits) of the byte
352 * @return long - numerical value of byte array passed
354 static public long toNumber(final byte[] array, final int numBits) {
355 int length = numBits / NetUtils.NumBitsInAByte;
356 int bitsRest = numBits % NetUtils.NumBitsInAByte;
357 int startOffset = array.length - length;
361 value = array[startOffset - 1] & getLSBMask(bitsRest);
362 value = array[startOffset - 1] < 0 ? array[startOffset - 1] + 256 : array[startOffset - 1];
363 ret = ret | value << (array.length - startOffset) * NetUtils.NumBitsInAByte;
365 for (int i = startOffset; i < array.length; i++) {
370 ret = ret | (long) value << (array.length - i - 1) * NetUtils.NumBitsInAByte;
377 * Accepts a number as input and returns its value in byte form in LSB
378 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
379 * [00010011] [10001000]
381 public static byte[] toByteArray(final Number input) {
382 Class<? extends Number> dataType = input.getClass();
384 long longValue = input.longValue();
386 if (dataType == Byte.class || dataType == byte.class) {
388 } else if (dataType == Short.class || dataType == short.class) {
390 } else if (dataType == Integer.class || dataType == int.class) {
392 } else if (dataType == Long.class || dataType == long.class) {
395 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
398 int length = size / NetUtils.NumBitsInAByte;
399 byte bytes[] = new byte[length];
401 // Getting the bytes from input value
402 for (int i = 0; i < length; i++) {
403 bytes[i] = (byte) (longValue >> NetUtils.NumBitsInAByte * (length - i - 1) & ByteMask);
409 * Accepts a number as input and returns its value in byte form in MSB
410 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
411 * [10011100] [01000000]
414 * - the number of bits to be returned
418 public static byte[] toByteArray(final Number input, final int numBits) {
419 Class<? extends Number> dataType = input.getClass();
421 long longValue = input.longValue();
423 if (dataType == Short.class) {
425 } else if (dataType == Integer.class) {
427 } else if (dataType == Long.class) {
430 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
433 int length = size / NetUtils.NumBitsInAByte;
434 byte bytes[] = new byte[length];
435 byte[] inputbytes = new byte[length];
438 // Getting the bytes from input value
439 for (int i = 0; i < length; i++) {
440 bytes[i] = (byte) (longValue >> NetUtils.NumBitsInAByte * (length - i - 1) & ByteMask);
443 if (bytes[0] == 0 && dataType == Long.class || bytes[0] == 0 && dataType == Integer.class) {
445 for (index = 0; index < length; ++index) {
446 if (bytes[index] != 0) {
447 bytes[0] = bytes[index];
451 System.arraycopy(bytes, index, inputbytes, 0, length - index);
452 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
454 System.arraycopy(bytes, 0, inputbytes, 0, length);
457 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
463 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
464 * aligned byte array.
466 * It aligns the last numBits bits to the head of the byte array following
467 * them with numBits % 8 zero bits.
469 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
470 * returns: shiftedBytes = [01110111][00010000]
473 * - number of bits to be left aligned
476 public static byte[] shiftBitsToMSB(final byte[] inputBytes, final int numBits) {
477 int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0;
478 int size = inputBytes.length;
479 byte[] shiftedBytes = new byte[size];
482 for (i = 0; i < Byte.SIZE; i++) {
483 if ((byte) (inputBytes[0] & getMSBMask(i + 1)) != 0) {
489 if (numBits % NetUtils.NumBitsInAByte == 0) {
490 numBitstoShiftBy = 0;
492 numBitstoShiftBy = NetUtils.NumBitsInAByte - numBits % NetUtils.NumBitsInAByte < leadZeroesMSB
493 ? NetUtils.NumBitsInAByte - numBits % NetUtils.NumBitsInAByte : leadZeroesMSB;
495 if (numBitstoShiftBy == 0) {
499 if (numBits < NetUtils.NumBitsInAByte) {
500 // inputbytes.length = 1 OR read less than a byte
501 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
503 // # of bits to read from last byte
504 numEndRestBits = NetUtils.NumBitsInAByte
505 - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy);
507 for (i = 0; i < size - 1; i++) {
508 if (i + 1 == size - 1) {
509 if (numEndRestBits > numBitstoShiftBy) {
510 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
511 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> numEndRestBits
513 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1]
514 & getLSBMask(numEndRestBits - numBitstoShiftBy)) << numBitstoShiftBy);
516 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
517 | (inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> NetUtils.NumBitsInAByte
521 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
522 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> NetUtils.NumBitsInAByte
531 * It aligns the first numBits bits to the right end of the byte array
532 * preceding them with numBits % 8 zero bits.
534 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
535 * returns: shiftedBytes = [00000111][01110001]
539 * - number of bits to be right aligned
542 public static byte[] shiftBitsToLSB(final byte[] inputBytes, final int numBits) {
543 int numBytes = inputBytes.length;
544 int numBitstoShift = numBits % NetUtils.NumBitsInAByte;
545 byte[] shiftedBytes = new byte[numBytes];
546 int inputLsb = 0, inputMsb = 0;
548 if (numBitstoShift == 0) {
552 for (int i = 1; i < numBytes; i++) {
553 inputLsb = inputBytes[i - 1] & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift);
554 inputLsb = inputLsb < 0 ? inputLsb + 256 : inputLsb;
555 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
556 inputMsb = inputBytes[i] < 0 ? inputBytes[i] + 256 : inputBytes[i];
557 shiftedBytes[i] = (byte) (inputLsb << numBitstoShift
558 | inputMsb >> NetUtils.NumBitsInAByte - numBitstoShift);
560 inputMsb = inputBytes[0] & getMSBMask(numBitstoShift);
561 inputMsb = inputMsb < 0 ? inputMsb + 256 : inputMsb;
562 shiftedBytes[0] = (byte) (inputMsb >> NetUtils.NumBitsInAByte - numBitstoShift);
567 * Insert in the data buffer at position dictated by the offset the number
568 * of bits specified from the input data byte array. The input byte array
569 * has the bits stored starting from the LSB
571 public static void insertBits(final byte[] data, final byte[] inputdataLSB, final int startOffset,
573 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
578 int numBytes = numBits / NetUtils.NumBitsInAByte;
579 int startByteOffset = startOffset / NetUtils.NumBitsInAByte;
580 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
581 int extranumBits = numBits % NetUtils.NumBitsInAByte;
582 int RestBits = numBits % NetUtils.NumBitsInAByte;
583 int InputMSBbits = 0, InputLSBbits = 0;
590 if (extraOffsetBits == 0) {
591 if (extranumBits == 0) {
592 numBytes = numBits / NetUtils.NumBitsInAByte;
593 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
595 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
596 data[startByteOffset + numBytes] = (byte) (data[startByteOffset + numBytes]
597 | inputdata[numBytes] & getMSBMask(extranumBits));
600 for (i = 0; i < numBytes; i++) {
602 InputLSBbits = inputdata[i - 1] & getLSBMask(extraOffsetBits);
604 InputMSBbits = (byte) (inputdata[i] & getMSBMask(NetUtils.NumBitsInAByte - extraOffsetBits));
605 InputMSBbits = InputMSBbits >= 0 ? InputMSBbits : InputMSBbits + 256;
606 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
607 | InputLSBbits << NetUtils.NumBitsInAByte - extraOffsetBits | InputMSBbits >> extraOffsetBits);
608 InputMSBbits = InputLSBbits = 0;
610 if (RestBits < NetUtils.NumBitsInAByte - extraOffsetBits) {
612 InputLSBbits = inputdata[i - 1] & getLSBMask(extraOffsetBits);
614 InputMSBbits = (byte) (inputdata[i] & getMSBMask(RestBits));
615 InputMSBbits = InputMSBbits >= 0 ? InputMSBbits : InputMSBbits + 256;
616 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
617 | InputLSBbits << NetUtils.NumBitsInAByte - extraOffsetBits | InputMSBbits >> extraOffsetBits);
618 } else if (RestBits == NetUtils.NumBitsInAByte - extraOffsetBits) {
620 InputLSBbits = inputdata[i - 1] & getLSBMask(extraOffsetBits);
622 InputMSBbits = (byte) (inputdata[i] & getMSBMask(NetUtils.NumBitsInAByte - extraOffsetBits));
623 InputMSBbits = InputMSBbits >= 0 ? InputMSBbits : InputMSBbits + 256;
624 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
625 | InputLSBbits << NetUtils.NumBitsInAByte - extraOffsetBits | InputMSBbits >> extraOffsetBits);
628 InputLSBbits = inputdata[i - 1] & getLSBMask(extraOffsetBits);
630 InputMSBbits = (byte) (inputdata[i] & getMSBMask(NetUtils.NumBitsInAByte - extraOffsetBits));
631 InputMSBbits = InputMSBbits >= 0 ? InputMSBbits : InputMSBbits + 256;
632 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
633 | InputLSBbits << NetUtils.NumBitsInAByte - extraOffsetBits | InputMSBbits >> extraOffsetBits);
635 InputLSBbits = inputdata[i]
636 & getLSBMask(RestBits - (NetUtils.NumBitsInAByte - extraOffsetBits)) << NetUtils.NumBitsInAByte
638 data[startByteOffset + i + 1] = (byte) (data[startByteOffset + i + 1]
639 | InputLSBbits << NetUtils.NumBitsInAByte - extraOffsetBits);
645 * Checks for overflow and underflow exceptions
647 * @throws BufferException
648 * when the startOffset and numBits parameters are not congruent
649 * with the data buffer's size
651 public static void checkExceptions(final byte[] data, final int startOffset, final int numBits)
652 throws BufferException {
655 endOffsetByte = startOffset / NetUtils.NumBitsInAByte + numBits / NetUtils.NumBitsInAByte
656 + (numBits % NetUtils.NumBitsInAByte != 0 ? 1 : startOffset % NetUtils.NumBitsInAByte != 0 ? 1 : 0);
657 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
660 throw new BufferException("data[] is null\n");
663 if (startOffset < 0 || startByteOffset >= data.length || endOffsetByte > data.length || numBits < 0
664 || numBits > NetUtils.NumBitsInAByte * data.length) {
665 throw new BufferException("Illegal arguement/out of bound exception - data.length = " + data.length
666 + " startOffset = " + startOffset + " numBits " + numBits);