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.sal.packet;
14 import java.util.Arrays;
16 import org.opendaylight.controller.sal.utils.NetUtils;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
21 * BitBufferHelper class that provides utility methods to
22 * - fetch specific bits from a serialized stream of bits
23 * - convert bits to primitive data type - like short, int, long
24 * - store bits in specified location in stream of bits
25 * - convert primitive data types to stream of bits
28 public abstract class BitBufferHelper {
29 protected static final Logger logger = LoggerFactory
30 .getLogger(BitBufferHelper.class);
32 public static final long ByteMask = 0xFF;
35 // data: array where data are stored
36 // startOffset: bit from where to start reading
37 // numBits: number of bits to read
38 // All this function return an exception if overflow or underflow
41 * Returns the first byte from the byte array
45 public static byte getByte(byte[] data) {
46 if ((data.length * NetUtils.NumBitsInAByte) > Byte.SIZE) {
48 throw new BufferException(
49 "Container is too small for the number of requested bits");
50 } catch (BufferException e) {
58 * Returns the short value for the byte array passed.
59 * Size of byte array is restricted to Short.SIZE
63 public static short getShort(byte[] data) {
64 if (data.length > Short.SIZE) {
66 throw new BufferException(
67 "Container is too small for the number of requested bits");
68 } catch (BufferException e) {
72 return (short) toNumber(data);
76 * Returns the int value for the byte array passed.
77 * Size of byte array is restricted to Integer.SIZE
79 * @return int - the integer value of byte array
81 public static int getInt(byte[] data) {
82 if (data.length > Integer.SIZE) {
84 throw new BufferException(
85 "Container is too small for the number of requested bits");
86 } catch (BufferException e) {
90 return (int) toNumber(data);
94 * Returns the long value for the byte array passed.
95 * Size of byte array is restricted to Long.SIZE
97 * @return long - the integer value of byte array
99 public static long getLong(byte[] data) {
100 if (data.length > Long.SIZE) {
102 throw new BufferException(
103 "Container is too small for the number of requested bits");
104 } catch (Exception e) {
108 return (long) toNumber(data);
112 * Returns the short value for the last numBits of the byte array passed.
113 * Size of numBits is restricted to Short.SIZE
115 * @param int - numBits
116 * @return short - the short value of byte array
118 public static short getShort(byte[] data, int numBits) {
119 if (numBits > Short.SIZE) {
121 throw new BufferException(
122 "Container is too small for the number of requested bits");
123 } catch (BufferException e) {
127 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
130 bits = BitBufferHelper.getBits(data, startOffset, numBits);
131 } catch (BufferException e) {
134 return (short) toNumber(bits, numBits);
138 * Returns the int value for the last numBits of the byte array passed.
139 * Size of numBits is restricted to Integer.SIZE
141 * @param int - numBits
142 * @return int - the integer value of byte array
144 public static int getInt(byte[] data, int numBits) {
145 if (numBits > Integer.SIZE) {
147 throw new BufferException(
148 "Container is too small for the number of requested bits");
149 } catch (BufferException e) {
153 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
156 bits = BitBufferHelper.getBits(data, startOffset, numBits);
157 } catch (BufferException e) {
160 return (int) toNumber(bits, numBits);
164 * Returns the long value for the last numBits of the byte array passed.
165 * Size of numBits is restricted to Long.SIZE
167 * @param int - numBits
168 * @return long - the integer value of byte array
170 public static long getLong(byte[] data, int numBits) {
171 if (numBits > Long.SIZE) {
173 throw new BufferException(
174 "Container is too small for the number of requested bits");
175 } catch (BufferException e) {
179 if (numBits > data.length * NetUtils.NumBitsInAByte) {
181 throw new BufferException(
182 "Trying to read more bits than contained in the data buffer");
183 } catch (BufferException e) {
187 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
190 bits = BitBufferHelper.getBits(data, startOffset, numBits);
191 } catch (BufferException e) {
194 return (long) toNumber(bits, numBits);
198 * Reads the specified number of bits from the passed byte array
199 * starting to read from the specified offset
200 * The bits read are stored in a byte array which size is dictated
201 * by the number of bits to be stored.
202 * The bits are stored in the byte array LSB aligned.
205 * Read 7 bits at offset 10
207 * 0101000010 | 0000101 | 1111001010010101011
208 * will be returned as {0,0,0,0,0,1,0,1}
211 * @param int startOffset - offset to start fetching bits from data from
212 * @param int numBits - number of bits to be fetched from data
213 * @return byte [] - LSB aligned bits
215 * @throws BufferException
216 * when the startOffset and numBits parameters are not congruent
217 * with the data buffer size
219 public static byte[] getBits(byte[] data, int startOffset, int numBits)
220 throws BufferException {
222 int startByteOffset = 0;
223 int valfromcurr, valfromnext;
224 int extranumBits = numBits % NetUtils.NumBitsInAByte;
225 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
226 int numBytes = (numBits % NetUtils.NumBitsInAByte != 0) ? 1 + numBits
227 / NetUtils.NumBitsInAByte : numBits / NetUtils.NumBitsInAByte;
228 byte[] shiftedBytes = new byte[numBytes];
229 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
230 byte[] bytes = new byte[numBytes];
235 checkExceptions(data, startOffset, numBits);
237 if (extraOffsetBits == 0) {
238 if (extranumBits == 0) {
239 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
242 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
243 bytes[numBytes - 1] = (byte) ((int) data[startByteOffset
244 + numBytes - 1] & getMSBMask(extranumBits));
248 for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) {
249 // Reading numBytes starting from offset
250 valfromcurr = (data[startByteOffset + i])
251 & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
252 valfromnext = (data[startByteOffset + i + 1])
253 & getMSBMask(extraOffsetBits);
254 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
256 // Now adding the rest of the bits if any
257 if (extranumBits != 0) {
258 if (extranumBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
259 valfromnext = (byte) (data[startByteOffset + i] & ((getMSBMask(extranumBits)) >> extraOffsetBits));
260 bytes[i] = (byte) (valfromnext << extraOffsetBits);
261 } else if (extranumBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
262 valfromcurr = (data[startByteOffset + i])
263 & getLSBMask(NetUtils.NumBitsInAByte
265 bytes[i] = (byte) (valfromcurr << extraOffsetBits);
267 valfromcurr = (data[startByteOffset + i])
268 & getLSBMask(NetUtils.NumBitsInAByte
270 valfromnext = (data[startByteOffset + i + 1])
271 & (getMSBMask(extranumBits
272 - (NetUtils.NumBitsInAByte - extraOffsetBits)));
273 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
278 // Aligns the bits to LSB
279 shiftedBytes = shiftBitsToLSB(bytes, numBits);
284 // data: array where data will be stored
285 // input: the data that need to be stored in the data array
286 // startOffset: bit from where to start writing
287 // numBits: number of bits to read
290 * Bits are expected to be stored in the input byte array from LSB
291 * @param byte[] - data to set the input byte
292 * @param byte - input byte to be inserted
293 * @param startOffset - offset of data[] to start inserting byte from
294 * @param numBits - number of bits of input to be inserted into data[]
296 * @throws BufferException
297 * when the input, startOffset and numBits are not congruent
298 * with the data buffer size
300 public static void setByte(byte[] data, byte input, int startOffset,
301 int numBits) throws BufferException {
302 byte[] inputByteArray = new byte[1];
303 Arrays.fill(inputByteArray, 0, 1, input);
304 setBytes(data, inputByteArray, startOffset, numBits);
308 * Bits are expected to be stored in the input byte array from LSB
309 * @param byte[] - data to set the input byte
310 * @param byte[] - input bytes to be inserted
311 * @param startOffset - offset of data[] to start inserting byte from
312 * @param numBits - number of bits of input to be inserted into data[]
314 * @throws BufferException
315 * when the startOffset and numBits parameters are not congruent
316 * with data and input buffers' size
318 public static void setBytes(byte[] data, byte[] input, int startOffset,
319 int numBits) throws BufferException {
320 checkExceptions(data, startOffset, numBits);
321 insertBits(data, input, startOffset, numBits);
325 * Returns numBits 1's in the MSB position
330 public static int getMSBMask(int numBits) {
332 for (int i = 0; i < numBits; i++) {
333 mask = mask | (1 << (7 - i));
339 * Returns numBits 1's in the LSB position
344 public static int getLSBMask(int numBits) {
346 for (int i = 0; i < numBits; i++) {
347 mask = mask | (1 << i);
353 * Returns the numerical value of the byte array passed
355 * @param byte[] - array
356 * @return long - numerical value of byte array passed
358 static public long toNumber(byte[] array) {
360 long length = array.length;
362 for (int i = 0; i < length; i++) {
367 | (long) ((long) value << ((length - i - 1) * NetUtils.NumBitsInAByte));
373 * Returns the numerical value of the last numBits (LSB bits) of the byte
376 * @param byte[] - array
377 * @param int - numBits
378 * @return long - numerical value of byte array passed
380 static public long toNumber(byte[] array, int numBits) {
381 int length = numBits / NetUtils.NumBitsInAByte;
382 int bitsRest = numBits % NetUtils.NumBitsInAByte;
383 int startOffset = array.length - length;
387 value = array[startOffset - 1] & getLSBMask(bitsRest);
388 value = (array[startOffset - 1] < 0) ? (array[startOffset - 1] + 256)
389 : array[startOffset - 1];
391 | (value << ((array.length - startOffset) * NetUtils.NumBitsInAByte));
393 for (int i = startOffset; i < array.length; i++) {
398 | (long) ((long) value << ((array.length - i - 1) * NetUtils.NumBitsInAByte));
405 * Accepts a number as input and returns its value in byte form in LSB
406 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
407 * [00010011] [10001000]
414 public static byte[] toByteArray(Number input) {
415 Class<? extends Number> dataType = input.getClass();
417 long longValue = input.longValue();
419 if (dataType == Byte.class || dataType == byte.class) {
421 } else if (dataType == Short.class || dataType == short.class) {
423 } else if (dataType == Integer.class || dataType == int.class) {
425 } else if (dataType == Long.class || dataType == long.class) {
428 throw new IllegalArgumentException(
429 "Parameter must one of the following: Short/Int/Long\n");
432 int length = size / NetUtils.NumBitsInAByte;
433 byte bytes[] = new byte[length];
435 // Getting the bytes from input value
436 for (int i = 0; i < length; i++) {
437 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
438 - i - 1))) & ByteMask);
444 * Accepts a number as input and returns its value in byte form in MSB
445 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
446 * [10011100] [01000000]
450 * @param int numBits - the number of bits to be returned
454 public static byte[] toByteArray(Number input, int numBits) {
455 Class<? extends Number> dataType = input.getClass();
457 long longValue = input.longValue();
459 if (dataType == Short.class) {
461 } else if (dataType == Integer.class) {
463 } else if (dataType == Long.class) {
466 throw new IllegalArgumentException(
467 "Parameter must one of the following: Short/Int/Long\n");
470 int length = size / NetUtils.NumBitsInAByte;
471 byte bytes[] = new byte[length];
472 byte[] inputbytes = new byte[length];
475 // Getting the bytes from input value
476 for (int i = 0; i < length; i++) {
477 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
478 - i - 1))) & ByteMask);
481 if ((bytes[0] == 0 && dataType == Long.class)
482 || (bytes[0] == 0 && dataType == Integer.class)) {
484 for (index = 0; index < length; ++index) {
485 if (bytes[index] != 0) {
486 bytes[0] = bytes[index];
490 System.arraycopy(bytes, index, inputbytes, 0, length - index);
491 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
493 System.arraycopy(bytes, 0, inputbytes, 0, length);
496 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
502 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
510 * It aligns the last numBits bits to the head of the byte array following
511 * them with numBits % 8 zero bits.
513 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
514 * returns: shiftedBytes = [01110111][00010000]
516 * @param byte[] inputBytes
517 * @param int numBits - number of bits to be left aligned
520 public static byte[] shiftBitsToMSB(byte[] inputBytes, int numBits) {
521 int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0;
522 int size = inputBytes.length;
523 byte[] shiftedBytes = new byte[size];
526 for (i = 0; i < Byte.SIZE; i++) {
527 if (((byte) (inputBytes[0] & getMSBMask(i + 1))) != 0) {
533 if (numBits % NetUtils.NumBitsInAByte == 0) {
534 numBitstoShiftBy = 0;
536 numBitstoShiftBy = ((NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) < leadZeroesMSB) ? (NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte))
539 if (numBitstoShiftBy == 0) {
543 if (numBits < NetUtils.NumBitsInAByte) {
544 // inputbytes.length = 1 OR read less than a byte
545 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
547 // # of bits to read from last byte
548 numEndRestBits = NetUtils.NumBitsInAByte
549 - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy);
551 for (i = 0; i < (size - 1); i++) {
552 if ((i + 1) == (size - 1)) {
553 if (numEndRestBits > numBitstoShiftBy) {
554 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (numEndRestBits - numBitstoShiftBy)));
555 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1] & getLSBMask(numEndRestBits
556 - numBitstoShiftBy)) << numBitstoShiftBy);
558 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> (NetUtils.NumBitsInAByte - numEndRestBits)));
560 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (NetUtils.NumBitsInAByte - numBitstoShiftBy));
568 * It aligns the first numBits bits to the right end of the byte array
569 * preceding them with numBits % 8 zero bits.
571 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
572 * returns: shiftedBytes = [00000111][01110001]
574 * @param byte[] inputBytes
575 * @param int numBits - number of bits to be right aligned
578 public static byte[] shiftBitsToLSB(byte[] inputBytes, int numBits) {
579 int numBytes = inputBytes.length;
580 int numBitstoShift = numBits % NetUtils.NumBitsInAByte;
581 byte[] shiftedBytes = new byte[numBytes];
582 int inputLsb = 0, inputMsb = 0;
584 if (numBitstoShift == 0) {
588 for (int i = 1; i < numBytes; i++) {
589 inputLsb = inputBytes[i - 1]
590 & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift);
591 inputLsb = (inputLsb < 0) ? (inputLsb + 256) : inputLsb;
592 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
593 inputMsb = (inputBytes[i] < 0) ? (inputBytes[i] + 256)
595 shiftedBytes[i] = (byte) ((inputLsb << numBitstoShift) | (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift)));
597 inputMsb = inputBytes[0] & (getMSBMask(numBitstoShift));
598 inputMsb = (inputMsb < 0) ? (inputMsb + 256) : inputMsb;
599 shiftedBytes[0] = (byte) (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift));
604 * Insert in the data buffer at position dictated by the offset the number
605 * of bits specified from the input data byte array. The input byte array
606 * has the bits stored starting from the LSB
609 * @param byte[] inputdata
610 * @param int startOffset
613 public static void insertBits(byte[] data, byte[] inputdataLSB,
614 int startOffset, int numBits) {
615 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
619 int numBytes = numBits / NetUtils.NumBitsInAByte;
620 int startByteOffset = startOffset / NetUtils.NumBitsInAByte;
621 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
622 int extranumBits = numBits % NetUtils.NumBitsInAByte;
623 int RestBits = numBits % NetUtils.NumBitsInAByte;
624 int InputMSBbits = 0, InputLSBbits = 0;
631 if (extraOffsetBits == 0) {
632 if (extranumBits == 0) {
633 numBytes = numBits / NetUtils.NumBitsInAByte;
634 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
636 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
637 data[startByteOffset + numBytes] = (byte) (data[startByteOffset
638 + numBytes] | (inputdata[numBytes] & getMSBMask(extranumBits)));
641 for (i = 0; i < numBytes; i++) {
643 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
644 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
645 - extraOffsetBits)));
646 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
647 : InputMSBbits + 256;
648 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
649 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
650 InputMSBbits = InputLSBbits = 0;
652 if (RestBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
654 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
655 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(RestBits)));
656 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
657 : InputMSBbits + 256;
658 data[startByteOffset + i] = (byte) ((data[startByteOffset + i])
659 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
660 } else if (RestBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
662 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
663 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
664 - extraOffsetBits)));
665 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
666 : InputMSBbits + 256;
667 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
668 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
671 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
672 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
673 - extraOffsetBits)));
674 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
675 : InputMSBbits + 256;
676 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
677 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
679 InputLSBbits = (inputdata[i] & (getLSBMask(RestBits
680 - (NetUtils.NumBitsInAByte - extraOffsetBits)) << (NetUtils.NumBitsInAByte - RestBits)));
681 data[startByteOffset + i + 1] = (byte) (data[startByteOffset
682 + i + 1] | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)));
688 * Checks for overflow and underflow exceptions
692 * @throws PacketException when the startOffset and numBits parameters
693 * are not congruent with the data buffer's size
695 public static void checkExceptions(byte[] data, int startOffset, int numBits)
696 throws BufferException {
699 endOffsetByte = startOffset
700 / NetUtils.NumBitsInAByte
702 / NetUtils.NumBitsInAByte
703 + ((numBits % NetUtils.NumBitsInAByte != 0) ? 1 : ((startOffset
704 % NetUtils.NumBitsInAByte != 0) ? 1 : 0));
705 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
708 throw new BufferException("data[] is null\n");
711 if ((startOffset < 0) || (startByteOffset >= data.length)
712 || (endOffsetByte > data.length) || (numBits < 0)
713 || (numBits > NetUtils.NumBitsInAByte * data.length)) {
714 throw new BufferException(
715 "Illegal arguement/out of bound exception - data.length = "
716 + data.length + " startOffset = " + startOffset
717 + " numBits " + numBits);