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 org.eclipse.jdt.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) {
134 LOG.error("getLong", new BufferException("Trying to read more bits than contained in the data buffer"));
136 int startOffset = data.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits;
138 byte[] bits = BitBufferHelper.getBits(data, startOffset, numBits);
139 return toNumber(bits, numBits);
140 } catch (final BufferException e) {
141 LOG.error("getBits failed", e);
147 * Reads the specified number of bits from the passed byte array starting to
148 * read from the specified offset The bits read are stored in a byte array
149 * which size is dictated by the number of bits to be stored. The bits are
150 * stored in the byte array LSB aligned.
153 * Ex. Read 7 bits at offset 10 0 9 10 16 17 0101000010 | 0000101 |
154 * 1111001010010101011 will be returned as {0,0,0,0,0,1,0,1}
157 * - offset to start fetching bits from data from
159 * - number of bits to be fetched from data
160 * @return byte [] - LSB aligned bits
162 * @throws BufferException
163 * when the startOffset and numBits parameters are not congruent
164 * with the data buffer size
166 public static byte @NonNull [] getBits(final byte[] data, final int startOffset, final int numBits)
167 throws BufferException {
169 int extranumBits = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
170 final int extraOffsetBits = startOffset % NetUtils.NUM_BITS_IN_A_BYTE;
171 int numBytes = numBits % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1 + numBits / NetUtils.NUM_BITS_IN_A_BYTE
172 : numBits / NetUtils.NUM_BITS_IN_A_BYTE;
173 startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
174 byte[] bytes = new byte[numBytes];
179 checkExceptions(data, startOffset, numBits);
181 if (extraOffsetBits == 0) {
182 if (extranumBits == 0) {
183 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
186 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
187 bytes[numBytes - 1] = (byte) (data[startByteOffset + numBytes - 1] & getMSBMask(extranumBits));
193 for (index = 0; index < numBits / NetUtils.NUM_BITS_IN_A_BYTE; index++) {
194 // Reading numBytes starting from offset
195 valfromcurr = data[startByteOffset + index] & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
196 valfromnext = data[startByteOffset + index + 1] & getMSBMask(extraOffsetBits);
197 bytes[index] = (byte) (valfromcurr << extraOffsetBits
198 | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
200 // Now adding the rest of the bits if any
201 if (extranumBits != 0) {
202 if (extranumBits < NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
203 valfromnext = (byte) (data[startByteOffset + index] & getMSBMask(extranumBits) >> extraOffsetBits);
204 bytes[index] = (byte) (valfromnext << extraOffsetBits);
205 } else if (extranumBits == NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits) {
206 valfromcurr = data[startByteOffset + index]
207 & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
208 bytes[index] = (byte) (valfromcurr << extraOffsetBits);
210 valfromcurr = data[startByteOffset + index]
211 & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
212 valfromnext = data[startByteOffset + index + 1]
213 & getMSBMask(extranumBits - (NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits));
214 bytes[index] = (byte) (valfromcurr << extraOffsetBits
215 | valfromnext >> NetUtils.NUM_BITS_IN_A_BYTE - extraOffsetBits);
220 // Aligns the bits to LSB
221 return shiftBitsToLSB(bytes, numBits);
225 * Store a byte in {@code data}, starting at {@code startOffset} (in bits from MSB).
228 * to set the input byte
230 * byte to be inserted
232 * offset of data[] to start inserting byte from
233 * @throws BufferException
234 * when the input, startOffset and numBits are not congruent
235 * with the data buffer size
237 static void setByte(final byte[] data, final byte input, final int startOffset)
238 throws BufferException {
239 copyBitsFromLsb(data, new byte[] { input }, startOffset, Byte.SIZE);
243 * Bits are expected to be stored in the input byte array from LSB.
246 * to set the input byte
248 * bytes to be inserted
250 * offset of data[] to start inserting byte from
252 * number of bits of input to be inserted into data[]
253 * @throws BufferException
254 * when the startOffset and numBits parameters are not congruent
255 * with data and input buffers' size
256 * @deprecated Use {@link #copyBitsFromLsb(byte[], byte[], int, int)} instead.
259 public static void setBytes(final byte[] data, final byte[] input, final int startOffset, final int numBits)
260 throws BufferException {
261 copyBitsFromLsb(data, input, startOffset, numBits);
265 * Copy {@code count} bits from {@code src} to {@code dest}, starting at {@code startOffset} in {@code dest}.
266 * Bits are copied from the low end of the source.
268 * @param dest The destination byte array.
269 * @param src The source byte array.
270 * @param startOffset The source offset (in bits, counted from the MSB) in the destination byte array.
271 * @param count The number of bits to copy.
273 * @throws BufferException if the destination byte array can't fit the requested number of bits at the requested
276 static void copyBitsFromLsb(final byte[] dest, final byte[] src, final int startOffset, final int count)
277 throws BufferException {
278 checkExceptions(dest, startOffset, count);
279 copyBits(src, dest, count, src.length * Byte.SIZE - count, startOffset);
283 * Copy {@code count} bits from {@code src} to {@code dest}, starting at {@code startOffset} in {@code dest}.
284 * Bits are copied from the low end of the source.
286 * @param dest The destination byte array.
287 * @param src The source byte array.
288 * @param startOffset The source offset (in bits, counted from the MSB) in the destination byte array.
289 * @param count The number of bits to copy.
291 * @throws BufferException if the destination byte array can't fit the requested number of bits at the requested
294 static void copyBitsFromMsb(final byte[] dest, final byte[] src, final int startOffset, final int count)
295 throws BufferException {
296 checkExceptions(dest, startOffset, count);
297 copyBits(src, dest, count, 0, startOffset);
300 private static void copyBits(final byte[] src, final byte[] dest, final int count,
301 int srcBitIndex, int destBitIndex) {
302 int bitsRemaining = count;
303 while (bitsRemaining > 0) {
304 // How many bits can we, and do we need to, write, this time round?
305 int bitsToCopy = bitsRemaining % Byte.SIZE;
306 if (bitsToCopy == 0) {
307 bitsToCopy = Byte.SIZE;
309 int targetByteIndex = destBitIndex / Byte.SIZE;
310 int targetBitIndexInByte = destBitIndex % Byte.SIZE;
311 if (targetBitIndexInByte > 0 && Byte.SIZE - targetBitIndexInByte < bitsToCopy) {
312 // We can't write that many bits
313 bitsToCopy = Byte.SIZE - targetBitIndexInByte;
315 int sourceByteIndex = srcBitIndex / Byte.SIZE;
316 int sourceBitIndexInByte = srcBitIndex % Byte.SIZE;
317 if (sourceBitIndexInByte > 0 && Byte.SIZE - sourceBitIndexInByte < bitsToCopy) {
318 // We can't read that many bits
319 bitsToCopy = Byte.SIZE - sourceBitIndexInByte;
323 if (sourceByteIndex >= src.length || targetByteIndex >= dest.length) {
327 if (bitsToCopy == Byte.SIZE) {
329 dest[targetByteIndex] = src[sourceByteIndex];
331 // We need to mask and shift
332 // Read the target *byte* and keep the bits we're not going to touch
335 if (targetBitIndexInByte > 0) {
336 targetMask |= getMSBMask(targetBitIndexInByte);
338 if (targetBitIndexInByte + bitsToCopy < Byte.SIZE) {
339 targetMask |= getLSBMask(Byte.SIZE - (targetBitIndexInByte + bitsToCopy));
340 // We'll need to shift left
341 sourceShift = Byte.SIZE - (targetBitIndexInByte + bitsToCopy);
343 final byte target = (byte) (dest[targetByteIndex] & targetMask);
345 // Read the source *byte* and keep the bits we need
346 byte sourceMask = -1;
347 if (sourceBitIndexInByte > 0) {
348 sourceMask &= ~getMSBMask(sourceBitIndexInByte);
350 if (sourceBitIndexInByte + bitsToCopy < Byte.SIZE) {
351 sourceMask &= ~getLSBMask(Byte.SIZE - (sourceBitIndexInByte + bitsToCopy));
352 // We'll need to shift right
353 sourceShift -= Byte.SIZE - (sourceBitIndexInByte + bitsToCopy);
355 byte source = (byte) (src[sourceByteIndex] & sourceMask);
356 if (sourceShift < 0) {
357 source = (byte) ((source & 0xFF) >>> -sourceShift);
358 } else if (sourceShift > 0) {
359 source <<= sourceShift;
363 dest[targetByteIndex] = (byte) (target | source);
366 // All done, update indexes
367 bitsRemaining -= bitsToCopy;
368 srcBitIndex += bitsToCopy;
369 destBitIndex += bitsToCopy;
374 * Returns numBits 1's in the MSB position.
376 public static int getMSBMask(final int numBits) {
378 for (int i = 0; i < numBits; i++) {
379 mask = mask | 1 << 7 - i;
385 * Returns numBits 1's in the LSB position.
387 public static int getLSBMask(final int numBits) {
389 for (int i = 0; i < numBits; i++) {
390 mask = mask | 1 << i;
396 * Returns the numerical value of the byte array passed.
398 * @return long - numerical value of byte array passed
400 public static long toNumber(final byte[] array) {
402 for (byte anArray : array) {
407 ret = ret << Byte.SIZE | value;
413 * Returns the numerical value of the last numBits (LSB bits) of the byte array passed.
415 * @return long - numerical value of byte array passed
417 public static long toNumber(final byte[] array, final int numBits) {
418 int length = numBits / Byte.SIZE;
419 int bitsRest = numBits % Byte.SIZE;
420 int startOffset = array.length - length;
423 for (int i = Math.max(0, startOffset - 1); i < array.length; i++) {
424 int value = array[i];
425 if (i == startOffset - 1) {
426 value &= getLSBMask(bitsRest);
431 ret = ret << Byte.SIZE | value;
438 * Accepts a number as input and returns its value in byte form in LSB
439 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
440 * [00010011] [10001000].
442 public static byte[] toByteArray(final Number input) {
443 Class<? extends Number> dataType = input.getClass();
445 long longValue = input.longValue();
447 if (dataType == Byte.class || dataType == byte.class) {
449 } else if (dataType == Short.class || dataType == short.class) {
451 } else if (dataType == Integer.class || dataType == int.class) {
453 } else if (dataType == Long.class || dataType == long.class) {
456 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
459 int length = size / NetUtils.NUM_BITS_IN_A_BYTE;
460 byte[] bytes = new byte[length];
462 // Getting the bytes from input value
463 for (int i = 0; i < length; i++) {
464 bytes[i] = (byte) (longValue >> NetUtils.NUM_BITS_IN_A_BYTE * (length - i - 1) & BYTE_MASK);
470 * Accepts a number as input and returns its value in byte form in MSB
471 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
472 * [10011100] [01000000].
475 * - the number of bits to be returned
479 public static byte[] toByteArray(final Number input, final int numBits) {
480 Class<? extends Number> dataType = input.getClass();
482 long longValue = input.longValue();
484 if (dataType == Short.class) {
486 } else if (dataType == Integer.class) {
488 } else if (dataType == Long.class) {
491 throw new IllegalArgumentException("Parameter must one of the following: Short/Int/Long\n");
494 int length = size / NetUtils.NUM_BITS_IN_A_BYTE;
495 byte[] bytes = new byte[length];
496 byte[] inputbytes = new byte[length];
499 // Getting the bytes from input value
500 for (int i = 0; i < length; i++) {
501 bytes[i] = (byte) (longValue >> NetUtils.NUM_BITS_IN_A_BYTE * (length - i - 1) & BYTE_MASK);
504 if (bytes[0] == 0 && dataType == Long.class || bytes[0] == 0 && dataType == Integer.class) {
506 for (index = 0; index < length; ++index) {
507 if (bytes[index] != 0) {
508 bytes[0] = bytes[index];
512 System.arraycopy(bytes, index, inputbytes, 0, length - index);
513 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
515 System.arraycopy(bytes, 0, inputbytes, 0, length);
518 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
524 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
525 * aligned byte array.
528 * It aligns the last numBits bits to the head of the byte array following
529 * them with numBits % 8 zero bits.
532 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
533 * returns: shiftedBytes = [01110111][00010000]
536 * - number of bits to be left aligned
539 public static byte[] shiftBitsToMSB(final byte[] inputBytes, final int numBits) {
540 int numBitstoShiftBy;
541 int leadZeroesMSB = 8;
543 int size = inputBytes.length;
544 byte[] shiftedBytes = new byte[size];
546 for (int i = 0; i < Byte.SIZE; i++) {
547 if ((byte) (inputBytes[0] & getMSBMask(i + 1)) != 0) {
553 if (numBits % NetUtils.NUM_BITS_IN_A_BYTE == 0) {
554 numBitstoShiftBy = 0;
556 numBitstoShiftBy = NetUtils.NUM_BITS_IN_A_BYTE - numBits % NetUtils.NUM_BITS_IN_A_BYTE < leadZeroesMSB
557 ? NetUtils.NUM_BITS_IN_A_BYTE - numBits % NetUtils.NUM_BITS_IN_A_BYTE : leadZeroesMSB;
559 if (numBitstoShiftBy == 0) {
563 if (numBits < NetUtils.NUM_BITS_IN_A_BYTE) {
564 // inputbytes.length = 1 OR read less than a byte
565 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
567 // # of bits to read from last byte
568 numEndRestBits = NetUtils.NUM_BITS_IN_A_BYTE
569 - (inputBytes.length * NetUtils.NUM_BITS_IN_A_BYTE - numBits - numBitstoShiftBy);
571 for (int i = 0; i < size - 1; i++) {
572 if (i + 1 == size - 1) {
573 if (numEndRestBits > numBitstoShiftBy) {
574 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
575 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> numEndRestBits
577 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1]
578 & getLSBMask(numEndRestBits - numBitstoShiftBy)) << numBitstoShiftBy);
580 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
581 | (inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> NetUtils.NUM_BITS_IN_A_BYTE
585 shiftedBytes[i] = (byte) (inputBytes[i] << numBitstoShiftBy
586 | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> NetUtils.NUM_BITS_IN_A_BYTE
595 * It aligns the first numBits bits to the right end of the byte array
596 * preceding them with numBits % 8 zero bits.
599 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
600 * returns: shiftedBytes = [00000111][01110001]
602 * @param inputBytes input bytes
604 * - number of bits to be right aligned
607 public static byte[] shiftBitsToLSB(final byte[] inputBytes, final int numBits) {
608 int numBytes = inputBytes.length;
609 int numBitstoShift = numBits % NetUtils.NUM_BITS_IN_A_BYTE;
610 byte[] shiftedBytes = new byte[numBytes];
614 if (numBitstoShift == 0) {
618 for (int i = 1; i < numBytes; i++) {
619 inputLsb = inputBytes[i - 1] & getLSBMask(NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
620 inputLsb = inputLsb < 0 ? inputLsb + 256 : inputLsb;
621 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
622 inputMsb = inputBytes[i] < 0 ? inputBytes[i] + 256 : inputBytes[i];
623 shiftedBytes[i] = (byte) (inputLsb << numBitstoShift
624 | inputMsb >> NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
626 inputMsb = inputBytes[0] & getMSBMask(numBitstoShift);
627 inputMsb = inputMsb < 0 ? inputMsb + 256 : inputMsb;
628 shiftedBytes[0] = (byte) (inputMsb >> NetUtils.NUM_BITS_IN_A_BYTE - numBitstoShift);
633 * Checks for overflow and underflow exceptions.
635 * @throws BufferException
636 * when the startOffset and numBits parameters are not congruent
637 * with the data buffer's size
639 public static void checkExceptions(final byte[] data, final int startOffset, final int numBits)
640 throws BufferException {
643 endOffsetByte = startOffset / NetUtils.NUM_BITS_IN_A_BYTE + numBits / NetUtils.NUM_BITS_IN_A_BYTE
644 + (numBits % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1
645 : startOffset % NetUtils.NUM_BITS_IN_A_BYTE != 0 ? 1 : 0);
646 startByteOffset = startOffset / NetUtils.NUM_BITS_IN_A_BYTE;
649 throw new BufferException("data[] is null\n");
652 if (startOffset < 0 || startByteOffset >= data.length || endOffsetByte > data.length || numBits < 0
653 || numBits > NetUtils.NUM_BITS_IN_A_BYTE * data.length) {
654 throw new BufferException("Illegal arguement/out of bound exception - data.length = " + data.length
655 + " startOffset = " + startOffset + " numBits " + numBits);