Generated C Function Format and Calling Parameters

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

   asn1D_[<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 decode function is as follows:

   status = asn1D_<name> (OSCTXT* pctxt,
                          <name> *pvalue,
                          ASN1TagType tagging,
                          int length);

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 decode 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. The variable must be initialized using the rtInitContext run-time function before use.

The pvalue argument is a pointer to a variable of the generated type that will receive the decoded data.

The tagging and length arguments are for internal use when calls to decode functions are nested to accomplish decoding of complex variables. At the top level, these parameters should always be set to the constants ASN1EXPL and zero respectively.

The function result variable status returns the status of the decode operation. The return status will be zero if decoding is successful or negative if an error occurs. Return status values are defined in the "asn1type.h" include file.

Procedure for Calling C Decode Functions

This section describes the step-by-step procedure for calling a C BER or DER decode 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 decode function can be called; the user must first initialize a context variable. This is a variable of type OSCTXT. This variable holds all of the working data used during the decoding of a message. The context variable is declared as a normal automatic variable within the top-level calling function. It must be initialized before use. This can be accomplished as follows:

   OSCTXT ctxt;

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

The next step is the specification of a buffer containing a message to be decoded. This is accomplished by calling the xd_setp run-time library function. This function takes as an argument the start address of the message to be decoded. The function returns the starting tag value and overall length of the message. This makes it possible to identify the type of message received and apply the appropriate decode function to decode it.

A decode function can then be called to decode the message. If the return status indicates success, the C variable that was passed as an argument will contain the decoded message contents. Note that the decoder may have allocated dynamic memory and stored pointers to objects in the C structure. After processing on the C structure is complete, the run-time library function rtxMemFree should be called to free the allocated memory.

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

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

   main ()
   {
      OSOCTET   msgbuf[1024];
      ASN1TAG   msgtag;
      int       msglen;
      OSCTXT    ctxt;
      PersonnelRecord employee;

      .. logic to read message into msgbuf ..

      /* Step 1: Initialize a context variable for decoding */

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

      xd_setp (&ctxt, msgbuf, 0, &msgtag, &msglen);

      /* Step 2: Test message tag for type of message received */
      /* (note: this is optional, the decode function can be */
      /* called directly if the type of message is known).. */

      if (msgtag == TV_PersonnelRecord)
      {
         /* Step 3: Call decode function (note: last two args */
         /* should always be ASN1EXPL and 0).. */

         status = asn1D_PersonnelRecord (&ctxt,
                                         &employee,
                                         ASN1EXPL, 0);

         /* Step 4: Check return status */

         if (status == 0)
         {
            process received data in ‘employee’ variable..

            /* Remember to release dynamic memory when done! */

            rtxMemFree (&ctxt);
         }
         else
            error processing...
      }
      else
         check for other known message types..
   }

Decoding a Series of Messages Using the C Decode Functions

The above example is fine as a sample for decoding a single message, but what happens in the more typical scenario of having a long-running loop that continuously decodes messages? It will be necessary to put the decoding logic into a loop:

   main ()
   {
      OSOCTET   msgbuf[1024];
      ASN1TAG   msgtag;
      int       msglen;
      OSCTXT    ctxt;
      PersonnelRecord employee;

      /* Step 1: Initialize a context variable for decoding */

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

      for (;;) {

         .. logic to read message into msgbuf ..

         xd_setp (&ctxt, msgbuf, 0, &msgtag, &msglen);

         /* Step 2: Test message tag for type of message received */
         /* (note: this is optional, the decode function can be */
         /* called directly if the type of message is known).. */

         /* Now switch on initial tag value to determine what type of
            message was received.. */

         switch (msgtag)
         {
            case TV_PersonnelRecord: /* compiler generated constant */
            {
               status = asn1D_PersonnelRecord (&ctxt,
                                               &employee,
                                               ASN1EXPL, 0);
               if (status == 0)
               {

               /* decoding successful, data in employee */

               process received data..
            }
            else
               error processing...
         }
         break;

         default:
            /* handle unknown message type here */
      } /* switch */

      /* Need to reinitialize objects for next iteration */

      rtxMemReset (&ctxt);
   }
}

The only changes were the addition of the for (;;) loop and the call to rtxMemReset that was added at the bottom of the loop. This function resets the memory tracking parameters within the context to allow previously allocated memory to be reused for the next decode operation. Optionally, rtxMemFree can be called to release all memory. This will allow the loop to start again with no outstanding memory allocations for the next pass.

The example above assumes that logic existed that would read each message to be processed into the same buffer for every message processed inside the loop (i.e the buffer is reused each time). In the case in which the buffer already contains multiple messages, encoded back-to-back, it is necessary to advance the buffer pointer in each iteration:

   main ()
   {
      OSOCTET   msgbuf[1024];
      ASN1TAG   msgtag;
      int       offset = 0, msglen, len;
      OSCTXT    ctxt;
      PersonnelRecord employee;
      FILE*     fp;

      /* Step 1: Initialize a context variable for decoding */

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

      if (fp = fopen (filename, "rb")) {
         msglen = fread (msgbuf, 1, sizeof(msgbuf), fp);
      }
      else {
         ... handle error ...
      }

      for (; offset < msglen; ) {
         xd_setp (&ctxt, msgbuf + offset, msglen - offset, &msgtag, &len);

         /* Decode */

         if (tag == TV_PersonnelRecord) {

            /* Call compiler generated decode function */

            stat = asn1D_PersonnelRecord (&ctxt, &employee, ASN1EXPL, 0);
            if (stat == 0) {

               /* decoding successful, data in employee */

            }
            else {
               /* error handling */
               return -1;
            }
         }
         else {
            printf ("unexpected tag %hx received\n", tag);
         }
         offset += ctxt.buffer.byteIndex;
         rtxMemReset (&ctxt);
      }
   }