content: package local.org.bouncycastle.asn1;
  
  import java.io.ByteArrayOutputStream;
  import java.io.EOFException;
  import java.io.IOException;
  import java.io.InputStream;
  
  import local.org.bouncycastle.util.Arrays;
  import local.org.bouncycastle.util.io.Streams;
  
  
  public class DERBitString
      extends ASN1Primitive
      implements ASN1String
  {
      private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
      
      protected byte[]      data;
      protected int         padBits;
  
      /**
       * return the correct number of pad bits for a bit string defined in
       * a 32 bit constant
       */
      static protected int getPadBits(
          int bitString)
      {
          int val = 0;
          for (int i = 3; i >= 0; i--) 
          {
              //
              // this may look a little odd, but if it isn't done like this pre jdk1.2
              // JVM's break!
              //
              if (i != 0)
              {
                  if ((bitString >> (i * 8)) != 0) 
                  {
                      val = (bitString >> (i * 8)) & 0xFF;
                      break;
                  }
              }
              else
              {
                  if (bitString != 0)
                  {
                      val = bitString & 0xFF;
                      break;
                  }
              }
          }
   
          if (val == 0)
          {
              return 7;
          }
  
  
          int bits = 1;
  
          while (((val <<= 1) & 0xFF) != 0)
          {
              bits++;
          }
  
          return 8 - bits;
      }
  
      /**
       * return the correct number of bytes for a bit string defined in
       * a 32 bit constant
       */
      static protected byte[] getBytes(int bitString)
      {
          int bytes = 4;
          for (int i = 3; i >= 1; i--)
          {
              if ((bitString & (0xFF << (i * 8))) != 0)
              {
                  break;
              }
              bytes--;
          }
          
          byte[] result = new byte[bytes];
          for (int i = 0; i < bytes; i++)
          {
              result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
          }
  
          return result;
      }
  
      /**
       * return a Bit String from the passed in object
       *
       * @exception IllegalArgumentException if the object cannot be converted.
       */
      public static DERBitString getInstance(
          Object  obj)
      {
          if (obj == null || obj instanceof DERBitString)
          {
              return (DERBitString)obj;
          }
  
          throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
      }
  
      /**
       * return a Bit String from a tagged object.
       *
       * @param obj the tagged object holding the object we want
       * @param explicit true if the object is meant to be explicitly
       *              tagged false otherwise.
       * @exception IllegalArgumentException if the tagged object cannot
       *               be converted.
       */
      public static DERBitString getInstance(
          ASN1TaggedObject obj,
          boolean          explicit)
      {
          ASN1Primitive o = obj.getObject();
  
          if (explicit || o instanceof DERBitString)
          {
              return getInstance(o);
          }
          else
          {
              return fromOctetString(((ASN1OctetString)o).getOctets());
          }
      }
      
      protected DERBitString(
          byte    data,
          int     padBits)
      {
          this.data = new byte[1];
          this.data[0] = data;
          this.padBits = padBits;
      }
  
      /**
       * @param data the octets making up the bit string.
       * @param padBits the number of extra bits at the end of the string.
       */
      public DERBitString(
          byte[]  data,
          int     padBits)
      {
          this.data = data;
          this.padBits = padBits;
      }
  
      public DERBitString(
          byte[]  data)
      {
          this(data, 0);
      }
  
      public DERBitString(
          int value)
      {
          this.data = getBytes(value);
          this.padBits = getPadBits(value);
      }
  
      public DERBitString(
          ASN1Encodable obj)
          throws IOException
      {
          this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
          this.padBits = 0;
      }
  
      public byte[] getBytes()
      {
          return data;
      }
  
      public int getPadBits()
      {
          return padBits;
      }
  
  
      /**
       * @return the value of the bit string as an int (truncating if necessary)
       */
      public int intValue()
      {
          int value = 0;
          
          for (int i = 0; i != data.length && i != 4; i++)
          {
              value |= (data[i] & 0xff) << (8 * i);
          }
          
          return value;
      }
  
      boolean isConstructed()
      {
          return false;
      }
  
      int encodedLength()
      {
          return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
      }
  
      void encode(
          ASN1OutputStream  out)
          throws IOException
      {
          byte[]  bytes = new byte[getBytes().length + 1];
  
          bytes[0] = (byte)getPadBits();
          System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
  
          out.writeEncoded(BERTags.BIT_STRING, bytes);
      }
  
      public int hashCode()
      {
          return padBits ^ Arrays.hashCode(data);
      }
  
      protected boolean asn1Equals(
          ASN1Primitive  o)
      {
          if (!(o instanceof DERBitString))
          {
              return false;
          }
  
          DERBitString other = (DERBitString)o;
  
          return this.padBits == other.padBits
              && Arrays.areEqual(this.data, other.data);
      }
  
      public String getString()
      {
          StringBuffer          buf = new StringBuffer("#");
          ByteArrayOutputStream bOut = new ByteArrayOutputStream();
          ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
          
          try
          {
              aOut.writeObject(this);
          }
          catch (IOException e)
          {
             throw new RuntimeException("internal error encoding BitString");
          }
          
          byte[]    string = bOut.toByteArray();
          
          for (int i = 0; i != string.length; i++)
          {
              buf.append(table[(string[i] >>> 4) & 0xf]);
              buf.append(table[string[i] & 0xf]);
          }
          
          return buf.toString();
      }
  
      public String toString()
      {
          return getString();
      }
  
      static DERBitString fromOctetString(byte[] bytes)
      {
          if (bytes.length < 1)
          {
              throw new IllegalArgumentException("truncated BIT STRING detected");
          }
  
          int padBits = bytes[0];
          byte[] data = new byte[bytes.length - 1];
  
          if (data.length != 0)
          {
              System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
          }
  
          return new DERBitString(data, padBits);
      }
  
      static DERBitString fromInputStream(int length, InputStream stream)
          throws IOException
      {
          if (length < 1)
          {
              throw new IllegalArgumentException("truncated BIT STRING detected");
          }
  
          int padBits = stream.read();
          byte[] data = new byte[length - 1];
  
          if (data.length != 0)
          {
              if (Streams.readFully(stream, data) != data.length)
              {
                  throw new EOFException("EOF encountered in middle of BIT STRING");
              }
          }
  
          return new DERBitString(data, padBits);
      }
  }
  