The following topics describe the representation of the ASN.1 type notation in C:
Contents
For each type reference, a typedef is generated.
ASN.1 | C |
---|---|
Age ::= INTEGER |
typedef int Age; |
For SET and SEQUENCE types, a struct is generated.
ASN.1 | C |
---|---|
Personal ::= SEQUENCE { age INTEGER, weight INTEGER } |
typedef struct Personal { int age; int weight; } Personal; |
For OPTIONAL elements in a SET or SEQUENCE, a bit_mask field and a #define constant are generated to indicate which element is present.
ASN.1 | C |
---|---|
Personal ::= SEQUENCE { married BOOLEAN, age INTEGER, weight INTEGER OPTIONAL, name IA5String OPTIONAL } |
typedef struct Personal { unsigned char bit_mask; # define weight_present 0x80 ossBoolean married; int age; int weight; /* optional; set in bit_mask weight_present if * present */ char *name; /* NULL for not present */ } Personal; |
NOTE: Bit mask values are generally not needed for optional elements that have pointers since their absence is indicated by a NULL pointer.
For CHOICE types, a struct consisting of a selector field and a union containing alternatives (represented as fundamental C data types) are generated. For each element in the CHOICE, a #define is generated to indicate which element is present.
ASN.1 | C |
---|---|
Personal ::= CHOICE { married BOOLEAN, age INTEGER } |
typedef struct Personal { unsigned short choice; # define married_chosen 1 # define age_chosen 2 union { ossBoolean married; /* to choose, set choice to married_chosen */ int age; /* to choose, set choice to age_chosen */ } u; } Personal; |
For each element of a named number list or named bit list, a #define is generated.
ASN.1 | C |
---|---|
Name ::= BIT STRING {red(0), white(1), blue(2)} |
typedef unsigned char Name; #define red 0x80 #define white 0x40 #define blue 0x20 |
Compiler directives provide control over the C representation of ASN.1 types.
Local directives take precedence over type constraints. This rule does not apply to global or module directives.
When certain directives (--<SHORT>--, for example) conflict with schema constraints, the compiler issues a warning. At run time, the encoder/decoder ensures that values do not override the bounds of the C data type.
Subtype constraints and compiler directives do not affect how data is encoded when BER, CER, or DER encoding rules are used.
You can apply more than one compiler directive to an ASN.1 type. However, when mutually exclusive directives are applied to the same type, the last one specified is used.
Recursive types are types that reference themselves and may not be available in the generated code. To convert them to PDUs, use the ASN1.PDU or the ASN1.WorkingSet directive.
ASN.1 | C |
---|---|
Age ::= INTEGER Info ::= SEQUENCE { Age, married BOOLEAN, names SEQUENCE OF NameInfo } NameInfo ::= SEQUENCE { firstName VisibleString (SIZE(20)), lastName VisibleString (SIZE(20)) } |
typedef int Age; typedef struct NameInfo { char firstName[21]; char lastName[21]; } NameInfo; typedef struct Info { Age age; ossBoolean married; struct _seqof1 { struct _seqof1 *next; NameInfo value; } *names; } Info; |
PrimaryColors ::= INTEGER {blue(0), yellow(1), red(2)} FlagColors ::= INTEGER {blue(0), white(1), red(2)} |
typedef int PrimaryColors; #define PrimaryColors_blue 0 #define yellow 1 #define PrimaryColors_red 2 typedef int FlagColors; #define FlagColors_blue 0 #define white 1 #define FlagColors_red 2 |
Personal ::= SEQUENCE { married BOOLEAN, age INTEGER OPTIONAL, weight INTEGER, name IA5String OPTIONAL } |
typedef struct Personal { unsigned char bit_mask; # define age_present 0x80 ossBoolean married; int age; /* optional; set in bit_mask age_present if present */ int weight; char *name; /* NULL for not present */ } Personal; |
Names from the input ASN.1 definitions are preserved in the generated header files. When element identifiers are missing, the ASN.1 compiler generates names derived from the type name. When the element of a SET OF or SEQUENCE OF is not a simple defined type (but is a SEQUENCE OF or SET OF), the compiler generates names in the header output (_setof# or _seqof# where '#' is an integer value).
ASN.1 | C |
---|---|
Age ::= INTEGER Info ::= SEQUENCE { Age, married BOOLEAN, names SEQUENCE OF NameInfo } NameInfo ::= SEQUENCE { firstName VisibleString (SIZE(20)), lastName VisibleString (SIZE(20)) } |
typedef int Age; typedef struct NameInfo { char firstName[21]; char lastName[21]; } NameInfo; typedef struct Info { Age age; ossBoolean married; struct _seqof1 { struct _seqof1 *next; NameInfo value; } *names; } Info; |
The compiler switches the order of the Info and NameInfo structures so that NameInfo can be referenced from within Info. SEQUENCE OF is named struct _seqof1 in the C header file. The other names in the ASN.1 input are passed by default to the header file without change.
When the -helperNames option is specified, the ASN.1 compiler generates names for nested C structures derived from built-in ASN.1 types.
Built-in simple types that are represented by a C structure have intuitive names derived from ASN.1 type names. All names are prefixed with an underscore except for OpenType, UTCTime, GeneralizedTime, which are defined in the asn1hdr.h file.
ASN.1 type | C structure name |
---|---|
BIT STRING | _BitStr |
BMPString | _BMPStr |
All character string types with -- <UNBOUNDED>-- directive applied | _UnbCharStr |
GeneralizedTime --<TIMESTRUCT>-- | GeneralizedTime |
INTEGER --<HUGE>-- | _HugeInt |
OBJECT IDENTIFIER | _OID |
OCTET STRING | _OctStr |
Open type | OpenTypeor generated 'typedef' name |
RELATIVE-OID | _RelOID |
UniversalString | _UnivStr |
Open type | OpenTypeor generated 'typedef' name |
UTCTime --<TIMESTRUCT>-- | UTCTime |
UTF8String --<UNBOUNDED>-- | _UTF8Str |
ASN.1 | C |
---|---|
B ::= BIT STRING {bit(1)} S ::= SET OF BIT STRING |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef _BitStr B; #define bit 0x40 #define bit_byte 0 typedef struct S { struct S_node *head; struct S_node *tail; unsigned int count; } S; typedef struct S_node { struct S_node *next; struct S_node *prev; struct _BitStr *value; } S_node; |
Compiler-generated context-based names for structures that are not derived from built-in simple types are created based on the position of the built-in type within a complex type with a user-defined name. These names have the following format:
TypedefName_componentId1[componentId2[...]][number]
For SET OF and SEQUENCE OF types whose elements do not have identifiers and instead have built-in types represented by a C structure, the structures for these elements have names that include one of the following component names instead of identifiers:
ASN.1 | C |
---|---|
S ::= SEQUENCE { s1 SEQUENCE OF SET { a1 INTEGER, a2 BOOLEAN } } |
typedef struct S { struct S_s1 *s1; } S; typedef struct S_s1_set { int a1; ossBoolean a2; } S_s1_set; typedef struct S_s1 { struct S_s1_node *head; struct S_s1_node *tail; unsigned int count; } S_s1; typedef struct S_s1_node { struct S_s1_node *next; struct S_s1_node *prev; struct S_s1_set *value; } S_s1_node; |
One of the following keywords can be added to the name of the structure that appears within CONTAINING or CONSTRAINED BY constraints: seq, set, choice, setof, seqof, enum, any, boolean, null, integer, real.
ASN.1 | C |
---|---|
SeqBit ::= SEQUENCE { a BIT STRING (CONTAINING INTEGER) } |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef struct SeqBit { struct SeqBit_a *a; } SeqBit; typedef int SeqBit_integer; typedef struct SeqBit_a { /* ContentsConstraint is applied to SeqBit_a */ _BitStr encoded; SeqBit_integer *decoded; } SeqBit_a; |
I ::= INTEGER (CONSTRAINED BY {SEQUENCE {a INTEGER}}) |
typedef int I; typedef struct I_seq { int a; } I_seq; |
The -shortenNames compiler command-line option and the SHORTENNAMES compiler directive allow you to limit all variable names generated by the OSS Language Translator to a maximum of 31 characters. The compiler shortens user-supplied and compiler-generated names that are too long.
The compiler applies a disambiguation algorithm to ensure that all generated variable names are unique within the current scope of reference. Additionally, it ensures that no generated name conflicts with any C reserved name.
If a compiler-generated name is ambiguous and the named type is a member of a structure (CHOICE, SET, SET OF, SEQUENCE or SEQUENCE OF), the name is prefixed with the containing-structure's name. If the name is still ambiguous, it is further prefixed with the next higher-level containing-structure's name (if present), and so on.
If an ENUMERATED type, or an INTEGER or BIT STRING with named values generates an ambiguous name, the compiler adds a name prefix of the defined type.
ASN.1 | C |
---|---|
PrimaryColors ::= INTEGER {blue(0), yellow(1), red(2)} FlagColors ::= INTEGER {red(0), white(1), blue(2)} |
typedef int PrimaryColors; #define PrimaryColors_blue 0 #define yellow 1 #define PrimaryColors_red 2 typedef int FlagColors; #define FlagColors_red 0 #define white 1 #define FlagColors_blue 2 |
The shared colors, red and blue, are prefixed with their parent-type's name and an underscore.
Subtypes used on ASN.1 user-defined types have the same effect as on ASN.1 built-in types.
ASN.1 | C |
---|---|
Name ::= VisibleString (SIZE (1..32)) |
typedef char Name[33]; |
Name ::= VisibleString First-name ::= Name (SIZE (1..32)) |
typedef char First_name[33]; |
ASN.1 | C (with -helperNames) |
---|---|
Name ::= VisibleString First-name ::= Name (SIZE (1..32)) |
typedef char *First_name; |
When the SingleValue subtype is used with the INTEGER type and the OSS.SHORT | OSS.INT | OSS.LONG | LONGLONG local directive and the -helperNames option are not specified, the ASN.1 compiler checks if the C variable is large enough to hold the value specified in the SingleValue subtype.
When the OSS.SHORT | OSS.INT | OSS.LONG | LONGLONG local directive and the -helperNames option are not specified, the OSS ASN.1 compiler uses the ValueRange subtype to determine the size of the integer C data types. When a ValueRange subtype is specified, the compiler generates a C integer data type that is large enough to hold the minimum and maximum values indicated in ValueRange.
At run time, the decoder checks if the decoded value is not too large to fit into the C data type associated with ValueRange.
NOTE: The ValueRange subtype has no effect on the representation of the REAL type.
The SizeConstraint subtype is used to determine the size of the C data types associated with BIT STRING, OCTET STRING, Character String types, SET OF and SEQUENCE OF, if -helperNames is not specified or implied.
At run-time the decoder checks if the decoded value is not too large to fit into the C data type associated with the SizeConstraint. The encoder ensures that the size or number of occurrences of the data it is encoding is within the uppermost and lowermost bounds of the SizeConstraint.
PermittedAlphabet subtypes have no effect on C representations.
The representation of a type with a Contained subtype is the same as that of the type it "INCLUDES".
ASN.1 | C |
---|---|
NormalBit ::= BIT STRING --<VARYING>-- Flags ::= BIT STRING (SIZE(1..61)) --<VARYING>-- MoreFlags ::= BIT STRING (INCLUDES Flags) --<VARYING>-- |
typedef struct NormalBit { unsigned short length; /* number of significant bits */ unsigned char value[1]; /* first element of the array */ } *NormalBit; typedef struct Flags { unsigned short length; /* number of significant bits */ unsigned char value[8]; } Flags; typedef struct MoreFlags { unsigned short length; /* number of significant bits */ unsigned char value[8]; } MoreFlags; |
In this case, the SizeConstraint subtype, (SIZE(1..61)), affects the C data type generated for the Flags structure (64 bits (8 bytes) are allocated for the value field). The Contained subtype (INCLUDES Flags) above instructs the compiler to treat MoreFlags in the same way as Flags.
If the subtype has no effect on the data type to be "INCLUDED", the type that "INCLUDES" is not affected. For example:
ASN.1 | C |
---|---|
Flags ::= BIT STRING (SIZE(1..61)) --<UNBOUNDED>-- MoreFlags ::= BIT STRING (INCLUDES Flags) --<UNBOUNDED>-- |
typedef struct Flags { short length; /* number of significant bits */ unsigned char *value; } Flags; typedef struct MoreFlags { short length; /* number of significant bits */ unsigned char *value; } MoreFlags; |
The OSS.UNBOUNDED representation is not affected by SizeConstraint, so Contained has no effect on the MoreFlags structure. However, at run time, the encoder/decoder enforces SizeConstraint for both Flags and MoreFlags.
The Inner subtypes has the same effect on the compiler-generated data as if the subtype information that follows WITH COMPONENTS was specified in the parent type element definition.
ASN.1 | C |
---|---|
Flags ::= BIT STRING (SIZE(1..61)) --<VARYING>-- ManyFlags ::= SET OF Flags LessFlags ::= ManyFlags (WITH COMPONENT (SIZE(1..51))) |
typedef struct Flags { unsigned short length; /* number of significant bits */ unsigned char value[8]; } Flags; typedef struct ManyFlags { struct ManyFlags *next; /* number of significant bits */ Flags value; } *ManyFlags; typedef struct LessFlags { struct LessFlags *next; struct { short length; /* number of significant bits */ unsigned char value[7]; } value; } *LessFlags; |
In this case, the Inner subtype of LessFlags, WITH COMPONENT(SIZE(1..51)), affects the generated C data type (value is of size 7 instead of 8). The Inner subtype affects the generated C data type also because the compiler rules for VARYING bit strings allow SizeConstraint to affect the C representation.
If a different directive (OSS.UNBOUNDED) is used for Flags, a SizeConstraint subtype applied to LessFlags has no effect on the C representation.
ASN.1 | C |
---|---|
Flags ::= BIT STRING (SIZE(1..61)) --<UNBOUNDED>-- ManyFlags ::= SET OF Flags LessFlags ::= ManyFlags (WITH COMPONENT (SIZE(1..51))) |
typedef struct Flags { short length; /* number of significant bits */ unsigned char *value; } Flags; typedef struct ManyFlags { struct ManyFlags *next; Flags value; } *ManyFlags; typedef struct LessFlags { struct LessFlags *next; struct { short length; /* number of significant bits */ unsigned char *value; } value; } *LessFlags; |
The value field for Flags and LessFlags is represented by unsigned char*.
This notation allows you to restrict a character string type to be of a preset sequence of acceptable character patterns. This constraint notation has the general format:
<TypeName> ::= <RestrictedCharType> (PATTERN <RegularExpression>)
NoZeros ::= NumericString (PATTERN noZero) noZero UniversalString ::= "[^0]"
The type of RegularExpression must be UniversalString. Refer to Annex A "ASN.1 regular expressions" of the ASN.1 standard to learn the language for specifying character patterns using a regular expression.
PATTERN constraints are only enforced by the Space-optimized and Lean encoders/decoders at runtime.
The header file output of the ASN.1 compiler usually does not change if a PATTERN constraint is applied.
Here is a list of the ASN.1 types described in this section:
ASN.1 | C |
---|---|
Any ::= ANY |
typedef struct _Any { unsigned long length; unsigned char *value; } _Any; typedef _Any Any; |
AnyD ::= ANY --<DLINKED>-- |
typedef struct AnyD { struct AnyD *next; struct AnyD *prev; unsigned long length; unsigned char *value; } *AnyD; |
AnyL ::= ANY --<LINKED>-- |
typedef struct AnyL { struct AnyL *next; unsigned long length; unsigned char *value; } *AnyL; |
AnyP ::= ANY --<POINTER>-- |
typedef _Any *AnyP; |
The OSS.DLINKED and the OSS.LINKED directives allow you to represent the ANY type as a doubly linked list of values and as a linked list of values respectively, which when concatenated form the entire value of the Any type. The OSS.UNBOUNDED directive is always implied.
The OSS.POINTER directive introduces a level of indirection by declaring a C pointer to the type.
The conversion of the ASN.1 value notation to C is not supported for the ANY type. If you need this feature, contact Sales <info@oss.com>.
When the OSS.HelperMacro directive is applied to ANY, the following helper macros are generated:
Note that "main" helper macros are produced for generated _Any types and then referenced by macros for each user-defined ANY type.
ASN.1 | Helper macro |
---|---|
AnyHM ::= ANY |
typedef struct _Any { unsigned long length; unsigned char *value; } _Any; typedef _Any AnyHM; /* allocates memory for AnyHM_PDU */ #define oss_AnyHM_new_pdu(world) \ (AnyHM *)ossGetInitializedMemory(world, sizeof(AnyHM)) /* allocates memory for AnyHM_PDU and initializes it by copy of an input * string */ #define oss_AnyHM_copy_pdu(world, value_, length_) \ (AnyHM *)oss__UnbCharString_copy(world, (char *)value_, length_) /* initializes 'value' and 'length' fields of a structure by given values */ #define oss_AnyHM_setv(outp, value_, length_) \ { \ (outp)->value = (value_); \ (outp)->length = (length_); \ } /* initializes 'value' and 'length' fields of a structure by given values */ #define oss_AnyHM_setv(outp, value_, length_) \ oss__Any_setv(outp, value_, length_) |
ASN.1 | C |
---|---|
Name ::= BIT STRING (SIZE (5)) |
typedef struct Name { unsigned short length; /* number of significant bits */ unsigned char *value; } Name; |
--<OSS.VARYING MyModule.Flags >-- Flags ::= BIT STRING (SIZE(1..61)) |
typedef struct Flags { unsigned short length; /* number of significant bits */ unsigned char *value; } Flags; |
Size constraints for BIT STRING types are specified in bits.
When a NamedBitList is present, constants with a _byte suffix are generated to indicate the location of NamedBit.
ASN.1 | C | C (with -helperNames) |
---|---|---|
BitStrNbB ::= BIT STRING {red(0), white(1), blue(50)} |
typedef struct BitStrNbB { unsigned int length; /* number of significant bits */ unsigned char *value; } BitStrNbB; #define red 0x80 #define red_byte 0 #define white 0x40 #define white_byte 0 #define blue 0x20 #define blue_byte 6 |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef _BitStr BitStrNbA; #define a 0x80 #define a_byte 0 #define b 0x40 #define b_byte 0 #define c 0x08 #define c_byte 0 |
When a NamedBitList generates an array of characters, constants with a _byte suffix are generated to indicate the location of NamedBit.
ASN.1 | C | C (with -helperNames) |
---|---|---|
BitStrNbB ::= BIT STRING {red(0), white(1), blue(50)} |
typedef unsigned char BitStrNbB[7]; #define red 0x80 #define red_byte 0 #define white 0x40 #define white_byte 0 #define blue 0x20 #define blue_byte 6 |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef _BitStr BitStrNbB; #define red 0x80 #define red_byte 0 #define white 0x40 #define white_byte 0 #define blue 0x20 #define blue_byte 6 |
The compiler uses the SizeConstraint subtype to determine the maximum number of bytes that BIT STRING contains. Each byte contains as many bits as it can fit. When directives and the -helperNames option are not specified, an unsigned char, short, int, long or an array of unsigned chars is generated (the PADDED representation), depending on the number of bytes needed to hold the bit string. If -helperNames is specified, an unsigned int is always generated.
ASN.1 | C | C (with -helperNames) |
---|---|---|
BitStrSz ::= BIT STRING (SIZE(50)) |
typedef struct BitStrSz { unsigned short length; /* number of significant bits */ unsigned char *value; } BitStrSz; |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef _BitStr BitStrSz; |
The OSS.PADDED directive requires the presence of a NamedBitList or the SizeConstraint subtype, because the compiler must be able to determine the maximum length of the bit string. An unsigned char, short, int, long or array of unsigned chars is generated depending on the number of bytes needed to satisfy the SizeConstraint or accommodate the largest named bit. If the OSS.PADDED directive is specified without the required length indicators, the compiler issues an error message.
ASN.1 | C |
---|---|
BitStrPa ::= BIT STRING --<PADDED>-- (SIZE(10)) |
typedef unsigned short BitStrPa; |
If the OSS.VARYING directive is specified, the result is a pointer to a structure containing a length field of type unsigned short and a character field of unspecified length. The effect is the same as when the OSS.VARYING and OSS.POINTER directives are specified together (OSS.POINTER is assumed because the array length is indeterminable by the compiler).
ASN.1 | C |
---|---|
BitStrVa ::= BIT STRING --<VARYING>-- |
typedef struct BitStrVa { unsigned short length; /* number of significant bits */ unsigned char value[1]; /* first element of the array */ } *BitStrVa; |
The OSS.HelperMacro directive applied to a BIT STRING generates the following helper macros:
Note that "main" helper macros are produced for generated _BitStr types and then referenced by macros for each user-defined BIT STRING type. Here is an example:
ASN.1 | Helper macros |
---|---|
BitStrHM ::= BIT STRING |
typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; /* allocates memory for an empty string */ #define oss__BitStr_new(world) \ (_BitStr *)ossGetInitializedMemory(world, sizeof(_BitStr)) /* allocates memory for the string and initializes it by copying the input * value, length_ is number of bits */ #define oss__BitStr_copy(world, value_, length_) \ (_BitStr *)oss__UnbBitStr_copy(world, value_, length_) /* initializes 'value' and 'length' fields of a string by given values, length_ * is number of bits */ #define oss__BitStr_setv(outp, value_, length_) \ { \ (outp)->value = (value_); \ (outp)->length = (length_); \ } typedef _BitStr BitStrHM; /* allocates memory for BitStrHM_PDU */ #define oss_BitStrHM_new_pdu(world) \ oss__BitStr_new(world) /* allocates memory for BitStrHM_PDU and initializes it by copy of an input * string */ #define oss_BitStrHM_copy_pdu(world, value_, length_) \ oss__BitStr_copy(world, value_, length_) /* initializes 'value' and 'length' fields of a string by given values, length_ * is number of bits */ #define oss_BitStrHM_setv(outp, value_, length_) \ oss__BitStr_setv(outp, value_, length_) |
ASN.1 | C |
---|---|
Married ::= BOOLEAN |
typedef char ossBoolean; typedef ossBoolean Married; |
The OSS.HelperMacro directive applied to a BOOLEAN generates the following helper macros:
Macros for a BOOLEAN type are produced only if this type is a PDU or it is referenced by a pointer. If a BOOLEAN type is a non-typereference field of a SET or SEQUENCE, then its parent type must have its own macros to produce macros for BOOLEAN (the same rule is true for any non-typereference ASN.1 type in place of BOOLEAN).
ASN.1 | C |
---|---|
--<OSS.HelperMacro Mod.BoolHM>-- --<OSS.HelperMacro Mod.SetBoolHM>-- --<OSS.HelperMacro Mod.SetBoolHM.bool>-- Mod DEFINITIONS ::= BEGIN BoolHM ::= BOOLEAN --<PDU>-- SetBoolHM ::= SET {bool BOOLEAN --<POINTER>--} END |
/* allocates memory for BoolHM_PDU */ #define oss_BoolHM_new_pdu(world) \ (BoolHM *)ossGetMemory(world, sizeof(BoolHM)) /* allocates memory for BoolHM_PDU and initializes it by given value */ #define oss_BoolHM_copy_pdu(world, value_) \ oss__Bool_copy(world, value_) typedef struct SetBoolHM { ossBoolean *bool; } SetBoolHM; ... /* allocates memory for an instance of the boolean type */ #define oss_SetBoolHM_bool_new(world) \ (ossBoolean *)ossGetMemory(world, sizeof(ossBoolean)) /* allocates memory for an instance of the boolean type and initializes it by * given value */ #define oss_SetBoolHM_bool_copy(world, value_) \ oss__Bool_copy(world, value_) |
typedef struct Name1 { unsigned short choice; /* selector for the union */ # define alternative1_chosen; # define alternative2_chosen; . . . # define alternativen_chosen; union { type1 alternative1; type2 alternative2; . . . typen alternativen; } u; } Name1;
Note that the alternatives ( alternative1, for example) are the identifiers associated with the alternatives of the CHOICE type. If an identifier is missing, the compiler generates a name based on the name of the alternative.
The compiler generates manifest constants to identify which alternative is contained within the union. The names generated for these manifest constants are the identifier and a suffix of _chosen.
ASN.1 | C |
---|---|
ProductDesignator ::= CHOICE { departmentNo INTEGER, description [0] IMPLICIT VisibleString, inventoryNo [1] IMPLICIT INTEGER } |
typedef struct ProductDesignator { unsigned short _choice; # define departmentNo_chosen 1 # define description_chosen 2 # define inventoryNo_chosen 3 union { int departmentNo; /* to choose, set _choice to * departmentNo_chosen */ char *description; /* to choose, set _choice to * description_chosen */ int inventoryNo; /* to choose, set _choice to * inventoryNo_chosen */ } u; } ProductDesignator; |
When the OSS.HelperMacro directive is applied to a CHOICE, the following helper macros are generated:
ASN.1 | Helper macros |
---|---|
C ::= CHOICE { a1 INTEGER, a2 C } --<PDU>-- |
typedef struct C { unsigned short choice; # define a1_chosen 1 # define a2_chosen 2 union { int a1; /* to choose, set choice to a1_chosen */ struct C *a2; /* to choose, set choice to a2_chosen */ } u; } C; /* allocates memory for an empty instance of the choice type */ #define oss_C_new(world) \ (C *)ossGetInitializedMemory(world, sizeof(C)) /* allocates memory for C_PDU */ #define oss_C_new_pdu(world) \ oss_C_new(world) /* gets index of currently selected alternative */ #define oss_C_which(inp) \ (inp)->choice /* gets "a1" alternative value */ #define oss_C_a1_get(inp) \ (inp)->u.a1 /* selects "a1" alternative and set its value */ #define oss_C_a1_set(outp, a1_) \ { \ (outp)->choice = a1_chosen; \ (outp)->u.a1 = (a1_); \ } /* gets "a2" alternative value */ #define oss_C_a2_get(inp) \ (inp)->u.a2 /* selects "a2" alternative and set its value */ #define oss_C_a2_set(outp, a2_) \ { \ (outp)->choice = a2_chosen; \ (outp)->u.a2 = (a2_); \ } |
The CONTAINING and ENCODED BY subtype constraint notation (defined in X.682) allows you to define the contents of BIT STRING and OCTET STRING types. You can specify a simple or structured ASN.1 value contained in a bit stream or octet stream and you can also indicate which encoding rules are used to generate the value.
Ex1 ::= OCTET STRING ( CONTAINING INTEGER ENCODED BY {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)} ) Ex2 ::= BIT STRING ( CONTAINING IA5String (SIZE(0..8)) ENCODED BY {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)} )
Ex1 illustrates how to constrain an octet stream to contain INTEGER values encoded with PER (Packed Encoding Rules).
Ex2 illustrates how to constrain a bit stream to contain IA5String values which are between 0 and 8 bytes long and are encoded with PER.
The encoding rules are specified using an OBJECT IDENTIFIER value. The OBJECT IDENTIFIER value must refer to a valid node in the object identifier tree for a set of encoding rules for ASN.1 data structures. Note that the encoding rules may be different from the ones defined by the ASN.1 standard.
If the OBJECT IDENTIFIER value used for ENCODED BY does not match any of the permitted encoding rules supported by the OSS ASN.1 Tools, the ASN.1 compiler issues a warning and the ENCODED BY clause is ignored.
For example, for the following notation:
OS ::= OCTET STRING ( CONTAINING INTEGER ENCODED BY {1 2 3} )
The following warning is issued:
"sample.asn", line 3 (Sample): C0673W: The object identifier value specified in the ENCODED BY clause of ContentsConstraint for 'OS' does not match any of the permitted encoding rules. Refer to OSS documentation for a list of permitted values.
Note that the compiler ignores the ENCODED BY clause while encoding/decoding the type OS.
To improve readability, you can predefine values for the encoding rules you wish to use:
enc-BER OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) basic-encoding(1)} enc-DER OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) asn1(1) ber-derived(2) distinguished-encoding(1)} enc-CER OBJECT IDENTIFIER(2) ::= { joint-iso-itu-t(2) asn1(1) ber-derived(2) canonical-encoding(0)} enc-PER-Aligned OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)} enc-PER-Unaligned OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) unaligned(1)} enc-CPER-aligned OBJECT IDENTIFIER ::= {joint-iso-itu-t asn1 (1) packed-encoding (3) canonical (1) aligned (0)} enc-CPER-unaligned OBJECT IDENTIFIER ::= {joint-iso-itu-t asn1 (1) packed-encoding (3) canonical (1) unaligned (1)} enc-XER-Basic OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) xml-encoding(5) basic(0)} enc-XER-Canonical OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) xml-encoding(5) canonical(1)}
Contents constraint:
-- Pre-defined values for OBJECT IDENTIFIER -- enc-PER-Aligned OBJECT IDENTIFIER ::= {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)} -- Contents constraint definition -- Ex1 ::= OCTET STRING ( CONTAINING INTEGER ENCODED BY enc-PER-Aligned )
The following data structures are generated using the above notation:
C | C (with -helperNames) |
---|---|
#define Ex1_PDU 1 #define Ex1_integer_PDU 2 typedef struct ObjectID { unsigned short length; unsigned char *value; } ObjectID; typedef int Ex1_integer; /* Contents constraint definition*/ /**/ typedef struct Ex1 { /* ContentsConstraint is applied to Ex1 */ struct { unsigned int length; unsigned char *value; } encoded; Ex1_integer *decoded; } Ex1; |
#define Ex1_PDU 1 #define Ex1_integer_PDU 2 typedef struct _OID { unsigned int length; unsigned char *value; } _OID; typedef struct _OctStr { unsigned int length; unsigned char *value; } _OctStr; typedef int Ex1_integer; /* Contents constraint definition */ /**/ typedef struct Ex1 { /* ContentsConstraint is applied to Ex1 */ _OctStr encoded; Ex1_integer *decoded; } Ex1; |
When the -helperNames option is not specified, compiler generates PDU constants for both the containing OCTET STRING and the contained integer.
Note that when the containing type is not specified, decoded is declared as void*.
Also note that when the global or local OSS.NOPDU directive is applied to a contained type or when the -noPdusForContainingTypes compiler option is specified, PDU constants are not generated for these types.
When the -helperNames option is specified, the encoded field gets the extracted but still non-pointered type.
When the OSS.HelperMacro directive is applied to a type with contents constraints, the following helper macros are generated:
ASN.1 | C |
---|---|
--<OSS.HelperMacro M.CC.*>-- M DEFINITIONS ::= BEGIN CC ::= SEQUENCE OF OCTET STRING (CONTAINING NULL) END |
... typedef struct CC_seq { /* ContentsConstraint is applied to CC_seq */ _OctStr encoded; CC_null *decoded; } CC_seq; /* allocates memory for the octet string with a ContentsConstraint applied */ #define oss_CC_seq_new(world) \ (CC_seq *)ossGetInitializedMemory(world, sizeof(CC_seq)) /* allocates memory for the octet string with a ContentsConstraint applied and * initializes it by copying the encoded value */ #define oss_CC_seq_copy_encoded(world, value_, length_) \ (CC_seq *)oss__OctStrCC_copy_encoded(world, value_, length_) /* allocates memory for the octet string with a ContentsConstraint applied and * initializes it by copying the decoded value */ #define oss_CC_seq_copy_decoded(world, decoded_) \ (CC_seq *)oss__CC_copy_decoded(world, decoded_, CC_null_PDU) /* sets encoded value of the unbounded octet string */ #define oss_CC_seq_set_encoded(outp, value_, length_) \ { \ (outp)->encoded.value = (value_); \ (outp)->encoded.length = (length_); \ (outp)->decoded = NULL; \ } /* sets decoded value of the type within CONTAINING clause */ #define oss_CC_seq_set_decoded(outp, decoded_) \ (outp)->decoded = (decoded_) |
To enable the encoder to automatically encode the contained type, set the encoded field of the generated structure to { 0, NULL } and the decoded field to point to a decoded value of the contained type. The encoder encodes this value using the encoding rules specified with the ENCODED BY notation (or the current encoding rules in use if the ENCODED BY notation was omitted) and places it in the encoded field.
If the encoding of the decoded field is successful, the encoder will again encode the contained value but this time treating it as the containing OCTET STRING or BIT STRING. The encoded field is encoded as a normal OCTET STRING value or BIT STRING value.
If the encoding rules which correspond to the OBJECT IDENTIFIER value used for ENCODED BY are not specified (either implicitly or explicitly) during ASN.1-compiling, the automatic encoding of contained types is not possible and the encoder issues the following error message:
"E0103S: The requested encoding rules were not linked".
When the encoded field is not empty, the decoded field is ignored. The encoder skips the automatic encoding of the contained type and encodes the value of the encoded field. For example, you can manually encode the contained type and place it in the encoded field and when you call the encoder, it will simply encode your preset octet stream or bit stream as a value of the BIT STRING or OCTET STRING type.
Note that the encoder takes the value "as is", it does not check if it is a valid encoding of an abstract value of the containing type.
Note that automatic encoding/decoding is not performed at runtime in the following cases:
After receiving an encoded PDU corresponding to a type with contents constraint, the decoder decodes the PDU as a normal OCTET STRING or BIT STRING value and places the result in the encoded field of the generated structure. Then a second decode is automatically performed on the contents of the encoded field using the encoding rules specified with the ENCODED BY clause and the result is placed in the decoded field. At the same time the encoder discards the contents of the encoded field and sets it to an empty value.
If the encoding rule that corresponds to the OBJECT IDENTIFIER value used for the ENCODED BY clause is not linked during ASN.1-compiling, automatic decoding is not possible and the decoder issues the following error message:
D0103S: The requested encoding rules were not linked.
The above paragraph assumes that contents constraint with the CONTAINING and ENCODED BY keywords is used. If the CONTAINING keyword is omitted, the second decode is not automatically performed. If the ENCODED BY clause is omitted, the current encoded rules are employed.
The value notation associated with a BIT STRING or OCTET STRING type defined with a contents constraint can either correspond to a value for the contained type or (if the ENCODED BY clause is present) to a bit or octet stream. In the first case, the keyword CONTAINING must be inserted before the assigned value to indicate that the value corresponds to the contained type. For example, the following type definition:
A ::= OCTET STRING ( CONTAINING INTEGER ENCODED BY {joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)} )
allows for the following two value notation alternatives:
a1 A ::= '1A25'H -- specifies a value for containing OCTET STRING a2 A ::= CONTAINING 22 -- specifies a value for the contained type
The OCTET STRING or BIT STRING type must have the UNBOUNDED representation (the default). You cannot use the PADDED or VARYING directive with the OCTET STRING and the BIT STRING types.
ASN.1 | C | C (with -helperNames) |
---|---|---|
DefaultEPDV ::= EMBEDDED PDV |
typedef struct ObjectID { unsigned short length; unsigned char *value; } ObjectID; typedef struct EmbeddedPDV { struct { unsigned short choice; # define syntaxes_chosen 1 # define syntax_chosen 2 # define presentation_context_id_chosen 3 # define context_negotiation_chosen 4 # define transfer_syntax_chosen 5 # define fixed_chosen 6 union { struct EmbeddedPDV_syntaxes { ObjectID abstract; ObjectID transfer; } syntaxes; /* to choose, set choice to syntaxes_chosen */ ObjectID syntax; /* to choose, set choice to syntax_chosen */ int presentation_context_id; /* to choose, set choice to presentation_context_id_chosen */ struct EmbeddedPDV_negotiation { int presentation_context_id; ObjectID transfer_syntax; } context_negotiation; /* to choose, set choice to context_negotiation_chosen */ ObjectID transfer_syntax; /* to choose, set choice to transfer_syntax_chosen */ Nulltype fixed; /* to choose, set choice to fixed_chosen */ } u; } identification; struct { unsigned int length; unsigned char *value; } data_value; } EmbeddedPDV; typedef EmbeddedPDV DefaultEPDV; |
typedef struct _OID { unsigned int length; unsigned char *value; } _OID; typedef struct _OctStr { unsigned int length; unsigned char *value; } _OctStr; typedef struct EmbeddedPDV { struct EmbeddedPDV_identification *identification; _OctStr *data_value; } EmbeddedPDV; typedef EmbeddedPDV DefaultEPDV; typedef struct EmbeddedPDV_syntaxes { struct _OID *abstract; struct _OID *transfer; } EmbeddedPDV_syntaxes; typedef struct EmbeddedPDV_negotiation { int presentation_context_id; struct _OID *transfer_syntax; } EmbeddedPDV_negotiation; typedef struct EmbeddedPDV_identification { unsigned short choice; # define syntaxes_chosen 1 # define syntax_chosen 2 # define presentation_context_id_chosen 3 # define context_negotiation_chosen 4 # define transfer_syntax_chosen 5 # define fixed_chosen 6 union { struct EmbeddedPDV_syntaxes *syntaxes; /* to choose, set choice * to syntaxes chosen */ struct _OID *syntax;/*to choose, set choice to syntax_chosen */ int presentation_context_id; /* to choose, set choice to presentation_context_id_chosen */ struct EmbeddedPDV_negotiation *context_negotiation; /* to choose, set choice to context_negotiation_chosen */ struct _OID *transfer_syntax; /* to choose, set choice to * transfer_syntax_chosen */ Nulltype fixed; /* to choose, set choice to fixed_chosen */ } u; } EmbeddedPDV_identification; |
ASN.1 | C |
---|---|
Colors ::= ENUMERATED { red(1), white(2), blue(purple) } purple INTEGER ::= 3 |
typedef enum Colors { red = 1, white = 2, blue = 3 } Colors; extern int purple; int purple = 3; |
When you assign values, you must always use the defined enumerator and not the INTEGER equivalent. For example, use:
Color myColor = blue
and not:
Color myColor = 3
When you store unknown ENUMERATED types in relay-safe mode, the value is preserved in enciphered form which is represented locally as (MAXINT-enumValue). See the following limitations:
ASN.1 | C | C (with -helperNames) |
---|---|---|
MyExternal ::= EXTERNAL |
typedef struct ObjectID { unsigned short length; unsigned char *value; } ObjectID; typedef struct External { unsigned char bit_mask; #define direct_reference_present 0x80 #define indirect_reference_present 0x40 ObjectID direct_reference; /* optional; set in bit_mask direct_reference_present if present */ int indirect_reference; /* optional; set in bit_mask indirect_reference_present if present */ char *data_value_descriptor; /* NULL for not present */ struct { unsigned short choice; #define single_ASN1_type_chosen 1 #define octet_aligned_chosen 2 #define arbitrary_chosen 3 union { OpenType single_ASN1_type; /* to choose, set choice to single_ASN1_type_chosen */ struct External_octet_aligned { unsigned int length; unsigned char *value; } octet_aligned; /* to choose, set choice to octet_aligned_chosen */ struct External_arbitrary { unsigned int length; /* number of significant bits */ unsigned char *value; } arbitrary; /* to choose, set choice to arbitrary_chosen */ } u; } encoding; } External; |
typedef struct _OID { unsigned int length; unsigned char *value; } _OID; typedef struct _BitStr { unsigned int length; /* number of significant bits */ unsigned char *value; } _BitStr; typedef struct _OctStr { unsigned int length; unsigned char *value; } _OctStr; typedef struct External { unsigned char bit_mask; # define indirect_reference_present 0x80 struct _OID *direct_reference; /* NULL for not present */ int indirect_reference; /* optional; set in bit_mask * indirect_reference_present if * present */ char *data_value_descriptor; /* NULL for not present */ struct External_encoding *encoding; } External; typedef External MyExternal; typedef _OctStr External_octet_aligned; typedef _BitStr External_arbitrary; typedef struct External_encoding { unsigned short choice; #define single_ASN1_type_chosen 1 #define octet_aligned_chosen 2 #define arbitrary_chosen 3 union { OpenType *single_ASN1_type; /* to choose, set choice to * single_ASN1_type_chosen */ External_octet_aligned *octet_aligned; /* to choose, set choice to * octet_aligned_chosen */ External_arbitrary *arbitrary; /* to choose, set choice to * arbitrary_chosen */ } u; } External_encoding; |
The C representation of the EXTERNAL type components is affected by global directives. For example, the representation for ObjectID can be altered by the global OSS.UNBOUNDED directive.
EXTERNAL is used when you need to carry a value that uses a different encoding scheme. For example, if you are using BER and the value is encoded using PER, you need to encode the value in PER and then put the encoded value in the External.encoding field. Also, you must set direct_reference or indirect_reference to indicate the type/encoding of the value in External.encoding.
EXTERNAL is also used when the application that you are implementing has a "container" type. For example, suppose you have a message that can carry a value of any ASN.1 specification. You encode it using other application that knows the ASN.1 definition of the type. The value is already encoded for you to put into the External.encoding field. You would also receive a direct_reference or an indirect_reference which indicates to the communications peer the type of the value that is contained in External.encoding.
You can customize the representation of EXTERNAL by creating a type reference, External, whose tag is UNIVERSAL 8 and whose elements mirror those of the UNIVERSAL type as defined in ISO 8824 | X.208. You can also add local directives to the individual elements.
External ::= [UNIVERSAL 8] IMPLICIT SEQUENCE { direct reference OBJECT IDENTIFIER --<OBJECTID 10>-- OPTIONAL, indirect reference INTEGER OPTIONAL, data value descriptor ObjectDescriptor OPTIONAL, encoding CHOICE { single ASN1 type [0] ANY --<LINKED>--, octet aligned [1] IMPLICIT OCTET STRING, arbitrary [2] IMPLICIT BIT STRING}}
The following warning is generated:
A0307W: OSS has relaxed its implementation of the standards to allow the definition of a type with tag [UNIVERSAL 8]. This is normally invalid ASN.1.
ASN.1 | C |
---|---|
Generaltime ::= GeneralizedTime |
typedef GeneralizedTime Generaltime;where GeneralizedTime is defined in the asn1hdr.h file as follows: typedef struct { short year; /* YYYY format when used for GeneralizedTime */ /* YY format when used for UTCTime */ short month; short day; short hour; short minute; short second; short millisec; short mindiff; /* UTC +/- minute differential */ ossBoolean utc; /* TRUE means UTC time */ } GeneralizedTime; |
The GeneralizedTime type can be represented either as GeneralizedTime structs (for example { short year; short month; etc. } ) or as strings. Strings are used when the -lean option, the -helperNames option or the NULLTERM directive is used.
When the NULLTERM directive is used or the -lean option or the -helperNames option is specified, the GeneralizedTime type is represented as GeneralizedTime strings.
ASN.1 | C |
---|---|
Generaltime ::= GeneralizedTime --<NULLTERM>-- t Generaltime ::= "19851106210627.3-0500" -- local time 6 minutes, 27.3 seconds after 9 pm on 6 November 1985 with -- local time 5 hours retarded in relation to coordinated universal time |
typedef char *Generaltime; Generaltime t = "19851106210627.3-0500"; |
The OSS.HelperMacro directive applied to a GeneralizedTime with the NULLTERM representation generates the following helper macros:
The OSS.HelperMacro directive applied to a GeneralizedTime with the TIMESTRUCT directive generates the following helper macros:
ASN.1 | Helper macros |
---|---|
--<OSS.HelperMacro Mod.SeqTime>-- --<OSS.HelperMacro Mod.SeqTime.n-time>-- --<OSS.TIMESTRUCT Mod.SeqTime.s-time>-- --<OSS.HelperMacro Mod.SeqTime.s-time>-- Mod DEFINITIONS ::= BEGIN SeqTime ::= SEQUENCE { n-time GeneralizedTime, s-time GeneralizedTime } END |
typedef struct SeqTime { char *n_time; GeneralizedTime *s_time; } SeqTime; ... /* allocates memory for a string of given length */ #define oss_SeqTime_n_time_new(world, length_) \ oss__CharStr_new(world, length_) /* allocates memory and returns a copy of an input string */ #define oss_SeqTime_n_time_copy(world, value_) \ oss__CharStr_copy(world, value_) ... /* allocates memory for the time structure */ #define oss_SeqTime_s_time_new(world) \ (GeneralizedTime *)ossGetInitializedMemory(world, sizeof(GeneralizedTime)) /* allocates memory for the time structure and initializes it by parsing a * string containing a GeneralizedTime value */ #define oss_SeqTime_s_time_copy_nullterm(world, value_) \ oss__GeneralizedTime_copy_nullterm(world, value_) /* initializes the structure by parsing a string containing a GeneralizedTime * value */ #define oss_SeqTime_s_time_setv_nullterm(world, outp, value_) \ oss__GeneralizedTime_setv_nullterm(world, outp, value_) |
NOTE: As specified in ITU-T Rec. X.680 | ISO/IEC 8824-1, year is specified in YYYY format for GeneralizedTime and in YY format for UTCTime. The time differential, mindiff, indicates the minute differential from Coordinated Universal Time (UTC time or Greenwich Mean Time); mindiff may have either a positive or negative value. The minute differential can have an absolute value greater than 59, so to express a time differential of one hour and thirty minutes, you would set mindiff to 90. A utc value of TRUE indicates that the time contained in the GeneralizedTime structure is the UTC time. If utc is TRUE, mindiff is ignored. If utc is FALSE, and mindiff contains zero, the time in the GeneralizedTime structure is the local time. If utc is FALSE, and mindiff is non zero, the hour, minute, and second minus the minute differential yields the UTC time.
The INSTANCE OF type is equivalent to an EXTERNAL type that is used to specify an attribute-id / attribute-value pair.
Only the OSS.OBJHANDLE directive is allowed with the INSTANCE OF type and it does not affect the C representation.
ASN.1:
Module DEFINITIONS ::= BEGIN MHS-BODY ::= TYPE-IDENTIFIER InstanceOfMHS ::= INSTANCE OF MHS-BODY END
where TYPE-IDENTIFIER is internally defined as:
TYPE-IDENTIFIER ::= CLASS { &id OBJECT IDENTIFIER UNIQUE, &Type }
and IntanceOfMHS has the following implied associated SEQUENCE:
InstanceOfMHS ::= SEQUENCE { type-id MHS-BODY.&id, value [0] MHS-BODY.&Type }
C | C (with -helperNames) |
---|---|
#define InstanceOfMHS_PDU 1 typedef struct ObjectID { unsigned short length; unsigned char *value; } ObjectID; typedef struct InstanceOfMHS { ObjectID type_id; OpenType value; } InstanceOfMHS; |
#define InstanceOfMHS_PDU 1 typedef struct _OID { unsigned int length; unsigned char *value; } _OID; typedef struct InstanceOfMHS { struct _OID *type_id; OpenType *value; } InstanceOfMHS; |
ASN.1 | C |
---|---|
RegInt ::= INTEGER NumLstInt ::= INTEGER { one(1), two(2), three(3) } SizeInt ::= INTEGER (1..123456789) |
typedef int RegInt; typedef int NumLstInt; #define one 1 #define two 2 #define three 3 typedef unsigned int SizeInt; |
When a ValueRange or SingleValue constraint conflicts with a local directive (the value is too large or too small for the type indicated by the directive), the directive takes precedence and the compiler issues a warning.
ASN.1 | C |
---|---|
Name1 ::= NULL |
typedef char Nulltype; typedef Nulltype Name1; |
ASN.1 | C |
---|---|
DefaultOID ::= OBJECT IDENTIFIER |
typedef struct ObjectID { unsigned short length; unsigned char *value; } ObjectID; typedef ObjectID DefaultOID; |
ArrayOID ::= OBJECT IDENTIFIER --<ARRAY>-- |
typedef struct ArrayOID { unsigned short count; unsigned short value[1]; } *ArrayOID; |
ObjIdOID ::= OBJECT IDENTIFIER --<OBJECTID 8>-- |
typedef struct ObjIdOID { unsigned short length; unsigned char value[8]; } ObjIdOID; |
LongOID ::= OBJECT IDENTIFIER --<LONG>-- |
typedef struct LongOID { unsigned short length; unsigned char *value; } LongOID; |
UnboundedLongOID ::= OBJECT IDENTIFIER --<UNBOUNDED|LONG>-- |
typedef struct UnboundedLongOID { unsigned short count; unsigned long *value; } UnboundedLongOID; |
OIDUnbndOID ::= OBJECT IDENTIFIER --<OBJECTID 10 | UNBOUNDED>-- |
typedef struct OIDUnbndOID { unsigned short count; unsigned short *value; } OIDUnbndOID; |
OIDLnkPtrOID ::= OBJECT IDENTIFIER --<OBJECTID 8 | LINKED | POINTER>-- |
typedef struct OIDLnkPtrOID { struct OIDLnkPtrOID *next; unsigned short value; } **OIDLnkPtrOID; |
char *value contains the BER-encoded OBJECT IDENTIFIER ({1 2 3 4 5} stored as: {0x2A, 0x03, 0x04, 0x05}). This allows the representation with node values to exceed the maximum INTEGER representation on a particular machine. The encoded value is derived according to the ITU-T Rec.X.690 (2021) document.
short value[] contains an array of short INTEGERs which include the individual node values of the OBJECT IDENTIFIER.
ASN.1 | C |
---|---|
DefaultOctStr ::= OCTET STRING |
// UNBOUNDED representation by default. typedef struct DefaultOctStr { unsigned int length; unsigned char *value; } DefaultOctStr; |
SizeOctStr ::= OCTET STRING (SIZE(10)) |
// VARYING representation by default. typedef struct SizeOctStr { unsigned short length; unsigned char value[10]; } SizeOctStr; |
Open types are the equivalent of ANY and ANY DEFINED BY in ASN.1:1990. Unlike other types in this section, the open type does not have a reserved identifier in the ASN.1 notation which causes it to be generated. Rather, it results when information object notation is written in a way that renders ambiguous the exact type intended for a certain field. Note that the open type is particularly useful for embedding a separately encoded ASN.1 type within other ASN.1 type. The representation for open type contains information about an ASN.1 type and its decoded value or stores encoded data of the type.
An open type is represented with the decoded value as a union of PDU type alternatives if the following conditions are met:
Otherwise, the predefined OpenType structure is used to represent an ASN.1 open type.
ASN.1 | C |
---|---|
DEFINITIONS ::= BEGIN C ::= CLASS { &code INTEGER, &Type } Object C ::= { { &code 1, &Type INTEGER } | { &code 2, &Type UTF8String } } S ::= SEQUENCE { key C.&code ({Object}), value C.&Type ({Object}{@key}) } END |
#define S_PDU 1 #define Object_integer_PDU 2 #define Object_UTF8String_PDU 3 typedef int Object_integer; typedef unsigned char *Object_UTF8String; enum Object_Type_PDUs { PDU_Object_Type_UNKNOWN = 0, PDU_Object_Type_integer = Object_integer_PDU, PDU_Object_Type_UTF8String = Object_UTF8String_PDU }; union Object_Type_union { Object_integer *pdu_Object_integer; /* PDU_Object_Type_integer */ Object_UTF8String *pdu_Object_UTF8String; /* PDU_Object_Type_UTF8String */ }; typedef struct Object_Type { enum Object_Type_PDUs pduNum; OssBuf encoded; union Object_Type_union decoded; } Object_Type; typedef struct S { int key; Object_Type value; } S; |
The generated Object_Type type consists of the following fields:
typedef struct { long length; unsigned char *value; } OssBuf;A non-zero value of both the length and value fields indicates that encoded data is present for OpenType. The address of the encoded field can be passed directly to the ossDecode() and ossEncode() runtime API functions as the input and output parameters respectively.
In the above structure, the pduNum and decoded fields are only used if the AUTOMATIC_ENCDEC flag is set using the ossSetEncodingFlags() and ossSetDecodingFlags() functions. Specifically, the pduNum field should reference the PDU identification constant of the data structure to be contained in the open type and the decoded field should reference the value-filled compiler-generated data structure to be encoded. Finally, when AUTOMATIC_ENCDEC is specified, the length and value fields in the encoded field should be set to zero and NULL respectively, before calling the encoder.
When the AUTOMATIC_ENCDEC flag is not set, the pduNum and decoded fields should be set to UNKNOWN and NULL, respectively, while the value and length fields in the encoded field should be set to the pre-encoded data and its length, respectively.
This C representation uses a predefined OpenType structure. This was the only possible representation prior to version 9.0 of the ASN.1 compiler. Starting with version 9.0, the following representation is used:
ASN.1 | C |
---|---|
Module DEFINITIONS ::= BEGIN A ::= TYPE-IDENTIFIER.&Type ENDwhere TYPE-IDENTIFIER is defined as follows: TYPE-IDENTIFIER ::= CLASS { &id OBJECT IDENTIFIER UNIQUE, &Type } |
#define A_PDU 1 typedef OpenType A; |
Note that the OpenType type has a declaration of the following form:
typedef struct { int pduNum; long length; /* length of "encoded" */ void *encoded; void *decoded; } OpenType;
The pduNum field can contain the PDU identification number of the decoded structure/value. The length field can contain the length of the encoded value returned by the encoder. The encoded field can contain the address of the encoded value returned by the encoder. The decoded field can contain the address of the decoded structure/value.
In the above structure, the pduNum and decoded fields are only used if the AUTOMATIC_ENCDEC flag is set using the ossSetEncodingFlags() and ossSetDecodingFlags() functions. The pduNum field should reference the compiler-generated PDU identification number of the data structure to be contained in the open type and the decoded field should reference the value-filled compiler-generated data structure to be encoded. When AUTOMATIC_ENCDEC is specified, the length and encoded fields should be set to zero and NULL respectively, before calling the encoder.
When the AUTOMATIC_ENCDEC flag is not set, the pduNum and decoded fields should be set to zero and NULL, respectively, while the encoded and length fields should be set to the pre-encoded data and its length, respectively.
The OSS.HelperMacro directive applied to an open type generates the following helper macros:
ASN.1 | Helper macros |
---|---|
ArbitraryType ::= [0] TYPE-IDENTIFIER.&Type |
typedef OpenType ArbitraryType; /* allocates memory for ArbitraryType_PDU */ #define oss_ArbitraryType_new_pdu(world) \ (ArbitraryType *)ossGetInitializedMemory(world, sizeof(ArbitraryType)) /* allocates memory for ArbitraryType_PDU and initializes it by copying the * encoded value */ #define oss_ArbitraryType_copy_encoded_pdu(world, value_, length_) \ (ArbitraryType *)oss__OpenType_copy_encoded(world, value_, length_, sizeof(ArbitraryType)) /* allocates memory for ArbitraryType_PDU and initializes it by copying the * decoded value */ #define oss_ArbitraryType_copy_decoded_pdu(world, decoded_, pdunum_) \ (ArbitraryType *)oss__OpenType_copy_decoded(world, decoded_, pdunum_, sizeof(ArbitraryType)) /* sets encoded value of the OpenType */ #define oss_ArbitraryType_set_encoded(outp, value_, length_) \ { \ (outp)->encoded = (value_); \ (outp)->length = (length_); \ (outp)->decoded = NULL; \ } /* sets decoded value of the OpenType */ #define oss_ArbitraryType_set_decoded(outp, decoded_, pdunum_) \ { \ (outp)->decoded = (decoded_); \ (outp)->pduNum = (pdunum_); \ } |
An OpenType is also generated for elements of CHOICE, SEQUENCE, and SET types that have the ASN1.DeferDecoding or OSS.ENCODABLE directive applied to them.
If the -extendOpenType compiler option is specified, a user-specific field (userField) is generated within the OpenType structure that is not encoded by the encoder. This field has the following format:
void userField;
ASN.1 | C |
---|---|
DefaultReal ::= REAL FloatReal ::= REAL --<FLOAT>-- DecimalReal ::= REAL --<DECIMAL>-- MyMixReal ::= REAL --<MIXED>-- |
typedef double DefaultReal; typedef float FloatReal; typedef char *DecimalReal; typedef MixedReal MyMixReal; |
SingleValue and ValueRange constraints have no effect on the representation of REAL types.
A relative object identifier can be used to identify an object in the object identifier tree relative to other known object identifier. Thus, the relative object identifier type can be used to transmit the trailing component or components of an object identifier value. This leads to bandwidth savings over transmitting the entire object identifier value.
ASN.1 | C |
---|---|
DefaultROID ::= RELATIVE-OID myUniversity OBJECT IDENTIFIER ::= {iso member-body country(29) universities(56) universityOfNokalva(32)} relativeDept DefaultROID ::= {engineering(4) digitalSignalProcessing(3)} dspDept OBJECT IDENTIFIER ::= { myUniversity relativeDept } |
static unsigned char _v0[] = { 0x2A, 0x1D, 0x38, 0x20 }; ObjectID myUniversity = { 4, /* length in octets */ _v0 /* BER-encoded value */ }; static unsigned char _v1[] = { 0x04, 0x03 }; DefaultROID relativeDept = { 2, /* length in octets */ _v1 /* BER-encoded value */ }; static unsigned char _v2[] = { 0x2A, 0x1D, 0x38, 0x20, 0x04, 0x03 }; ObjectID dspDept = { 6, /* length in octets */ _v2 /* BER-encoded value */ }; |
In the above example, relativeDept can be transmitted alone if both the sender and the receiver are aware that engineering(4) is an offshoot (branch) of {iso member-body country(29) universities(56) universityOfNokalva(32)}.
Note how the value field contents are simply the BER-encoded octets of the object identifier value.
ASN.1 | C | C (with -helperNames) |
---|---|---|
DefaultROID ::= RELATIVE-OID |
typedef struct OssRelativeOID { unsigned short length; unsigned char *value; } OssRelativeOID; typedef OssRelativeOID DefaultROID; |
typedef struct _RelOID { unsigned int length; unsigned char *value; } _RelOID; typedef _RelOID DefaultROID; |
If the OSS.OBJECTID directive is specified with a length operand, a fixed-size character string is generated preceded by a length field giving the size in octets of the character string.
ASN.1 | C |
---|---|
BigROID ::= RELATIVE-OID --<OBJECTID 80>-- |
typedef struct BigROID { unsigned short length; unsigned char value[80]; } BigROID; |
The value field contains the BER-encoded contents of the OBJECT IDENTIFIER (for example, {1 2 3 4 5} would be stored as: {0x2A, 0x03, 0x04, 0x05}).
The following helper macros are generated when the OSS.HelperMacro directive is applied:
Note that "main" helper macros are produced for generated _RelOID types and then referenced by macros for each user-defined RELATIVE-OID type.
ASN.1 | Helper macros |
---|---|
OID-Tail ::= RELATIVE-OID |
typedef struct _RelOID { unsigned int length; unsigned char *value; } _RelOID; /* allocates memory for an empty structure */ #define oss__RelOID_new(world) \ (_RelOID *)ossGetInitializedMemory(world, sizeof(_RelOID)) /* allocates memory for the structure and initializes it by copying the input * value */ #define oss__RelOID_copy(world, value_, length_) \ (_RelOID *)oss__UnbCharString_copy(world, (char *)value_, length_) /* initializes 'value' and 'length' fields of a structure by given values */ #define oss__RelOID_setv(outp, value_, length_) \ { \ (outp)->value = (value_); \ (outp)->length = (length_); \ } typedef _RelOID OID_Tail; /* allocates memory for OID_Tail_PDU */ #define oss_OID_Tail_new_pdu(world) \ oss__RelOID_new(world) /* allocates memory for OID_Tail_PDU and initializes it by copy of an input * string */ #define oss_OID_Tail_copy_pdu(world, value_, length_) \ oss__RelOID_copy(world, value_, length_) /* initializes 'value' and 'length' fields of a structure by given values */ #define oss_OID_Tail_setv(outp, value_, length_) \ oss__RelOID_setv(outp, value_, length_) |
ASN.1 | C |
---|---|
Str ::= VisibleString |
typedef char *Str; |
SmallStr ::= VisibleString (SIZE(10)) |
typedef char SmallStr[11]; |
BigStr ::= PrintableString (SIZE(257)) |
typedef struct BigStr { unsigned short length; char *value; } BigStr; |
For Strings larger than 256 bytes, the compiler automatically uses the UNBOUNDED representation.
ASN.1 | C |
---|---|
Info1 ::= CHOICE {name VisibleString, age INTEGER} --<PDU>-- Info2 ::= SEQUENCE {birthdate INTEGER, name < Info1} |
typedef struct Info1 { unsigned short choice; # define name_chosen 1 # define age_chosen 2 union { char *name; int age; } u; } Info1; typedef struct Info2 { int birthdate; char *name; } Info2; |
The Selection type < Info1 has the same representation as VisibleString.
ASN.1 | C |
---|---|
Names ::= SEQUENCE { ceo VisibleString (SIZE(1..32)), secretary VisibleString (SIZE(1..32)), VisibleString (SIZE(1..32)) } |
typedef struct Names { char ceo[33]; char secretary[33]; char visibleString[33]; } Names; |
Names2 ::= SEQUENCE { ceo UTF8String (SIZE(1..32)) OPTIONAL, secretary UTF8String OPTIONAL, other UTF8String (SIZE(1..32)) } |
typedef struct Names2 { unsigned char bit_mask; # define ceo_present 0x80 unsigned char ceo[193]; /* optional; set in bit_mask ceo_present if * present */ unsigned char *secretary; /* NULL for not present */ unsigned char other[193]; } Names2; |
Names3 ::= SEQUENCE { ceo VisibleString (SIZE(1..32)), secretary VisibleString (SIZE(1..32)), COMPONENTS OF More } More ::= SEQUENCE { vp VisibleString (SIZE(1..32)), VisibleString (SIZE(1..32)) } |
typedef struct Names3 { char ceo[33]; char secretary[33]; char vp[33]; char visibleString[33]; } Names3; typedef struct More { char vp[33]; char visibleString[33]; } More; |
bit_mask indicates absent or present components that are not referenced via a pointer. These components can be OPTIONAL or DEFAULT.
When components are referenced via a pointer, the non-NULL or NULL pointer value indicates whether the component is present or absent.
ASN.1 | C |
---|---|
X ::= SEQUENCE OF INTEGER |
typedef struct X { struct X *next; int value; } *X; |
SeqDlnkOfInt ::= SEQUENCE --<DLINKED>-- OF INTEGER |
typedef struct SeqDlnkOfInt { struct SeqDlnkOfInt *next; struct SeqDlnkOfInt *prev; int value; } *SeqDlnkOfInt; |
SeqUnbdOfInt ::= SEQUENCE --<UNBOUNDED>-- OF INTEGER |
typedef struct SeqUnbdOfInt { unsigned int count; int *value; } SeqUnbdOfInt; |
SeqSzArOfInt ::= SEQUENCE SIZE(33000) --<ARRAY>-- OF INTEGER |
typedef struct SeqSzArOfInt { unsigned short count; int value[33000]; } SeqSzArOfInt; |
Street-addresses ::= SEQUENCE SIZE (1..20) --<ARRAY>-- OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses { unsigned short count; char value[20][65]; } Street_addresses; |
SeqDlinkPlOfInt ::= SEQUENCE --<DLINKED-PLUS>-- OF INTEGER |
typedef struct SeqDlinkPlOfInt { struct SeqDlinkPlOfInt_node *head; struct SeqDlinkPlOfInt_node *tail; unsigned int count; } SeqDlinkPlOfInt; typedef struct SeqDlinkPlOfInt_node { struct SeqDlinkPlOfInt_node *next; struct SeqDlinkPlOfInt_node *prev; int value; } SeqDlinkPlOfInt_node; |
The first link in the link-list that represents the SEQUENCE OF does not store any values. It is used to indicate whether the SEQUENCE is empty (next = NULL) or not.
empty X ::= {} struct X empty; one X ::= {1} struct X one0, one1; empty.next = NULL; one0.next = &one1; one1.value = 1; one1.next = NULL;
typedef struct Name1 { type1 element1; . . . typen elementn; } Name1;
The elements (element1, for example) are the identifiers associated with each component of the SET type. If an identifier is missing, the compiler generates a C identifier based on the ASN.1 type.
ASN.1 | C | C (with -helperNames) |
---|---|---|
NamesOfOfficers1 ::= SET { president [0] VisibleString (SIZE(1..32)), vicePresident [1] VisibleString (SIZE(1..32)), secretary [2] VisibleString (SIZE(1..32)), [3] VisibleString (SIZE(1..32)) } |
typedef struct NamesOfOfficers1 { char president[33]; char vicePresident[33]; char secretary[33]; char treasurer[33]; } NamesOfOfficers1; |
typedef struct NamesOfOfficers1 { char *president; char *vicePresident; char *secretary; char *visibleString; } NamesOfOfficers1; |
When the COMPONENTS OF notation is used in a SET, the elements of the referenced SET are copied inline.
ASN.1 | C |
---|---|
NamesOfOfficers3 ::= SET { president [0] VisibleString (SIZE(1..32)), vicePresident [1] VisibleString (SIZE(1..32)), COMPONENTS OF MoreOfficers } MoreOfficers ::= [1] SET { secretary [2] VisibleString (SIZE(1..32)), treasurer [3] VisibleString (SIZE(1..32))} |
typedef struct NamesOfOfficers3 { char president[33]; char vicePresident[33]; char secretary[33]; char treasurer[33]; } NamesOfOfficers3; |
If a component of SET is OPTIONAL or has a DEFAULT value, the C representation is of the form:
typedef struct Name1{ unsigned char bit_mask; # define elementj_present 0x80 type1 element1; . . . typen elementn; } Name1;
The bit_mask variable is used to indicate the presence or absence of SET components that are OPTIONAL or have a DEFAULT value, but are not referenced via a pointer. For components that are referenced via a pointer, a non-NULL or NULL pointer value indicates the component's presence or absence.
The type of the bit_mask variable (char, short, int, long, or unsigned char* array) depends on the smallest type that can accommodate all optional components that are not referenced via a pointer.
For each SET component represented in the bitmask, the compiler generates a bitmask manifest constant whose name is the component's name with a suffix of _present. The application programmer should perform a logical AND with this manifest constant and the bit_mask variable to determine if a specific component is present.
ASN.1 | C | C (with -helperNames) |
---|---|---|
NamesOfOfficers2::= SET { president [0] VisibleString (SIZE(1..32)) OPTIONAL, vicePresident [1] VisibleString (SIZE(1..32)), treasurer [2] VisibleString OPTIONAL, secretary [3] VisibleString (SIZE(1..32)) OPTIONAL } |
typedef struct NamesOfOfficers2 { unsigned char bit_mask; #define president_present 0x80 #define secretary_present 0x40 char president[33]; /* optional; set in bit_mask * president_present if present */ char vicePresident[33]; char *treasurer; /* NULL for not present */ char secretary[33]; /* optional; set in bit_mask * secretary_present if present */ } NamesOfOfficers2; |
typedef struct NamesOfOfficers2 { char *president; /* NULL for not present */ char *vicePresident; char *treasurer; /* NULL for not present */ char *secretary; /* NULL for not present */ } NamesOfOfficers2; |
Note that, when -helperNames is not specified, although the field treasurer is OPTIONAL, it is not represented in the bitmask since its pointer can simply be set to the address of a character string or to NULL to indicate its presence or absence, respectively.
Macros generated for SET are identical to the ones generated for the SEQUENCE type.
C | C (with -helperNames) |
---|---|
typedef struct Name1 { struct Name1 *next; Type1 value; } *Name1; |
typedef struct Name1 { struct Name1 *next; Type1 value; } *Name1; typedef struct Name1 { struct Name1_node *head; struct Name1_node *tail; unsigned int count; } Name1; typedef struct Name1_node { struct Name1_node *next; struct Name1_node *prev; Type1 *value; } Name1_node; |
By default, when -helperNames is specified, the DLINKED-PLUS representation is generated for SET OF types with structured or pointered non-structured elements.
The UNBOUNDED representation is generated for SET OF types with simple non-pointered elements:
typedef struct Name1 { unsigned int count; ossBoolean *value; } Name1;
In the absence of directives SizeConstraint does not affect the representation.
ASN.1 | C |
---|---|
SetSzOfInt ::= SET SIZE(5) OF INTEGER |
typedef struct SetSzOfInt { struct SetSzOfInt *next; int value; } *SetSzOfInt; |
If the OSS.ARRAY directive is specified, an array of types is generated, preceded by a count of the number of occurrences that follow. The OSS.POINTER directive is implied when the OSS.ARRAY directive is used. If SizeConstraint is also specified, depending on the array size, a short or int is generated to indicate the size of the array.
ASN.1 | C |
---|---|
SetArOfInt ::= SET --<ARRAY>-- OF INTEGER |
typedef struct SetArOfInt { unsigned int count; int value[1]; /* first element of the array */ } *SetArOfInt; |
If only the OSS.DLINKED directive is specified alone, a pointer to a doubly linked list of values is generated.
ASN.1 | C |
---|---|
SetDlOfInt ::= SET --<DLINKED>-- OF INTEGER |
typedef struct SetDlOfInt { struct SetDlOfInt *next; struct SetDlOfInt *prev; int value; } *SetDlOfInt; |
If the OSS.LINKED directive is specified, a pointer to a linked list of values is generated.
ASN.1 | C |
---|---|
SetLnOfInt ::= SET --<LINKED>-- OF INTEGER |
typedef struct SetLnOfInt { struct SetLnOfInt *next; int value; } *SetLnOfInt; |
If the OSS.UNBOUNDED directive is specified, a pointer to a string of types is generated, preceded by a count of the number of occurrences pointed to.
ASN.1 | C |
---|---|
SetUnOfInt ::= SET --<UNBOUNDED>-- OF INTEGER |
typedef struct SetUnOfInt { unsigned int count; int *value; } SetUnOfInt; |
If the OSS.DLINKED-PLUS directive is specified, two structures are generated. One structure whose name has the _node suffix is a doubly linked list of values similar to the DLINKED representation. The second structure keeps pointers to the head and to the tail nodes in the doubly-linked list as well as the count field where the number of nodes in the list is stored. This structure is used to facilitate linked-list manipulation in the helper list API.
ASN.1 | C |
---|---|
SetDPlusOfInt ::= SET --<DLINKED-PLUS>-- OF INTEGER |
typedef struct SetDPlusOfInt { struct SetDPlusOfInt_node *head; struct SetDPlusOfInt_node *tail; unsigned int count; } SetDPlusOfInt; typedef struct SetDPlusOfInt_node { struct SetDPlusOfInt_node *next; struct SetDPlusOfInt_node *prev; int value; } SetDPlusOfInt_node; |
When the OSS.UNBOUNDED directive is used with SizeConstraint, the UNBOUNDED representation is used. However, if -helperNames is not specified or implied, the type of the count field varies depending on the size needed to accommodate the maximum number of occurrences in the SET OF. Without SizeConstraint, an int is always generated for the count field for the UNBOUNDED representation.
ASN.1 | C | C (with -helperNames) |
---|---|---|
SetSzUnOfInt ::= SET SIZE(33) --<UNBOUNDED>-- OF INTEGER |
typedef struct SetSzUnOfInt { unsigned short count; int *value; } SetSzUnOfInt; |
typedef struct SetSzUnOfInt { unsigned int count; int *value; } SetSzUnOfInt; |
When the OSS.ARRAY directive is used with SizeConstraint, the ARRAY representation is used, but the type of the count field varies depending on the size needed to accommodate the maximum number of occurrences in the SET OF. Without the SizeConstraint, an int is always generated for the count field under the ARRAY representation.
ASN.1 | C |
---|---|
SetSzArOfInt ::= SET SIZE(33000) --<ARRAY>-- OF INTEGER |
typedef struct SetSzArOfInt { unsigned short count; int value[33000]; } SetSzArOfInt; |
SizeConstraint has no effect on the LINKED representation. OSS.POINTER simply introduces an additional level of indirection.
ASN.1 | C |
---|---|
SetSzLnPtOfInt ::= SET SIZE(5) --<LINKED|POINTER>-- OF INTEGER |
typedef struct SetSzLnPtOfInt { struct SetSzLnPtOfInt *next; int value; } **SetSzLnPtOfInt; |
The following examples illustrate the effect of a SizeConstraint on TypeX (where the SET OF type is of the form Street-addresses ::= SET OF TypeX):
ASN.1 | C |
---|---|
Street-addresses1 ::= SET SIZE (1..20) --<ARRAY>-- OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses1 { unsigned short count; char value[20][65]; } Street_addresses1; |
Street-addresses2 ::= SET SIZE (1..20) --<LINKED>-- OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses2 { struct Street_addresses2 *next; char value[65]; } *Street_addresses2; |
Street-addresses3 ::= SET SIZE (1..20) --<UNBOUNDED>-- OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses3 { unsigned short count; char (*value)[65]; } Street_addresses3; |
Here is an example of a nested SET OF with no directives:
ASN.1 | C | C (with -helperNames) |
---|---|---|
Street-addresses4 ::= SET OF SET OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses4 { struct Street_addresses4 *next; struct _setof1 { struct _setof1 *next; char value[65]; } *value; } *Street_addresses4; |
typedef struct Street_addresses4 { struct Street_addresses4_node *head; struct Street_addresses4_node *tail; unsigned int count; } Street_addresses4; typedef struct Street_addresses4_node { struct Street_addresses4_node *next; struct Street_addresses4_node *prev; struct Street_addresses4_setof *value; } Street_addresses4_node; typedef struct Street_addresses4_setof { struct Street_addresses4_setof_node *head; struct Street_addresses4_setof_node *tail; unsigned int count; } Street_addresses4_setof; typedef struct Street_addresses4_setof_node { struct Street_addresses4_setof_node *next; struct Street_addresses4_setof_node *prev; char *value; } Street_addresses4_setof_node; |
Here is an example of a nested SET OF, the OSS.LINKED directive, and the OSS.UNBOUNDED directive:
ASN.1 | C |
---|---|
Street-addresses5 ::= SET --<LINKED>-- OF SET --<UNBOUNDED>-- OF VisibleString (SIZE (1..64)) |
typedef struct Street_addresses5 { struct Street_addresses5 *next; struct { unsigned int count; char (*value)[65]; } value; } *Street_addresses5; |
Macros generated for SET OF are identical to the ones for the SEQUENCE OF type.
ASN.1 | C |
---|---|
TaggedIntegerType ::= INTEGER MySeq ::= SEQUENCE { builtInInt INTEGER, tagggedInt TaggedIntegerType } |
typedef int TaggedIntegerType; typedef struct MySeq { int builtInInt; TaggedIntegerType tagggedInt; } MySeq; |
Note that int and TaggedIntegerType are equated above using a typedef statement.
ASN.1 | C |
---|---|
Time ::= TIME t Time ::= "R/P12Y" Date ::= DATE d Date ::= "1999-10-12" Time t = "R/P12Y";;; Date d = "1999-10-12"; |
typedef char *Time; typedef char *Date; |
These time types are always represented as null-terminated strings.
ASN.1 | C | C (with -helperNames) |
---|---|---|
Name1 ::= UTCTime |
typedef UTCTime Name1;where UTCTime is defined in file asn1hdr.h as typedef GeneralizedTime UTCTime; |
typedef char *Name1; |
Although UTCTime uses the same C structure as GeneralizedTime, the semantics of the two structures differ. When used to represent UTCTime, the year field is in YY format, and the millisec field is ignored by the encoder and set to zero by the decoder.
The UTCTime type can be represented either as UTCTime structs ( { short year; short month; } ) or as strings. Strings are used when the -lean option or the NULLTERM directive is specified.
When the NULLTERM directive is used, UTCTime types are represented as strings. The -lean or -helperNames option has the same effect as NULLTERM.
ASN.1 | C |
---|---|
Name1 ::= UTCTime --<NULLTERM>-- -- If local time is 7am on 2 January 1982 and coordinated universal -- time is 12 noon on 2 January 1982, the value of UTCTime is either of: t1 Name1 ::= "8201021200Z" t2 Name1 ::= "8201020700-0500" |
typedef char *Name1; Name1 t1 = "8201021200Z"; Name1 t2 = "8201020700-0500"; |
The OSS.HelperMacro directive applied to a UTCTime with NULLTERM representation generates the following helper macros:
The OSS.HelperMacro directive applied to a UTCTime with TIMESTRUCT directive generates the following helper macros:
ASN.1 | Helper macros |
---|---|
--<OSS.HelperMacro Mod.SeqUTime>-- --<OSS.HelperMacro Mod.SeqUTime.n-time>-- --<OSS.TIMESTRUCT Mod.SeqUTime.s-time>-- --<OSS.HelperMacro Mod.SeqUTime.s-time>-- Mod DEFINITIONS ::= BEGIN SeqUTime ::= SEQUENCE { n-time UTCTime, s-time UTCTime } END |
typedef struct SeqUTime { char *n_time; UTCTime *s_time; } SeqUTime; ... /* allocates memory for a string of given length */ #define oss_SeqUTime_n_time_new(world, length_) \ oss__CharStr_new(world, length_) /* allocates memory and returns a copy of an input string */ #define oss_SeqUTime_n_time_copy(world, value_) \ oss__CharStr_copy(world, value_) ... /* allocates memory for the time structure */ #define oss_SeqUTime_s_time_new(world) \ (UTCTime *)ossGetInitializedMemory(world, sizeof(UTCTime)) /* allocates memory for the time structure and initializes it by parsing a * string containing an UTCTime value */ #define oss_SeqUTime_s_time_copy_nullterm(world, value_) \ oss__UTCTime_copy_nullterm(world, value_) /* initializes the structure by parsing a string containing an UTCTime value */ #define oss_SeqUTime_s_time_setv_nullterm(world, outp, value_) \ oss__UTCTime_setv_nullterm(world, outp, value_) |
By default, the C representation is NULLTERM-POINTER.
ASN.1 | C | C (with -helperNames) |
---|---|---|
ID ::= OID-IRI --<NULLTERM>-- |
typedef char *ID; |
|
ID ::= OID-IRI --<UNBOUNDED>-- |
typedef struct ID { unsigned int length; char *value; } ID; |
typedef struct _UnbCharStr { unsigned int length; char *value; } _UnbCharStr; typedef _UnbCharStr ID; |
The OSS.HelperMacro applied on the OID-IRI and RELATIVE-OID-IRI types has the same effect as on the restricted character string types.
The representation of ASN.1 macro instances in C is determined by the type returned by the macro instance, irrespective of the C representation of the returned type.
For example, when an ASN.1 macro (ERROR), is defined as follows:
ERROR MACRO ::= BEGIN TYPE NOTATION ::= "PARAMETER" NamedType | empty VALUE NOTATION ::= value (VALUE INTEGER) NamedType ::= identifier type | type END
and is used as follows:
ErrorRecord ::= SEQUENCE { rectype ERROR PARAMETER VisibleString, sector INTEGER }
The C representation is:
typedef struct ErrorRecord { int rectype; int sector; } ErrorRecord;
Each PDU defined in your ASN.1 input files can be represented in C when you compile your syntax. Representations of PDUs can be affected by the following:
Values are declared and initialized in the generated C code. To declare values, you can use either the ASN.1 syntax or the XML syntax. For example, the following lines of code are equivalent:
studentAge INTEGER ::= 23
studentAge ::= <INTEGER>23</INTEGER>
ASN.1 comments are transferred into C code.
ASN.1 tags do not affect the C representation of ASN.1 types.
ASN.1 | C |
---|---|
Int ::= INTEGER Big ::= INTEGER (12345678900) Small ::= INTEGER (255) Var ::= INTEGER --<POINTER>-- |
typedef int Int; typedef ULONG_LONG Big; typedef unsigned short Small; typedef int *Var; |
The ASN.1 extensibility marker ("...") ensures compatibility between current and future versions of the schema. Types defined as extensible receive a bitmask that indicates whether elements defined after the extensibility marker are present or absent.
ASN.1 | C |
---|---|
/* Version 1 */ Names ::= SEQUENCE { president VisibleString (SIZE(1..32)), vicePresident VisibleString (SIZE(1..32)), secretary VisibleString (SIZE(1..32)), ... } |
/* Version 1 */ typedef struct Names { char president[33]; char vicePresident[33]; char secretary[33]; } Names; |
/* Version 2 */ Names ::= SEQUENCE { president VisibleString (SIZE(1..32)), vicePresident VisibleString (SIZE(1..32)), secretary VisibleString (SIZE(1..32)), ..., treasurer VisibleString (SIZE(1..32)) } |
/* Version 2 */ typedef struct Names { unsigned char bit_mask; # define treasurer_present 0x80 char president[33]; char vicePresident[33]; char secretary[33]; char treasurer[33]; /* extension #1; set in bit_mask * treasurer_present if present */ } Names; |
The compiler generates a special code to inform the encoder/decoder about the presence of extensible elements. For each PDU, the decoder is instructed to:
Parameterized types are represented in the same way as types resulted from parameter substitution.
ASN.1 | C |
---|---|
Module DEFINITIONS ::= BEGIN DesiredType ::= INTEGER Record {TypeForSubstitution} ::= SET{ myTypeVar TypeForSubstitution, filled BOOLEAN } MyRec ::= Record {DesiredType} END |
typedef int DesiredType; typedef struct Record { DesiredType myTypeVar; ossBoolean filled; } Record; typedef Record MyRec; |
Information object classes contain three common types of fields:
In the following example, &ArgumentType and &ResultType are type fields, &Errors and &Linked are object set fields, and &resultReturned and &operationCode are value fields:
ASN.1 | C |
---|---|
OPERATION ::= CLASS { &ArgumentType OPTIONAL, &ResultType OPTIONAL, &Errors ERROR OPTIONAL, &Linked OPERATION OPTIONAL, &resultReturned BOOLEAN DEFAULT TRUE, &operationCode INTEGER UNIQUE } ERROR ::= CLASS { &ParameterType OPTIONAL, &errorCode INTEGER UNIQUE } intIsValid OPERATION ::= { &ArgumentType INTEGER, &ResultType BOOLEAN, &Errors {intIsNegative}, &operationCode 7 } intIsNegative ERROR ::= { &errorCode 1 } intIsZero ERROR ::= { &errorCode 2 } NegZeroInfoObjSet ERROR ::= { intIsNegative | intIsZero, ... } |
typedef struct ObjectSetEntry { struct ObjectSetEntry *next; void *object; struct ObjectSetEntry *prev; char *object_name; } ObjectSetEntry; #define IntIsValid_integer_PDU 1 #define IntIsValid_boolean_PDU 2 #define NegZeroInfoObjSet_OSET 1 typedef struct OPERATION { unsigned char bit_mask; # define ArgumentType_present 0x80 # define ResultType_present 0x40 # define resultReturned_present 0x20 unsigned short ArgumentType; unsigned short ResultType; ObjectSetEntry *Errors; /* NULL for not present */ ObjectSetEntry *Linked; /* NULL for not present */ ossBoolean resultReturned; int operationCode; } OPERATION; typedef struct ERROR { unsigned char bit_mask; # define ParameterType_present 0x80 unsigned short ParameterType; int errorCode; } ERROR; typedef int IntIsValid_integer; typedef ossBoolean IntIsValid_boolean; extern OPERATION intIsValid; extern ERROR intIsNegative; extern ERROR intIsZero; |
The ObjectSetEntry structure is used to traverse a series of information objects which together form an information object set. Each object field points to an information object.
The first item in the ObjectSetEntry double-linked list has its prev pointer set to NULL and the last element has its next pointer set to NULL.
The compiler generates bitmasks and #defined constants for OPTIONAL and DEFAULT fields in the information object classes.
The above example is based on clause 11 and 12 of ITU-T Rec. X681 (2021) | ISO/IEC 8824-2 : 2021.
This documentation applies to the OSS® ASN.1 Tools for C release 11.3 and later.
Copyright © 2024 OSS Nokalva, Inc. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording or otherwise, without the prior permission of OSS Nokalva, Inc.
Every distributed copy of the OSS® ASN.1 Tools for C is associated with a specific license and related unique license number. That license determines, among other things, what functions of the OSS ASN.1 Tools for C are available to you.