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 21:52:31 +0100 (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 }