/*** * Copyright 2002-2010 jamod development team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***/ package net.wimpi.modbus.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class that implements a collection for * bits, storing them packed into bytes. * Per default the access operations will index from * the LSB (rightmost) bit. * * @author Dieter Wimberger * @version @version@ (@date@) */ public final class BitVector { private static final Logger logger = LoggerFactory.getLogger(BitVector.class); // instance attributes private int m_Size; private byte[] m_Data; private boolean m_MSBAccess = false; /** * Constructs a new <tt>BitVector</tt> instance * with a given size. * <p> * * @param size the number of bits the <tt>BitVector</tt> * should be able to hold. */ public BitVector(int size) { // store bits m_Size = size; // calculate size in bytes if ((size % 8) > 0) { size = (size / 8) + 1; } else { size = (size / 8); } m_Data = new byte[size]; }// constructor /** * Toggles the flag deciding whether the LSB * or the MSB of the byte corresponds to the * first bit (index=0). * * @param b true if LSB=0 up to MSB=7, false otherwise. */ public void toggleAccess(boolean b) { m_MSBAccess = !m_MSBAccess; }// toggleAccess /** * Tests if this <tt>BitVector</tt> has * the LSB (rightmost) as the first bit * (i.e. at index 0). * * @return true if LSB=0 up to MSB=7, false otherwise. */ public boolean isLSBAccess() { return !m_MSBAccess; }// isLSBAccess /** * Tests if this <tt>BitVector</tt> has * the MSB (leftmost) as the first bit * (i.e. at index 0). * * @return true if LSB=0 up to MSB=7, false otherwise. */ public boolean isMSBAccess() { return m_MSBAccess; }// isMSBAccess /** * Returns the <tt>byte[]</tt> which is used to store * the bits of this <tt>BitVector</tt>. * <p> * * @return the <tt>byte[]</tt> used to store the bits. */ public final byte[] getBytes() { return m_Data; }// getBytes /** * Sets the <tt>byte[]</tt> which stores * the bits of this <tt>BitVector</tt>. * <p> * * @param data a <tt>byte[]</tt>. */ public final void setBytes(byte[] data) { System.arraycopy(data, 0, m_Data, 0, data.length); }// setBytes /** * Sets the <tt>byte[]</tt> which stores * the bits of this <tt>BitVector</tt>. * <p> * * @param data a <tt>byte[]</tt>. */ public final void setBytes(byte[] data, int size) { System.arraycopy(data, 0, m_Data, 0, data.length); m_Size = size; }// setBytes /** * Returns the state of the bit at the given index of this * <tt>BitVector</tt>. * <p> * * @param index the index of the bit to be returned. * * @return true if the bit at the specified index is set, * false otherwise. * * @throws IndexOutOfBoundsException if the index is out of bounds. */ public final boolean getBit(int index) throws IndexOutOfBoundsException { index = translateIndex(index); logger.trace("Get bit #{}", index); return ((m_Data[byteIndex(index)] & (0x01 << bitIndex(index))) != 0) ? true : false; }// getBit /** * Sets the state of the bit at the given index of * this <tt>BitVector</tt>. * <p> * * @param index the index of the bit to be set. * @param b true if the bit should be set, false if it should be reset. * * @throws IndexOutOfBoundsException if the index is out of bounds. */ public final void setBit(int index, boolean b) throws IndexOutOfBoundsException { index = translateIndex(index); logger.trace("Set bit #{}", index); int value = ((b) ? 1 : 0); int byteNum = byteIndex(index); int bitNum = bitIndex(index); m_Data[byteNum] = (byte) ((m_Data[byteNum] & ~(0x01 << bitNum)) | ((value & 0x01) << bitNum)); }// setBit /** * Returns the number of bits in this <tt>BitVector</tt> * as <tt>int</tt>. * <p> * * @return the number of bits in this <tt>BitVector</tt>. */ public final int size() { return m_Size; }// size /** * Forces the number of bits in this <tt>BitVector</tt>. * * @param size * @throws IllegalArgumentException if the size exceeds * the byte[] store size multiplied by 8. */ public final void forceSize(int size) { if (size > m_Data.length * 8) { throw new IllegalArgumentException("Size exceeds byte[] store."); } else { m_Size = size; } }// forceSize /** * Returns the number of bytes used to store the * collection of bits as <tt>int</tt>. * <p> * * @return the number of bits in this <tt>BitVector</tt>. */ public final int byteSize() { return m_Data.length; }// byteSize /** * Returns a <tt>String</tt> representing the * contents of the bit collection in a way that * can be printed to a screen or log. * <p> * Note that this representation will <em>ALLWAYS</em> * show the MSB to the left and the LSB to the right * in each byte. * * @return a <tt>String</tt> representing this <tt>BitVector</tt>. */ @Override public String toString() { StringBuffer sbuf = new StringBuffer(); for (int i = 0; i < size(); i++) { int idx = doTranslateIndex(i); sbuf.append(((((m_Data[byteIndex(idx)] & (0x01 << bitIndex(idx))) != 0) ? true : false) ? '1' : '0')); if (((i + 1) % 8) == 0) { sbuf.append(" "); } } return sbuf.toString(); }// toString /** * Returns the index of the byte in the the byte array * that contains the given bit. * <p> * * @param index the index of the bit. * * @return the index of the byte where the given bit is stored. * * @throws IndexOutOfBoundsException if index is * out of bounds. */ private final int byteIndex(int index) throws IndexOutOfBoundsException { if (index < 0 || index >= m_Data.length * 8) { throw new IndexOutOfBoundsException(); } else { return index / 8; } }// byteIndex /** * Returns the index of the given bit in the byte * where it it stored. * <p> * * @param index the index of the bit. * * @return the bit index relative to the position in the byte * that stores the specified bit. * * @throws IndexOutOfBoundsException if index is * out of bounds. */ private final int bitIndex(int index) throws IndexOutOfBoundsException { if (index < 0 || index >= m_Data.length * 8) { throw new IndexOutOfBoundsException(); } else { return index % 8; } }// bitIndex private final int translateIndex(int idx) { if (m_MSBAccess) { int mod4 = idx % 4; int div4 = idx / 4; if ((div4 % 2) != 0) { // odd return (idx + ODD_OFFSETS[mod4]); } else { // straight return (idx + STRAIGHT_OFFSETS[mod4]); } } else { return idx; } }// translateIndex private static final int doTranslateIndex(int idx) { int mod4 = idx % 4; int div4 = idx / 4; if ((div4 % 2) != 0) { // odd return (idx + ODD_OFFSETS[mod4]); } else { // straight return (idx + STRAIGHT_OFFSETS[mod4]); } }// translateIndex /** * Factory method for creating a <tt>BitVector</tt> instance * wrapping the given byte data. * * @param data a byte[] containing packed bits. * @return the newly created <tt>BitVector</tt> instance. */ public static BitVector createBitVector(byte[] data, int size) { BitVector bv = new BitVector(data.length * 8); bv.setBytes(data); bv.m_Size = size; return bv; }// createBitVector /** * Factory method for creating a <tt>BitVector</tt> instance * wrapping the given byte data. * * @param data a byte[] containing packed bits. * @return the newly created <tt>BitVector</tt> instance. */ public static BitVector createBitVector(byte[] data) { BitVector bv = new BitVector(data.length * 8); bv.setBytes(data); return bv; }// createBitVector public static void main(String[] args) { BitVector test = new BitVector(24); System.out.println(test.isLSBAccess()); test.setBit(7, true); System.out.println(test.getBit(7)); test.toggleAccess(true); System.out.println(test.getBit(7)); test.toggleAccess(true); test.setBit(6, true); test.setBit(3, true); test.setBit(2, true); test.setBit(0, true); test.setBit(8, true); test.setBit(10, true); System.out.println(test); test.toggleAccess(true); System.out.println(test); test.toggleAccess(true); System.out.println(test); System.out.println(ModbusUtil.toHex(test.getBytes())); } private static final int[] ODD_OFFSETS = { -1, -3, -5, -7 }; private static final int[] STRAIGHT_OFFSETS = { 7, 5, 3, 1 }; }// class BitVector