BIT STRING

The ASN.1 BIT STRING type is converted into a C or C++ structured type containing an integer to hold the number of bits and an array of unsigned characters ("OCTETs") to hold the bit string contents. The number of bits integer specifies the actual number of bits used in the bit string and takes into account any unused bits in the last byte.

The type definition of the contents field depends on how the bit string is specified in the ASN.1 definition. If a size constraint is used, a static array is generated; otherwise, a pointer variable is generated to hold a dynamically allocated string. The decoder will automatically allocate memory to hold a parsed string based on the received length of the string.

In the static case, the length of the character array is determined by adjusting the given size value (which represents the number of bits) into the number of bytes required to hold the bits.

Dynamic Bit String

ASN.1 production:

   <name> ::= BIT STRING

Generated C code:

   typedef ASN1DynBitStr <name>;

Generated C++ code:

   typedef ASN1TDynBitStr ASN1T_<name>;

In this case, different base types are used for C and C++. The difference between the two is the C++ version includes constructors that initialize the value and methods for setting the value.

The ASN1DynBitStr type (i.e., the type used in the C mapping) is defined in the asn1type.h header file as follows:

   typedef struct ASN1DynBitStr {
      OSUINT32 numbits;
      const OSOCTET* data;
   } ASN1DynBitStr;

The ASN1TDynBitStr type is defined in the asn1CppTypes.h header file as follows:

   struct ASN1TDynBitStr : public ASN1DynBitStr {
      // ctors
      ASN1TDynBitStr () : numbits(0) {}
      ASN1TDynBitStr (OSUINT32 _numbits, OSOCTET* _data);
      ASN1TDynBitStr (ASN1DynBitStr& _bs);
   } ASN1TDynBitStr;

Note that memory management of the byte array containing the bit string data is the responsibility of the user. The wrapper class does not free the memory on destruction nor deep-copy the data when a string is copied.

Static (sized) BIT STRING

ASN.1 production:

   <name> ::= BIT STRING (SIZE (<len>))

Generated C code:

   typedef struct {
      OSUINT32 numbits;
      OSOCTET data[<adjusted_len>*];
   } <name>;

If the -strict-size command-line option is used, the numbits component within this type definition may be of a different type (OSUINT8 or OSUINT16) or eliminated completely if the type is constrained to be a fixed-size.

Generated C++ code:

   typedef struct <name> {
      OSUINT32 numbits;
      OSOCTET data[<adjusted_len>*];
      // ctors
      ASN1T_<name> ();
      ASN1T_<name> (OSUINT32 _numbits, const OSOCTET* _data);
   } ASN1T_<name>;

   * <adjusted_len> = ((<len> - 1)/8) + 1;

If the -strict-size command-line option is used, the numbits component within this type definition may be of a different type (OSUINT8 or OSUINT16) or eliminated completely if the type is constrained to be a fixed-size.

For example, the following ASN.1 production:

   BS ::= [PRIVATE 220] BIT STRING (SIZE (42))

Would translate to the following C typedef:

   typedef struct BS {
      OSUINT32 numbits;
      OSOCTET data[6];
   } BS;

In this case, six octets would be required to hold the 42 bits: eight in the first five bytes, and two in the last byte.

In the case of small-sized strings (less than or equal to 32 bits), a built-in type is used rather than generating a custom type. This built-in type is defined as follows:

   typedef struct ASN1BitStr32 {
      OSUINT32 numbits;
      OSOCTET data[4];
   } ASN1BitStr32;

The C++ variant (ASN1TBitStr32) adds constructors for initialization and copying.

Note that for C++, ASN1C generates special constructors and assignment operators to make populating a structure easier. In this case, two constructors were generated: a default constructor and one that takes numbits and data as arguments.

If the -strict-size command-line option is used, the numbits component would be eliminated since the type is constrained to be a fixed-size.

Named Bits

In the ASN.1 standard, it is possible to define an enumerated bit string that specifies named constants for different bit positions. ASN1C provides support for this type by generating symbolic constants and optional macros that can be used to set, clear, or test these named bits. These symbolic constants equate the bit name to the bit number defined in the specification. They can be used with the rtxSetBit, rtxClearBit, and rtxTestBit run-time functions to set, clear, and test the named bits. In addition, generated C++ code contains an enumerated constant added to the control class with an entry for each of the bit numbers. These entries can be used in calls to the methods of the ASN1CBitStr class to set, clear, and test bits.

The -genBitMacros command line option can be used to generate macros to set, clear, or test the named bits in a bit string structure. These macros offer better performance then using the run-time functions because all calculations of mask and index values are done at compile time. However, they can result in a large amount of additional generated code.

For example, the following ASN.1 production:

   NamedBS ::= BIT STRING { bitOne(1), bitTen(10) }

Would translate to the following if -genBitMacros was specified:

   /* Named bit constants */

   #define NamedBS_bitOne      1

   #define SET_BS3_bitOne(bs) \
   <code to set bit..>

   #define CLEAR_BS3_bitOne(bs) \
   <code to clear bit..>

   #define TEST_BS3_bitOne(bs) \
   <code to test bit..>

   #define NamedBS_bitTen      10

   #define SET_BS3_bitTen(bs) \
   <code to set bit..>

   #define CLEAR_BS3_bitTen(bs) \
   <code to clear bit..>

   #define TEST_BS3_bitTen(bs) \
   <code to test bit..>

   /* Type definitions */

   typedef struct ASN1T_NamedBS {
      OSUINT32 numbits;
      OSOCTET data[2];
   } NamedBS;

The named bit constants would be used to access the data array within the ASN1T_NamedBS type. If bit macros were not generated, the rtxSetBit function could be used to set the named bit bitOne with the following code:

   NamedBS bs;
   memset (&bs, 0, sizeof(bs));
   rtxSetBit (bs.data, 10, NamedBS_bitOne);

The statement to clear the bit using rtxClearBit would be as follows:

   rtxClearBit (bs.data, 10, NamedBS_bitOne);

Finally, the bit could be tested using rtxTestBit with the following statement:

   if (rtxTestBit (bs.data, 10, NamedBS_bitOne) {
      ... bit is set
   }
            

Note that the compiler generated a fixed length data array for this specification. It did this because the maximum size of the string is known due to the named bits - it must only be large enough to hold the maximum valued named bit constant.

Contents Constraint

It is possible to specify a contents constraint on a BIT STRING type using the CONTAINING keyword. This indicates that the encoded contents of the specified type should be packed within the BIT STRING container. An example of this type of constraint is as follows:

   ContainingBS ::= BIT STRING (CONTAINING INTEGER)

ASN1C will generate a type definition that references the type that is within the containing constraint. In this case, that would be INTEGER; therefore, the generated type definition would be as follows:

   typedef OSINT32 ContainingBS;

The generated encoders and decoders would handle the extra packing and unpacking required to get this to and from a BIT STRING container. This direct use of the containing type can be suppressed through the use of the -noContaining command-line argument. In this case, a normal BIT STRING type will be used and it will be the users responsibility to do the necessary packing and unpacking operations to encode and decode the variable correctly.

ASN1CBitStr Control Class

When C++ code generation is specified, a control class is generated for operating on the target bit string. This class is derived from the ASN1CBitStr class. This class contains methods for operating on bits within the string.

Objects of this class can also be declared inline to make operating on bits within other ASN.1 constructs easier. For example, in a SEQUENCE containing a bit string element the generated type will contain a public member variable containing the ASN1T type that holds the message data. If one wanted to operate on the bit string contained within that element, they could do so by using the ASN1CBitStr class inline as follows:

   ASN1CBitStr bs (<seqVar>.<element>);
   bs.set (0);

In this example, <seqVar> would represent a generated SEQUENCE variable type and <element> would represent a bit string element within this type.

See the section on the ASN1CBitStr class in the ASN1C C/C++ Common Run-time User's Manual for details on all of the methods available in this class.