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
42 public static byte getByte(final byte[] data) {
43 if ((data.length * NetUtils.NumBitsInAByte) > Byte.SIZE) {
45 throw new BufferException(
46 "Container is too small for the number of requested bits");
47 } catch (final BufferException e) {
55 * Returns the short value for the byte array passed.
56 * Size of byte array is restricted to Short.SIZE
59 public static short getShort(final byte[] data) {
60 if (data.length > Short.SIZE) {
62 throw new BufferException(
63 "Container is too small for the number of requested bits");
64 } catch (final BufferException e) {
68 return (short) toNumber(data);
72 * Returns the int value for the byte array passed.
73 * Size of byte array is restricted to Integer.SIZE
74 * @return int - the integer value of byte array
76 public static int getInt(final byte[] data) {
77 if (data.length > Integer.SIZE) {
79 throw new BufferException(
80 "Container is too small for the number of requested bits");
81 } catch (final BufferException e) {
85 return (int) toNumber(data);
89 * Returns the long value for the byte array passed.
90 * Size of byte array is restricted to Long.SIZE
91 * @return long - the integer value of byte array
93 public static long getLong(final byte[] data) {
94 if (data.length > Long.SIZE) {
96 throw new BufferException(
97 "Container is too small for the number of requested bits");
98 } catch (final Exception e) {
102 return toNumber(data);
106 * Returns the short value for the last numBits of the byte array passed.
107 * Size of numBits is restricted to Short.SIZE
108 * @return short - the short value of byte array
110 public static short getShort(final byte[] data, final int numBits) {
111 if (numBits > Short.SIZE) {
113 throw new BufferException(
114 "Container is too small for the number of requested bits");
115 } catch (final BufferException e) {
119 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
122 bits = BitBufferHelper.getBits(data, startOffset, numBits);
123 } catch (final BufferException e) {
126 return (short) toNumber(bits, numBits);
130 * Returns the int value for the last numBits of the byte array passed.
131 * Size of numBits is restricted to Integer.SIZE
132 * @return int - the integer value of byte array
134 public static int getInt(final byte[] data, final int numBits) {
135 if (numBits > Integer.SIZE) {
137 throw new BufferException(
138 "Container is too small for the number of requested bits");
139 } catch (final BufferException e) {
143 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
146 bits = BitBufferHelper.getBits(data, startOffset, numBits);
147 } catch (final BufferException e) {
150 return (int) toNumber(bits, numBits);
154 * Returns the long value for the last numBits of the byte array passed.
155 * Size of numBits is restricted to Long.SIZE
156 * @return long - the integer value of byte array
158 public static long getLong(final byte[] data, final int numBits) {
159 if (numBits > Long.SIZE) {
161 throw new BufferException(
162 "Container is too small for the number of requested bits");
163 } catch (final BufferException e) {
167 if (numBits > data.length * NetUtils.NumBitsInAByte) {
169 throw new BufferException(
170 "Trying to read more bits than contained in the data buffer");
171 } catch (final BufferException e) {
175 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
178 bits = BitBufferHelper.getBits(data, startOffset, numBits);
179 } catch (final BufferException e) {
182 return toNumber(bits, numBits);
186 * Reads the specified number of bits from the passed byte array
187 * starting to read from the specified offset
188 * The bits read are stored in a byte array which size is dictated
189 * by the number of bits to be stored.
190 * The bits are stored in the byte array LSB aligned.
193 * Read 7 bits at offset 10
195 * 0101000010 | 0000101 | 1111001010010101011
196 * will be returned as {0,0,0,0,0,1,0,1}
198 * @param startOffset - offset to start fetching bits from data from
199 * @param numBits - number of bits to be fetched from data
200 * @return byte [] - LSB aligned bits
202 * @throws BufferException
203 * when the startOffset and numBits parameters are not congruent
204 * with the data buffer size
206 public static byte[] getBits(final byte[] data, final int startOffset, final int numBits)
207 throws BufferException {
209 int startByteOffset = 0;
210 int valfromcurr, valfromnext;
211 int extranumBits = numBits % NetUtils.NumBitsInAByte;
212 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
213 int numBytes = (numBits % NetUtils.NumBitsInAByte != 0) ? 1 + numBits
214 / NetUtils.NumBitsInAByte : numBits / NetUtils.NumBitsInAByte;
215 byte[] shiftedBytes = new byte[numBytes];
216 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
217 byte[] bytes = new byte[numBytes];
222 checkExceptions(data, startOffset, numBits);
224 if (extraOffsetBits == 0) {
225 if (extranumBits == 0) {
226 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
229 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
230 bytes[numBytes - 1] = (byte) (data[startByteOffset
231 + numBytes - 1] & getMSBMask(extranumBits));
235 for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) {
236 // Reading numBytes starting from offset
237 valfromcurr = (data[startByteOffset + i])
238 & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
239 valfromnext = (data[startByteOffset + i + 1])
240 & getMSBMask(extraOffsetBits);
241 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
243 // Now adding the rest of the bits if any
244 if (extranumBits != 0) {
245 if (extranumBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
246 valfromnext = (byte) (data[startByteOffset + i] & ((getMSBMask(extranumBits)) >> extraOffsetBits));
247 bytes[i] = (byte) (valfromnext << extraOffsetBits);
248 } else if (extranumBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
249 valfromcurr = (data[startByteOffset + i])
250 & getLSBMask(NetUtils.NumBitsInAByte
252 bytes[i] = (byte) (valfromcurr << extraOffsetBits);
254 valfromcurr = (data[startByteOffset + i])
255 & getLSBMask(NetUtils.NumBitsInAByte
257 valfromnext = (data[startByteOffset + i + 1])
258 & (getMSBMask(extranumBits
259 - (NetUtils.NumBitsInAByte - extraOffsetBits)));
260 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
265 // Aligns the bits to LSB
266 shiftedBytes = shiftBitsToLSB(bytes, numBits);
271 // data: array where data will be stored
272 // input: the data that need to be stored in the data array
273 // startOffset: bit from where to start writing
274 // numBits: number of bits to read
277 * Bits are expected to be stored in the input byte array from LSB
278 * @param data to set the input byte
279 * @param input byte to be inserted
280 * @param startOffset offset of data[] to start inserting byte from
281 * @param numBits number of bits of input to be inserted into data[]
283 * @throws BufferException
284 * when the input, startOffset and numBits are not congruent
285 * with the data buffer size
287 public static void setByte(final byte[] data, final byte input, final int startOffset,
288 final int numBits) throws BufferException {
289 byte[] inputByteArray = new byte[1];
290 Arrays.fill(inputByteArray, 0, 1, input);
291 setBytes(data, inputByteArray, startOffset, numBits);
295 * Bits are expected to be stored in the input byte array from LSB
296 * @param data to set the input byte
297 * @param input bytes to be inserted
298 * @param startOffset offset of data[] to start inserting byte from
299 * @param numBits number of bits of input to be inserted into data[]
300 * @throws BufferException
301 * when the startOffset and numBits parameters are not congruent
302 * with data and input buffers' size
304 public static void setBytes(final byte[] data, final byte[] input, final int startOffset,
305 final int numBits) throws BufferException {
306 checkExceptions(data, startOffset, numBits);
307 insertBits(data, input, startOffset, numBits);
311 * Returns numBits 1's in the MSB position
313 public static int getMSBMask(final int numBits) {
315 for (int i = 0; i < numBits; i++) {
316 mask = mask | (1 << (7 - i));
322 * Returns numBits 1's in the LSB position
324 public static int getLSBMask(final int numBits) {
326 for (int i = 0; i < numBits; i++) {
327 mask = mask | (1 << i);
333 * Returns the numerical value of the byte array passed
335 * @return long - numerical value of byte array passed
337 static public long toNumber(final byte[] array) {
339 long length = array.length;
341 for (int i = 0; i < length; i++) {
347 | (long) value << ((length - i - 1) * NetUtils.NumBitsInAByte);
353 * Returns the numerical value of the last numBits (LSB bits) of the byte
356 * @return long - numerical value of byte array passed
358 static public long toNumber(final byte[] array, final int numBits) {
359 int length = numBits / NetUtils.NumBitsInAByte;
360 int bitsRest = numBits % NetUtils.NumBitsInAByte;
361 int startOffset = array.length - length;
365 value = array[startOffset - 1] & getLSBMask(bitsRest);
366 value = (array[startOffset - 1] < 0) ? (array[startOffset - 1] + 256)
367 : array[startOffset - 1];
369 | (value << ((array.length - startOffset) * NetUtils.NumBitsInAByte));
371 for (int i = startOffset; i < array.length; i++) {
377 | (long) value << ((array.length - i - 1) * NetUtils.NumBitsInAByte);
384 * Accepts a number as input and returns its value in byte form in LSB
385 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
386 * [00010011] [10001000]
388 public static byte[] toByteArray(final Number input) {
389 Class<? extends Number> dataType = input.getClass();
391 long longValue = input.longValue();
393 if (dataType == Byte.class || dataType == byte.class) {
395 } else if (dataType == Short.class || dataType == short.class) {
397 } else if (dataType == Integer.class || dataType == int.class) {
399 } else if (dataType == Long.class || dataType == long.class) {
402 throw new IllegalArgumentException(
403 "Parameter must one of the following: Short/Int/Long\n");
406 int length = size / NetUtils.NumBitsInAByte;
407 byte bytes[] = new byte[length];
409 // Getting the bytes from input value
410 for (int i = 0; i < length; i++) {
411 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
412 - i - 1))) & ByteMask);
418 * Accepts a number as input and returns its value in byte form in MSB
419 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
420 * [10011100] [01000000]
422 * @param numBits - the number of bits to be returned
426 public static byte[] toByteArray(final Number input, final int numBits) {
427 Class<? extends Number> dataType = input.getClass();
429 long longValue = input.longValue();
431 if (dataType == Short.class) {
433 } else if (dataType == Integer.class) {
435 } else if (dataType == Long.class) {
438 throw new IllegalArgumentException(
439 "Parameter must one of the following: Short/Int/Long\n");
442 int length = size / NetUtils.NumBitsInAByte;
443 byte bytes[] = new byte[length];
444 byte[] inputbytes = new byte[length];
447 // Getting the bytes from input value
448 for (int i = 0; i < length; i++) {
449 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
450 - i - 1))) & ByteMask);
453 if ((bytes[0] == 0 && dataType == Long.class)
454 || (bytes[0] == 0 && dataType == Integer.class)) {
456 for (index = 0; index < length; ++index) {
457 if (bytes[index] != 0) {
458 bytes[0] = bytes[index];
462 System.arraycopy(bytes, index, inputbytes, 0, length - index);
463 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
465 System.arraycopy(bytes, 0, inputbytes, 0, length);
468 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
474 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
475 * aligned byte array.
477 * It aligns the last numBits bits to the head of the byte array following
478 * them with numBits % 8 zero bits.
480 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
481 * returns: shiftedBytes = [01110111][00010000]
483 * @param numBits - number of bits to be left aligned
486 public static byte[] shiftBitsToMSB(final byte[] inputBytes, final int numBits) {
487 int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0;
488 int size = inputBytes.length;
489 byte[] shiftedBytes = new byte[size];
492 for (i = 0; i < Byte.SIZE; i++) {
493 if (((byte) (inputBytes[0] & getMSBMask(i + 1))) != 0) {
499 if (numBits % NetUtils.NumBitsInAByte == 0) {
500 numBitstoShiftBy = 0;
502 numBitstoShiftBy = ((NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) < leadZeroesMSB) ? (NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte))
505 if (numBitstoShiftBy == 0) {
509 if (numBits < NetUtils.NumBitsInAByte) {
510 // inputbytes.length = 1 OR read less than a byte
511 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
513 // # of bits to read from last byte
514 numEndRestBits = NetUtils.NumBitsInAByte
515 - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy);
517 for (i = 0; i < (size - 1); i++) {
518 if ((i + 1) == (size - 1)) {
519 if (numEndRestBits > numBitstoShiftBy) {
520 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (numEndRestBits - numBitstoShiftBy)));
521 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1] & getLSBMask(numEndRestBits
522 - numBitstoShiftBy)) << numBitstoShiftBy);
524 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> (NetUtils.NumBitsInAByte - numEndRestBits)));
526 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (NetUtils.NumBitsInAByte - numBitstoShiftBy));
534 * It aligns the first numBits bits to the right end of the byte array
535 * preceding them with numBits % 8 zero bits.
537 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
538 * returns: shiftedBytes = [00000111][01110001]
541 * @param numBits - number of bits to be right aligned
544 public static byte[] shiftBitsToLSB(final byte[] inputBytes, final int numBits) {
545 int numBytes = inputBytes.length;
546 int numBitstoShift = numBits % NetUtils.NumBitsInAByte;
547 byte[] shiftedBytes = new byte[numBytes];
548 int inputLsb = 0, inputMsb = 0;
550 if (numBitstoShift == 0) {
554 for (int i = 1; i < numBytes; i++) {
555 inputLsb = inputBytes[i - 1]
556 & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift);
557 inputLsb = (inputLsb < 0) ? (inputLsb + 256) : inputLsb;
558 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
559 inputMsb = (inputBytes[i] < 0) ? (inputBytes[i] + 256)
561 shiftedBytes[i] = (byte) ((inputLsb << numBitstoShift) | (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift)));
563 inputMsb = inputBytes[0] & (getMSBMask(numBitstoShift));
564 inputMsb = (inputMsb < 0) ? (inputMsb + 256) : inputMsb;
565 shiftedBytes[0] = (byte) (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift));
570 * Insert in the data buffer at position dictated by the offset the number
571 * of bits specified from the input data byte array. The input byte array
572 * has the bits stored starting from the LSB
574 public static void insertBits(final byte[] data, final byte[] inputdataLSB,
575 final int startOffset, final int numBits) {
576 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
580 int numBytes = numBits / NetUtils.NumBitsInAByte;
581 int startByteOffset = startOffset / NetUtils.NumBitsInAByte;
582 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
583 int extranumBits = numBits % NetUtils.NumBitsInAByte;
584 int RestBits = numBits % NetUtils.NumBitsInAByte;
585 int InputMSBbits = 0, InputLSBbits = 0;
592 if (extraOffsetBits == 0) {
593 if (extranumBits == 0) {
594 numBytes = numBits / NetUtils.NumBitsInAByte;
595 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
597 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
598 data[startByteOffset + numBytes] = (byte) (data[startByteOffset
599 + numBytes] | (inputdata[numBytes] & getMSBMask(extranumBits)));
602 for (i = 0; i < numBytes; i++) {
604 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
606 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
607 - extraOffsetBits)));
608 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
609 : InputMSBbits + 256;
610 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
611 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
612 InputMSBbits = InputLSBbits = 0;
614 if (RestBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
616 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
618 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(RestBits)));
619 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
620 : InputMSBbits + 256;
621 data[startByteOffset + i] = (byte) ((data[startByteOffset + i])
622 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
623 } else if (RestBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
625 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
627 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
628 - extraOffsetBits)));
629 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
630 : InputMSBbits + 256;
631 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
632 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
635 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
637 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
638 - extraOffsetBits)));
639 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
640 : InputMSBbits + 256;
641 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
642 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
644 InputLSBbits = (inputdata[i] & (getLSBMask(RestBits
645 - (NetUtils.NumBitsInAByte - extraOffsetBits)) << (NetUtils.NumBitsInAByte - RestBits)));
646 data[startByteOffset + i + 1] = (byte) (data[startByteOffset
647 + i + 1] | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)));
653 * Checks for overflow and underflow exceptions
654 * @throws BufferException when the startOffset and numBits parameters
655 * are not congruent with the data buffer's size
657 public static void checkExceptions(final byte[] data, final int startOffset, final int numBits)
658 throws BufferException {
661 endOffsetByte = startOffset
662 / NetUtils.NumBitsInAByte
664 / NetUtils.NumBitsInAByte
665 + ((numBits % NetUtils.NumBitsInAByte != 0) ? 1 : ((startOffset
666 % NetUtils.NumBitsInAByte != 0) ? 1 : 0));
667 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
670 throw new BufferException("data[] is null\n");
673 if ((startOffset < 0) || (startByteOffset >= data.length)
674 || (endOffsetByte > data.length) || (numBits < 0)
675 || (numBits > NetUtils.NumBitsInAByte * data.length)) {
676 throw new BufferException(
677 "Illegal arguement/out of bound exception - data.length = "
678 + data.length + " startOffset = " + startOffset
679 + " numBits " + numBits);