The format of the name of each streaming decode function generated is as follows:
asn1BSD_[<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 = asn1BSD_<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 berStrmInitContext 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 rtxErrCodes.h include file.
This section describes the step-by-step procedure for calling a streaming C BER decode function. This procedure must be followed if C code generation was done. This procedure 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 by using the berStrmInitContext function.
OSCTXT ctxt; // context variable 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_INPUT constant value to indicate an input stream is being created (see the C/C++ Common Run-Time Library Reference Manual for a full description of these functions).
A simplified version of the Open functions are the CreateReader functions:
rtxStreamFileCreateReader
rtxStreamMemoryCreateReader
rtxStreamSocketCreateReader
After initializing the context and populating a variable of the structure to be encoded, a decode function can be called to decode a message from the stream. 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.
After stream processing is complete, the stream is closed by invoking the rtxStreamClose function.
A program fragment that could be used to decode an employee record is as follows:
#include “employee.h” /* include file generated by ASN1C */ #include "rtxsrc/rtxStreamFile.h" main () { ASN1TAG msgtag; int msglen; OSCTXT ctxt; PersonnelRecord employee; const char* filename = “message.dat” /* Step 1: Initialize a context variable for decoding */ if (berStrmInitContext (&ctxt) != 0) { /* initialization failed, could be a license problem */ printf (“context initialization failed (check license)\n”); return –1; } /* Step 2: Open the input stream to read data */ stat = rtxStreamFileCreateReader (&ctxt, filename); if (stat != 0) { rtxErrPrint (&ctxt); return stat; } /* Step 3: 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 ((stat = berDecStrmPeekTagAndLen (&ctxt, &tag, &len)) != 0) { rtxErrPrint (&ctxt); return stat; } if (msgtag == TV_PersonnelRecord) { /* Step 4: Call decode function (note: last two args */ /* should always be ASN1EXPL and 0).. */ status = asn1BSD_PersonnelRecord (&ctxt, &employee, ASN1EXPL, 0); /* Step 5: Check return status */ if (status == 0) { process received data in ‘employee’ variable.. } else error processing... } else check for other known message types.. /* Step 6: Close the stream */ rtxStreamClose (&ctxt); /* Remember to release dynamic memory when done! */ rtFreeContext (&ctxt); }
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.
A code fragment showing a way to do this is as follows:
#include “employee.h” /* include file generated by ASN1C */ #include "rtxsrc/rtxStreamFile.h" main () { ASN1TAG msgtag; int msglen, stat; OSCTXT ctxt; PersonnelRecord employee; const char* filename = “message.dat” /* Step 1: Initialize a context variable for decoding */ if (berStrmInitContext (&ctxt) != 0) { /* initialization failed, could be a license problem */ printf (“context initialization failed (check license)\n”); return –1; } /* Step 2: Open the input stream to read data */ stat = rtxStreamFileCreateReader (&ctxt, filename); if (stat != 0) { rtxErrPrint (&ctxt); return stat; } for (;;) { /* Step 3: 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 ((stat = berDecStrmPeekTagAndLen (&ctxt, &tag, &len)) != 0) { rtxErrPrint (&ctxt); return stat; } if (msgtag == TV_PersonnelRecord) { /* Step 4: Call decode function (note: last two args */ /* should always be ASN1EXPL and 0).. */ stat = asn1BSD_PersonnelRecord (&ctxt, &employee, ASN1EXPL, 0); /* Step 5: Check return status */ if (stat == 0) { process received data in ‘employee’ variable.. } else error processing... } else check for other known message types.. /* Need to reset all memory for next iteration */ rtxMemReset (&ctxt); } /* end of loop */ /* Step 6: Close the stream */ rtxStreamClose (&ctxt); /* Remember to release dynamic memory when done! */ rtFreeContext (&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.