2 * Copyright (c) 2013 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
21 * - fetch specific bits from a serialized stream of bits
22 * - convert bits to primitive data type - like short, int, long
23 * - store bits in specified location in stream of bits
24 * - convert primitive data types to stream of bits
26 public abstract class BitBufferHelper {
27 protected static final Logger logger = LoggerFactory
28 .getLogger(BitBufferHelper.class);
30 public static final long ByteMask = 0xFF;
33 // data: array where data are stored
34 // startOffset: bit from where to start reading
35 // numBits: number of bits to read
36 // All this function return an exception if overflow or underflow
39 * Returns the first byte from the byte array
43 public static byte getByte(byte[] data) {
44 if ((data.length * NetUtils.NumBitsInAByte) > Byte.SIZE) {
46 throw new BufferException(
47 "Container is too small for the number of requested bits");
48 } catch (BufferException e) {
56 * Returns the short value for the byte array passed.
57 * Size of byte array is restricted to Short.SIZE
61 public static short getShort(byte[] data) {
62 if (data.length > Short.SIZE) {
64 throw new BufferException(
65 "Container is too small for the number of requested bits");
66 } catch (BufferException e) {
70 return (short) toNumber(data);
74 * Returns the int value for the byte array passed.
75 * Size of byte array is restricted to Integer.SIZE
77 * @return int - the integer value of byte array
79 public static int getInt(byte[] data) {
80 if (data.length > Integer.SIZE) {
82 throw new BufferException(
83 "Container is too small for the number of requested bits");
84 } catch (BufferException e) {
88 return (int) toNumber(data);
92 * Returns the long value for the byte array passed.
93 * Size of byte array is restricted to Long.SIZE
95 * @return long - the integer value of byte array
97 public static long getLong(byte[] data) {
98 if (data.length > Long.SIZE) {
100 throw new BufferException(
101 "Container is too small for the number of requested bits");
102 } catch (Exception e) {
106 return (long) toNumber(data);
110 * Returns the short value for the last numBits of the byte array passed.
111 * Size of numBits is restricted to Short.SIZE
113 * @param int - numBits
114 * @return short - the short value of byte array
116 public static short getShort(byte[] data, int numBits) {
117 if (numBits > Short.SIZE) {
119 throw new BufferException(
120 "Container is too small for the number of requested bits");
121 } catch (BufferException e) {
125 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
128 bits = BitBufferHelper.getBits(data, startOffset, numBits);
129 } catch (BufferException e) {
132 return (short) toNumber(bits, numBits);
136 * Returns the int value for the last numBits of the byte array passed.
137 * Size of numBits is restricted to Integer.SIZE
139 * @param int - numBits
140 * @return int - the integer value of byte array
142 public static int getInt(byte[] data, int numBits) {
143 if (numBits > Integer.SIZE) {
145 throw new BufferException(
146 "Container is too small for the number of requested bits");
147 } catch (BufferException e) {
151 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
154 bits = BitBufferHelper.getBits(data, startOffset, numBits);
155 } catch (BufferException e) {
158 return (int) toNumber(bits, numBits);
162 * Returns the long value for the last numBits of the byte array passed.
163 * Size of numBits is restricted to Long.SIZE
165 * @param int - numBits
166 * @return long - the integer value of byte array
168 public static long getLong(byte[] data, int numBits) {
169 if (numBits > Long.SIZE) {
171 throw new BufferException(
172 "Container is too small for the number of requested bits");
173 } catch (BufferException e) {
177 if (numBits > data.length * NetUtils.NumBitsInAByte) {
179 throw new BufferException(
180 "Trying to read more bits than contained in the data buffer");
181 } catch (BufferException e) {
185 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
188 bits = BitBufferHelper.getBits(data, startOffset, numBits);
189 } catch (BufferException e) {
192 return (long) toNumber(bits, numBits);
196 * Reads the specified number of bits from the passed byte array
197 * starting to read from the specified offset
198 * The bits read are stored in a byte array which size is dictated
199 * by the number of bits to be stored.
200 * The bits are stored in the byte array LSB aligned.
203 * Read 7 bits at offset 10
205 * 0101000010 | 0000101 | 1111001010010101011
206 * will be returned as {0,0,0,0,0,1,0,1}
209 * @param int startOffset - offset to start fetching bits from data from
210 * @param int numBits - number of bits to be fetched from data
211 * @return byte [] - LSB aligned bits
213 * @throws BufferException
214 * when the startOffset and numBits parameters are not congruent
215 * with the data buffer size
217 public static byte[] getBits(byte[] data, int startOffset, int numBits)
218 throws BufferException {
220 int startByteOffset = 0;
221 int valfromcurr, valfromnext;
222 int extranumBits = numBits % NetUtils.NumBitsInAByte;
223 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
224 int numBytes = (numBits % NetUtils.NumBitsInAByte != 0) ? 1 + numBits
225 / NetUtils.NumBitsInAByte : numBits / NetUtils.NumBitsInAByte;
226 byte[] shiftedBytes = new byte[numBytes];
227 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
228 byte[] bytes = new byte[numBytes];
233 checkExceptions(data, startOffset, numBits);
235 if (extraOffsetBits == 0) {
236 if (extranumBits == 0) {
237 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
240 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
241 bytes[numBytes - 1] = (byte) ((int) data[startByteOffset
242 + numBytes - 1] & getMSBMask(extranumBits));
246 for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) {
247 // Reading numBytes starting from offset
248 valfromcurr = (data[startByteOffset + i])
249 & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
250 valfromnext = (data[startByteOffset + i + 1])
251 & getMSBMask(extraOffsetBits);
252 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
254 // Now adding the rest of the bits if any
255 if (extranumBits != 0) {
256 if (extranumBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
257 valfromnext = (byte) (data[startByteOffset + i] & ((getMSBMask(extranumBits)) >> extraOffsetBits));
258 bytes[i] = (byte) (valfromnext << extraOffsetBits);
259 } else if (extranumBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
260 valfromcurr = (data[startByteOffset + i])
261 & getLSBMask(NetUtils.NumBitsInAByte
263 bytes[i] = (byte) (valfromcurr << extraOffsetBits);
265 valfromcurr = (data[startByteOffset + i])
266 & getLSBMask(NetUtils.NumBitsInAByte
268 valfromnext = (data[startByteOffset + i + 1])
269 & (getMSBMask(extranumBits
270 - (NetUtils.NumBitsInAByte - extraOffsetBits)));
271 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
276 // Aligns the bits to LSB
277 shiftedBytes = shiftBitsToLSB(bytes, numBits);
282 // data: array where data will be stored
283 // input: the data that need to be stored in the data array
284 // startOffset: bit from where to start writing
285 // numBits: number of bits to read
288 * Bits are expected to be stored in the input byte array from LSB
289 * @param byte[] - data to set the input byte
290 * @param byte - input byte to be inserted
291 * @param startOffset - offset of data[] to start inserting byte from
292 * @param numBits - number of bits of input to be inserted into data[]
294 * @throws BufferException
295 * when the input, startOffset and numBits are not congruent
296 * with the data buffer size
298 public static void setByte(byte[] data, byte input, int startOffset,
299 int numBits) throws BufferException {
300 byte[] inputByteArray = new byte[1];
301 Arrays.fill(inputByteArray, 0, 1, input);
302 setBytes(data, inputByteArray, startOffset, numBits);
306 * Bits are expected to be stored in the input byte array from LSB
307 * @param byte[] - data to set the input byte
308 * @param byte[] - input bytes to be inserted
309 * @param startOffset - offset of data[] to start inserting byte from
310 * @param numBits - number of bits of input to be inserted into data[]
312 * @throws BufferException
313 * when the startOffset and numBits parameters are not congruent
314 * with data and input buffers' size
316 public static void setBytes(byte[] data, byte[] input, int startOffset,
317 int numBits) throws BufferException {
318 checkExceptions(data, startOffset, numBits);
319 insertBits(data, input, startOffset, numBits);
323 * Returns numBits 1's in the MSB position
328 public static int getMSBMask(int numBits) {
330 for (int i = 0; i < numBits; i++) {
331 mask = mask | (1 << (7 - i));
337 * Returns numBits 1's in the LSB position
342 public static int getLSBMask(int numBits) {
344 for (int i = 0; i < numBits; i++) {
345 mask = mask | (1 << i);
351 * Returns the numerical value of the byte array passed
353 * @param byte[] - array
354 * @return long - numerical value of byte array passed
356 static public long toNumber(byte[] array) {
358 long length = array.length;
360 for (int i = 0; i < length; i++) {
365 | (long) ((long) value << ((length - i - 1) * NetUtils.NumBitsInAByte));
371 * Returns the numerical value of the last numBits (LSB bits) of the byte
374 * @param byte[] - array
375 * @param int - numBits
376 * @return long - numerical value of byte array passed
378 static public long toNumber(byte[] array, int numBits) {
379 int length = numBits / NetUtils.NumBitsInAByte;
380 int bitsRest = numBits % NetUtils.NumBitsInAByte;
381 int startOffset = array.length - length;
385 value = array[startOffset - 1] & getLSBMask(bitsRest);
386 value = (array[startOffset - 1] < 0) ? (array[startOffset - 1] + 256)
387 : array[startOffset - 1];
389 | (value << ((array.length - startOffset) * NetUtils.NumBitsInAByte));
391 for (int i = startOffset; i < array.length; i++) {
396 | (long) ((long) value << ((array.length - i - 1) * NetUtils.NumBitsInAByte));
403 * Accepts a number as input and returns its value in byte form in LSB
404 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
405 * [00010011] [10001000]
412 public static byte[] toByteArray(Number input) {
413 Class<? extends Number> dataType = input.getClass();
415 long longValue = input.longValue();
417 if (dataType == Byte.class || dataType == byte.class) {
419 } else if (dataType == Short.class || dataType == short.class) {
421 } else if (dataType == Integer.class || dataType == int.class) {
423 } else if (dataType == Long.class || dataType == long.class) {
426 throw new IllegalArgumentException(
427 "Parameter must one of the following: Short/Int/Long\n");
430 int length = size / NetUtils.NumBitsInAByte;
431 byte bytes[] = new byte[length];
433 // Getting the bytes from input value
434 for (int i = 0; i < length; i++) {
435 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
436 - i - 1))) & ByteMask);
442 * Accepts a number as input and returns its value in byte form in MSB
443 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
444 * [10011100] [01000000]
448 * @param int numBits - the number of bits to be returned
452 public static byte[] toByteArray(Number input, int numBits) {
453 Class<? extends Number> dataType = input.getClass();
455 long longValue = input.longValue();
457 if (dataType == Short.class) {
459 } else if (dataType == Integer.class) {
461 } else if (dataType == Long.class) {
464 throw new IllegalArgumentException(
465 "Parameter must one of the following: Short/Int/Long\n");
468 int length = size / NetUtils.NumBitsInAByte;
469 byte bytes[] = new byte[length];
470 byte[] inputbytes = new byte[length];
473 // Getting the bytes from input value
474 for (int i = 0; i < length; i++) {
475 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
476 - i - 1))) & ByteMask);
479 if ((bytes[0] == 0 && dataType == Long.class)
480 || (bytes[0] == 0 && dataType == Integer.class)) {
482 for (index = 0; index < length; ++index) {
483 if (bytes[index] != 0) {
484 bytes[0] = bytes[index];
488 System.arraycopy(bytes, index, inputbytes, 0, length - index);
489 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
491 System.arraycopy(bytes, 0, inputbytes, 0, length);
494 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
500 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
508 * It aligns the last numBits bits to the head of the byte array following
509 * them with numBits % 8 zero bits.
511 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
512 * returns: shiftedBytes = [01110111][00010000]
514 * @param byte[] inputBytes
515 * @param int numBits - number of bits to be left aligned
518 public static byte[] shiftBitsToMSB(byte[] inputBytes, int numBits) {
519 int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0;
520 int size = inputBytes.length;
521 byte[] shiftedBytes = new byte[size];
524 for (i = 0; i < Byte.SIZE; i++) {
525 if (((byte) (inputBytes[0] & getMSBMask(i + 1))) != 0) {
531 if (numBits % NetUtils.NumBitsInAByte == 0) {
532 numBitstoShiftBy = 0;
534 numBitstoShiftBy = ((NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) < leadZeroesMSB) ? (NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte))
537 if (numBitstoShiftBy == 0) {
541 if (numBits < NetUtils.NumBitsInAByte) {
542 // inputbytes.length = 1 OR read less than a byte
543 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
545 // # of bits to read from last byte
546 numEndRestBits = NetUtils.NumBitsInAByte
547 - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy);
549 for (i = 0; i < (size - 1); i++) {
550 if ((i + 1) == (size - 1)) {
551 if (numEndRestBits > numBitstoShiftBy) {
552 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (numEndRestBits - numBitstoShiftBy)));
553 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1] & getLSBMask(numEndRestBits
554 - numBitstoShiftBy)) << numBitstoShiftBy);
556 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> (NetUtils.NumBitsInAByte - numEndRestBits)));
558 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (NetUtils.NumBitsInAByte - numBitstoShiftBy));
566 * It aligns the first numBits bits to the right end of the byte array
567 * preceding them with numBits % 8 zero bits.
569 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
570 * returns: shiftedBytes = [00000111][01110001]
572 * @param byte[] inputBytes
573 * @param int numBits - number of bits to be right aligned
576 public static byte[] shiftBitsToLSB(byte[] inputBytes, int numBits) {
577 int numBytes = inputBytes.length;
578 int numBitstoShift = numBits % NetUtils.NumBitsInAByte;
579 byte[] shiftedBytes = new byte[numBytes];
580 int inputLsb = 0, inputMsb = 0;
582 if (numBitstoShift == 0) {
586 for (int i = 1; i < numBytes; i++) {
587 inputLsb = inputBytes[i - 1]
588 & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift);
589 inputLsb = (inputLsb < 0) ? (inputLsb + 256) : inputLsb;
590 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
591 inputMsb = (inputBytes[i] < 0) ? (inputBytes[i] + 256)
593 shiftedBytes[i] = (byte) ((inputLsb << numBitstoShift) | (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift)));
595 inputMsb = inputBytes[0] & (getMSBMask(numBitstoShift));
596 inputMsb = (inputMsb < 0) ? (inputMsb + 256) : inputMsb;
597 shiftedBytes[0] = (byte) (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift));
602 * Insert in the data buffer at position dictated by the offset the number
603 * of bits specified from the input data byte array. The input byte array
604 * has the bits stored starting from the LSB
607 * @param byte[] inputdata
608 * @param int startOffset
611 public static void insertBits(byte[] data, byte[] inputdataLSB,
612 int startOffset, int numBits) {
613 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
617 int numBytes = numBits / NetUtils.NumBitsInAByte;
618 int startByteOffset = startOffset / NetUtils.NumBitsInAByte;
619 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
620 int extranumBits = numBits % NetUtils.NumBitsInAByte;
621 int RestBits = numBits % NetUtils.NumBitsInAByte;
622 int InputMSBbits = 0, InputLSBbits = 0;
629 if (extraOffsetBits == 0) {
630 if (extranumBits == 0) {
631 numBytes = numBits / NetUtils.NumBitsInAByte;
632 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
634 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
635 data[startByteOffset + numBytes] = (byte) (data[startByteOffset
636 + numBytes] | (inputdata[numBytes] & getMSBMask(extranumBits)));
639 for (i = 0; i < numBytes; i++) {
641 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
642 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
643 - extraOffsetBits)));
644 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
645 : InputMSBbits + 256;
646 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
647 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
648 InputMSBbits = InputLSBbits = 0;
650 if (RestBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
652 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
653 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(RestBits)));
654 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
655 : InputMSBbits + 256;
656 data[startByteOffset + i] = (byte) ((data[startByteOffset + i])
657 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
658 } else if (RestBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
660 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
661 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
662 - extraOffsetBits)));
663 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
664 : InputMSBbits + 256;
665 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
666 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
669 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
670 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
671 - extraOffsetBits)));
672 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
673 : InputMSBbits + 256;
674 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
675 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
677 InputLSBbits = (inputdata[i] & (getLSBMask(RestBits
678 - (NetUtils.NumBitsInAByte - extraOffsetBits)) << (NetUtils.NumBitsInAByte - RestBits)));
679 data[startByteOffset + i + 1] = (byte) (data[startByteOffset
680 + i + 1] | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)));
686 * Checks for overflow and underflow exceptions
690 * @throws PacketException when the startOffset and numBits parameters
691 * are not congruent with the data buffer's size
693 public static void checkExceptions(byte[] data, int startOffset, int numBits)
694 throws BufferException {
697 endOffsetByte = startOffset
698 / NetUtils.NumBitsInAByte
700 / NetUtils.NumBitsInAByte
701 + ((numBits % NetUtils.NumBitsInAByte != 0) ? 1 : ((startOffset
702 % NetUtils.NumBitsInAByte != 0) ? 1 : 0));
703 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
706 throw new BufferException("data[] is null\n");
709 if ((startOffset < 0) || (startByteOffset >= data.length)
710 || (endOffsetByte > data.length) || (numBits < 0)
711 || (numBits > NetUtils.NumBitsInAByte * data.length)) {
712 throw new BufferException(
713 "Illegal arguement/out of bound exception - data.length = "
714 + data.length + " startOffset = " + startOffset
715 + " numBits " + numBits);