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
8 package org.opendaylight.openflowplugin.libraries.liblldp;
10 import java.util.Arrays;
11 import javax.annotation.Nonnull;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
16 * BitBufferHelper class that provides utility methods to - fetch specific bits
17 * from a serialized stream of bits - convert bits to primitive data type - like
18 * short, int, long - store bits in specified location in stream of bits -
19 * convert primitive data types to stream of bits.
21 public abstract class BitBufferHelper {
22 private static final Logger LOG = LoggerFactory.getLogger(BitBufferHelper.class);
24 public static final long BYTE_MASK = 0xFF;
27 // data: array where data are stored
28 // startOffset: bit from where to start reading
29 // numBits: number of bits to read
30 // All this function return an exception if overflow or underflow
33 * Returns the first byte from the byte array.
37 public static byte getByte(final byte[] data) {
38 if (data.length * NetUtils.NUM_BITS_IN_A_BYTE > Byte.SIZE) {
39 LOG.error("getByte", new BufferException("Container is too small for the number of requested bits"));
45 * Returns the short value for the byte array passed. Size of byte array is
46 * restricted to Short.SIZE
50 public static short getShort(final byte[] data) {
51 if (data.length > Short.SIZE) {
52 LOG.error("getShort", new BufferException("Container is too small for the number of requested bits"));
54 return (short) toNumber(data);
58 * Returns the short value for the last numBits of the byte array passed.
59 * Size of numBits is restricted to Short.SIZE
61 * @return short - the short value of byte array
63 public static short getShort(final byte[] data, final int numBits) {
64 if (numBits > Short.SIZE) {
65 LOG.error("getShort", new BufferException("Container is too small for the number of requested bits"));
67 int startOffset = data.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits;
69 byte[] bits = BitBufferHelper.getBits(data, startOffset, numBits);
70 return (short) toNumber(bits, numBits);
71 } catch (final BufferException e) {
72 LOG.error("getBits failed", e);
78 * Returns the int value for the byte array passed. Size of byte array is
79 * restricted to Integer.SIZE
81 * @return int - the integer value of byte array
83 public static int getInt(final byte[] data) {
84 if (data.length > Integer.SIZE) {
85 LOG.error("getInt", new BufferException("Container is too small for the number of requested bits"));
87 return (int) toNumber(data);
91 * Returns the int value for the last numBits of the byte array passed. Size
92 * of numBits is restricted to Integer.SIZE
94 * @return int - the integer value of byte array
96 public static int getInt(final byte[] data, final int numBits) {
97 if (numBits > Integer.SIZE) {
98 LOG.error("getInt", new BufferException("Container is too small for the number of requested bits"));
100 int startOffset = data.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits;
102 byte[] bits = BitBufferHelper.getBits(data, startOffset, numBits);
103 return (int) toNumber(bits, numBits);
104 } catch (final BufferException e) {
105 LOG.error("getBits failed", e);
111 * Returns the long value for the byte array passed. Size of byte array is
112 * restricted to Long.SIZE
114 * @return long - the integer value of byte array
116 public static long getLong(final byte[] data) {
117 if (data.length > Long.SIZE) {
118 LOG.error("getLong", new BufferException("Container is too small for the number of requested bits"));
120 return toNumber(data);
124 * Returns the long value for the last numBits of the byte array passed.
125 * Size of numBits is restricted to Long.SIZE
127 * @return long - the integer value of byte array
129 public static long getLong(final byte[] data, final int numBits) {
130 if (numBits > Long.SIZE) {
131 LOG.error("getLong", new BufferException("Container is too small for the number of requested bits"));
133 if (numBits > data.length * NetUtils.NUM_BITS_IN_A_BYTE) {
135 throw new BufferException("Trying to read more bits than contained in the data buffer");
136 } catch (final BufferException e) {
140 int startOffset = data.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits;
142 byte[] bits = BitBufferHelper.getBits(data, startOffset, numBits);
143 return toNumber(bits, numBits);
144 } catch (final BufferException e) {
145 LOG.error("getBits failed", e);
151 * Reads the specified number of bits from the passed byte array starting to
152 * read from the specified offset The bits read are stored in a byte array
153 * which size is dictated by the number of bits to be stored. The bits are
154 * stored in the byte array LSB aligned.
157 * Ex. Read 7 bits at offset 10 0 9 10 16 17 0101000010 | 0000101 |
158 * 1111001010010101011 will be returned as {0,0,0,0,0,1,0,1}
161 * - offset to start fetching bits from data from
163 * - number of bits to be fetched from data
164 * @return byte [] - LSB aligned bits
166 * @throws BufferException
167 * when the startOffset and numBits parameters are not congruent
168 * with the data buffer size
171 public static byte[] getBits(final byte[] data, final int startOffset, final int numBits) throws BufferException {
173 int extranumBits = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
174 final int extraOffsetBits = startOffset % NetUtils.NUM_BITS_IN_A_BYTE;
175 int numBytes = numBits % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1 + numBits / NetUtils.NUM_BITS_IN_A_BYTE
176 : numBits / NetUtils.NUM_BITS_IN_A_BYTE;
177 startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
178 byte[] bytes = new byte[numBytes];
183 checkExceptions(data, startOffset, numBits);
185 if (extraOffsetBits == 0) {
186 if (extranumBits == 0) {
187 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
190 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
191 bytes[numBytes - 1] = (byte) (data[startByteOffset + numBytes - 1] & getMSBMask(extranumBits));
197 for (index = 0; index < numBits / NetUtils.NUM_BITS_IN_A_BYTE; index++) {
198 // Reading numBytes starting from offset
199 valfromcurr = data[startByteOffset + index] & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
200 valfromnext = data[startByteOffset + index + 1] & getMSBMask(extraOffsetBits);
201 bytes[index] = (byte) (valfromcurr << extraOffsetBits
202 | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
204 // Now adding the rest of the bits if any
205 if (extranumBits != 0) {
206 if (extranumBits < NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
207 valfromnext = (byte) (data[startByteOffset + index] & getMSBMask(extranumBits) >> extraOffsetBits);
208 bytes[index] = (byte) (valfromnext << extraOffsetBits);
209 } else if (extranumBits == NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
210 valfromcurr = data[startByteOffset + index]
211 & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
212 bytes[index] = (byte) (valfromcurr << extraOffsetBits);
214 valfromcurr = data[startByteOffset + index]
215 & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
216 valfromnext = data[startByteOffset + index + 1]
217 & getMSBMask(extranumBits - (NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
218 bytes[index] = (byte) (valfromcurr << extraOffsetBits
219 | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
224 // Aligns the bits to LSB
225 return shiftBitsToLSB(bytes, numBits);
229 // data: array where data will be stored
230 // input: the data that need to be stored in the data array
231 // startOffset: bit from where to start writing
232 // numBits: number of bits to read
235 * Bits are expected to be stored in the input byte array from LSB.
238 * to set the input byte
240 * byte to be inserted
242 * offset of data[] to start inserting byte from
244 * number of bits of input to be inserted into data[]
246 * @throws BufferException
247 * when the input, startOffset and numBits are not congruent
248 * with the data buffer size
250 public static void setByte(final byte[] data, final byte input, final int startOffset, final int numBits)
251 throws BufferException {
252 byte[] inputByteArray = new byte[1];
253 Arrays.fill(inputByteArray, 0, 1, input);
254 setBytes(data, inputByteArray, startOffset, numBits);
258 * Bits are expected to be stored in the input byte array from LSB.
261 * to set the input byte
263 * bytes to be inserted
265 * offset of data[] to start inserting byte from
267 * number of bits of input to be inserted into data[]
268 * @throws BufferException
269 * when the startOffset and numBits parameters are not congruent
270 * with data and input buffers' size
272 public static void setBytes(final byte[] data, final byte[] input, final int startOffset, final int numBits)
273 throws BufferException {
274 checkExceptions(data, startOffset, numBits);
275 insertBits(data, input, startOffset, numBits);
279 * Returns numBits 1's in the MSB position.
281 public static int getMSBMask(final int numBits) {
283 for (int i = 0; i < numBits; i++) {
284 mask = mask | 1 << 7 - i;
290 * Returns numBits 1's in the LSB position.
292 public static int getLSBMask(final int numBits) {
294 for (int i = 0; i < numBits; i++) {
295 mask = mask | 1 << i;
301 * Returns the numerical value of the byte array passed.
303 * @return long - numerical value of byte array passed
305 public static long toNumber(final byte[] array) {
307 long length = array.length;
309 for (int i = 0; i < length; i++) {
314 ret = ret | (long) value << (length - i - 1) * NetUtils.NUM_BITS_IN_A_BYTE;
320 * Returns the numerical value of the last numBits (LSB bits) of the byte array passed.
322 * @return long - numerical value of byte array passed
324 public static long toNumber(final byte[] array, final int numBits) {
325 int length = numBits / NetUtils.NUM_BITS_IN_A_BYTE;
326 int bitsRest = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
327 int startOffset = array.length - length;
331 value = array[startOffset - 1] & getLSBMask(bitsRest);
332 value = array[startOffset - 1] < 0 ? array[startOffset - 1] + 256 : array[startOffset - 1];
333 ret = ret | value << (array.length - startOffset) * NetUtils.NUM_BITS_IN_A_BYTE;
335 for (int i = startOffset; i < array.length; i++) {
340 ret = ret | (long) value << (array.length - i - 1) * NetUtils.NUM_BITS_IN_A_BYTE;
347 * Accepts a number as input and returns its value in byte form in LSB
348 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
349 * [00010011] [10001000].
351 public static byte[] toByteArray(final Number input) {
352 Class<? extends Number> dataType = input.getClass();
354 long longValue = input.longValue();
356 if (dataType == Byte.class || dataType == byte.class) {
358 } else if (dataType == Short.class || dataType == short.class) {
360 } else if (dataType == Integer.class || dataType == int.class) {
362 } else if (dataType == Long.class || dataType == long.class) {
365 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
368 int length = size / NetUtils.NUM_BITS_IN_A_BYTE;
369 byte[] bytes = new byte[length];
371 // Getting the bytes from input value
372 for (int i = 0; i < length; i++) {
373 bytes[i] = (byte) (longValue >> NetUtils.NUM_BITS_IN_A_BYTE * (length - i - 1) & BYTE_MASK);
379 * Accepts a number as input and returns its value in byte form in MSB
380 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
381 * [10011100] [01000000].
384 * - the number of bits to be returned
388 public static byte[] toByteArray(final Number input, final int numBits) {
389 Class<? extends Number> dataType = input.getClass();
391 long longValue = input.longValue();
393 if (dataType == Short.class) {
395 } else if (dataType == Integer.class) {
397 } else if (dataType == Long.class) {
400 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
403 int length = size / NetUtils.NUM_BITS_IN_A_BYTE;
404 byte[] bytes = new byte[length];
405 byte[] inputbytes = new byte[length];
408 // Getting the bytes from input value
409 for (int i = 0; i < length; i++) {
410 bytes[i] = (byte) (longValue >> NetUtils.NUM_BITS_IN_A_BYTE * (length - i - 1) & BYTE_MASK);
413 if (bytes[0] == 0 && dataType == Long.class || bytes[0] == 0 && dataType == Integer.class) {
415 for (index = 0; index < length; ++index) {
416 if (bytes[index] != 0) {
417 bytes[0] = bytes[index];
421 System.arraycopy(bytes, index, inputbytes, 0, length - index);
422 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
424 System.arraycopy(bytes, 0, inputbytes, 0, length);
427 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
433 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
434 * aligned byte array.
437 * It aligns the last numBits bits to the head of the byte array following
438 * them with numBits % 8 zero bits.
441 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
442 * returns: shiftedBytes = [01110111][00010000]
445 * - number of bits to be left aligned
448 public static byte[] shiftBitsToMSB(final byte[] inputBytes, final int numBits) {
449 int numBitstoShiftBy;
450 int leadZeroesMSB = 8;
452 int size = inputBytes.length;
453 byte[] shiftedBytes = new byte[size];
455 for (int i = 0; i < Byte.SIZE; i++) {
456 if ((byte) (inputBytes[0] & getMSBMask(i + 1)) != 0) {
462 if (numBits % NetUtils.NUM_BITS_IN_A_BYTE == 0) {
463 numBitstoShiftBy = 0;
465 numBitstoShiftBy = NetUtils.NUM_BITS_IN_A_BYTE - numBits % NetUtils.NUM_BITS_IN_A_BYTE < leadZeroesMSB
466 ? NetUtils.NUM_BITS_IN_A_BYTE - numBits % NetUtils.NUM_BITS_IN_A_BYTE : leadZeroesMSB;
468 if (numBitstoShiftBy == 0) {
472 if (numBits < NetUtils.NUM_BITS_IN_A_BYTE) {
473 // inputbytes.length = 1 OR read less than a byte
474 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
476 // # of bits to read from last byte
477 numEndRestBits = NetUtils.NUM_BITS_IN_A_BYTE
478 - (inputBytes.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits - numBitstoShiftBy);
480 for (int i = 0; i < size - 1; i++) {
481 if (i + 1 == size - 1) {
482 if (numEndRestBits > numBitstoShiftBy) {
483 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
484 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> numEndRestBits
486 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1]
487 & getLSBMask(numEndRestBits - numBitstoShiftBy)) << numBitstoShiftBy);
489 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
490 | (inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> NetUtils.NUM_BITS_IN_A_BYTE
494 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
495 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> NetUtils.NUM_BITS_IN_A_BYTE
504 * It aligns the first numBits bits to the right end of the byte array
505 * preceding them with numBits % 8 zero bits.
508 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
509 * returns: shiftedBytes = [00000111][01110001]
511 * @param inputBytes input bytes
513 * - number of bits to be right aligned
516 public static byte[] shiftBitsToLSB(final byte[] inputBytes, final int numBits) {
517 int numBytes = inputBytes.length;
518 int numBitstoShift = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
519 byte[] shiftedBytes = new byte[numBytes];
523 if (numBitstoShift == 0) {
527 for (int i = 1; i < numBytes; i++) {
528 inputLsb = inputBytes[i - 1] & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
529 inputLsb = inputLsb < 0 ? inputLsb + 256 : inputLsb;
530 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
531 inputMsb = inputBytes[i] < 0 ? inputBytes[i] + 256 : inputBytes[i];
532 shiftedBytes[i] = (byte) (inputLsb << numBitstoShift
533 | inputMsb >> NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
535 inputMsb = inputBytes[0] & getMSBMask(numBitstoShift);
536 inputMsb = inputMsb < 0 ? inputMsb + 256 : inputMsb;
537 shiftedBytes[0] = (byte) (inputMsb >> NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
542 * Insert in the data buffer at position dictated by the offset the number
543 * of bits specified from the input data byte array. The input byte array
544 * has the bits stored starting from the LSB
546 public static void insertBits(final byte[] data, final byte[] inputdataLSB, final int startOffset,
548 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
553 int numBytes = numBits / NetUtils.NUM_BITS_IN_A_BYTE;
554 int startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
555 int extraOffsetBits = startOffset % NetUtils.NUM_BITS_IN_A_BYTE;
556 int extranumBits = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
557 int restBits = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
559 int inputLSBbits = 0;
565 if (extraOffsetBits == 0) {
566 if (extranumBits == 0) {
567 numBytes = numBits / NetUtils.NUM_BITS_IN_A_BYTE;
568 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
570 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
571 data[startByteOffset + numBytes] = (byte) (data[startByteOffset + numBytes]
572 | inputdata[numBytes] & getMSBMask(extranumBits));
576 for (index = 0; index < numBytes; index++) {
578 inputLSBbits = inputdata[index - 1] & getLSBMask(extraOffsetBits);
580 inputMSBbits = (byte) (inputdata[index] & getMSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
581 inputMSBbits = inputMSBbits >= 0 ? inputMSBbits : inputMSBbits + 256;
582 data[startByteOffset + index] = (byte) (data[startByteOffset + index]
583 | inputLSBbits << NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits
584 | inputMSBbits >> extraOffsetBits);
585 inputMSBbits = inputLSBbits = 0;
587 if (restBits < NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
589 inputLSBbits = inputdata[index - 1] & getLSBMask(extraOffsetBits);
591 inputMSBbits = (byte) (inputdata[index] & getMSBMask(restBits));
592 inputMSBbits = inputMSBbits >= 0 ? inputMSBbits : inputMSBbits + 256;
593 data[startByteOffset + index] = (byte) (data[startByteOffset + index]
594 | inputLSBbits << NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits
595 | inputMSBbits >> extraOffsetBits);
596 } else if (restBits == NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
598 inputLSBbits = inputdata[index - 1] & getLSBMask(extraOffsetBits);
600 inputMSBbits = (byte) (inputdata[index] & getMSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
601 inputMSBbits = inputMSBbits >= 0 ? inputMSBbits : inputMSBbits + 256;
602 data[startByteOffset + index] = (byte) (data[startByteOffset + index]
603 | inputLSBbits << NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits
604 | inputMSBbits >> extraOffsetBits);
607 inputLSBbits = inputdata[index - 1] & getLSBMask(extraOffsetBits);
609 inputMSBbits = (byte) (inputdata[index] & getMSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
610 inputMSBbits = inputMSBbits >= 0 ? inputMSBbits : inputMSBbits + 256;
611 data[startByteOffset + index] = (byte) (data[startByteOffset + index]
612 | inputLSBbits << NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits
613 | inputMSBbits >> extraOffsetBits);
615 inputLSBbits = inputdata[index]
616 & getLSBMask(restBits - (NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits))
617 << NetUtils.NUM_BITS_IN_A_BYTE
619 data[startByteOffset + index + 1] = (byte) (data[startByteOffset + index + 1]
620 | inputLSBbits << NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
626 * Checks for overflow and underflow exceptions.
628 * @throws BufferException
629 * when the startOffset and numBits parameters are not congruent
630 * with the data buffer's size
632 public static void checkExceptions(final byte[] data, final int startOffset, final int numBits)
633 throws BufferException {
636 endOffsetByte = startOffset / NetUtils.NUM_BITS_IN_A_BYTE + numBits / NetUtils.NUM_BITS_IN_A_BYTE
637 + (numBits % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1
638 : startOffset % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1 : 0);
639 startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
642 throw new BufferException("data[] is null\n");
645 if (startOffset < 0 || startByteOffset >= data.length || endOffsetByte > data.length || numBits < 0
646 || numBits > NetUtils.NUM_BITS_IN_A_BYTE * data.length) {
647 throw new BufferException("Illegal arguement/out of bound exception - data.length = " + data.length
648 + " startOffset = " + startOffset + " numBits " + numBits);