001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.shared.asn1.primitives;
021
022
023 import java.io.Serializable;
024
025
026 /**
027 * Implement the Bit String primitive type. A BitString is internally stored as
028 * an array of int.
029 *
030 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
031 * @version $Rev: 664290 $, $Date: 2008-06-07 08:28:06 +0200 (Sam, 07 jui 2008) $
032 */
033 public class BitString implements Serializable
034 {
035 private static final long serialVersionUID = 1L;
036
037 // ~ Static fields/initializers
038 // -----------------------------------------------------------------
039
040 /** A null MutableString */
041 public static final BitString EMPTY_STRING = new BitString( 1 );
042
043 /**
044 * A flag to mark the OctetString as Streamed (for OctetString larger than
045 * 1024 chars)
046 */
047 // TODO implement the streaming...
048 public static final boolean STREAMED = true;
049
050 /** The default length of a BitString */
051 private static final int DEFAULT_LENGTH = 1024;
052
053 // ~ Instance fields
054 // ----------------------------------------------------------------------------
055
056 /** The number of unused ints */
057 private int nbUnusedBits;
058
059 /** Tells if the OctetString is streamed or not */
060 private boolean isStreamed;
061
062 /** The string is stored in a byte array */
063 private byte[] bytes;
064
065 /** Actual length of the byte array */
066 private int nbBytes;
067
068 /** Actual length of the bit string */
069 private int nbBits;
070
071
072 // ~ Constructors
073 // -------------------------------------------------------------------------------*
074 /**
075 * Creates a BitString with a specific length (length is the number of
076 * bits).
077 *
078 * @param length The BitString length (it's a number of bits)
079 */
080 public BitString( int length )
081 {
082 if ( length <= 0 )
083 {
084 // This is not allowed
085 throw new IndexOutOfBoundsException( "Null or negative length are not allowed" );
086 }
087
088 nbBits = length;
089
090 // As we store values in bytes, we must divide the length by 8
091 nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
092
093 nbUnusedBits = ( 8 - length % 8 ) % 8;
094
095 if ( nbBytes > DEFAULT_LENGTH )
096 {
097
098 // TODO : implement the streaming
099 isStreamed = true;
100 bytes = new byte[nbBytes];
101 }
102 else
103 {
104 isStreamed = false;
105 bytes = new byte[nbBytes];
106 }
107 }
108
109
110 /**
111 * Creates a streamed BitString with a specific length. Actually, it's just
112 * a simple BitString. TODO Implement streaming.
113 *
114 * @param length
115 * The BitString length, in number of bits
116 * @param isStreamed
117 * Tells if the BitString must be streamed or not
118 */
119 public BitString( int length, boolean isStreamed )
120 {
121 if ( length <= 0 )
122 {
123 // This is not allowed
124 throw new IndexOutOfBoundsException( "Null or negative length are not allowed" );
125 }
126
127 nbBits = length;
128 this.isStreamed = isStreamed;
129 nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
130
131 nbUnusedBits = length % 8;
132
133 if ( isStreamed )
134 {
135
136 // TODO : implement the streaming
137 bytes = new byte[nbBytes];
138 }
139 else
140 {
141 bytes = new byte[nbBytes];
142 }
143 }
144
145
146 /**
147 * Creates a BitString with a value.
148 *
149 * @param bytes The value to store. The first byte contains the number of
150 * unused bits
151 */
152 public BitString( byte[] bytes )
153 {
154 nbBytes = bytes.length - 1;
155
156 if ( nbBytes > DEFAULT_LENGTH )
157 {
158 isStreamed = true;
159
160 // It will be a streamed OctetString.
161 // TODO : implement the streaming
162 this.bytes = new byte[nbBytes];
163 }
164 else
165 {
166 isStreamed = false;
167
168 this.bytes = new byte[nbBytes];
169 }
170
171 setBytes( bytes, nbBytes );
172 }
173
174
175 // ~ Methods
176 // ------------------------------------------------------------------------------------
177
178 /**
179 * Set the value into the bytes.
180 *
181 * @param bytes The bytes to copy
182 * @param nbBytes Number of bytes to copy
183 */
184 private void setBytes( byte[] bytes, int nbBytes )
185 {
186
187 // The first byte contains the number of unused ints
188 nbUnusedBits = bytes[0] & 0x07;
189 nbBits = ( nbBytes * 8 ) - nbUnusedBits;
190
191 // We have to transfer the data from bytes to ints
192 for ( int i = 0; i < nbBytes; i++ )
193 {
194 this.bytes[i] = bytes[i + 1];
195 }
196 }
197
198
199 /**
200 * Set a new BitString in the BitString. It will replace the old BitString,
201 * and reset the current length with the new one.
202 *
203 * @param bytes The string to store
204 */
205 public void setData( byte[] bytes )
206 {
207
208 if ( ( bytes == null ) || ( bytes.length == 0 ) )
209 {
210 nbBits = -1;
211 return;
212 }
213
214 int nbb = bytes.length - 1;
215
216 if ( ( nbb > DEFAULT_LENGTH ) && ( bytes.length < nbb ) )
217 {
218
219 // The current size is too small.
220 // We have to allocate more space
221 // TODO : implement the streaming
222 bytes = new byte[nbb];
223 }
224
225 setBytes( bytes, nbb );
226 }
227
228
229 /**
230 * Get the representation of a BitString
231 *
232 * @return A byte array which represent the BitString
233 */
234 public byte[] getData()
235 {
236 return bytes;
237 }
238
239
240 /**
241 * Get the number of unused bits
242 *
243 * @return A byte which represent the number of unused bits
244 */
245 public byte getUnusedBits()
246 {
247 return ( byte ) nbUnusedBits;
248 }
249
250 /**
251 * Set a bit at a specified position.
252 * The bits are stored from left to right.
253 * 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
254 *
255 * @param pos The bit to set
256 */
257 public void setBit( int pos )
258 {
259 if ( ( pos < 0 ) || ( pos > nbBits ) )
260 {
261 throw new IndexOutOfBoundsException( "Bad bit number : out of bound" );
262 }
263
264 int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
265 int bitNumber = ( pos + nbUnusedBits ) % 8;
266
267 bytes[posInt] |= ( 1 << bitNumber );
268 }
269
270 /**
271 * Clear a bit at a specified position.
272 * The bits are stored from left to right.
273 * For instance, if we have 10 bits, then they are coded
274 * as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
275 *
276 * @param pos The bit to clear
277 */
278 public void clearBit( int pos )
279 {
280 if ( ( pos < 0 ) || ( pos > nbBits ) )
281 {
282 throw new IndexOutOfBoundsException( "Bad bit number : out of bound" );
283 }
284
285 int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
286 int bitNumber = ( pos + nbUnusedBits ) % 8;
287
288 bytes[posInt] &= ~( 1 << bitNumber );
289 }
290
291 /**
292 * Get the bit stored into the BitString at a specific position.
293 * The bits are stored from left to right, the LSB on the right and the
294 * MSB on the left
295 * For instance, if we have 10 bits, then they are coded as
296 * b9 b8 b7 b6 - b5 b4 b3 b2 - b1 b0 x x - x x x x
297 *
298 * With '1001 000x', where x is an unused bit,
299 * ^ ^ ^
300 * | | |
301 * | | |
302 * | | +----- getBit(0) = 0
303 * | +---------- getBit(4) = 0
304 * +------------ getBit(6) = 1
305 *
306 * @param pos The position of the requested bit.
307 *
308 * @return <code>true</code> if the bit is set, <code>false</code>
309 * otherwise
310 */
311 public boolean getBit( int pos )
312 {
313
314 if ( pos > nbBits )
315 {
316 throw new IndexOutOfBoundsException( "Cannot get a bit at position " + pos + " when the BitString contains only "
317 + nbBits + " ints" );
318 }
319
320 int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
321 int bitNumber = ( pos + nbUnusedBits ) % 8;
322
323 int res = bytes[posInt] & ( 1 << bitNumber );
324 return res != 0;
325 }
326
327 /**
328 * @return The number of bytes used to encode this BitString
329 */
330 public int size()
331 {
332 return nbBytes;
333 }
334
335
336 /**
337 * Return a native String representation of the BitString.
338 *
339 * @return A String representing the BitString
340 */
341 public String toString()
342 {
343
344 StringBuffer sb = new StringBuffer();
345
346 for ( int i = nbBits; i > 0; i-- )
347 {
348
349 if ( getBit( i ) )
350 {
351 sb.append( '1' );
352 }
353 else
354 {
355 sb.append( '0' );
356 }
357 }
358
359 return sb.toString();
360 }
361
362
363 /**
364 * Tells if the OctetString is streamed or not
365 *
366 * @return <code>true</code> if the OctetString is streamed.
367 */
368 public boolean isStreamed()
369 {
370 return isStreamed;
371 }
372 }