View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.asn1.util;
21  
22  
23  import org.apache.directory.api.i18n.I18n;
24  
25  
26  /**
27   * Implement the Bit String primitive type. A BitString is internally stored as
28   * an array of byte.
29   *
30   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
31   */
32  public class BitString
33  {
34      /** A null MutableString */
35      public static final BitString EMPTY_STRING = new BitString( 1 );
36  
37      /** The number of unused bits in the last byte */
38      private int nbUnusedBits;
39  
40      /** The string is stored in a byte array */
41      private byte[] bytes;
42  
43      /** Actual length of the byte array */
44      private int nbBytes;
45  
46      /** Actual length of the bit string */
47      private int nbBits;
48  
49  
50      /**
51       * Creates a BitString with a specific length (length is the number of
52       * bits).
53       *
54       * @param length The BitString length (it's a number of bits)
55       */
56      public BitString( int length )
57      {
58          if ( length <= 0 )
59          {
60              // This is not allowed
61              throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00029_NULL_OR_NEG_LENGTH_NOT_ALLOWED ) );
62          }
63  
64          nbBits = length;
65  
66          // As we store values in bytes, we must divide the length by 8
67          nbBytes = length / 8;
68  
69          if ( ( length % 8 ) != 0 )
70          {
71              nbBytes += 1;
72          }
73  
74          nbUnusedBits = ( 8 - ( length % 8 ) ) & 0x07;
75  
76          bytes = new byte[nbBytes];
77      }
78  
79  
80      /**
81       * Creates a BitString from a byte[]. As the first byte is the number of unused bits
82       * in the last byte, we have to ignore it.
83       *
84       * @param bytes The value to store. The first byte contains the number of
85       * unused bits
86       */
87      public BitString( byte[] bytes )
88      {
89          if ( ( bytes == null ) || ( bytes.length == 0 ) )
90          {
91              nbBits = -1;
92              return;
93          }
94  
95          setData( bytes );
96      }
97  
98  
99      /**
100      * Set a new BitString in the BitString. It will replace the old BitString,
101      * and reset the current length with the new one.
102      *
103      * @param data The string to store
104      */
105     public void setData( byte[] data )
106     {
107         if ( ( data == null ) || ( data.length == 0 ) )
108         {
109             nbBits = -1;
110             return;
111         }
112 
113         // The first byte contains the number of unused bits
114         nbUnusedBits = data[0] & 0x07;
115         nbBytes = data.length - 1;
116         nbBits = ( nbBytes * 8 ) - nbUnusedBits;
117         this.bytes = new byte[nbBytes];
118 
119         // We have to transfer the data
120         for ( int i = 0; i < nbBytes; i++ )
121         {
122             this.bytes[i] = data[i + 1];
123         }
124     }
125 
126 
127     /**
128      * Get the representation of a BitString. A first byte containing the number
129      * of unused bits is added
130      *
131      * @return A byte array which represent the BitString
132      */
133     public byte[] getData()
134     {
135         byte[] copy = new byte[bytes.length + 1];
136 
137         System.arraycopy( bytes, 0, copy, 1, bytes.length );
138         copy[0] = ( byte ) nbUnusedBits;
139 
140         return copy;
141     }
142 
143 
144     /**
145      * Get the number of unused bits
146      *
147      * @return A byte which represent the number of unused bits
148      */
149     public byte getUnusedBits()
150     {
151         return ( byte ) nbUnusedBits;
152     }
153 
154 
155     /**
156      * Set a bit at a specified position.
157      * The bits are stored from left to right.
158      * 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
159      *
160      * @param pos The bit to set
161      */
162     public void setBit( int pos )
163     {
164         if ( ( pos < 0 ) || ( pos > nbBits ) )
165         {
166             throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00030_BIT_NUMBER_OUT_OF_BOUND ) );
167         }
168 
169         int posBytes = pos >>> 3;
170         int bitNumber = 7 - pos % 8;
171         byte mask = ( byte ) ( 1 << bitNumber );
172 
173         bytes[posBytes] |= mask;
174     }
175 
176 
177     /**
178      * Clear a bit at a specified position.
179      * The bits are stored from left to right.
180      * For instance, if we have 10 bits, then they are coded
181      * as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
182      *
183      * @param pos The bit to clear
184      */
185     public void clearBit( int pos )
186     {
187         if ( ( pos < 0 ) || ( pos > nbBits ) )
188         {
189             throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00030_BIT_NUMBER_OUT_OF_BOUND ) );
190         }
191 
192         int posBytes = pos >>> 3;
193         int bitNumber = 7 - pos % 8;
194         byte mask = ( byte ) ( 1 << bitNumber );
195 
196         bytes[posBytes] &= ~mask;
197     }
198 
199 
200     /**
201      * Get the bit stored into the BitString at a specific position.
202      * The bits are stored from left to right, the LSB on the left and the
203      * MSB on the right.<br>
204      * For instance, if we have 10 bits, then they are coded as
205      * b0 b1 b2 b3 - b4 b5 b6 b7 - b8 b9 x x - x x x x
206      * <pre>
207      * With '1001 000x', where x is an unused bit,
208      *       ^ ^    ^
209      *       | |    |
210      *       | |    |
211      *       | |    +----- getBit(6) = 0
212      *       | +---------- getBit(2) = 0
213      *       +------------ getBit(0) = 1
214      * </pre>
215      * @param pos The position of the requested bit.
216      *
217      * @return <code>true</code> if the bit is set, <code>false</code> otherwise
218      */
219     public boolean getBit( int pos )
220     {
221         if ( pos > nbBits )
222         {
223             throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00031_CANNOT_FIND_BIT, pos, nbBits ) );
224         }
225 
226         int posBytes = pos >>> 3;
227         int bitNumber = 7 - pos % 8;
228         byte mask = ( byte ) ( 1 << bitNumber );
229 
230         int res = bytes[posBytes] & mask;
231 
232         return res != 0;
233     }
234 
235 
236     /**
237      * @return The number of bits stored in this BitString
238      */
239     public int size()
240     {
241         return nbBits;
242     }
243 
244 
245     /**
246      * Return a native String representation of the BitString.
247      *
248      * @return A String representing the BitString
249      */
250     @Override
251     public String toString()
252     {
253         StringBuilder sb = new StringBuilder();
254 
255         for ( int i = 0; i < nbBits; i++ )
256         {
257             if ( getBit( i ) )
258             {
259                 sb.append( '1' );
260             }
261             else
262             {
263                 sb.append( '0' );
264             }
265         }
266 
267         return sb.toString();
268     }
269 }