= The API

This section describes the TITAN Standalone ASN.1 Encoder's API on {cpp} level.

== Mapping ASN.1 Data Types to {cpp} Constructs

The mapping of ASN.1 data types to {cpp} takes place in two steps.

First, ASN.1 data types are mapped to TTCN-3. This is described in section 8 of the <<_2, 'Using ASN.1 with TTCN-3' part of the TTCN-3 standard>>.

Afterward, TTCN-3 data types are mapped to {cpp} classes. This is described in sections 6.1 through 6.4 of the <<_3, TITAN API guide>>.

== Contents of the Generated Code

The generated {cpp} header and source files contain the following:

- a class for each structured type defined in the ASN.1 module, or a `typedef` of an existing class for each type alias or subtype defined in the ASN.1 module (subtype constraints are ignored in both cases);
- an encoder and a decoder helper function for each type defined in the ASN.1 module, which can be used to encode or decode values of that type;
- an `init_module` function, which initializes all data related to the ASN.1 module's generated code;
- a type descriptor object for each type defined in the ASN.1 module, which contains data needed for encoding;
- a constant for each value defined in the ASN.1 module;
- all of the above are placed into a namespace, whose name is generated from the ASN.1 module's name.

== Usage Tips and Example

In order to use the types or values defined in an ASN.1 module from {cpp}, the header file generated from that module must be included, and the `init_module` function generated for that module must be called. If an ASN.1 module imports from another module, then its generated header will automatically include the imported module's header, and its `init_module` function will automatically call the imported module's `init_module` function.

NOTE: Everything defined in a module's generated code is inside a namespace created for that module. A namespace prefix, or a `using` declaration, will be required when referencing these definitions.

=== Initializing and Using Values

All values declared in ASN.1 are included in the generated code and can be accessed from {cpp}, including the default values of `SEQUENCE`/`SET` fields.

New values of ASN.1 types can also be defined in {cpp}. They can be initialized using the constructor of the corresponding class and various class methods.

Instances of classes corresponding to built-in ASN.1 types can be initialized with basic {cpp} values through their constructors. For example `INTEGER` instances can be initialized from an `int`, and string types can be initialized from a `const char*`.

Classes of structured types can be initialized one field or element at a time. `SEQUENCE`, `SET` and `CHOICE` classes have methods for accessing or setting each field, named after the field. `SEQUENCE OF` and `SET OF` classes have indexing operators for accessing or setting their elements. An optional field can be omitted in {cpp} by setting it to the special value `OMIT_VALUE`.

Classes of structured types also have a `set_implicit_omit` method, which sets all optional fields to the `OMIT_VALUE` and all default fields to their default values.

WARNING: Performing most operations on an unbound (i.e. uninitialized) object of a class mapped from ASN.1 will result in a runtime error. The object's `is_bound` method can be used to safely check whether it is initialized or not. An unbound optional field is not the same as an omitted optional field.

=== Encoding and Decoding

The Standalone ASN.1 Encoder supports the `BER`, `PER`, `OER` and `JSON` codecs for ASN.1 values. The `XER` codec is currently not supported.

The generated code provides a global encoder and decoder function for each type defined in the ASN.1 module. The names of these functions contain the {cpp} version of the type name, followed by `_encoder` and `_decoder`.

For example, the encoder and decoder functions for a type called `MyType` would have the following prototypes:

[source]
----
void MyType_encoder(const MyType& input_value, OCTETSTRING& output_stream, TTCN_EncDec::coding_t coding_type, unsigned int extra_options);
INTEGER MyType_decoder(OCTETSTRING& input_stream, MyType& output_value, TTCN_EncDec::coding_t coding_type, unsigned int extra_options);
----

The `OCTETSTRING` class (i.e. `OCTET STRING` in ASN.1) is used as the buffer that stores encoded ASN.1 values.

The `coding_type` parameter indicates which codec to use, and `extra_options` sets additional options for the specified codec. These are detailed in section 5 of the <<_3, TITAN API guide>>.

The encoder function overwrites the initial value of `output_stream` with the encoded value. The value of `input_value` is left untouched.

The decoder function overwrites the initial value of `output_value` with the decoded value. The encoding of `output_value` is extracted from the beginning of `input_stream`, if decoding was successful. The return value can be one of the following:

- `0`, if decoding was successful;
- `1`, if the buffer contains invalid data;
- `2`, if the buffer contains insufficient, but otherwise valid data.

Alternatively, the `encode` and `decode` member functions of each {cpp} class mapped from an ASN.1 type can be used directly. This requires using the `TTCN_Buffer` class (or its alias `ASN_Buffer`) and type descriptors.

The coders can be instructed about what to do when certain error situations occur. This is done through the `TTCN_EncDec` class (or its alias `ASN_EncDec`), which is described in section 5.1.1 of the <<_3, TITAN API guide>>.

NOTE: By default, the generated decoder functions throw exceptions for most error situations (instead of returning `1` or `2`). If these return values are desired instead of an exception, then the error situations need to be set to warnings.

=== Logging and Error Handling

The Standalone ASN.1 Encoder does not have a built-in logging tool. Instead, the `log` member functions of {cpp} class mapped from ASN.1 types return a `CHARSTRING` value.

[source]
----
CHARSTRING MyType::log() const
{
  ...
}
----

The returned `CHARSTRING` can be converted on demand to `const char *` using its overloaded casting operator, like in the following example:

[source]
----
INTEGER a = INTEGER(5);
printf("Value of a: %s\n", (const char*) a.log());
----

Errors that occur while using the generated code or the base library throw an exception of type `TTCN_Error` (or its alias `ASN_Error`). This has a method called `get_message`, that returns the error message as a `char *`.

Warnings that occur while using the generated code or the base library are printed onto the standard error.

NOTE: Most error and warning messages will refer to TTCN-3 language elements instead of ASN.1 ones (e.g. a `SEQUENCE` will be referred to as a `record`, a `CHOICE` will be referred to as a `union`, etc.). The string returned by the `log` methods contains the TTCN-3 representation of the value.

=== Example

This section contains an example of using a type defined in ASN.1 for PER encoding and decoding in {cpp}.

MyModule.asn:

[source]
----
MyModule
DEFINITIONS
AUTOMATIC TAGS ::= BEGIN
IMPORTS ;

MySequence ::= SEQUENCE {
  f1 [1] OCTET STRING,
  f2 [0] OCTET STRING,
  f3 [16] OCTET STRING OPTIONAL,
  ...,
  e1 OCTET STRING,
  e2 [2] OCTET STRING,
  [[
    g1 [3] OCTET STRING,
    g2 [4] OCTET STRING OPTIONAL
  ]],
  e3 [7] INTEGER (2),
  ...,
  f4 [15] OCTET STRING DEFAULT ''H
}

END
----

PerTest.cc:

[source]
----
#include "MyModule.hh"

int main() {
  try {
    // initializing the ASN.1 module
    MyModule::init_module();

    // initializing a value of type MySequence
    MyModule::MySequence myValue;
    myValue.f1() = OCTETSTRING("12");
    myValue.f2() = OCTETSTRING("34");
    myValue.f3() = OCTETSTRING("56");
    myValue.g1() = OCTETSTRING("AB");
    myValue.e3() = INTEGER(2);
    myValue.f4() = OCTETSTRING("78");
    myValue.set_implicit_omit();
    
    // PER encoding
    OCTETSTRING myBuffer;
    MyModule::MySequence_encoder(myValue, myBuffer,
      ASN_EncDec::CT_PER, PER_ALIGNED);
    printf("Encoding: %s\n", (const char*) myBuffer.log());
    
    // PER decoding
    MyModule::MySequence myDecodedValue;
    INTEGER myResult = MyModule::MySequence_decoder(myBuffer, myDecodedValue,
      ASN_EncDec::CT_PER, PER_ALIGNED);
    printf("Decoding: %s (returned: %s, remaining bytes: %s)\n",
      (const char*) myDecodedValue.log(),
      (const char*) myResult.log(),
      (const char*) myBuffer.log());
      
    return 0;
  }
  catch (ASN_Error e) {
    printf("%s\n", e.get_message());
    return 1;
  }
}
----

Result:

[source]
----
Encoding: 'E001120134015601780660030001AB0100'O
Decoding: { f1 := '12'O, f2 := '34'O, f3 := '56'O, e1 := <unbound>, e2 := <unbound>, g1 := 'AB'O, g2 := omit, e3 := 2, f4 := '78'O } (returned: 0, remaining bytes: ''O)
----
