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