blob: e93b3f2e8a173d922646687f89af06aa50beb919 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.apache.directory.api.asn1.util;
import org.apache.directory.api.i18n.I18n;
/**
* Implement the Bit String primitive type. A BitString is internally stored as
* an array of byte.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class BitString
{
/** A null MutableString */
public static final BitString EMPTY_STRING = new BitString( 1 );
/** The number of unused bits in the last byte */
private int nbUnusedBits;
/** The string is stored in a byte array */
private byte[] bytes;
/** Actual length of the byte array */
private int nbBytes;
/** Actual length of the bit string */
private int nbBits;
/**
* Creates a BitString with a specific length (length is the number of
* bits).
*
* @param length The BitString length (it's a number of bits)
*/
public BitString( int length )
{
if ( length <= 0 )
{
// This is not allowed
throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00000_NULL_OR_NEG_LENGTH_NOT_ALLOWED ) );
}
nbBits = length;
// As we store values in bytes, we must divide the length by 8
nbBytes = length / 8;
if ( ( length % 8 ) != 0 )
{
nbBytes += 1;
}
nbUnusedBits = ( 8 - ( length % 8 ) ) & 0x07;
bytes = new byte[nbBytes];
}
/**
* Creates a BitString from a byte[]. As the first byte is the number of unused bits
* in the last byte, we have to ignore it.
*
* @param bytes The value to store. The first byte contains the number of
* unused bits
*/
public BitString( byte[] bytes )
{
if ( ( bytes == null ) || ( bytes.length == 0 ) )
{
nbBits = -1;
return;
}
setData( bytes );
}
/**
* Set a new BitString in the BitString. It will replace the old BitString,
* and reset the current length with the new one.
*
* @param data The string to store
*/
public void setData( byte[] data )
{
if ( ( data == null ) || ( data.length == 0 ) )
{
nbBits = -1;
return;
}
// The first byte contains the number of unused bits
nbUnusedBits = data[0] & 0x07;
nbBytes = data.length - 1;
nbBits = ( nbBytes * 8 ) - nbUnusedBits;
this.bytes = new byte[nbBytes];
// We have to transfer the data
for ( int i = 0; i < nbBytes; i++ )
{
this.bytes[i] = data[i + 1];
}
}
/**
* Get the representation of a BitString. A first byte containing the number
* of unused bits is added
*
* @return A byte array which represent the BitString
*/
public byte[] getData()
{
byte[] copy = new byte[bytes.length + 1];
System.arraycopy( bytes, 0, copy, 1, bytes.length );
copy[0] = ( byte ) nbUnusedBits;
return copy;
}
/**
* Get the number of unused bits
*
* @return A byte which represent the number of unused bits
*/
public byte getUnusedBits()
{
return ( byte ) nbUnusedBits;
}
/**
* Set a bit at a specified position.
* The bits are stored from left to right.
* For instance, if we have 10 bits, then they are coded as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
*
* @param pos The bit to set
*/
public void setBit( int pos )
{
if ( ( pos < 0 ) || ( pos > nbBits ) )
{
throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00001_BIT_NUMBER_OUT_OF_BOUND ) );
}
int posBytes = pos >>> 3;
int bitNumber = 7 - pos % 8;
byte mask = ( byte ) ( 1 << bitNumber );
bytes[posBytes] |= mask;
}
/**
* Clear a bit at a specified position.
* The bits are stored from left to right.
* For instance, if we have 10 bits, then they are coded
* as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
*
* @param pos The bit to clear
*/
public void clearBit( int pos )
{
if ( ( pos < 0 ) || ( pos > nbBits ) )
{
throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00001_BIT_NUMBER_OUT_OF_BOUND ) );
}
int posBytes = pos >>> 3;
int bitNumber = 7 - pos % 8;
byte mask = ( byte ) ( 1 << bitNumber );
bytes[posBytes] &= ~mask;
}
/**
* Get the bit stored into the BitString at a specific position.
* The bits are stored from left to right, the LSB on the left and the
* MSB on the right.<br>
* For instance, if we have 10 bits, then they are coded as
* b0 b1 b2 b3 - b4 b5 b6 b7 - b8 b9 x x - x x x x
* <pre>
* With '1001 000x', where x is an unused bit,
* ^ ^ ^
* | | |
* | | |
* | | +----- getBit(6) = 0
* | +---------- getBit(2) = 0
* +------------ getBit(0) = 1
* </pre>
* @param pos The position of the requested bit.
*
* @return <code>true</code> if the bit is set, <code>false</code> otherwise
*/
public boolean getBit( int pos )
{
if ( pos > nbBits )
{
throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00002_CANNOT_FIND_BIT, pos, nbBits ) );
}
int posBytes = pos >>> 3;
int bitNumber = 7 - pos % 8;
byte mask = ( byte ) ( 1 << bitNumber );
int res = bytes[posBytes] & mask;
return res != 0;
}
/**
* @return The number of bits stored in this BitString
*/
public int size()
{
return nbBits;
}
/**
* Return a native String representation of the BitString.
*
* @return A String representing the BitString
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
for ( int i = 0; i < nbBits; i++ )
{
if ( getBit( i ) )
{
sb.append( '1' );
}
else
{
sb.append( '0' );
}
}
return sb.toString();
}
}