This section describes the step-by-step procedure for calling a C OER encode function. This method must be used if C code generation was done.
Before an OER encode function can be called, the user must first initialize an encoding context block structure. The context block is initialized by calling the rtInitContext function.
The user then has the option to do stream-based or memory-based encoding. If stream-based is to be done, the user must call a rtxStreamCreateWriter function for the type of stream to which data will be written. For example, if the user wishes to write data to a file, the rtxStreamFileCreateWriter function would be called.
To do memory-based encoding, the rtxInitContextBuffer function would be called. This can be used to specify use of a static or dynamic memory buffer. Specification of a dynamic buffer is possible by setting the buffer address argument to null and the buffer size argument to zero.
An encode function can then be called to encode the message. If the return status indicates success (0), then the message will have been encoded in the given buffer or written to the given stream. OER encoding starts from the beginning of the buffer and proceeds from low memory to high memory until the message is complete. This differs from definite-length BER where encoding was done from back-to-front. Therefore, the buffer start address is where the encoded OER message begins. The length of the encoded message can be obtained by calling the rtxCtxtGetMsgLen run-time function. If dynamic encoding was specified (i.e., a buffer start address and length were not given), the rtxCtxtGetMsgPtr run-time function can be used to obtain the start address of the message. This routine will also return the length of the encoded message. If a memory stream was used, the message start address and length can be obtained by calling the rtxStreamMemoryGetBuffer function.
A program fragment that could be used to encode an employee record is as follows:
#include employee.h /* include file generated by ASN1C */ main () { PersonnelRecord employee; OSCTXT ctxt; OSOCTET* msgptr; int i, len, stat; const char* filename = "message.dat"; /* Populate employee C structure */ asn1Init_PersonnelRecord (&employee); employee.name.givenName = "SMITH"; ... /* Initialize context */ stat = rtInitContext (&ctxt); if (stat != 0) { printf (“rtInitContext failed (check license)\n“); rtxErrPrint (&ctxt); return stat; } /* Create memory output stream */ stat = rtxStreamMemoryCreateWriter (&ctxt, 0, 0); if (stat < 0) { printf ("Create memory output stream failed\n"); rtxErrPrint (&ctxt); rtFreeContext (&ctxt); return stat; } /* Encode */ stat = OEREnc_PersonnelRecord (&ctxt, &employee); msgptr = rtxStreamMemoryGetBuffer (&ctxt, &len); if (trace) { printf ("Hex dump of encoded record:\n"); rtxHexDump (msgptr, len); } ... }
In general, static buffers should be used for encoding messages where possible as they offer a substantial performance benefit over dynamic buffer allocation. The problem with static buffers, however, is that you are required to estimate in advance the approximate size of the messages you will be encoding. There is no built-in formula to do this, the size of an ASN.1 message can vary widely based on data types and other factors.
The use of streams is a good alternative for large messages as the entire encoded message does not need to fit into memory.