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.
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.
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); }