2 * Copyright (c) 2015 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.protocol.util;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.base.Preconditions;
13 import com.google.common.primitives.UnsignedBytes;
14 import io.netty.buffer.ByteBuf;
17 * This class was created to minimize usage of Java BitSet class, as this one
18 * is hard to use within specifics of network protocols. Uses network byte
21 public final class BitArray {
23 private final int size;
25 private final byte[] backingArray;
27 private final int offset;
30 * Creates a BitArray with fixed size of bits. For sizes smaller than
31 * 8 the whole byte is allocated.
33 * @param size Number of bits relevant for this BitArray. Needs to be
36 public BitArray(final int size) {
37 Preconditions.checkArgument(size >= 1, "Minimum size is 1 bit.");
39 this.backingArray = new byte[calculateBytes(size)];
40 this.offset = (calculateBytes(this.size) * Byte.SIZE) - this.size;
43 private BitArray(final byte[] backingArray, final int size) {
44 requireNonNull(backingArray, "Byte Array cannot be null");
46 this.backingArray = (backingArray == null) ? null : backingArray.clone();
47 this.offset = (calculateBytes(this.size) * Byte.SIZE) - this.size;
51 * Returns a new BitArray created from bytes from given ByteBuf.
53 * @param buffer ByteBuf, whose readerIndex will be moved by
54 * minimum number of bytes required for the bit size.
55 * @param size Number of bits to be allocated in BitArray
56 * @return new BitArray
58 public static BitArray valueOf(final ByteBuf buffer, final int size) {
59 Preconditions.checkArgument(size >= 1, "Minimum size is 1 bit.");
60 requireNonNull(buffer, "Byte Array cannot be null");
61 final byte[] b = new byte[calculateBytes(size)];
62 buffer.readBytes(b, 0, b.length);
63 return new BitArray(b, size);
67 * Returns a new BitArray with given byte array as backing
70 * @param bytes byte array
71 * @return new BitArray
73 public static BitArray valueOf(final byte[] bytes) {
74 return new BitArray(bytes, bytes.length);
78 * Returns new BitArray with given byte as backing
81 * @param b unsigned byte
82 * @return new BitArray
84 public static BitArray valueOf(final byte b) {
85 return new BitArray(new byte[] {b}, Byte.SIZE);
89 * If the value given is TRUE, sets bit on given position.
90 * Checks for null value. Index is counted from the rightmost
91 * bit as 0 to size -1 being the leftmost bit.
93 * @param index position of bit that will be set
94 * @param value Boolean
96 public void set(final int index, final Boolean value) {
97 Preconditions.checkArgument(index < this.size, "Index out of bounds.");
98 if (value == null || value.equals(Boolean.FALSE)) {
101 final int pos = calculatePosition(index);
102 final byte b = this.backingArray[pos];
103 this.backingArray[pos] = (byte) (UnsignedBytes.toInt(b) | mask(index));
107 * Returns boolean value for a bit on specific position.
108 * Index is counted from the rightmost bit as 0 to
109 * size -1 being the leftmost bit.
111 * @param index position of bit
112 * @return boolean value
114 public boolean get(final int index) {
115 Preconditions.checkArgument(index < this.size, "Index out of bounds.");
116 final byte b = this.backingArray[calculatePosition(index)];
117 return ((byte) (UnsignedBytes.toInt(b) & mask(index))) != 0;
121 * Returns the backing byte array of this bitset
125 public byte[] array() {
126 return this.backingArray.clone();
130 * If possible, returns one byte as backing array.
134 public byte toByte() {
135 Preconditions.checkArgument(Byte.SIZE >= this.size, "Cannot put backing array to a single byte.");
136 return this.backingArray[0];
140 * Writes backing array to given ByteBuf, even if the backing array is
141 * empty, to preserve the number of allocated bits.
143 * @param buffer ByteBuf
145 public void toByteBuf(final ByteBuf buffer) {
146 buffer.writeBytes(this.backingArray);
150 * Calculates the size in bytes necessary for given number of bits.
153 * @return minimum byte size to contain the position of the bit
155 private static int calculateBytes(final int size) {
156 return (size + Byte.SIZE - 1) / Byte.SIZE;
160 * Calculates which byte in byte array is going to be affected.
162 * @param index index of the bit to be changed
163 * @return position in byte array
165 private int calculatePosition(final int index) {
166 return (index + this.offset) / Byte.SIZE;
170 * Returns a byte where only one bit is set
172 * @param index bit index within full byte array
173 * @return byte with one bit set
175 private byte mask(final int index) {
176 return (byte) (1 << ((this.size -1 - index) % Byte.SIZE));
180 public String toString() {
181 final StringBuilder b = new StringBuilder("BitArray [");
182 for (int i = 0; i < this.backingArray.length; i++) {
183 b.append(Integer.toBinaryString(UnsignedBytes.toInt(this.backingArray[i])));
184 if (i != this.backingArray.length - 1) {