Generated Streaming C Function Format and Calling Parameters

The format of the name of each generated streaming encode function is as follows:

   asn1BSE_[<prefix>]<prodName>

where <prodName> is the name of the ASN.1 production for which the function is being generated and <prefix> is an optional prefix that can be set via a configuration file setting. The configuration setting used to set the prefix is the <typePrefix> element. This element specifies a prefix that will be applied to all generated typedef names and function names for the production.

The calling sequence for each encode function is as follows:

   stat = asn1BSE_<name> (OSCTXT* pctxt,
                          <name>* pvalue,
                          ASN1TagType tagging);

In this definition, <name> denotes the prefixed production name defined above.

The pctxt argument is used to hold a context pointer to keep track of encode parameters. This is a basic "handle" variable that is used to make the function reentrant so it can be used in an asynchronous or threaded application. The user is required to supply a pointer to a variable of this type declared somewhere in his or her program. This variable must be initialized using the berStrmInitContext run-time library function (see the C/C++ Common Run-Time Library Reference Manual for a description of this function).

The pvalue argument holds a pointer to the data to be encoded and is of the type generated from the ASN.1 production.

The tagging argument is for internal use when calls to encode functions are nested to accomplish encoding of complex variables. It indicates whether the tag associated with the production should be applied or not (implicit versus explicit tagging). At the top level, the tag should always be applied so this parameter should always be set to the constant ASN1EXPL (for EXPLICIT).

The function result variable stat returns the completion status of the operation. 0 (0) means the success.

Procedure for Calling Streaming C Encode Functions

This section describes the step-by-step procedure for calling a streaming C BER encode function. This method must be used if C code generation was done. This method can also be used as an alternative to using the control class interface if C++ code generation was done.

Before any encode function can be called; the user must first initialize an encoding context. This is a variable of type OSCTXT. This variable holds all of the working data used during the encoding of a message. The context variable is within the top-level calling function. It must be initialized before use. This can be accomplished by using the berStrmInitContext function:

   OSCTXT ctxt;

   if (berStrmInitContext (&ctxt) != 0) {
      /* initialization failed, could be a license problem */
      printf (“context initialization failed (check license)\n”);
      return –1;
   }

The next step is to create a stream object within the context. This object is an abstraction of the output device to which the data is to be encoded and is initialized by calling one of the following functions:

  • rtxStreamFileOpen

  • rtxStreamFileAttach

  • rtxStreamSocketAttach

  • rtxStreamMemoryCreate

  • rtxStreamMemoryAttach

The flags parameter of these functions should be set to the OSRTSTRMF_OUTPUT constant value to indicate an output stream is being created (see the C/C++ Common Run-Time Library Reference Manual for a full description of these functions).

It is also possible to use a simplified form of these function calls to create a writer interface to a file, memory, or socket stream:

  • rtxStreamFileCreateWriter

  • rtxStreamMemoryCreateWriter

  • rtxStreamSocketCreateWriter

After initializing the context and populating a variable of the structure to be encoded, an encode function can be called to encode the message to the stream. The stream must then be closed by calling the rtxStreamClose function.

A program fragment that could be used to encode an employee record is as follows:

   #include employee.h            /* include file generated by ASN1C */

   int main ()
   {
      int       stat;
      OSCTXT    ctxt;
      Employee  employee;/* typedef generated by ASN1C */
      const char* filename = “message.dat”;

      /* Step 1: Initialize the context and stream */

      if (berStrmInitContext (&ctxt) != 0) {
         /* initialization failed, could be a license problem */
         printf (“context initialization failed (check license)\n”);
         return –1;
      }

      /* Step 2: create a file stream object within the context */

      stat = rtxStreamFileCreateWriter (&ctxt, filename);
      if (stat != 0) {
         rtxErrPrint (&ctxt);
         return stat;
      }

      /* Step 3: Populate the structure to be encoded */

      employee.name = "SMITH";
      ...

      /* Step 4: Call the generated encode function */

      stat = asn1BSE_Employee (&ctxt, &employee, ASN1EXPL);

      /* Step 5: Check the return status and close the stream */

      if (stat != 0) {

         ...error processing...
      }

      rtxStreamClose (&ctxt);
   }

In general, streaming encoding is slower than memory buffer based encoding. However, in the case of streaming encoding, it is not necessary to implement code to write or send the encoded data to an output device. The streaming functions also use less memory because there is no need for a large destination memory buffer. For this reason, the final performance of the streaming functions may be the same or better than buffer-oriented functions.

Encoding a Series of Messages Using the Streaming C Encode Functions

A common application of BER encoding is the repetitive encoding of a series of the same type of message over and over again. For example, a TAP3 batch application might read billing data out of a database table and encode each of the records for a batch transmission.

Encoding a series of messages using the streaming C encode functions is very similar to encoding of one message. All that is necessary is to set up a loop in which the asn1BSE_<name> functions will be called. It is also possible to call different asn1BSE_<name> functions one after another. An example showing how to do this is as follows:

   #include employee.h          // include file generated by ASN1C

   int main ()
   {
      int       stat;
      OSCTXT    ctxt;
      Employee  employee;/* typedef generated by ASN1C */
      const char* filename = “message.dat”;

      /* Step 1: Initialize the context and stream */

      if (berStrmInitContext (&ctxt) != 0) {
         /* initialization failed, could be a license problem */
         printf (“context initialization failed (check license)\n”);
         return –1;
      }

      stat = rtxStreamFileCreateWriter (&ctxt, filename);
      if (stat != 0) {
         rtxErrPrint (&ctxt);
         return stat;
      }

      for (;;) {
         /* Step 2: Populate the structure to be encoded */

         employee.name = "SMITH";
         ...

         /* Step 3: Call the generated encode function */

         stat = asn1BSE_Employee (&ctxt, &employee, ASN1EXPL);

         /* Step 4: Check the return status and break the loop
            if error occurs */

         if (stat != 0) {

            ...error processing...

            break;
         }
      }

      /* Step 5: Close the stream */

      rtxStreamClose (&ctxt);
   }