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
27 public abstract class BitBufferHelper {
28 protected static final Logger logger = LoggerFactory
29 .getLogger(BitBufferHelper.class);
31 public static final long ByteMask = 0xFF;
34 // data: array where data are stored
35 // startOffset: bit from where to start reading
36 // numBits: number of bits to read
37 // All this function return an exception if overflow or underflow
40 * Returns the first byte from the byte array
44 public static byte getByte(byte[] data) {
45 if ((data.length * NetUtils.NumBitsInAByte) > Byte.SIZE) {
47 throw new BufferException(
48 "Container is too small for the number of requested bits");
49 } catch (BufferException e) {
57 * Returns the short value for the byte array passed.
58 * Size of byte array is restricted to Short.SIZE
62 public static short getShort(byte[] data) {
63 if (data.length > Short.SIZE) {
65 throw new BufferException(
66 "Container is too small for the number of requested bits");
67 } catch (BufferException e) {
71 return (short) toNumber(data);
75 * Returns the int value for the byte array passed.
76 * Size of byte array is restricted to Integer.SIZE
78 * @return int - the integer value of byte array
80 public static int getInt(byte[] data) {
81 if (data.length > Integer.SIZE) {
83 throw new BufferException(
84 "Container is too small for the number of requested bits");
85 } catch (BufferException e) {
89 return (int) toNumber(data);
93 * Returns the long value for the byte array passed.
94 * Size of byte array is restricted to Long.SIZE
96 * @return long - the integer value of byte array
98 public static long getLong(byte[] data) {
99 if (data.length > Long.SIZE) {
101 throw new BufferException(
102 "Container is too small for the number of requested bits");
103 } catch (Exception e) {
107 return (long) toNumber(data);
111 * Returns the short value for the last numBits of the byte array passed.
112 * Size of numBits is restricted to Short.SIZE
114 * @param int - numBits
115 * @return short - the short value of byte array
117 public static short getShort(byte[] data, int numBits) {
118 if (numBits > Short.SIZE) {
120 throw new BufferException(
121 "Container is too small for the number of requested bits");
122 } catch (BufferException e) {
126 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
129 bits = BitBufferHelper.getBits(data, startOffset, numBits);
130 } catch (BufferException e) {
133 return (short) toNumber(bits, numBits);
137 * Returns the int value for the last numBits of the byte array passed.
138 * Size of numBits is restricted to Integer.SIZE
140 * @param int - numBits
141 * @return int - the integer value of byte array
143 public static int getInt(byte[] data, int numBits) {
144 if (numBits > Integer.SIZE) {
146 throw new BufferException(
147 "Container is too small for the number of requested bits");
148 } catch (BufferException e) {
152 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
155 bits = BitBufferHelper.getBits(data, startOffset, numBits);
156 } catch (BufferException e) {
159 return (int) toNumber(bits, numBits);
163 * Returns the long value for the last numBits of the byte array passed.
164 * Size of numBits is restricted to Long.SIZE
166 * @param int - numBits
167 * @return long - the integer value of byte array
169 public static long getLong(byte[] data, int numBits) {
170 if (numBits > Long.SIZE) {
172 throw new BufferException(
173 "Container is too small for the number of requested bits");
174 } catch (BufferException e) {
178 if (numBits > data.length * NetUtils.NumBitsInAByte) {
180 throw new BufferException(
181 "Trying to read more bits than contained in the data buffer");
182 } catch (BufferException e) {
186 int startOffset = data.length * NetUtils.NumBitsInAByte - numBits;
189 bits = BitBufferHelper.getBits(data, startOffset, numBits);
190 } catch (BufferException e) {
193 return (long) toNumber(bits, numBits);
197 * Reads the specified number of bits from the passed byte array
198 * starting to read from the specified offset
199 * The bits read are stored in a byte array which size is dictated
200 * by the number of bits to be stored.
201 * The bits are stored in the byte array LSB aligned.
204 * Read 7 bits at offset 10
206 * 0101000010 | 0000101 | 1111001010010101011
207 * will be returned as {0,0,0,0,0,1,0,1}
210 * @param int startOffset - offset to start fetching bits from data from
211 * @param int numBits - number of bits to be fetched from data
212 * @return byte [] - LSB aligned bits
214 * @throws BufferException
215 * when the startOffset and numBits parameters are not congruent
216 * with the data buffer size
218 public static byte[] getBits(byte[] data, int startOffset, int numBits)
219 throws BufferException {
221 int startByteOffset = 0;
222 int valfromcurr, valfromnext;
223 int extranumBits = numBits % NetUtils.NumBitsInAByte;
224 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
225 int numBytes = (numBits % NetUtils.NumBitsInAByte != 0) ? 1 + numBits
226 / NetUtils.NumBitsInAByte : numBits / NetUtils.NumBitsInAByte;
227 byte[] shiftedBytes = new byte[numBytes];
228 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
229 byte[] bytes = new byte[numBytes];
234 checkExceptions(data, startOffset, numBits);
236 if (extraOffsetBits == 0) {
237 if (extranumBits == 0) {
238 System.arraycopy(data, startByteOffset, bytes, 0, numBytes);
241 System.arraycopy(data, startByteOffset, bytes, 0, numBytes - 1);
242 bytes[numBytes - 1] = (byte) ((int) data[startByteOffset
243 + numBytes - 1] & getMSBMask(extranumBits));
247 for (i = 0; i < numBits / NetUtils.NumBitsInAByte; i++) {
248 // Reading numBytes starting from offset
249 valfromcurr = (data[startByteOffset + i])
250 & getLSBMask(NetUtils.NumBitsInAByte - extraOffsetBits);
251 valfromnext = (data[startByteOffset + i + 1])
252 & getMSBMask(extraOffsetBits);
253 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
255 // Now adding the rest of the bits if any
256 if (extranumBits != 0) {
257 if (extranumBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
258 valfromnext = (byte) (data[startByteOffset + i + 1] & ((getMSBMask(extranumBits)) >> extraOffsetBits));
259 bytes[i] = (byte) (valfromnext << extraOffsetBits);
260 } else if (extranumBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
261 valfromcurr = (data[startByteOffset + i])
262 & getLSBMask(NetUtils.NumBitsInAByte
264 bytes[i] = (byte) (valfromcurr << extraOffsetBits);
266 valfromcurr = (data[startByteOffset + i])
267 & getLSBMask(NetUtils.NumBitsInAByte
269 valfromnext = (data[startByteOffset + i + 1])
270 & (getMSBMask(extranumBits
271 - (NetUtils.NumBitsInAByte - extraOffsetBits)));
272 bytes[i] = (byte) (valfromcurr << (extraOffsetBits) | (valfromnext >> (NetUtils.NumBitsInAByte - extraOffsetBits)));
277 // Aligns the bits to LSB
278 shiftedBytes = shiftBitsToLSB(bytes, numBits);
283 // data: array where data will be stored
284 // input: the data that need to be stored in the data array
285 // startOffset: bit from where to start writing
286 // numBits: number of bits to read
289 * Bits are expected to be stored in the input byte array from LSB
290 * @param byte[] - data to set the input byte
291 * @param byte - input byte to be inserted
292 * @param startOffset - offset of data[] to start inserting byte from
293 * @param numBits - number of bits of input to be inserted into data[]
295 * @throws BufferException
296 * when the input, startOffset and numBits are not congruent
297 * with the data buffer size
299 public static void setByte(byte[] data, byte input, int startOffset,
300 int numBits) throws BufferException {
301 byte[] inputByteArray = new byte[1];
302 Arrays.fill(inputByteArray, 0, 1, input);
303 setBytes(data, inputByteArray, startOffset, numBits);
307 * Bits are expected to be stored in the input byte array from LSB
308 * @param byte[] - data to set the input byte
309 * @param byte[] - input bytes to be inserted
310 * @param startOffset - offset of data[] to start inserting byte from
311 * @param numBits - number of bits of input to be inserted into data[]
313 * @throws BufferException
314 * when the startOffset and numBits parameters are not congruent
315 * with data and input buffers' size
317 public static void setBytes(byte[] data, byte[] input, int startOffset,
318 int numBits) throws BufferException {
319 checkExceptions(data, startOffset, numBits);
320 insertBits(data, input, startOffset, numBits);
324 * Returns numBits 1's in the MSB position
329 public static int getMSBMask(int numBits) {
331 for (int i = 0; i < numBits; i++) {
332 mask = mask | (1 << (7 - i));
338 * Returns numBits 1's in the LSB position
343 public static int getLSBMask(int numBits) {
345 for (int i = 0; i < numBits; i++) {
346 mask = mask | (1 << i);
352 * Returns the numerical value of the byte array passed
354 * @param byte[] - array
355 * @return long - numerical value of byte array passed
357 static public long toNumber(byte[] array) {
359 long length = array.length;
361 for (int i = 0; i < length; i++) {
366 | (long) ((long) value << ((length - i - 1) * NetUtils.NumBitsInAByte));
372 * Returns the numerical value of the last numBits (LSB bits) of the byte
375 * @param byte[] - array
376 * @param int - numBits
377 * @return long - numerical value of byte array passed
379 static public long toNumber(byte[] array, int numBits) {
380 int length = numBits / NetUtils.NumBitsInAByte;
381 int bitsRest = numBits % NetUtils.NumBitsInAByte;
382 int startOffset = array.length - length;
386 value = array[startOffset - 1] & getLSBMask(bitsRest);
387 value = (array[startOffset - 1] < 0) ? (array[startOffset - 1] + 256)
388 : array[startOffset - 1];
390 | (value << ((array.length - startOffset) * NetUtils.NumBitsInAByte));
392 for (int i = startOffset; i < array.length; i++) {
397 | (long) ((long) value << ((array.length - i - 1) * NetUtils.NumBitsInAByte));
404 * Accepts a number as input and returns its value in byte form in LSB
405 * aligned form example: input = 5000 [1001110001000] bytes = 19, -120
406 * [00010011] [10001000]
413 public static byte[] toByteArray(Number input) {
414 Class<? extends Number> dataType = input.getClass();
416 long longValue = input.longValue();
418 if (dataType == Byte.class || dataType == byte.class) {
420 } else if (dataType == Short.class || dataType == short.class) {
422 } else if (dataType == Integer.class || dataType == int.class) {
424 } else if (dataType == Long.class || dataType == long.class) {
427 throw new IllegalArgumentException(
428 "Parameter must one of the following: Short/Int/Long\n");
431 int length = size / NetUtils.NumBitsInAByte;
432 byte bytes[] = new byte[length];
434 // Getting the bytes from input value
435 for (int i = 0; i < length; i++) {
436 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
437 - i - 1))) & ByteMask);
443 * Accepts a number as input and returns its value in byte form in MSB
444 * aligned form example: input = 5000 [1001110001000] bytes = -114, 64
445 * [10011100] [01000000]
449 * @param int numBits - the number of bits to be returned
453 public static byte[] toByteArray(Number input, int numBits) {
454 Class<? extends Number> dataType = input.getClass();
456 long longValue = input.longValue();
458 if (dataType == Short.class) {
460 } else if (dataType == Integer.class) {
462 } else if (dataType == Long.class) {
465 throw new IllegalArgumentException(
466 "Parameter must one of the following: Short/Int/Long\n");
469 int length = size / NetUtils.NumBitsInAByte;
470 byte bytes[] = new byte[length];
471 byte[] inputbytes = new byte[length];
474 // Getting the bytes from input value
475 for (int i = 0; i < length; i++) {
476 bytes[i] = (byte) ((longValue >> (NetUtils.NumBitsInAByte * (length
477 - i - 1))) & ByteMask);
480 if ((bytes[0] == 0 && dataType == Long.class)
481 || (bytes[0] == 0 && dataType == Integer.class)) {
483 for (index = 0; index < length; ++index) {
484 if (bytes[index] != 0) {
485 bytes[0] = bytes[index];
489 System.arraycopy(bytes, index, inputbytes, 0, length - index);
490 Arrays.fill(bytes, length - index + 1, length - 1, (byte) 0);
492 System.arraycopy(bytes, 0, inputbytes, 0, length);
495 shiftedBytes = shiftBitsToMSB(inputbytes, numBits);
501 * Takes an LSB aligned byte array and returned the LSB numBits in a MSB
509 * It aligns the last numBits bits to the head of the byte array following
510 * them with numBits % 8 zero bits.
512 * Example: For inputbytes = [00000111][01110001] and numBits = 12 it
513 * returns: shiftedBytes = [01110111][00010000]
515 * @param byte[] inputBytes
516 * @param int numBits - number of bits to be left aligned
519 public static byte[] shiftBitsToMSB(byte[] inputBytes, int numBits) {
520 int numBitstoShiftBy = 0, leadZeroesMSB = 8, numEndRestBits = 0;
521 int size = inputBytes.length;
522 byte[] shiftedBytes = new byte[size];
525 for (i = 0; i < Byte.SIZE; i++) {
526 if (((byte) (inputBytes[0] & getMSBMask(i + 1))) != 0) {
532 if (numBits % NetUtils.NumBitsInAByte == 0) {
533 numBitstoShiftBy = 0;
535 numBitstoShiftBy = ((NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte)) < leadZeroesMSB) ? (NetUtils.NumBitsInAByte - (numBits % NetUtils.NumBitsInAByte))
538 if (numBitstoShiftBy == 0) {
542 if (numBits < NetUtils.NumBitsInAByte) {
543 // inputbytes.length = 1 OR read less than a byte
544 shiftedBytes[0] = (byte) ((inputBytes[0] & getLSBMask(numBits)) << numBitstoShiftBy);
546 // # of bits to read from last byte
547 numEndRestBits = NetUtils.NumBitsInAByte
548 - (inputBytes.length * NetUtils.NumBitsInAByte - numBits - numBitstoShiftBy);
550 for (i = 0; i < (size - 1); i++) {
551 if ((i + 1) == (size - 1)) {
552 if (numEndRestBits > numBitstoShiftBy) {
553 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (numEndRestBits - numBitstoShiftBy)));
554 shiftedBytes[i + 1] = (byte) ((inputBytes[i + 1] & getLSBMask(numEndRestBits
555 - numBitstoShiftBy)) << numBitstoShiftBy);
557 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | ((inputBytes[i + 1] & getMSBMask(numEndRestBits)) >> (NetUtils.NumBitsInAByte - numEndRestBits)));
559 shiftedBytes[i] = (byte) ((inputBytes[i] << numBitstoShiftBy) | (inputBytes[i + 1] & getMSBMask(numBitstoShiftBy)) >> (NetUtils.NumBitsInAByte - numBitstoShiftBy));
567 * It aligns the first numBits bits to the right end of the byte array
568 * preceding them with numBits % 8 zero bits.
570 * Example: For inputbytes = [01110111][00010000] and numBits = 12 it
571 * returns: shiftedBytes = [00000111][01110001]
573 * @param byte[] inputBytes
574 * @param int numBits - number of bits to be right aligned
577 public static byte[] shiftBitsToLSB(byte[] inputBytes, int numBits) {
578 int numBytes = inputBytes.length;
579 int numBitstoShift = numBits % NetUtils.NumBitsInAByte;
580 byte[] shiftedBytes = new byte[numBytes];
581 int inputLsb = 0, inputMsb = 0;
583 if (numBitstoShift == 0) {
587 for (int i = 1; i < numBytes; i++) {
588 inputLsb = inputBytes[i - 1]
589 & getLSBMask(NetUtils.NumBitsInAByte - numBitstoShift);
590 inputLsb = (inputLsb < 0) ? (inputLsb + 256) : inputLsb;
591 inputMsb = inputBytes[i] & getMSBMask(numBitstoShift);
592 inputMsb = (inputBytes[i] < 0) ? (inputBytes[i] + 256)
594 shiftedBytes[i] = (byte) ((inputLsb << numBitstoShift) | (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift)));
596 inputMsb = inputBytes[0] & (getMSBMask(numBitstoShift));
597 inputMsb = (inputMsb < 0) ? (inputMsb + 256) : inputMsb;
598 shiftedBytes[0] = (byte) (inputMsb >> (NetUtils.NumBitsInAByte - numBitstoShift));
603 * Insert in the data buffer at position dictated by the offset the number
604 * of bits specified from the input data byte array. The input byte array
605 * has the bits stored starting from the LSB
608 * @param byte[] inputdata
609 * @param int startOffset
612 public static void insertBits(byte[] data, byte[] inputdataLSB,
613 int startOffset, int numBits) {
614 byte[] inputdata = shiftBitsToMSB(inputdataLSB, numBits); // Align to
618 int numBytes = numBits / NetUtils.NumBitsInAByte;
619 int startByteOffset = startOffset / NetUtils.NumBitsInAByte;
620 int extraOffsetBits = startOffset % NetUtils.NumBitsInAByte;
621 int extranumBits = numBits % NetUtils.NumBitsInAByte;
622 int RestBits = numBits % NetUtils.NumBitsInAByte;
623 int InputMSBbits = 0, InputLSBbits = 0;
630 if (extraOffsetBits == 0) {
631 if (extranumBits == 0) {
632 numBytes = numBits / NetUtils.NumBitsInAByte;
633 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
635 System.arraycopy(inputdata, 0, data, startByteOffset, numBytes);
636 data[startByteOffset + numBytes] = (byte) (data[startByteOffset
637 + numBytes] | (inputdata[numBytes] & getMSBMask(extranumBits)));
640 for (i = 0; i < numBytes; i++) {
642 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
643 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
644 - extraOffsetBits)));
645 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
646 : InputMSBbits + 256;
647 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
648 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
649 InputMSBbits = InputLSBbits = 0;
651 if (RestBits < (NetUtils.NumBitsInAByte - extraOffsetBits)) {
653 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
654 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(RestBits)));
655 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
656 : InputMSBbits + 256;
657 data[startByteOffset + i] = (byte) ((data[startByteOffset + i])
658 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
659 } else if (RestBits == (NetUtils.NumBitsInAByte - extraOffsetBits)) {
661 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
662 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
663 - extraOffsetBits)));
664 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
665 : InputMSBbits + 256;
666 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
667 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
670 InputLSBbits = (inputdata[i - 1] & getLSBMask(extraOffsetBits));
671 InputMSBbits = (byte) (inputdata[i] & (getMSBMask(NetUtils.NumBitsInAByte
672 - extraOffsetBits)));
673 InputMSBbits = (InputMSBbits >= 0) ? InputMSBbits
674 : InputMSBbits + 256;
675 data[startByteOffset + i] = (byte) (data[startByteOffset + i]
676 | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)) | (InputMSBbits >> extraOffsetBits));
678 InputLSBbits = (inputdata[i] & (getLSBMask(RestBits
679 - (NetUtils.NumBitsInAByte - extraOffsetBits)) << (NetUtils.NumBitsInAByte - RestBits)));
680 data[startByteOffset + i + 1] = (byte) (data[startByteOffset
681 + i + 1] | (InputLSBbits << (NetUtils.NumBitsInAByte - extraOffsetBits)));
687 * Checks for overflow and underflow exceptions
691 * @throws PacketException when the startOffset and numBits parameters
692 * are not congruent with the data buffer's size
694 public static void checkExceptions(byte[] data, int startOffset, int numBits)
695 throws BufferException {
698 endOffsetByte = startOffset
699 / NetUtils.NumBitsInAByte
701 / NetUtils.NumBitsInAByte
702 + ((numBits % NetUtils.NumBitsInAByte != 0) ? 1 : ((startOffset
703 % NetUtils.NumBitsInAByte != 0) ? 1 : 0));
704 startByteOffset = startOffset / NetUtils.NumBitsInAByte;
707 throw new BufferException("data[] is null\n");
710 if ((startOffset < 0) || (startByteOffset >= data.length)
711 || (endOffsetByte > data.length) || (numBits < 0)
712 || (numBits > NetUtils.NumBitsInAByte * data.length)) {
713 throw new BufferException(
714 "Illegal arguement/out of bound exception - data.length = "
715 + data.length + " startOffset = " + startOffset
716 + " numBits " + numBits);