The capability to print the contents of binary-encoded data in a human-readable form has always been an often used feature of our code generation products.  We have had since the beginning the standard -print code generation option for generating code that would print the contents of generated data structures to standard output.  These are straight-forward and simple to use.  But it was not long after that users wanted a way to print to other mediums, most commonly to a text buffer (i.e character array) so that the printed data could be sent to other places, for example, a display window within a GUI.

For this, the "print-to-string" capability was added.  The command-line option -prttostr was added for this purpose and the generated functions allowed a text buffer and size to be passed to receive the printed data.

This worked OK and was fine for printing small amounts of data.  But what we found was that users frequently wanted to print very large data structures using this capability and this led to very slow performance.  The primary reason for this was because in order to append to the buffer that was passed, the end of the string buffer had to be found and the only way to do this lacking any other state information was to make a call to the string length (strlen) run-time function.  If this was done over and over on a very large buffer containing a very large string, it quickly became a compute-intensive operation.

In order to remedy this, we introduced the "print-to-stream" capability.  This provided more flexibility in printing as a user-defined callback function could be declared that would be invoked to handle the printing of each individual data item.  Users could provide their own user-defined data structures to the callback functions making it possible to maintain state between operations.  One example of this would be in keeping track of where the end of a string buffer was after each print operation making the appending of additional data much faster.

We declared "print-to-string" to be deprecated in favor of the new "print-to-stream" capability, but what we have found 15 years down the road is print-to-string still being used.  A few possible reasons for this are 1) that is the way some users started doing it and did not want to change, and 2) it is simpler to use these functions as all that is necessary is to pass a string buffer directly to the function instead of having to design a callback function.

To address the latter point, as part of our next ASN1C release next year, we will provide a built-in callback function that can be used to model print-to-string using print-to-stream.  If users don't want to wait that long, the code for the new callback is shown below:

void rtxPrintStreamToStringCB
(void* pPrntStrmInfo, const char* fmtspec, va_list arglist)
{
   OSRTStrBuf* bufp = (OSRTStrBuf*) pPrntStrmInfo;
   if (bufp->bufsize > bufp->endx) {
      vsnprintf (&bufp->strbuf[bufp->endx], bufp->bufsize - bufp->endx,
                 fmtspec, arglist);
      bufp->endx += strlen (&bufp->strbuf[bufp->endx]);
   }
}

The OSRTStrBuf structure is defined as follows:

typedef struct {
   char* strbuf;
   OSSIZE bufsize;
   OSSIZE endx;
} OSRTStrBuf;

The code can then be inserted into a C program to print a populated type structure:

OSRTStrBuf strBufDescr;
char strbuf[10240];
...
/* Set up print-to-stream callback to write to character string buffer */
strBufDescr.strbuf = strbuf;
strBufDescr.bufsize = sizeof(strbuf);
strBufDescr.endx = 0;

rtxSetPrintStream (&ctxt, &rtxPrintStreamToStringCB, (void*)&strBufDescr);

asn1PrtToStrm_ (&ctxt, "Data", &data);

In this code snippet, <type> would be replaced with the type name of the data structure to be printed. The result of the call would be the data printed to the strbuf character array. Be sure to create a large enough array to hold all of the printed data. If not large enough, the data will be truncated.


Published

Category

ASN1C