

\documentstyle[11pt]{report}
\newlength{\spacing}
\setlength{\spacing}{\baselineskip}
\newcommand{\nspace}[1]{\setlength{\baselineskip}{#1\spacing}}
\newenvironment{linespacing}[1]{\nspace{#1}}{}

% myitemize environment - less space between items
\newenvironment{myitemize}[0]{\begin{itemize}\nspace{0.5}} {\end{itemize}}

\setlength{\topmargin}{-.50in}
\setlength{\textwidth}{6.0in}
\setlength{\textheight}{8.5in}
\setlength{\oddsidemargin}{.25in}
\def\myparindent{0in}
\setlength{\parindent}{\myparindent}
\def\myparskip{8pt}
\setlength{\parskip}{\myparskip}
\def\myparsep{\parsep}
\def\myitemsep{\itemsep}


\begin{document}
\thispagestyle{empty}
\title{Snacc 1.0: A High Performance ASN.1 to C/C++ Compiler}
\author{Michael Sample and Gerald Neufeld\\
        msample@cs.ubc.ca neufeld@cs.ubc.ca\\\\
        Department of Computer Science\\
        University of British Columbia\\
        6356 Agricultural Rd.\\
        Vancouver, British Columbia\\
        Canada, V6T 1Z2}


\date{February 1993}
\maketitle

% copyright notice on software and documentation

This work was made possible by grants from the Canadian Institute for
Telecommunications Research (CITR) and Natural Sciences and
Engineering Research Council of Canada (NSERC).

Copyright (C) 1990, 1991, 1992, 1993 Michael Sample
          and the University of British Columbia

This program, snacc, is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The runtime libraries are released under the GNU Library General
Public License.  They are free software; you can redistribute them
and/or modify them under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program and the associated libraries are distributed in the hope
that they will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the GNU General Public License and GNU Library General
Public License for more details.

You should have received a copy of the GNU General Public License and
the GNU Library General Public License along with this program; if
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.

Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
are preserved on all copies.

Permission is granted to copy and distribute modified versions of this
manual under the conditions for verbatim copying, provided that the
entire resulting derived work is distributed under the terms of a
permission notice identical to this one.

% table of contents
\setlength{\parskip}{0pt}
\tableofcontents
\setlength{\parskip}{\myparskip}

% list of figures
%\listoffigures

% list of tables
%listoftables

\chapter{\label{intro-chp}Introduction}

Snacc compiles ASN.1 \cite{X.208} (Abstract Syntax Notation One)
modules into C or C++.  The generated C or C++ code contains
equivalent data structures and routines to convert values between the
internal (C or C++) representation and the corresponding BER
\cite{X.209} (Basic Encoding Rules) format.  The name ``snacc'' is an
acronym for ``Sample Neufeld ASN.1 to C/C++ Compiler''.


The ASN.1 data structure language can specify complex types such as
lists and recursively defined types. BER data values are defined
independently of any computer architecture, providing a universal data
value representation that is useful for sharing data in heterogeneous
networks.

The process of converting an ASN.1 value from its C or C++
representation into an equivalent BER data value is called encoding
and the reverse process is called decoding.  This document was written
assuming that the reader is familiar ASN.1 and BER.  Further
information on ASN.1 and BER can be found in \cite{ASN.1Book},
\cite{ASN.1Overview}, \cite{X.208} and \cite{X.209}.

Compiling ASN.1 into C is not a new idea but many other tools such as
UBC's CASN1 \cite{CASN1}, ISODE's PEPY/POSY \cite{ISODE}, and
commercial tools either do not parse ASN.1 '90, produce slow encoders
and decoders or are outrageously expensive. The aim of this tool is to
provide an ASN.1 compiler that parses ASN.1 '90, produces efficient
encoding and decoding routines and is freely available.  Effort has
been made to make the generated encoders and decoders relatively easy
to fit into different software environments.

Some of snacc's features include:
\begin {itemize}
\item {parses CCITT ASN.1 '90 including  subtype notation}
\item {can compile and link inter-dependent ASN.1 modules (IMPORTS/EXPORTS)}
\item {some X.400 and SNMP macros are parsed}

\item {macro {\em definitions} do not generate syntax errors but are
are not processed. The macro definitions are retained as a string
internally (if you want to modify the compiler to process them).}

\item {value notation is parsed. OBJECT IDENTIFIERs, INTEGERs
and BOOLEANs are translated to C/C++ values. Any other value in \{\}'s
is kept as a string internally (if you want to modify the compiler to
process them).} 

\item {optionally supports ``;'' separated type or value definitions
      in the ASN.1 source. This is useful for dealing with some macros and
      other language ambiguities that introduce parsing problems.}
\item {ANY DEFINED BY types are supported via the SNMP OBJECT-TYPE macro}
\end{itemize}

\section{\label{install-sec}Installing snacc}

First of all, if you haven't already done so, un-archive snacc to
produce the directory {\em snacc} and its contents.  The following tools
are required to compile snacc:
\begin{itemize}
\item {\verb$lex$ or GNU's \verb$flex$ (\verb$flex$ is recommended)}
\item {\verb$yacc$ or GNU's \verb$bison$ (\verb$bison$ is recommended)}
\item {a C compiler and \verb$make$}
\end{itemize}

Some versions of \verb$yacc$ will choke due to the large size of the
\verb$asn1.yacc$ file, however, I have had no problems with \verb$bison$.
Our \verb$yacc$ grammar for ASN.1 has 61 shift/reduce errors and 2
reduce/reduce errors.  Most of these errors were introduced when
certain macros were added to the compiler.  Some of the shift/reduce
errors will require you to follow the offending macro in the ASN.1
module with a semi-colon.  The reduce/reduce errors were introduced by
macros that have ``Type or Value Lists'' because the NULL Type and
NULL values use the same symbol, ``NULL''.  This is not a problem
since no real processing is done with the macros in question at the
present.

\verb$Lex$ will work for the \verb$asn1.lex$ file but \verb$flex$ will typically
produce a smaller executable.  Most versions of \verb$lex$ have a small
maximum token size that will cause problems for long tokens in the
ASN.1 source files, such as quoted strings.  To avoid this problem,
increase the \verb$YYLMAX$ value in the generated {\em lex.yy.c} file to at least
2048.  \verb$Flex$ does not seem to have this problem.

The compiler and library C code has been written to support ANSI or
non-ANSI C.  ANSI C is used by default; this can be configured in
\verb$snacc/c_include/asn_config.h$.  

By default, the compiler's makefiles use \verb$flex$, \verb$bison$ and
\verb$gcc$.  If you wish to change these, edit the following files:

\begin{verbatim}
snacc/src/makefile
snacc/src/back_ends/c_gen/makefile
snacc/src/back_ends/c++_gen/makefile
\end{verbatim}

The C runtime library uses \verb$gcc$, and its makefile is
\verb$snacc/c_lib/makefile$.  The C++ runtime library uses \verb$g++$
(\verb$gcc-2.2.3$) and its makefile is \verb$snacc/c++_lib/makefile$. 

Finally, to compile \verb$snacc$ and the C and C++ runtime libraries,
type the following at the shell prompt:

\begin{verbatim}
%1 cd snacc
%2 make both
\end{verbatim}

If you wish to install only the C or only the C++ versions of the library, type
\verb$make c$ or \verb$make c++$, respectively, instead of \verb$make both$.
If the make succeeds, the snacc binary, {\em snacc}, should be in the
\verb$snacc/bin/$ directory, the C runtime libraries, {\em
libasn1csbuf.a}, {\em libasn1cebuf.a}, and {\em libasn1cmbuf.a},
should be in the \verb$snacc/c_lib$ and the C++ runtime library, {\em
libasn1c++.a} should be in the \verb$snacc/c++_lib$. The \verb$.o$ and
other junk files will have been removed.

After compiling the libraries, you can test the library routines with
\verb$snacc/c_examples/test_lib/test_lib$ or
\verb$snacc/c++_examples/test_lib/test_lib$.  These programs run
simple encoding and decoding tests on all of the library types.  You
can test the snacc compiler with the other examples. 

A manual page that contains information on running snacc can be found in
\verb$snacc/doc/snacc.1$.  This should be installed in section 1 of
the manual.  You can use \verb$nroff -man snacc.1$ to view it if you
don't want to install it.


\section{\label{run-sec}Running snacc}

Snacc is typically invoked from the shell command line an has the synopsis:
\begin{verbatim}
snacc [-h] [-c] [-C] [-P] [-t] [-e] [-d] [-p] [-f]
      [-u <useful types ASN.1 file>] [-mf <max file name len>]
      [-l <neg number>] <ASN.1 file list>
\end{verbatim}

Snacc generates C or C++ source code for BER encode and decode
routines as well as print and free routines for each type in the given
ASN.1 modules.  Most of the 1990 ASN.1 features are parsed although
some do not affect the generated code.  Fairly rigourous error
checking is performed on the ASN.1 source; any errors detected will be
reported.

Each file in the ASN.1 file list should contain a complete ASN.1
module.  ASN.1 modules that use the IMPORTS feature must be compiled
together (specify all necessary modules in the ASN.1 file list).  The
generated source files will include each module's header file in the
command line order.  This makes it important to order the modules from
least dependent to most dependent on the command line to avoid type
ordering problems. Currently, snacc assumes that each ASN.1 file
given on the command line depends on all of the others on the command
line.  No attempt is made to only include the header files from
modules referenced in the import list for that module.


If the target language is C, snacc will generate a \verb$.h$ and
\verb$.c$ file for each specified ASN.1 module.  If the target
language is C++, snacc will generate a \verb$.h$ and \verb$.C$ file
for each module.  The generated file names will be derived from the
module names.

The command line options are:
% zap bullet for items
%\def\labelitemi{}

\begin{description}
\item[-h    ] {short for ``help'', prints a synopsis of snacc
and exits.}

\item[-c    ] {causes snacc to generate C source code.
This is the default behaviour of snacc if neither of the \verb$-c$ or
\verb$-C$ options are given.  Only one of \verb$-c$ or \verb$-C$
should be specified.}

\item[-C    ] {causes snacc to generate C++ source code.}

\item[-P    ] {causes snacc to print the parsed ASN.1
modules to \verb$stdout$ after the types have been linked, sorted, and
processed.  This option is useful for debugging snacc and observing
the modifications snacc performs on the types to make code generation
simpler.}
\end{description}

The options, \verb$-t, -v, -e, -d, -p,$ and \verb$-f$ affect
what types and routines go into the generated source code.  If none of
them are given on the command line, snacc assumes that all of them are
in effect.  For example, if you do not need the Free or Print
routines, you should give the \verb$-t -v -e -d$ options to snacc.
This lets you trim the size of the generated code by removing
unnecessary routines; the code generated from large ASN.1
specifications can produce very large binaries.

\begin{description}
\item[-t    ] {causes snacc to generate type definitions in the
target language for each ASN.1 type.}

\item[-v    ] {causes snacc to generate value definitions in the
target language for each ASN.1 value.  Currently value definitions are
limited to INTEGERs, BOOLEANs and OBJECT IDENTIFIERs.}

\item[-e    ] {causes snacc to generate encode routines in the
target language for each ASN.1 type.}

\item[-d    ] {causes snacc to generate decode routines in the
target language for each ASN.1 type.}

\item[-p    ] {causes snacc to generate print routines in the
target language for each ASN.1 type.}

\item[-f    ] {causes snacc to generate free routines in the
target language for each ASN.1 type.  This option only works when the
target language is C.  The free routines hierarchically free C values.
A more efficient approach is to use the provided nibble-memory system.
The nibble memory permits freeing an entire decoded value without
traversing the decoded value.  This is the default memory allocator
used by snacc generated decoders.  See file
\verb$snacc/c_include/asn_config.h$ to change the default memory
system.  For more information on the memory management see Section \ref{lib-mem-C-sec}.}

\item[-u {\em file}] {causes snacc to read the useful types
definitions from the ASN.1 module in file {\em file}
for linking purposes.  For some ASN.1 specifications, such as SNMP,
the useful types are not needed.  The types in the given useful types
file are globally available to all modules; a useful type definition
is overridden by a local or explicitly imported type with the same
name. The useful type module can be found in
\verb$snacc/asn1specs/useful.asn1$ and contains: 

\begin{itemize}
\setlength{\itemsep}{0pt}
\setlength{\parsep}{0pt}
\nspace{0}
\item ObjectDesccriptor
\item NumericString
\item PrintableString
\item TeletexString
\item T61String
\item VideoTexString
\item IA5String
\item GraphicString
\item ISO646String
\item GeneralString
\item UTCTime
\item GeneralizedTime
\item EXTERNAL
\end{itemize}}

\item[-mf {\em number}] {causes the names of the generated source
files to have a maximum length of {\em number} characters, including
their suffix.  The {\em number} argument must be at least 3.  This option
is useful for supporting operating systems that only support short
file names.  A better solution is to shorten the module name of each
ASN.1 module.}

\item[-l {\em number}] {this is fairly obscure but may be useful.  Each
error that the decoders can report is given an id number.  The number
{\em number} is where the error ids start decreasing from as they are
assigned to errors.  The default is -100 if this option is not given.
Avoid using a number in the range -100 to 0 since they may conflict
with the library routines' error ids.  If you are re-compiling the
useful types for the library use -50.  Another use of this option is
to integrate newly generated code with older code; if done correctly,
the error ids will not conflict.}

\end{description}

Since ASN.1 has different scoping rules than C and C++, some name munging
is done for types, named-numbers etc. to eliminate conflicts.
Some capitalization schemes where chosen to fit common C programming
style.  For all names, dashes in the ASN.1 source are converted to
underscores.  See Sections \ref{naming-C-sec} and \ref{naming-Cpp-sec}
for more naming information.

The module name is used as a base name for the generated source file
names.  It will be put into lowercase and dashes will be replaced with
underscores.  Module names that result in file names longer than
specified with the \verb$-mf$ option will be truncated.  If the
\verb$-mf$ option was not given, file names will be truncated if they
are too long for the target file system. You may want to shorten long
module names to meaningful abbreviations. This will avoid file name
conflicts for module names that are truncated to the same substring.
Any module name and file name conflicts will be reported.

If your ASN.1 modules have syntactic or semantic errors, each error
will be printed to \verb$stderr$ along with the file name and line number of
where it occurred. These errors are usable by GNU emacs compiling
tools.  See the next chapter for more information on the types of
errors snacc can detect.

More errors can be detected and reported in a single compile if type
and value definitions are separated by semi-colons.  Separating type
and value definitions with semi-colons is not required, and if used,
need not be used to separate all type and value definitions.
Semi-colons are necessary after some macros that introduce ambiguity.
In general, if you get a parse error you can't figure out, try
separating the surrounding type/value definitions with semi-colons.


\section{\label{bug-sec}Reporting Bugs}
This is the first release of snacc so bugs are surely present.  If you
find some bugs or have other comments, please send email to
snacc-bugs@cs.ubc.ca. 

Please include the offending ASN.1 source, the command line options
you were using and the hardware and operating system configuration.

We will respond to bug report as time permits.


\chapter{\label{comp-des-chp}Compiler Design}

\section{\label{comp-overview-sec}Overview}
The snacc compiler is implemented with \verb$yacc$, \verb$lex$
(actually GNU's equivalents, \verb$bison$ and \verb$flex$) and
\verb$C$.  Despite the shortcomings of \verb$lex$ and \verb$yacc$,
they provide reasonable performance without too much programming
effort.  Since \verb$yacc$ parsers are extremely difficult to modify
during runtime, any macro that you want the compiler to handle must
be hand coded into the ASN.1 \verb$yacc$ grammar
(\verb$snacc/src/asn1.yacc$) followed by recompilation of snacc.
Macro definitions do not need special consideration since they are
skipped by the compiler.  Macro definitions and complex value notation
are kept as text in the data structure resulting from a parse if you
want to try to parse and process them.

To handle the anti-compiler nature of ASN.1's syntax, snacc makes
several passes on parse tree data structure when compiling.  None of
these passes creates temporary files; this allows snacc to process
large ASN.1 specifications quite quickly.  Each compiler pass is
explained in the next sections.  The main passes of the compiler are
executed in the following order:

\begin{itemize}
\item[1] { parse useful types ASN.1 module}
\item[2] { parse all user specified ASN.1 modules}
\item[3] { link local and imported type references in all modules }
\item[4] { parse values in all modules }
\item[5] { link local and imported value references in all modules}
\item[6] { process any macro types }
\item[7] { normalize types }
\item[8] { mark recursive types and signal any recursion related errors}
\item[9] { check for semantic errors in all modules }
\item[10]{ generate C/C++ type information for each ASN.1 type}
\item[11]{ Sort the types from least dependent to most dependent }
\item[12]{ generate the C/C++}
\end{itemize}

The source code for the compiler resides in \verb$snacc/src/$ and the
back ends are in \linebreak \verb$snacc/src/back_ends/c_gen/$ and 
\verb$snacc/src/back_ends/c++_gen/$.


\section{\label{comp-pass1-sec}Pass 1: Parsing the Useful Types Module}
The ASN.1 useful types are not hardwired into snacc.  Instead they
have been placed in a separate ASN.1 module.  This allows the user to
define his own useful types or re-define the existing ones without
modifying snacc.  This also has the benefit that names of useful types
are not key-words in the lexical analyzer.  This step is not really a
compiler pass on the module data, however it is described as one for
simplicity. 

The useful types module should be passed to snacc with the \verb$-u$
flag in front of it.  The \verb$-u$ flag tells snacc to treat the
module in a special way.  Instead of parsing the module and generating
code for it, snacc parses the module and makes the types in it
accessible to all of the other modules being parsed.  Note that the
other modules do not need to explicitly import from the useful types
module.  See Section \ref{comp-pass3-sec} for more information on how
useful types affect linking.

The encode, decode, and other routines for the useful types are in the
runtime library.  Currently, the useful types library routines are the
same as the ones the compiler would normally generate given the useful
types module.  However, since they are in the library, you can modify
them to check character sets (string types), or convert local time
formats into their BER equivalent (UTCTime, GeneralizedTime).

The following types are in the useful types module:
\begin{small}
\begin{verbatim}
ASN-USEFUL DEFINITIONS ::=
BEGIN
ObjectDescriptor ::= [UNIVERSAL 7]  IMPLICIT OCTET STRING
NumericString    ::= [UNIVERSAL 18] IMPLICIT OCTET STRING
PrintableString  ::= [UNIVERSAL 19] IMPLICIT OCTET STRING
TeletexString    ::= [UNIVERSAL 20] IMPLICIT OCTET STRING
T61String        ::= [UNIVERSAL 20] IMPLICIT OCTET STRING
VideotexString   ::= [UNIVERSAL 21] IMPLICIT OCTET STRING
IA5String        ::= [UNIVERSAL 22] IMPLICIT OCTET STRING
GraphicString    ::= [UNIVERSAL 25] IMPLICIT OCTET STRING
VisibleString    ::= [UNIVERSAL 26] IMPLICIT OCTET STRING
ISO646String     ::= [UNIVERSAL 26] IMPLICIT OCTET STRING
GeneralString    ::= [UNIVERSAL 27] IMPLICIT OCTET STRING
UTCTime          ::= [UNIVERSAL 23] IMPLICIT OCTET STRING
GeneralizedTime  ::= [UNIVERSAL 24] IMPLICIT OCTET STRING

EXTERNAL         ::= [UNIVERSAL 8] IMPLICIT SEQUENCE
{
    direct-reference      OBJECT IDENTIFIER OPTIONAL,
    indirect-reference    INTEGER OPTIONAL,
    data-value-descriptor ObjectDescriptor OPTIONAL,
    encoding CHOICE
    {
        single-ASN1-type [0] OCTET STRING,  -- should be ANY 
        octet-aligned    [1] IMPLICIT OCTET STRING,
        arbitrary        [2] IMPLICIT BIT STRING
    }
}
END
\end{verbatim}
\end{small}

If you use the EXTERNAL type, you must provide the mechanism to encode
and decode the value in the embedded CHOICE, \verb$encoding$.  The
type and transfer syntax of the value in an EXTERNAL type is not known
when the ASN.1 code is compiled by snacc.  Snacc cannot generate
encoders and decoders without complete type information and only
supports a single set of encoding rules, BER.

\section{\label{comp-pass2-sec}Pass 2: Parsing ASN.1 Modules}
During this pass, all of the specified modules are parsed into the {\em
Module} data structure.  The ASN.1 source files are not consulted
again, after they are parsed.  \verb$Yacc$ and \verb$lex$ are doing the work in
this step. (see files \verb$snacc.c$, \verb$asn1.yacc$, \verb$asn1.lex$
and \verb$asn1module.h$).

A lexical tie-in is where the yacc parser puts the lexical analyzer
into a different mode (and is usually considered a hack).  The
different modes tokenize symbols differently, which is useful for
skipping well delimited sections that cannot be parsed easily by a
\verb$yacc$ parser on the first pass.  Lexical tie-ins are used in two
places to simplify the ASN.1 grammar sufficiently for \verb$yacc$ and
\verb$lex$.  There are two special modes in the lexical analyzer, one
for ASN.1 macro definitions and the other for ASN.1 values enclosed in
\{\}'s.

The lexical tie-in for eating macro definition bodies works with macro
definitions of the following form:

\begin{verbatim}
<upper case identifier> MACRO ::= BEGIN ... END
\end{verbatim}

Everything between the \verb$BEGIN$ and \verb$END$ is stuffed into a
string by \verb$lex$ and passed back as single token to the
\verb$yacc$ parser.

Values within \{\}'s are grabbed in a similar way.  Value parsing
cannot really be done at this stage since complete type information is
needed and the types are not fully parsed or linked yet.

Most syntax errors are reported during this pass.  If syntax errors
are encountered, snacc will report as many as it can from the
offending module before the parser is hopelessly lost and then exit.
If the types and values are separated with semi-colons, the parser can
recover after a syntax error and attempt to find more errors in that
module before exiting.


\section{\label{comp-pass3-sec}Pass 3: Linking Types}
The third pass links all type references.  Snacc attempts to resolve
any currently visible (i.\ e.\  not in macro definitions or constructed
values) type reference.  This includes type references in simple value
definitions and subtyping information.  The useful types module (if
given) is linked first.  

Snacc will exit after this pass if any type references could not be
resolved.  Error messages with file and line number information will
be printed to \verb$stderr$.

This pass also counts and stores the number of times a type definition is
referenced locally and from other modules.  This information is used
during the type sorting pass.

First, each module identifier is checked for conflicts with the
others.  If the module identifier includes an OBJECT IDENTIFIER, snacc
only checks for conflicts with the other module identifier OBJECT
IDENTIFIERs.  When only a module name is provided, snacc checks for
conflicts with the the other module names, even if the other module
identifiers include OBJECT IDENTIFIERs.  If the OBJECT IDENTIFIER of
a module identifier contains any value references, it will be ignored
for module look-up purposes.  Note that value references within the
module identifier OBJECT IDENTIFIERs are not allowed in the 1992
version of ASN.1 due to the difficulty in module name resolution they
present. 

Two modules with the same name but different OBJECT IDENTIFIERs are
not considered an error within ASN.1.  However, because the generated
files use the module name as part of their name, the code generation
pass will gripe about and fail for modules with the same name.

Next, each module's import {\em lists} are resolved by finding the
named module and then verifying that the named module contains all of
the imported types.

Then for each module, each type reference (except those of the form
{\em modulename.typename}) is assumed to be a local type reference and
the linker attempts to find a local type definition of the same name
to resolve it with.  If a matching local definition is found, the type
reference is resolved and the linker continues with the next type
reference.

For each type reference of the form {\em modulename.typename}, the
linker looks in the module with name {\em modulename} for the type
{\em typename}.  If the type is found the reference is resolved,
otherwise an linking error is reported.  Note that this form of type
reference provides a special scope that does not conflict with other
local or imported types in that module.

For type references that failed to resolve locally and are not of the
form {\em modulename.typename}, the linker looks in the import lists
of the current type reference's module for a type to resolve with.  If
the type is found in the import lists, the reference is resolved.

For the remaining unresolved type references (failed local and legal
import resolution and are not of the form {\em modulename.typename}),
the linker looks in the useful types module, if one was specified with
the \verb$-u$ option.  If the type is found in the useful types module
then the reference is resolved, otherwise a linking error is reported.

Note that when a useful types module is specified, it is globally
available to all modules, but it has the lowest linking priority.
That is, if a type reference can be resolved legally without the
useful types module, it will be.

Some type checking must be done in this pass to link certain types
properly.  These include:
\begin{itemize}
\item {a SELECTION type must reference a field of a CHOICE type.}
\item {a COMPONENTS OF type in a SET must reference a SET.}
\item {a COMPONENTS OF type in a SEQUENCE must reference a SEQUENCE.}
\end{itemize}



\section{\label{comp-pass4-sec}Pass 4: Parsing Values}
The fourth pass attempts to parse any value is enclosed in \{\}'s in
the given modules.  INTEGERS, REALs and BOOLEANS that are not enclosed in
braces are parsed in the first pass. 

The value parser is implemented without \verb$yacc$ and \verb$lex$ and
uses each value's type information to help parse the value.  Values
within \{\}'s hidden within types such as default values and parts of
subtypes are not parsed.  Since subtypes and default values do not
affect the generated code, upgrading the value parser in this respect
is not very useful.

The only type of value in \{\}'s that is parsed is the OBJECT
IDENTIFIER.  All of the OBJECT IDENTIFIER value forms are supported
but snacc loosens the restrictions on using arc names defined in the
OBJECT IDENTIFIER tree.

ASN.1 allows OBJECT IDENTIFIER values to reference special built-in
arc names from the OBJECT IDENTIFIER tree defined in in Annexes B, C
and D of X.208.  For example the first arc in an OBJECT IDENTIFIER
value can be either \verb$ccitt$ \verb$iso$ or \verb$joint-iso-ccitt$.
The acceptable arc names are context dependent; for example the second
arc can be one of \verb$standard$, \verb$registration-authority$,
\verb$member-body$ or \verb$identified-organization$ only if the first
arc was \verb$iso$ or 1.

Snacc uses a simplified algorithm to handle references to the arc
names defined in OBJECT IDENTIFIER tree.  Any arc value that is
represented by a single identifier is checked to see if it is one of
the arc names defined in the OBJECT IDENTIFIER tree; context is
ignored.  If the identifier matches one of the arc names then its
value is set accordingly.  The lack of context sensitivity in snacc's
algorithm may cause the arc name to link with an arc name from the
OBJECT IDENTIFIER tree when a local or imported INTEGER was desired.
The following is the list special arc names that snacc understands and
their values (see \verb$snacc/src/oid.c$):

\begin{itemize}
\setlength{\itemsep}{0pt}
\setlength{\parsep}{0pt}
\nspace{0}
\item {ccitt = 0}
\item {iso = 1}
\item {joint-iso-ccitt = 2}
\item {standard = 0}
\item {registration-authority = 1}
\item {member-body = 2}
\item {identified-organization = 3}
\item {recommendation = 0}
\item {question = 1}
\item {administration = 2}
\item {network-operator = 3}
\end{itemize}

\section{\label{comp-pass5-sec}Pass 5: Linking Values}
The fifth pass links value references. The value linker looks for
value references to resolve in value definitions and type definitions,
including default values and subtyping information.  The value linking
algorithm is virtually identical to the type linking pass (see Section
\ref{comp-pass3-sec}).

Note that snacc allows INTEGER values to be defined in two ways, one
by the standard \verb$valuename Type ::= Value$ style and the other by
a certain form of OBJECT IDENTIFIER arc values.  For example, parsing
the following OBJECT IDENTIFIER value in pass four will define the
INTEGER values \verb$org$ as 3 and \verb$dod$ as 6 in addition to the
\verb$internet$ OBJECT IDENTIFIER value, \verb${1 3 6 1}$.

\begin{verbatim}
internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
\end{verbatim}

Here is an example that shows illustrates the OBJECT IDENTIFIER
parsing and linking described above.  The following values: 

\begin{verbatim}
foo OBJECT IDENTIFIER ::= { joint-iso-ccitt 2 88 28 }
bar OBJECT IDENTIFIER ::= { foo 1 }
bell INTEGER ::= 2
gumby OBJECT IDENTIFIER ::= { foo bell }
pokie OBJECT IDENTIFIER ::= { foo stimpy(3)}
\end{verbatim}

are equivalent to this:
\begin{verbatim}
foo OBJECT IDENTIFIER ::= { 2 2 88 28 }
bar OBJECT IDENTIFIER ::= { 2 2 88 28  1 }
bell INTEGER ::= 2
stimpy INTEGER ::= 3
gumby OBJECT IDENTIFIER ::= { 2 2 88 28 2}
pokie OBJECT IDENTIFIER ::= { 2 2 88 28 3}
\end{verbatim}


\section{\label{comp-pass6-sec}Pass 6: Processing Macros}

The fifth pass processes macros.  For all macros currently handled,
snacc converts type definitions inside the macro to type references
and puts the type definition in the normal scope.  This way, the code
generator does not have to know about macros to generate code for the
types defined within them.

The only macro that receives any special processing is the SNMP
OBJECT-TYPE macro.  This macro's information defines an OBJECT
IDENTIFIER or INTEGER to type mapping for use with any ANY DEFINED BY
type.  Note that the OBJECT-TYPE macro has been extended beyond its
SNMP definition to allow integer values for INTEGER to type mappings.

ASN.1 allows you to define new macros within an ASN.1 module;  this
can change the grammar of the ASN.1 language.  Since snacc is
implemented with \verb$yacc$ and yacc grammars cannot be modified
easily during runtime, snacc cannot change its parser in response to
macro definitions it parses.

Any macro that snacc can parse has been explicitly added to yacc
grammar before compiling snacc.  When a macro that snacc can parse is
parsed, a data structure that holds the relevant information from the
macro is added to the parse tree.  The type and value linking passes
as well as the macro processing and possibly the normalization pass
need to be modified to handle any new macros that you add.

The following macros are parsed:

\begin{itemize}
%\begin{linespacing}{0.5}
\setlength{\itemsep}{0pt}
\setlength{\parsep}{0pt}
\nspace{0}
\item{ OPERATION (ROS) }
\item{ ERROR (ROS) }
\item{ BIND (ROS) }
\item{ UNBIND (ROS) }
\item{ APPLICATION-SERVICE-ELEMENT (ROS) }
\item{ APPLICATION-CONTEXT }
\item{ EXTENSION (MTSAS)}
\item{ EXTENSIONS (MTSAS) }
\item{ EXTENSION-ATTRIBUTE (MTSAS) }
\item{ TOKEN (MTSAS) }
\item{ TOKEN-DATA (MTSAS)}
\item{ SECURITY-CATEGORY (MTSAS) }
\item{ OBJECT (X.407) }
\item{ PORT   (X.407) } 
\item{ REFINE (X.407)}
\item{ ABSTRACT-BIND (X.407) }
\item{ ABSTRACT-UNBIND (X.407) }
\item{ ABSTRACT-OPERATION (X.407) }
\item{ ABSTRACT-ERROR (X.407) }
\item{ ALGORITHM (X.509)} 
\item{ ENCRYPTED (X.509)}
\item{ PROTECTED (X.509)}
\item{ SIGNATURE (X.509)}
\item{ SIGNED    (X.509)}
\item{ OBJECT-TYPE (SNMP) }
%\end{linespacing}
\end{itemize}


\section{\label{comp-pass7-sec}Pass 7: Normalizing Types}
The sixth pass normalizes the types to make code generation simpler.
The following is done during normalization:
\begin{itemize}

\item[1] { COMPONENTS OF types are replaced with the contents of the SET
or SEQUENCE components that they reference.}

\item[2] { SELECTION types are replaced with the type they reference.}

\item[3] { SEQUENCE, SET, CHOICE, SET OF and SEQUENCE OF {\em definitions}
embedded in other types are made into separate type definitions. }

\item[4] { for modules in which ``IMPLICIT TAGS'' is specified, tagged
type references such as \verb$[APPLICATION 2] Foo$ are marked IMPLICIT
if the referenced type (\verb$FOO$ in this case) is not an untagged
CHOICE or untagged ANY type.} 

\item[5] { INTEGERs with named numbers, BIT STRINGs with named bits and
ENUMERATED types embedded in other types are made into separate type
definitions.}
\end{itemize}

The COMPONENTS OF and SELECTION type simplifications are obvious but
the motivation for the others may not be so obvious.  The third type of
simplification makes type definitions only one level deep.  This
simplifies the decoding routines since snacc uses local variables for
expected lengths, running length totals and tags instead of stacks.

The implicit references caused by ``IMPLICIT TAGS'' are marked
directly on type references that need it.  This saves the code
generators from worrying about whether implicit tagging is in effect
and which types can be referenced implicitly.

The types with named numbers or bits are made into a separate type to
allow the C++ back end to simply make a class that inherits from the
INTEGER or BIT STRING class and defines the named numbers or bits
inside an enum in the new class.  This is described further in the C++
code generation chapter.

\section{\label{comp-pass8-sec}Pass 8: Marking Recursive Types}

This pass marks recursive types and checks for recursion related
errors.  To determine whether a type definition is recursive, each
type definition is traced to its leaves, checking for references to
itself.  Both local and imported type references within a type are
followed to reach the leaves of the type.  A leaf type is simple
(non-aggregate) built-in type such as an INTEGER or BOOLEAN.  The
recursion information is only used during the type dependency sorting
pass at the moment.

Snacc attempts to detect two types of recursion related errors.  The
first type of error results from a recursive type that is composed
solely of type references.  Types of this form contain no real type
information and would result in zero sized values.  For example the
following recursive types will generate this type of warning:
\begin{verbatim}
A ::= B
B ::= C
C ::= A
\end{verbatim}

The other recursion related error results from a type whose value will
always be infinite in size.  This is caused by recursion with no optional
component that can terminate the recursion.  If the the recursion includes
an OPTIONAL member of a SET or SEQUENCE, a CHOICE member, or a
SET OF or SEQUENCE OF, the recursion can terminate.

Both of the recursion errors generate warnings from snacc but will not
stop code generation. 


\section{\label{comp-pass9-sec}Pass 9: Semantic Error Checking}
The ninth pass checks for semantic errors in the ASN.1 specification
that have not been checked already. Both the type linking pass and the
recursive type marking pass do some error checking as well. Snacc attempts
to detect the following errors in this pass:
 
\begin{itemize}
\item { elements of CHOICE and SET types must have distinct tags.}

\item { CHOICE, ANY, and ANY DEFINED BY types cannot be implicitly tagged. }

\item { type and value names within the same scope must be unique. }

\item { field names in a SET, SEQUENCE or CHOICE must be distinct.  If
a CHOICE is a member of a SET, SEQUENCE or CHOICE and has no field name,
then the embedded CHOICE's field names must be distinct from its
parents to avoid ambiguity in value notation.}

\item { an APPLICATION tag code can only be used once per module. }

\item { each value in a named bit list (BIT STRINGs) or named number
list (INTEGERs and ENUMERATED) must be unique within its list.}

\item { each identifier in a named bit list or named number list must
be unique within its list.}

\item { the tags on a series of one or more consecutive OPTIONAL or DEFAULT
SEQUENCE elements and the following element must be distinct. }

\item { gives a warning if an ANY DEFINED BY type appears in a
SEQUENCE before its identifier or in a SET.  These would allow encodings
where the ANY DEFINED BY value was prior to its identifier in the
encoded value; ANY DEFINED BY values are difficult to decode without
knowing their identifier.}

\end{itemize}

Snacc does not attempt to detect the following errors due the
limitations of the value parser.
\begin{itemize}
\item { SET and SEQUENCE values can be empty (\{\}) only if the SET or
SEQUENCE type was defined as empty or all of its elements are marked
as OPTIONAL or DEFAULT.}

\item { each identifier in a BIT STRING value must from that BIT
STRING's named bit list (this could be done in an improved value
linker instead of this pass).}
\end{itemize}


\section{\label{comp-pass10-sec}Pass 10: Generating C/C++ Type Information}

This pass fills in the target language type information.  The process
is different for the C and C++ back ends since the C++ ASN.1 model is
different and it was developed later (more design flaws had been
corrected for the C++ backend). 

For C and C++ there is an array that contains the type {\em definition}
information for each built-in type.  For each built-in ASN.1 type, the
C array holds: 

\begin{description}
\item[typename] {the C \verb$typedef$ name for this type definition.}

\item[isPdu]    {TRUE if this type definition is a PDU. This is set
for types used in ANY and ANY DEFINED BY types and those indicated by
the user via compiler directives.  Additional interfaces to the encode
and decode routines are generated for PDU types.  The SNMP OBJECT-TYPE
macro is the current means of indicating whether a type is used within
an ANY or ANY DEFINED BY type.}

\item[isPtrForTypeDef] { TRUE if other types defined solely by this type
definition are defined as a pointer to this type.}

\item[isPtrForTypeRef] { TRUE if type references to this type
definition from a SET or SEQUENCE are by pointer.}

\item[isPtrForOpt] { TRUE if OPTIONAL type references to this type
definition from a SET or SEQUENCE are by pointer.} 

\item[isPtrInChoice] { TRUE if type references to this type
definition from a CHOICE are by pointer.}

\item[optTestRoutineName] { name of the routine to test whether an
OPTIONAL element of this type in a SET or SEQUENCE is present.
Usually just the name of a C macro that tests for NULL.}

\item[printRoutineName] { name of this type definition's printing routine.}
\item[encodeRoutineName]{ name of this type definition's encoding routine.}
\item[decodeRoutineName]{ name of this type definition's decoding routine.}
\item[freeRoutineName]  { name of this type definition's freeing routine.}
\end{description}

The C++ type definition array is similar to C's. It contains:

\begin{description}
\item[classname] { holds the C++ \verb$class$ name for this type definition.}
\item[isPdu] { same as C isPdu except that is does not affect the code
generation since the C++ back end includes the extra PDU encode and
decode routines by default.} 
\item[isPtrForTypeDef] { same as C isPtrForTypeDef. }
\item[isPtrForOpt] { same as C isPtrForOpt.}
\item[isPtrInChoice] { same as C isPtrInChoice}
\item[isPtrInSetAndSeq] { whether type references to this class
from a SET or SEQUENCE are by pointer.}
\item[isPtrInList] {whether type references to this class
from a SET OF or SEQUENCE OF are by pointer.}
\item[optTestRoutineName] { name of the routine to test whether an
OPTIONAL element of this type in a SET or SEQUENCE is present.
Usually is just name of a C macro that tests for NULL.}
\end{description}

The first step of this pass uses the type arrays to fill in the C or
C++ type {\em definition} information for each module's ASN.1 type
definitions.  This is done for the useful types module as well.

The next step goes through each constructed type and fills in the type
{\em reference} information for each reference to a built-in, user defined
or useful type.  Much of the type reference information is taken from
the referenced type's definition information.  The type reference
information contains the following (for both C and C++):

\begin{description}
\item[fieldName] { field name for this type if it is referenced from
a CHOICE, SET or SEQUENCE.}
\item[typeName] { type name of the referenced type.}
\item[isPtr] { whether this reference is by pointer.}
\item[namedElmts] { named elements for INTEGER, ENUMERATED or BIT
STRING types with their C names and values.}
\item[choiceIdValue] { if this type reference is in a CHOICE, this
holds the value of the CHOICE's choiceId that indicates the presence
of this field.}
\item[choiceIdSymbol] { if this type reference is in a CHOICE, this
holds the C enum value symbol that has the choiceIdValue value.}
\item[optTestRoutineName] { name of the routine or macro to test for
the presence of this element if it is an OPTIONAL element of a SET or SEQUENCE.}
\end{description}

\section{\label{comp-pass11-sec}Pass 11: Sorting Types}

This pass sorts the type definitions within each module in order of
dependence.  ASN.1 does not require the types to be defined before
they are referenced but both C and C++ do.  Without this pass, the
generated types/classes would probably not compile due to type
dependency problems.  There is no attempt to order the modules;
command line order is used for the module dependence.  If you have
problems with mutually dependent modules, the simplest approach is to
combine the dependent modules into a single ASN.1 module.

Some compilers such as CASN1 \cite{CASN1} require the user to order
the types within the ASN.1 modules.  This can be tedious and since
snacc may generate new type definitions from nested aggregate type
definitions in the normalization pass, the user does not have complete
control over the order of every type definition.  (The user could use
the \verb$-P$ option to get the normalized ASN.1 and then order it but
that is painful as well.)

Snacc attempts to sort the types from least dependent to most
dependent using the following convoluted algorithm:

First, separate the type definitions within a module into the groups:
\begin{itemize}
\item[1] { type definitions that are defined directly from simple built-in
types such as INTEGER.}

\item[2] { types such as SET, SEQUENCE, SET OF, SEQUENCE OF and CHOICE
that contain no references to types defined in this module.  That, is
they are defined from only simple built-in types, imported types or
useful types.}

\item[3] { type definitions that reference locally defined types.}

\item[4] { type definitions that are not referenced by any local types.}
\end{itemize}

Only the 3rd group of type definitions needs more sorting.  After it
has been sorted, the groups are merged in the order 1, 2, 3, 4 to
yield a sorted type definition list.

Now we describe how the 3rd group of type definitions is sorted.
\begin{itemize}

\item[1] {for each type definition in the third group, a list of its local type
references is built and attached to it.  This type reference list only
goes one level deep; it does not follow type references to find more
type references.}

\item[2] { all of the linearly dependent types are removed and sorted.
This is done by repeatedly removing type definitions that do not
directly depend on any other type definitions that remain in the 3rd
group.  The process of removing the type definitions sorts them.}

\item[3] { the type definitions that were not removed in step 2 are
divided into two groups: recursive and non-recursive.  The
non-recursive types depend on the recursive ones since they are still
in the list after step 2.}

\item[4] { the non-recursive types from step 3 are sorted as in step
2.  All of them should sort linearly since none are recursive. }

\item[5] { if the target language is C, any SET OF or SEQUENCE OF
types are separated from the recursive type definitions built in step 3.
This is done because the C representation of a list type is generic
(uses a \verb$void*$ to reference the list element) and therefore does
not really depend on the list's element type.}

\item[6] { the list of local type references for the recursive types
from step 3 is re-generated as in step 1 using a relaxation:  types
referenced as pointers are not added to a type's reference list.}

\item[7] { the recursive types from step two are re-sorted as in step
2 using their new local type reference lists. Two lists are formed,
those that sorted linearly and those that did not. Hopefully the
latter list will be empty.}
\end{itemize}

To form a sorted third group, the lists are merged in the following order:
\begin{itemize}
\item {linearly sorted types from step 2}
\item {separated list types (C only) from step 5}
\item {sorted recursive types from step 7}
\item {unsorted recursive types from step 7 (hopefully empty)}
\item {sorted non-recursive types from step 4}
\end{itemize}


In C, the code generator defines both \verb$typedef$ names and
\verb$struct$ tags (names).  For example, 
\begin{verbatim}
Foo ::= SET { a INTEGER, b BOOLEAN }

Bar ::= SEQUENCE { a OBJECT IDENTIFIER, b Foo }
\end{verbatim}
translates to the following C data types:
\begin{verbatim}
typedef struct Foo /* SET */
{
    AsnInt a; /* INTEGER */
    AsnBool b; /* BOOLEAN */
} Foo;

typedef struct Bar /* SEQUENCE */
{
    AsnOid a; /* OBJECT IDENTIFIER */
    struct Foo* b; /* Foo */
} Bar;
\end{verbatim}

Note that both the \verb$struct$ and the \verb$typedef$ have the name
\verb$Foo$.  Also note that the Bar type references the \verb$Foo$ via
\verb$struct Foo*$.

For types such as \verb$Bar$ that contain the \verb$Foo$ type,
\verb$Foo$ is referenced as \verb$struct Foo*$ instead of just
\verb$Foo*$ because C allows you to use the type \verb$struct Foo*$
(incomplete type) in defining types even prior to the actual
declaration of the the \verb$struct Foo$. The \verb$Foo*$ type can
{\em only} be used after the \verb$Foo typedef$ declaration.  The use
of incomplete types can often overcome recursion related type ordering
problems (not relevant in this example since they are not recursive).  

\section{\label{comp-pass12-sec}Pass 12: Generating Code}

This pass creates and fills the source files with C or C++ code.  The
purpose of the normalization, sorting and error detection passes is to
simplify this pass. 

The normalization pass simplified the ASN.1 types in various ways to
make C/C++ type and code generation simpler.

The type sorting pass hopefully eliminates type dependency problems in the
generated code.  The C/C++ type generator simply proceeds through the
ordered type list writing the C/C++ type definitions to a header file.

The error detection and linking passes will make snacc exit if errors
are found, so the code generation pass can assume the ASN.1 types are
virtually error free.  This usually allows snacc to exit gracefully
instead of crashing due to an undetected error.

The next chapters describe the code that is generated by snacc and the
libraries the generated code uses.


\chapter{\label{c-code-gen-chp}C Code Generation}
\section{\label{intro-C-sec}Introduction}
Snacc was designed primarily to provide high-performance encoders and
decoders.  Key areas to optimize are buffer and memory management.
Buffers are used to hold encoded values and the memory
management is used when building the internal representation of value
when decoding.

C macros are used where possible to eliminate function call overhead
for small, commonly used routines.  Using macros with constant
expressions as parameters allows smarter C compilers to do some of the
calculations at compile time.  In general, any short-cuts can be taken
without sacrificing the robustness of code are used.

The generated code can be quite large; large reductions in of the size
of the binaries can be achieved by using the optimizing options of your
C compiler.

We will use an example ASN.1 module, EX1, to help explain snacc's code
generation.  The EX1 module uses some of the common built-in types and
contains some simple values.  The field names have been left out
to show snacc naming conventions.  The C generation code is in
\verb$snacc/src/back_ends/c_gen$ if you want to alter it.

\begin{small}
\begin{verbatim}
EX1 DEFINITIONS ::= 
BEGIN

anOidVal      OBJECT IDENTIFIER ::= { joint-iso-ccitt 40 foobar(29) } 
theSameOidVal OBJECT IDENTIFIER ::= { 2 40 29 } 
anIntVal      INTEGER ::= 1
aBoolVal      BOOLEAN ::= TRUE

T1 ::= SEQUENCE
{
   INTEGER OPTIONAL,
   OCTET STRING OPTIONAL,
   ENUMERATED { a(0), b(1), c(2) },
   SEQUENCE OF INTEGER,
   SEQUENCE { id OBJECT IDENTIFIER, value OCTET STRING },
   CHOICE { INTEGER, OBJECT IDENTIFIER }
}

END
\end{verbatim}
\end{small}

\noindent
Use the following command to compile the EX1 ASN.1 module.

\noindent
\verb$%1 snacc -u snacc/asn1specs/useful.asn1 snacc/asn1specs/ex1.asn1$
\noindent

This will produces the files \verb$ex1.h$ and \verb$ex1.c$.

For each ASN.1 type an equivalent C data type, a BER encoding routine,
a BER decoding routine, a printing routine a freeing routine will be
generated.  C values will also be generated from simple ASN.1 values.
Each aspect of the C code generation will be discussed in the next
sections.

\section{\label{naming-C-sec}ASN.1 to C Naming Conventions}

For any given module snacc may produce C type definitions, functions
and \verb$#defines$.  We assume that all C \verb$typedef$,
\verb$struct$, \verb$enum$ and \verb$union$ tag, \verb$enum$ value,
variable, \verb$#define$ and function names share a single name space.

The C type name for a type is the same as its ASN.1 type name (with
any hyphens converted to underscores) unless there is a conflict.
Since, unlike ASN.1, the C types for each ASN.1 module share the same
name space, snacc makes sure the C typenames are unique amoung all the
modules and that they do not conflict with C keywords.  The conflicts
are resolved by appending digits to the conflicting name.  To avoid
confusing numbered type names etc., you should edit the ASN.1 source
and name them properly.

Named numbers, ENUMERATED values and named bits are put in entirely in
upper case to match the common C convention for \verb$#define$ and
\verb$enum$ values.

Empty field names in SETs, SEQUENCEs, and CHOICEs will be filled.  The
field name is derived from the type name for that field. The library
types such as INTEGER etc. have default field names defined by the
compiler (see \verb$snacc/src/back_ends/c_gen/rules.c$ and \verb$
snacc/src/back_ends/cpp_rules.c$).  The first letter of the field name
is in lower case.  Again, empty field names should be fixed properly
by adding them to the ASN.1 source.

New type definitions will be generated for SETs, SEQUENCEs, CHOICEs,
ENUMERATED, INTEGERs with named numbers and BIT STRING with named bits
whose definitions are embedded in other SET, SEQUENCE, SET OF,
SEQUENCE OF, or CHOICE definitions.  The name of the new type is
derived from the name of the type in which it was embedded.  Perhaps a
better way would use the field name as well, if present.

\section{\label{type-gen-C-sec}ASN.1 to C Data Structure Translation}

To handle the different scoping rules between ASN.1 and C, some the
names of some ASN.1 data structure elements such as ENUMERATED type
symbols may be altered to avoid conflicts.  The T1 type in example
ASN.1 module EX1 has no field names so snacc will generate them.  It
is recommended to provide field names in the ASN.1 source
instead of relying on compiler generated names.  The following is the
generated C data structure for the EX1 module from the \verb$ex1.h$
file (function prototypes have been removed):

\begin{small}
\begin{verbatim}

typedef enum
{
    A = 0,
    B = 1,
    C = 2
} T1Enum; /* ENUMERATED { A(0), B(1), C(2) }  */

typedef struct T1Choice /* CHOICE */
{
    enum T1ChoiceChoiceId
    {
        T1CHOICE_INT1,
        T1CHOICE_OID
    } choiceId;
    union T1ChoiceChoiceUnion
    {
        AsnInt int1; /* INTEGER */
        AsnOid* oid; /* OBJECT IDENTIFIER */
    } a;
} T1Choice;

typedef struct T1Seq /* SEQUENCE */
{
    AsnOid id; /* OBJECT IDENTIFIER */
    AsnOcts value; /* OCTET STRING */
} T1Seq;

typedef AsnList T1SeqOf; /* SEQUENCE OF INTEGER */

typedef struct T1 /* SEQUENCE */
{
    AsnInt* int1; /* INTEGER OPTIONAL */
    AsnOcts octs; /* OCTET STRING OPTIONAL */
    T1Enum t1Enum; /* T1Enum */
    T1SeqOf* t1SeqOf; /* T1SeqOf */
    struct T1Seq* t1Seq; /* T1Seq */
    struct T1Choice* t1Choice; /* T1Choice */
} T1;
\end{verbatim}
\end{small}

Every ASN.1 type definition maps into a C \verb$typedef$.  SETs and
SEQUENCEs map into C structures and other simple types map into their
obvious C counterpart.  SET OF and SEQUENCE OF types map into a
generic list type which is doubly linked and NULL terminated.  The
reverse link on the lists allows for simpler backwards encoding.  More
information on the library types can be found in chapter \ref{lib-C-chp}.

Comments that contain a fragment of each type's ASN.1 definition are
inserted in the header file to clarify cases where elements have been
re-named. 

Aggregate types that are defined in other type definitions are moved
to their own type definitions.  For example, notice how the SEQUENCE
and CHOICE that are in type \verb$T1$ have been moved to the types
\verb$T1Seq$ and \verb$T1Choice$ in the C code.  This simplifies code
generation at the cost of introducing new types.

Identifiers for named numbers from INTEGER and ENUMERATED types and
named bits from the BIT STRING type are capitalized in the C
representation.  The ENUMERATED type maps to a C \verb$enum$ and the INTEGER
and BIT STRING named numbers/bits are handled with \verb$#define$
statements. 

Most OPTIONAL elements of SEQUENCEs and SETs are referenced by
pointer.  An element is considered present if its pointer is non-NULL.
OCTET STRINGs, BIT STRINGs and OBJECT IDENTIFIERs are the exceptions,
and are included by value even when they are OPTIONAL because they are
small and contain an internal pointer that can be used to determine
their presence.  For an example of this, look at the first two
elements of type \verb$T1$.  The INTEGER type is referenced by pointer
because it is OPTIONAL, but the OCTET STRING type is included
(non-pointer) in the \verb$T1$ type even though it is OPTIONAL.


\section{\label{encode-gen-C-sec}Encode Routines}

Snacc generates two kinds of encoding routines. One is PDU oriented
and encodes the type's tag, length and content and the other
only encodes the type's content.  The generated encoders only call the
content encoders, except in the case of ANY and ANY DEFINED BY types.
Typically, you will only call the PDU oriented routines from your
code.

The content and PDU encoding routine interfaces are similar for all
ASN.1 types.  They both take two parameters, one is a buffer pointer
and the other is a pointer to the value to be encoded.  For example
the \verb$T1$ type from module EX1 has the following prototypes for
its encoding routines.

\begin{verbatim}
AsnLen BEncT1Content(BUF_TYPE b, T1* v);
AsnLen BEncT1(BUF_TYPE b, T1* v);
\end{verbatim}

\verb$BEnc$ is short for ``BER Encode''. The \verb$BUF_TYPE$
parameter is the buffer to encode the value into and the \verb$T1*$
parameter is a pointer to the instance of the \verb$T1$ type that is to be
encoded.

The \verb$BEncT1Content$ routine only encodes the content of a \verb$T1$
type and returns its encoded length; it does not encode its tag
(UNIVERSAL (CONSTRUCTED) 16 for SEQUENCE) or length.  The job of
encoding the tag and length is up to any type that encapsulates \verb$T1$.
This design allows decisions about implicit tagging to be made at code
generation time instead of runtime, improving performance.  Also,
different encoding rules may fit into this model more easily.

The \verb$BEncT1$ routine encodes the tag (UNIVERSAL (CONSTRUCTED) 16
for SEQUENCE), length and content of a \verb$T1$ type and returns its encoded
length.  This is the PDU oriented routine and will only be generated
if the user designates the type as a PDU type via a compiler directive
or the type is used as 
the content of an ANY or ANY DEFINED BY type (as indicated by an
OBJECT-TYPE macro).  A PDU type is a type that defines an entire PDU;
the user will typically be calling the encode and decode routine for
PDU types directly.  See Section \ref{compiler-dir-C-sec} for how to
designate PDU types with compiler directives. 

The snacc encoders are somewhat strange; they encode a value starting
from the end of its BER representation and work back to its beginning.
This ``backwards'' encoding technique simplifies the use of definite
lengths on constructed values.  Other encoders that encode forwards,
such as those of CASN1, use an intermediate buffer format so that a
buffer containing the encoded length of a constructed value can be
inserted before its encoded content, after the content has been
encoded.  Use of intermediate buffers hurts performance.  Other
compilers' approaches have been to only encode indefinite lengths for
constructed values, however, this will not support some encoding rules
such as DER.  The drawback of encoding backwards is that BER values
cannot be written to stream oriented connections as they are encoded.

Both definite and indefinite length encodings for constructed values'
lengths are supported.  Currently the choice is made when compiling
the generated code, via the \verb$USE_INDEF_LEN$ flag.  If both length
forms, definite and indefinite, are required, it easy to modify the
length encoding macros to to check a global variable for the length
form to use.  For most types, using definite lengths produces smaller
encodings with little performance difference.

After calling and encode routine you should always check the buffer
you encoded into for a write error.  This is the only error reporting
mechanism used for the encoders.  See the C buffer section (Section
\ref{lib-buf-sec}) for how to check a buffer for a write error.


\section{\label{decode-gen-C-sec}Decode Routines}

Decoding routines are like the encoding routines in that there are two
kinds, one that decodes the type's tag, length and content and one
that only decodes the type's content.  As mentioned in the encoder
section, the content style interface allows implicit tagging decisions
to be made at compile time.

Unlike the encoding routines, the PDU and content decoding routines
take different arguments.  For the \verb$T1$ type the following would be
produced:
\begin{verbatim}
void BDecT1Content(BUF_TYPE b, AsnTag tagId0, AsnLen elmtLen0,
                   T1* v, AsnLen* bytesDecoded, ENV_TYPE env);
void BDecT1(BUF_TYPE b, T1* v, AsnLen* bytesDecoded, ENV_TYPE env);
\end{verbatim}

Notice that the content decoder, \verb$BDecT1Content$, has tag and
length parameters that the PDU decoder, \verb$BDecT1$, does not have.
Since the content decoder does not decode the tag and length on the
value, it is necessary to pass them in as parameters. Only OCTET
STRING and BIT STRING decoders will use actually use the information
in the tag parameter.

The \verb$BUF_TYPE$ parameter is the buffer that holds the BER value
being decoded.

The \verb$tagId0$ parameter the last tag that was decoded on the
content of the type that is about to be decoded.  In the case of type
\verb$T1$, \verb$BDecT1Content$ gets a tagId0 of UNIVERSAL (CONSTRUCTED) 16,
unless it is implicitly tagged by another type.  Most content decoding
routines ignore the tag information. OCTET STRING, and BIT STRING
decoders use the tag information to determine whether the contents are
constructed or primitive.  CHOICE decoders use the tag information to
determine which CHOICE element is present.  CHOICE values are treated
differently, as will be explained shortly.

The \verb$elmtLen0$ parameter is the length of the content of the type
being decoded.  This is simply the length decoded from the buffer by
the containing type's decoder just before calling this decode routine.

The \verb$v$ parameter is a pointer to space allocated for the type
being decoded.  This memory is not allocated by the decoding routine
itself; this supports the cases where the type is enclosed in the
struct of the parent (i.\ e.\  no extra allocation is necessary).  If
the type to be decoded is referenced by pointer from its parent type,
the parent type's decoding routine must allocate the type.

The \verb$bytesDecoded$ parameter maintains the running total of the
number of octets that have been decoded.  For example, if I call
\verb$BDecT1Content$ with a \verb$bytesDecoded$ parameter that points
to 20 and the encoded length of the \verb$T1$ value is 30 octets,
\verb$bytesDecoded$ will point to 50 when \verb$BDecT1Content$
returns.  Maintaining the length is vital to determining the presence
or absence of OPTIONAL elements in a SET or at the end of SEQUENCE.
Local variables are used to hold the lengths; there is no global stack
of lengths as with CASN1.

The \verb$env$ parameter is used in conjunction with \verb$longjmp$
calls.  When an decoder encounters a fatal error such as a missing
tag, it uses the \verb$env$ with a \verb$longjmp$ call to pop back to the
initial decode call. Section \ref{lib-err-C-sec} has more error
management details.

CHOICEs are decoded a little differently from other types.  For all
types except CHOICEs, all of the tag and length pairs on the content
are decoded by the parent type, and the last pair a passed into to
content decoding routine via the \verb$tagId0$ and \verb$elmtLen0$
parameters.  For CHOICEs, all of the tag and length pairs on the
content are decoded and then the first tag and length pair in the
CHOICE content is decoded by the parent and passed into the CHOICE
content decoding routine.  The first tag in a CHOICE's content is the
important tag by which the CHOICE determines which element is present.
This technique simplifies the code for dealing with untagged CHOICEs
embedded in other CHOICEs.  CHOICEs nested in this way mean that a
single tag determines which element is present in more than one
CHOICE.

The decoding routines allocate memory to hold the decoded value.  By
default snacc decoders use nibble memory (see Section
\ref{lib-mem-C-sec}) which is very efficient in allocation and
virtually cost free for freeing.

To save memory, decoders generated by some other tools build values
that reference the data in the encoded PDU for types like OCTET
STRING.  Snacc decoded values do not reference the BER data in any way
for several reasons.  One, the encoded value may be held in some
bizarre buffer making access to the value difficult.  Two, with more
encoding rules being formalized, this technique may not always work
since the encoded format may be different from the desired internal
format.  Three, snacc decoders concatenate any constructed BIT and
OCTET STRINGs values when decoding, to simplify processing in the
application.

Snacc decoders can detect a variety of errors which will be reported
by \verb$longjmp$.  Any tagging errors are reported. SETs must contain
all non-OPTIONAL components and SEQUENCEs must be in order and contain
all non-OPTIONAL components. Extra components in SETs and SEQUENCEs
are considered an error.  Errors will also be reported if you attempt
to decode values that exceed the limitations of the internal
representation (e.\ g.\ an integer that is larger than a \verb$long
int$ allows).

\section{\label{print-gen-C-sec}Print Routines}
All of the generated print routines take similar parameters. For
example the \verb$T1$ type's print routine prototype is:
\begin{verbatim}
void PrintT1(FILE* f, T1* v, unsigned short int indent);
\end{verbatim}

The print routine writes the given value, \verb$v$, to the given FILE*,
\verb$f$.  The printed value is indented by \verb$indent$ spaces.  The
values are printed in an ASN.1 value notation style.   \verb$PrintT1$ prints
the in the following style:
\begin{small}
\begin{verbatim}
{ -- SEQUENCE --
    17,
    '436c696d6220617420537175616d697368'H  -- "Climb at Squamish" --,
    0,
    { -- SEQUENCE OF -- 
        18,
        19
    },
    { -- SEQUENCE --
        id {2 40 29},
        value '736f6d6520737472696e67'H  -- "some string" --
    },
    20
}
\end{verbatim}
\end{small}

OCTET STRINGs are printed in a hexadecimal notation, and any printable
characters are included after the string in an ASN.1 comment.  Note
that the enumerated type value, 0, did not print its symbol, ``A''
from the ENUMERATED type.  It would be fairly easy to modify the C and
C++ back ends to generate print routines that printed the ENUMERATED
types' symbols instead of their values.

\section{\label{free-gen-C-sec}Free Routines}

Snacc generates free routines of the form:
\begin{verbatim}
void FreeT1(T1* v);
\end{verbatim}

These routines will free all the components named type.
For example the above \verb$FreeT1$ routine will free all the
components of the given \verb$T1$ value, but not the \verb$T1$ value itself.  The
passed in pointer is not freed because it may be embedded in another
type which will be freed by another call to \verb$Asn1Free$.  All the pieces
of memory are freed using the \verb$Asn1Free$ macro defined in
\verb$asn_config.h$.  Each library type has its own free routine that
may call \verb$Asn1Free$. The values are typically allocated during
decoding, using the \verb$Asn1Alloc$ macro.

The memory management can be changed by editing the \verb$asn_config.h$
file to use you own memory management routines.  By default the memory
manager uses the nibble memory system described in Section
\ref{lib-mem-C-sec}.  The nibble memory system does not need explicit
frees of each component so the generated free routines are not needed.
However, if you change the memory management to use something like
\verb$malloc$ and \verb$free$, you should use the generated free routines.


\section{\label{val-gen-C-sec}ASN.1 to C Value Translation}

C values will be produced for INTEGER, BOOLEAN and OBJECT IDENTIFIER
values.  C \verb$extern$ declarations for the value are put at
the end of the header file (after all of the type definitions).  The
value definitions are put at the beginning of the source file. For
example, the following will be produced for the EX1 module. 

at the end of file ex1.h:
\begin{small}
\begin{verbatim}
extern AsnOid anOidVal;
extern AsnOid theSameOidVal;
extern AsnInt anIntVal;
extern AsnBool aBoolVal;
extern AsnInt foobar;
\end{verbatim}
\end{small}

at the beginning of file ex1.c:
\begin{small}
\begin{verbatim}
AsnOid anOidVal = { 2, "\170\35" };
AsnOid theSameOidVal = { 2, "\170\35" };
AsnInt anIntVal = 1;
AsnBool aBoolVal = TRUE;
AsnInt foobar = 29;
\end{verbatim}
\end{small}

\section{\label{compiler-dir-C-sec}Compiler Directives}

Snacc allows the user to control some aspects of the generated code by
inserting special comments in the ASN.1 source.  Warning! only the
\verb$isPdu$ directive has been tested to any extent. Use the others
very carefully and only if you really need to.  The compiler
directives have the form:

\begin{verbatim}
--snacc <attribute>:"<value>"  <attribute>:"<value>" ...
\end{verbatim}

The \verb$attribute$ is the name of one of the accepted attributes the
\verb$value$ is what the \verb$attribute$'s new value will be.  The
attribute value pairs can be listed in a single \verb$--snacc$ comment
or spread out is a list of consecutive comments.

Compiler directives are only accepted in certain places in the ASN.1
code.  Depending on their location in the ASN.1 source, the compiler
directives affect type definitions or type references.  The directives
for type definitions and references are different.  Module level
compiler directives to specify output file names and other information
would be useful, but are not implemented.

Here is an example to present some of the compiler directives and
their uses.  Lets say your data structure always deals with
\verb$PrintableStrings$ that are null terminated (internally, not in
the encoding).  The default snacc string type is a structure that
includes a length and \verb$char*$ for the string octets.  To change
the default type to a simple \verb$char*$ the best way would be define
your own string type, lets say \verb$MyString$ as follows:

\begin{small}
\begin{verbatim}
Foo ::= SET
{
    s1 [0] MyString OPTIONAL,
    s2 [1] MyString,
    i1 [2] INTEGER
}

Bar ::= CHOICE
{
    s1 MyString,
    i1 INTEGER
}

Bell ::= MyString

MyString ::= --snacc isPtrForTypeDef:"FALSE" 
             --snacc isPtrForTypeRef:"FALSE" 
             --snacc isPtrInChoice:"FALSE" 
             --snacc isPtrForOpt:"FALSE" 
             --snacc optTestRoutineName:"MYSTRING_NON_NULL"
             --snacc genPrintRoutine:"FALSE"
             --snacc genEncodeRoutine:"FALSE"
             --snacc genDecodeRoutine:"FALSE"
             --snacc genFreeRoutine:"FALSE"
             --snacc printRoutineName:"printMyString"
             --snacc encodeRoutineName:"EncMyString"
             --snacc decodeRoutineName:"DecMyString"
             --snacc freeRoutineName:"FreeMyString"
             PrintableString --snacc cTypeName:"char*"
\end{verbatim}
\end{small}

All but the last \verb$--snacc$ comment bind with the \verb$MyString$ type
definition. The last directive comment binds with the \verb$PrintableString$
type.  The C data structure resulting from the above ASN.1 and compiler
directives is the following:
\begin{small}
\begin{verbatim}
typedef char* MyString; /* PrintableString */

typedef struct Foo /* SET */
{
    MyString s1; /* [0] MyString OPTIONAL */
    MyString s2; /* [1] MyString */
    AsnInt i1; /* [2] INTEGER */
} Foo;

typedef struct Bar /* CHOICE */
{
    enum BarChoiceId
    {
        BAR_S1,
        BAR_I1
    } choiceId;
    union BarChoiceUnion
    {
    MyString s1; /* MyString */
    AsnInt i1; /* INTEGER */
    } a;
} Bar;

typedef MyString Bell; /* MyString */
\end{verbatim}
\end{small}

The compiler directives used on the \verb$MyString$ type have some
interesting effects.  Notice that \verb$MyString$ is not referenced by
pointer in the CHOICE, SET, or type definition, \verb$Bell$. 

The generated code for encoding field \verb$s1$ of \verb$Foo$ type
will use the code\linebreak``\verb$MYSTRING_NON_NULL(&fooVal->s1)$'' to check
for the presence of the OPTIONAL \verb$s1$ field.  The code associated
with MYSTRING\_NON\_NULL should return TRUE if the \verb$s1$ field
value is present and might look like:
\begin{verbatim}
#define MYSTRING_NON_NULL(s) (*s != NULL)
\end{verbatim}

The argument to \verb$optTestRoutine$ routine will be a pointer to the
field type's defining type.  Note that in the above example,
\verb$MyString$ is a \verb$char*$, therefore the  \verb$MYSTRING_NON_NULL$
macro's argument will be a \verb$char**$.

Setting the \verb$genPrintRoutine$ etc.\  attributes to false makes
snacc not define or generate any encode, decode, print, or free
routines for the \verb$MyString$ type.  You must provide these
yourself; the best approach is to take the normal \verb$PrintableString$
routines and modify them to handle your special string type.

The names of the encode, decode, print and free routines used for the
\verb$MyString$ type will be based on the ones given with the
\verb$printRoutineName$ etc.\  attributes.  Snacc will prepend and a
``B'' (for BER) and append a ``Content'' to the encode and decode routines
names, so you must provide the \verb$BEncMyStringContent$ and
\verb$BDecMyStringContent$ routines.  You may also need the
\verb$BEncMyString$ and \verb$BDecMyString$ routines if \verb$MyString$ is a
PDU type or used in an ANY or ANY DEFINED type.

The \verb$PrintableString$ type has its C type name changed to
\verb$char*$ by the last compiler directive.  Thus \verb$MyString$ is defined
as a \verb$char*$.  This directive applies to the \verb$PrintableString$
type reference.  Note that these directives do not affect the tags or
the encoded representation of the \verb$MyString$ type

The location of the \verb$--snacc$ comment(s) is important.
\verb$--snacc$ comment(s) between the \verb$::=$ sign and the
following type are associated with the type being defined. Any
compiler directives after the type and before the next type or value
definition are associated with the type.  Fields in SETs, SEQUENCEs
and CHOICEs can be modified by putting the compiler directive after
the comma that follows the field type that you with to modify.  In the
case of the last element of one of these types, where there is no
comma, just place it after the field and before the closing bracket of
the parent type.  

Attributes shadow the type attributes filled in during the target
language type information generation pass of the compiler.  The type
definition attributes are:

\begin{description}
\item[cTypeName] { this is the type name that the generated type will
have. Its value can be any string that is valid as a C type name.}

\item[isPdu]     { whether this is a PDU type.  A PDU type will have
extra interfaces to the encode and decode routines generated. Its
value can be ``TRUE'' or ``FALSE''}

\item[isPtrForTypeDef] { TRUE if other types defined solely by this type
definition are defined as a pointer to this type. Its
value can be ``TRUE'' or ``FALSE''.}

\item[isPtrForTypeRef]{ TRUE if type references to this type
definition from a SET or SEQUENCE are by pointer. Its 
value can be ``TRUE'' or ``FALSE''.}

\item[isPtrInChoice] {TRUE if type references to this type definition
from a CHOICE are by pointer. Its value can be ``TRUE'' or ``FALSE''.}


\item[isPtrForOpt]  { TRUE if OPTIONAL type references to this type
definition from a SET or SEQUENCE are by pointer. Its value can be
``TRUE'' or ``FALSE''.} 

\item[optTestRoutineName] {name of the routine to test whether an
OPTIONAL element of this type in a SET or SEQUENCE is present. The
routine should return TRUE if the element is present.  The value of
this field is usually just the name of a C macro that tests for NON-NULL.
The argument to the routine will be a pointer to the type definition's
type.  The optTestRoutineName value can be any string value.}

\item[defaultFieldName] { if a this type is used in a SET, SEQUENCE or
CHOICE without a field name then this value is used with a digit
appended to it.  Its value can be any string that is a valid C field
name in a struct or union.}
\item[printRoutineName] { name of this type definition's printing
routine.  Its value can be any string that is a value C function or
macro name.}
\item[encodeRoutineName]{ name of this type definition's encoding
routine. Its value can be any string that is a value C function or
macro name.}
\item[decodeRoutineName]{ name of this type definition's decoding
routine. Its value can be any string that is a value C function or
macro name.}
\item[freeRoutineName]  { name of this type definition's freeing
routine. Its value can be any string that is a value C function or
macro name.}

\item[isEncDec] {If this type is used in a SET or SEQUENCE then it is not
encoded or decoded.  Its value can be ``TRUE'' or ``FALSE''.  This is
handy for adding your own types to a standard that are only for local
use, and are not included in encoded values.}

\item[genTypeDef] { TRUE if you want a C type to be generated for this
type definition. Its values can be ``TRUE'' or ``FALSE''.}

\item[genPrintRoutine] { TRUE if you want a printing routine to be
generated for this type definition. Its values can be ``TRUE'' or
``FALSE''.}
\item[genEncodeRoutine] { TRUE if you want an encoding routine to be
generated for this type definition. Its values can be ``TRUE'' or
``FALSE''.}
\item[genDecodeRoutine] { TRUE if you want a decoding routine to be
generated for this type definition. Its values can be ``TRUE'' or
``FALSE''.}
\item[genFreeRoutine] { TRUE if you want a free routine to be
generated for this type definition. Its values can be ``TRUE'' or
``FALSE''.}
\end{description}


The type reference attributes are slightly different from the type
definition attributes due to the semantic differences between a type
definition and a type reference.  Type references will inherit some of
their attributes from the referenced type definition.  The following
are the valid type reference attributes:
\begin{description}
\item[cTypeName] { this is the type name that the generated type will
have. Its value can be any string that is valid as a C type name.}

\item[cFieldName] { if this is a field in a CHOICE, SET or SEQUENCE
then this holds the C field name for this reference. Its value can be
any string that is valid as a C field name.} 

\item[isPtr] { TRUE if this is a pointer to the type named by
cTypeName.  This is usually determined from the referenced type
definitions attributes. Its value can be ``TRUE'' or ``FALSE''.}

\item[optTestRoutineName] {if this field is an OPTIONAL component then
this is the name of the routine to test whether it is present.  The
routine should return TRUE if the element is present.  The value of
Usually just the name of a C macro that tests for NULL. The argument
to the routine will be a pointer to the type definition's type.  The
optTestRoutineName value can be any string value.}

\item[printRoutineName] { name of this type reference's printing
routine.  This and the other routine name attributes are useful for
special instances of the referenced type.  It is easier to modify the
referenced type definition if you want every instance of this type to
use a certain print etc.\  routine.  Its value can be any string that is
a value C function or macro name.}

\item[encodeRoutineName]{ name of this type reference's encoding
routine. Its value can be any string that is a value C function or
macro name.}

\item[decodeRoutineName]{ name of this type reference's decoding
routine. Its value can be any string that is a value C function or
macro name.}

\item[freeRoutineName]  { name of this type reference's freeing
routine. Its value can be any string that is a value C function or
macro name.}

\item[isEncDec] { If this type is used in a SET or SEQUENCE then it is not
encoded or decoded.  Its value can be ``TRUE'' or ``FALSE''.  This is
handy for adding your own types to a standard that are only for local
use, and are not inlcuded in encoded values.}

\item[choiceIdSymbol] {if this is a component of a CHOICE, this string
attribute will be the defined/enum symbol whose value in the choiceId
field indicates the presence of this field.}
\item[choiceIdValue]  {if this is a component of a CHOICE, this integer
attribute will be the value associated with the symbol in choiceIdSymbol.}

\end{description}




\section{\label{compiling-gen-C-sec}Compiling the Generated C Code}

The generated C code (and libraries) can be compiled by both ANSI and
non-ANSI C compilers.  C function prototypes use the \verb$PROTO$
macro and C function declarations use the \verb$PARAMS$ macro.  These
macros are defined in \verb$snacc/include/asn_config.h$ and their
definitions depend on whether the \verb$__USE_ANSI_C__$ flag has been
defined.  When compiling the generated code you will need:
\begin{itemize}
\item[1] {\verb$snacc/c_include$ directory in your include path
(\verb$-I snacc/c_include$)} 
\item[2] {to link with one of the C ASN.1 runtime libraries in
\verb$snacc/c_lib$. There are three libraries to choose from, their
only difference is the buffer types they use.}
\item[3] {to link with the math library, since the ASN.1 REAL type's
encode and decode routine use some math routines. (\verb$-lm$)}
\end{itemize}

See the example in \verb$snacc/c_examples/simple$ for a complete
example. The makefile and main routines are probably the most
important.  There are several other examples in the
\verb$snacc/c_examples$ directory. 



\chapter{\label{lib-C-chp}C ASN.1 Library}
\section{\label{libover-C-sec}Overview}

Each library type has a file in the \verb$snacc/c_lib$ and
\verb$snacc/c_include$ directories.  Each source file contains the encode,
decode, free and print routines for the given type. This chapter
contains a description of each library type and its routines.
This library is also refered to as the runtime library.

After installing snacc, you should test the library types to make sure
that they are encoding and decoding properly.  Use the
\verb$snacc/c_example/test_lib$ example to check them.

In addition to other errors, most decoding routines will report an
error if they attempt to read past the end of the data.  Be aware that
some buffer types do not support this type of checking.  This is
explained more in the buffer management section.

\section{\label{tag-C-sec}Tags}

Snacc's tag representation was motivated by several things.
\begin{itemize}
\item[1] {the tags must be easy to compare for equality in \verb$if$
and \verb$switch$ statements to make tag based decisions cheap.}
\item[2] {a tag must be cheap to decode.}
\item[3] {a tag must be cheap to encode.}
\end{itemize}

The first requirement meant that tags had to be integer types (for the
\verb$switch statement$).  The representation of the tag within the integer
was set by the second requirement.

The best way to decode cheaply is minimize the transformation between
the encoded and decoded (internal) format.  So the four (can be set-up
for two) bytes of the long integer are used to hold the encoded tag,
starting with the first octet of the tag in the most significant byte
of the integer and the rest (if any) following.  Any unused (always
trailing) bytes in the integer are zero.  This limits the
representable tag code to less than $2^{21}$ but for reasonable ASN.1
specifications this should not be a problem.

To meet the third requirement the decoded tag representation was
bypassed entirely by using macros (\verb$BEncTag1()$ etc.\ ) that
write the encoded tag octet(s) to the buffer. The writing of an
encoded tag octet involves bit shifting, bitwise ands and bitwise ors
with constant values; most optimizing C compilers can compute these at
compile time.  This simplifies encoding a tag to writing some constant
byte value(s) to the buffer.

The following excerpt from \verb$snacc/c_include/asn_tag.h$ shows some
of the tag routines.
\begin{small}
\begin{verbatim}
typedef unsigned long int AsnTag;

#define MAKE_TAG_ID( class, form, code) ...
#define TAG_IS_CONS(tag) ...

#define BEncTag1( b, class, form, code) ...
#define BEncTag2( b, class, form, code) ...
#define BEncTag3( b, class, form, code) ...
#define BEncTag4( b, class, form, code) ...
#define BEncTag5( b, class, form, code) ...

AsnTag BDecTag(BUF_TYPE b, AsnLen* bytesDecoded, ENV_TYPE env);
\end{verbatim}
\end{small}

The generated decode routines use the \verb$BDecTag$ to decode a tag
from the buffer.  The returned tag value is either used in an
\verb$if$ expression or as the argument to \verb$switch$ statements.
The \verb$MAKE_TAG_ID$ macro is used to make a tag for comparison to
the one returned by \verb$BDecTag$. The \verb$MAKE_TAG_ID$ is used
\verb$switch$ statement case labels and in \verb$if$ statements.

Most of the time tags are only compared for equality, however, the
OCTET STRING and BIT STRING decoders check the constructed bit in the
tag using the \verb$TAG_IS_CONS$ macro.

The \verb$BEncTag$ macros are quite fragile because they return the
encoded length of the tag; they cannot be treated as a single
statement.  This requires careful use of braces when using them in
your own code in places such as the sole statement in an \verb$if$
statement.  This ugliness is caused by the difficulty in returning
values from multi-line macros (macros are used for performance here
since encoding tags can be a significant part of BER encoding).

The \verb$BDecTag$ routine will report an error via \verb$longjmp$ if
the encoded tag is longer than can be held in the \verb$AsnTag$ type
or if the it read past the end of the data when decoding the tag.

\section{\label{len-C-sec}Lengths}

Decoded lengths are represented by unsigned long integers, with the
maximum value indicating indefinite length.

Snacc users can choose between using only indefinite or only definite
lengths when encoding constructed values' lengths when compiling the
generated code.  Of course, the generated decoders can handle both
forms.  Define the \verb$USE_INDEF_LEN$ symbol when compiling the
generated code if you want to use indefinite lengths when encoding
constructed values.  Primitive values are always encoded with definite
lengths as required by the standard; this is necessary to avoid
confusion between a value's content and the End-Of-Contents marker.

There is no loss of performance when using definite lengths with snacc
encoders.  This is due the ``backwards'' encoding as described in
section \ref{encode-gen-C-sec}.  The schemes used by other compilers'
encoders to handle definite lengths may hurt performance.

Most of the routines in the following code are obvious except for
\verb$BEncDefLenTo127()$.  This is used instead of \verb$BEncDefLen$
in the generated code when the compiler knows the value being encode
will not be over 127 octets long.  Values such as BOOLEANS,
INTEGERs, and REALs are assumed to be shorter than 127 octets
(constraints on the decoded representation of INTEGERs and REALs make
this valid).
\begin{small}
\begin{verbatim}
typedef unsigned long int AsnLen;

/* max unsigned value  - used for internal rep of indef len */
#define INDEFINITE_LEN  ~0L 

#ifdef USE_INDEF_LEN
#define BEncEocIfNec(b) BEncEoc(b); 
#define BEncConsLen(b, len) 2 + BEncIndefLen(b)
#else
#define BEncEocIfNec(b)  
#define BEncConsLen(b, len) BEncDefLen(b, len)
#endif 

#define BEncIndefLen(b) ...
#define BEncDefLenTo127(b, len) ...
AsnLen  BEncDefLen(BUF_TYPE b, AsnLen len);
AsnLen  BDecLen(BUF_TYPE b, AsnLen* bytesDecoded, ENV_TYPE env);

#define BEncEoc(b) ...
#define BDEC_2ND_EOC_OCTET(b, bytesDecoded, env) ...
void    BDecEoc(BUF_TYPE b, AsnLen* bytesDecoded, ENV_TYPE env);
\end{verbatim}
\end{small}

The \verb$BDecLen$ routine will report an error via \verb$longjmp$ if
it attempts to read past the end of the data or the decoded length is
too large to be held in the \verb$AsnLen$ representation.
\verb$BDecEoc$ will report an error if it attempts to read past the
end of the data or one of the EOC (End-Of-Contents) octets is
non-zero.

\section{\label{bool-C-sec}BOOLEAN}

The BOOLEAN type is represented by an \verb$unsigned char$.  It has
the following routines for manipulating it.
\begin{small}
\begin{verbatim}
typedef unsigned char AsnBool;

AsnLen BEncAsnBool(BUF_TYPE b, AsnBool* data);
void   BDecAsnBool(BUF_TYPE b, AsnBool* result, AsnLen* bytesDecoded,
                   ENV_TYPE env);

AsnLen BEncAsnBoolContent(BUF_TYPE b, AsnBool* data);
void   BDecAsnBoolContent(BUF_TYPE b, AsnTag tag, AsnLen len,
                          AsnBool* result, AsnLen* bytesDecoded,
                          ENV_TYPE env);

#define FreeAsnBool(v)
void PrintAsnBool(FILE* f, AsnBool* b, unsigned short int indent);
\end{verbatim}
\end{small}

As discussed in Sections \ref{encode-gen-C-sec} and \ref{decode-gen-C-sec}, 
\verb$BEncAsnBool$ and \verb$BDecAsnBool$ encode/decode the UNIVERSAL
tag, length and content of the given BOOLEAN value.  The\linebreak \verb$BEncAsnBoolContent$ and \verb$BDecAsnBoolContent$ routine only
encode/decode the content of the given BOOLEAN value.

The \verb$FreeAsnBool$ routine does nothing since the BOOLEAN type
does not contain pointers to data; The free routine generator does not
have to check which types need freeing and simply calls the type's
free routine.  It also allows the user to modify the types and their
free routines without changing the free routine generator.  However,
the ANY and ANY DEFINED BY type hash table init routine generator does
need to know which types have empty free routines because the hash
entries contain pointers to the free functions (NULL is used for the
empty free functions like \verb$FreeAsnBool$).  The INTEGER, NULL,
REAL and ENUMERATED types have empty free routines for the same
reason.

\verb$BDecAsnBool$ will report an error if the tag is not
UNIVERSAL-PRIM-1.  \verb$BDecAsnBoolContent$ will report an error if it
decodes past the end of the data or the length of the encoded value
(given by the \verb$len$ parameter) is not exactly 1 octet.

\section{\label{int-C-sec}INTEGER}

The INTEGER type is represented by a long int, \verb$AsnInt$.  If you
are using INTEGER types that are only positive (via subtyping or
protocol definition) you may want to use the \verb$UAsnInt$ and
associated routines that use the unsigned long int for a larger
positive value range.
\begin{small}
\begin{verbatim}
typedef long int AsnInt;
typedef unsigned long int UAsnInt;

AsnLen BEncAsnInt(BUF_TYPE b, AsnInt* data);
void BDecAsnInt(BUF_TYPE b, AsnInt* result, AsnLen* bytesDecoded,
                ENV_TYPE env);

AsnLen BEncAsnIntContent(BUF_TYPE b, AsnInt* data);
void BDecAsnIntContent(BUF_TYPE b, AsnTag tag, AsnLen elmtLen, 
                       AsnInt*  result, AsnLen* bytesDecoded,
                       ENV_TYPE env);

#define FreeAsnInt(v)
void PrintAsnInt(FILE* f, AsnInt* v, unsigned short int indent);

AsnLen BEncUAsnInt(BUF_TYPE b, UAsnInt* data);
void BDecUAsnInt(BUF_TYPE b, UAsnInt* result, AsnLen* bytesDecoded,
                 ENV_TYPE env);

AsnLen BEncUAsnIntContent(BUF_TYPE b, UAsnInt* data);
void BDecUAsnIntContent(BUF_TYPE b, AsnTag tagId, AsnLen len,
                        UAsnInt* result, AsnLen* bytesDecoded,
                        ENV_TYPE env);

#define FreeUAsnInt(v) 
void PrintUAsnInt(FILE* f, UAsnInt* v, unsigned short int indent);
\end{verbatim}
\end{small}

\verb$BDecAsnInt$ will report an error if the tag is not
UNIVERSAL-PRIM-2.  \verb$BDecAsnIntContent$ will report an error if it
decodes past the end of the data or the integer value is too large for
an \verb$AsnInt$.

\section{\label{null-C-sec}NULL}

The NULL type is represented by the \verb$AsnNull$ type.  Its content
is always empty and hence its encoded length always is zero.
\begin{small}
\begin{verbatim}
typedef char AsnNull;

AsnLen BEncAsnNull(BUF_TYPE b, AsnNull* data);
void BDecAsnNull(BUF_TYPE b, AsnNull* result, AsnLen* bytesDecoded,
                 ENV_TYPE env);

/* 'return' length of encoded NULL value, 0 */
#define BEncAsnNullContent(b, data) 0
void BDecAsnNullContent(BUF_TYPE b, AsnTag tag, AsnLen len,
                        AsnNull* result, AsnLen* bytesDecoded,
                        ENV_TYPE env);

#define FreeAsnNull(v)
void PrintAsnNull(FILE* f, AsnNull * b, unsigned short int indent);
\end{verbatim}
\end{small}

\section{\label{real-C-sec}REAL}

The REAL type is represented by \verb$AsnReal$, a double.  This type's
representation can depend on the compiler or system you are using so
several different encoding routines are provided.  Even so, you may
need to modify the code.  

If you are using the REAL type in your ASN.1 modules, you should call the
\verb$InitAsnInfinity()$ routine to setup the \verb$PLUS_INFINITY$
and \verb$MINUS_INFINITY$ values.

There are three encode routines included and they can be selected by
defining one of \verb$IEEE_REAL_FMT$, \verb$IEEE_REAL_LIB$ or nothing.
Defining \verb$IEEE_REAL_FMT$ uses the encode routine that assumes the
double representation is the standard IEEE double \cite{68881}.
Defining \verb$IEEE_REAL_LIB$ uses the encode routine that assumes the
IEEE functions library (isinf, scalbn, signbit etc.\ ) is available.
If neither are defined, the default encode routine uses \verb$frexp$.

There is only one content decoding routine and it builds the value
through multiplication and the \verb$pow$ routine (requires the math
library).  The content decoding routine only supports the binary
encoding a of REAL, not the decimal encoding.

\begin{small}
\begin{verbatim}
typedef double AsnReal;

extern AsnReal PLUS_INFINITY;
extern AsnReal MINUS_INFINITY;

void InitAsnInfinity();
AsnLen BEncAsnReal(BUF_TYPE b, AsnReal* data);
void BDecAsnReal(BUF_TYPE b, AsnReal* result, AsnLen* bytesDecoded,
                       ENV_TYPE env);

AsnLen BEncAsnRealContent(BUF_TYPE b, AsnReal* data);
void BDecAsnRealContent(BUF_TYPE b, AsnTag tag, AsnLen len,
                               AsnReal* result, AsnLen* bytesDecoded,
                               ENV_TYPE env);

/* do nothing */ 
#define FreeAsnReal(v)
void PrintAsnReal(FILE* f, AsnReal* b, unsigned short int indent);
\end{verbatim}
\end{small}

\verb$BDecAsnReal$ will report an error if the value's tag is not
UNIVERSAL-PRIM-9.  \verb$BDecAsnRealContent$ will report an error if the base
is not supported or the decimal type REAL encoding is received.


\section{\label{bits-C-sec}BIT STRING}

The BIT STRING type is represented by the \verb$AsnBits$ structure.  It
contains a pointer to the bits and and integer that holds the length
in bits of the BIT STRING.

In addition to the standard encode, decode, print and free routines,
there are some other utility routines.  \verb$AsnBitsEquiv$ returns
TRUE if the given BIT STRINGs are identical.  The \verb$SetAsnBit$,
\verb$ClrAsnBit$ and \verb$GetAsnBit$ are routines for writing and
reading a BIT STRING value.  

You may notice that the AsnBits type does not have any means of
handling linked pieces of BIT STRINGs. Some ASN.1 tools use lists of
structures like \verb$AsnBits$ to represent BIT STRINGs.  This is done
because, as you should be aware, BIT STRINGs can be encoded in a
nested, constructed fashion.  The snacc BIT STRING decoder attempts to
save you the hassle of dealing with fragments of BIT STRINGs by
concatenating them in the decoding step.  Every BIT STRING value
returned by the decoder will have contiguous bits.

Some people contend that fragmented BIT STRINGs are necessary to
support systems that lack enough memory to hold the entire value.
Snacc encodes value ``backwards'' so the entire value must be encoded
before it can be sent, thus you must have enough memory to hold the
whole encoded value.  If the fragmented representation is useful to
your protocol implementation for other reasons, it should be fairly
simple to modify the BIT STRING routines.  Remember, no significance
should be placed on where constructed BIT STRING values are fragmented.

Snacc uses a table to hold pointers to the BIT STRING fragments in the
buffer while it is decoding them. Once the whole BIT STRING value has
been decoded, a block of memory that is large enough to hold the
entire BIT STRING is allocated and the fragments are copied into it.
The table initially can hold pointers to 128 fragments.  If more table
entries are needed the stack will grow via \verb$realloc$ (with
associated performances loss) and will not shrink after growing.  If
you wish to modify this behaviour, change the
\verb$snacc/c_include/str_stk.h$ file.

The \verb$FreeAsnBits$ routine will free memory referenced by the
\verb$bits$ pointer. 

\begin{small}
\begin{verbatim}
typedef struct AsnBits
{
    int   bitLen;
    char* bits;
} AsnBits;

extern char numToHexCharTblG[];
#define TO_HEX(fourBits) (numToHexCharTblG[(fourBits) & 0x0f])
#define ASNBITS_PRESENT(abits) ((abits)->bits != NULL)

AsnLen BEncAsnBits(BUF_TYPE b, AsnBits* data);
void BDecAsnBits(BUF_TYPE b, AsnBits* result, AsnLen* bytesDecoded,
                 ENV_TYPE env);

AsnLen BEncAsnBitsContent (BUF_TYPE b, AsnBits* bits);
void BDecAsnBitsContent(BUF_TYPE b, AsnLen len, AsnTag tagId,
                        AsnBits* result, AsnLen* bytesDecoded,
                        ENV_TYPE env);

void FreeAsnBits(AsnBits* v);
void PrintAsnBits(FILE* f, AsnBits* b, unsigned short int indent);

int AsnBitsEquiv(AsnBits* b1, AsnBits* b2);
void SetAsnBit(AsnBits* b1, unsigned long int bit);
void ClrAsnBit(AsnBits* b1, unsigned long int bit);
int GetAsnBit(AsnBits* b1, unsigned long int bit);
\end{verbatim}
\end{small}

\verb$BDecAsnBits$ will report an error if the tag is not
UNIVERSAL-CONS-3 or UNIVERSAL-PRIM-3.  When decoding constructed BIT
STRING BER values, an error will be reported if a component other than
the last one has non-zero unused bits in its last octet or an internal
component does not have the UNIVERSAL-3 tag. If the decoder attempts to
read past the end of the data an error will be reported.


\section{\label{octets-C-sec}OCTET STRING}
The OCTET STRING type is represented by the \verb$AsnOcts$ structure.  It
contains a pointer to the octets and an integer that holds the length
in octets of the OCTET STRING.

As with BIT STRINGs, OCTET STRINGs can have constructed values.  These
are handled in the same way as the constructed BIT STRING values. The
decoded representation of an OCTET STRING is always contiguous.

The \verb$FreeAsnOcts$ routine will free the memory referenced by the
\verb$octs$ pointer.  The \verb$AsnOctsEquiv$ routine will return TRUE
if the given OCTET STRINGs are identical.

\begin{small}
\begin{verbatim}
typedef struct AsnOcts
{
    unsigned long int octetLen;
    char* octs;
} AsnOcts;

#define ASNOCTS_PRESENT(aocts) ((aocts)->octs != NULL)

AsnLen BEncAsnOcts(BUF_TYPE b, AsnOcts* data);

void BDecAsnOcts(BUF_TYPE b, AsnOcts* result, AsnLen* bytesDecoded,
                 ENV_TYPE env);

AsnLen BEncAsnOctsContent (BUF_TYPE b, AsnOcts* octs);
void BDecAsnOctsContent (BUF_TYPE b, AsnLen len, AsnTag tagId,
                         AsnOcts* result, AsnLen* bytesDecoded,
                         ENV_TYPE env);

void FreeAsnOcts (AsnOcts* o);
void PrintAsnOcts (FILE* f, AsnOcts *o, unsigned short int indent);

int AsnOctsEquiv (AsnOcts* o1, AsnOcts* o2);
\end{verbatim}
\end{small}

\verb$BDecAsnOcts$ will report an error if the tag is not
UNIVERSAL-CONS-4 or UNIVERSAL-PRIM-4.  When decoding constructed OCTET
STRING BER values, an error will be reported if an internal component
does not have the UNIVERSAL-4 tag. if the decoder attempts to read
past the end of the data an error will be reported.


\section{\label{oid-C-sec}OBJECT IDENTIFIER}

In snacc, OBJECT IDENTIFIERs are kept in their encoded form to improve
performance.  The \verb$AsnOid$ type is defined as \verb$AsnOcts$, as
it holds the octets of the encoded OBJECT IDENTIFIER.  It seems that
the most common operation with OBJECT IDENTIFIERs is to compare for
equality, for which the encoded representation (which is canonical)
works well.

There is a linked OBJECT IDENTIFIER representation called \verb$OID$
and routines to convert it to and from the \verb$AsnOid$ format, but it
should not be used if performance is an issue.

Since the OBJECT IDENTIFIERs are represented \verb$AsnOcts$, the
\verb$AsnOcts$ content encoding routine can be used for the
\verb$AsnOid$ content encoding routine.  The other \verb$AsnOcts$
encoding and decoding routines cannot be used because the OBJECT
IDENTIFIER has a different tag and cannot be encoded in a constructed
fashion.

An OBJECT IDENTIFIER must have a minimum of two arc numbers but the
decoding routines do not check this.

\begin{small}
\begin{verbatim}
typedef AsnOcts AsnOid;

#define ASNOID_PRESENT(aoid) ASNOCTS_PRESENT(aoid)

AsnLen BEncAsnOid(BUF_TYPE b, AsnOid* data);
void BDecAsnOid(BUF_TYPE b, AsnOid* result, AsnLen* bytesDecoded,
                ENV_TYPE env);

#define BEncAsnOidContent(b, oid)  BEncAsnOctsContent(b, oid)
void BDecAsnOidContent(BUF_TYPE b, AsnTag tag, AsnLen len,
                       AsnOid*  result, AsnLen* bytesDecoded,
                       ENV_TYPE env);

#define FreeAsnOid FreeAsnOcts
void PrintAsnOid (FILE* f, AsnOid* b, unsigned short int indent);

#define AsnOidsEquiv(o1, o2) AsnOctsEquiv(o1, o2)
\end{verbatim}
\end{small}


\section{\label{list-C-sec}SET OF and SEQUENCE OF}

The SET OF and SEQUENCE OF type are represented by the \verb$AsnList$
structure.  An \verb$AsnList$ consists of a head object that has
pointers to the first, current and last nodes and the current number
of nodes in the list.  Each list node has a pointer to its next and
previous list member and the node's data.  The first list node's
previous pointer is always NULL and the last list node's next pointer
is always NULL.

Each SET OF or SEQUENCE OF type is defined as an \verb$AsnList$, so the
element type information (kept via a \verb$void*$) is not kept,
therefore, the \verb$AsnList$ type is not type safe.

The \verb$AsnList$ is doubly linked lists to simplify ``backwards''
encoding.  The reverse link allows the list to be traversed in reverse
so the components can be encoded from last to first.

Initially, the lists were designed to allow the list element itself to
be contained in the list node (hence the \verb$elmtSize$ parameter to
the AsnListNew() routine).  The design eventually changed such that
every list element was reference by pointer from the list node.

As small problem with the \verb$AsnListNew$ routine is the memory
allocation.  Since it is used by the decoding routines to allocate new
lists, it uses whatever memory management you have setup with the
\verb$Asn1Alloc$ macro (see Section \ref{lib-mem-C-sec}).  This may not be
desirable when building values to be transmitted.  You may need to
provide another AsnListNew routine that uses a different allocation
scheme to solve this.

\begin{small}
\begin{verbatim}
typedef struct AsnListNode
{
    struct AsnListNode *prev;
    struct AsnListNode *next;
    void            *data; /* this must be the last field of this structure  */
} AsnListNode;

typedef struct AsnList
{
    AsnListNode *first;
    AsnListNode *last;
    AsnListNode *curr;
    int        count;    /* number of elements in list               */
    int        dataSize; /* space required in each node for the data */
} AsnList;

#define  FOR_EACH_LIST_ELMT( elmt, list) ...
#define  FOR_EACH_LIST_ELMT_RVS( elmt, list) ...
#define  FOR_REST_LIST_ELMT( elmt, al) ...

#define CURR_LIST_ELMT(al)    (al)->curr->data
#define NEXT_LIST_ELMT(al)    (al)->curr->next->data
#define PREV_LIST_ELMT(al)    (al)->curr->prev->data
#define LAST_LIST_ELMT(al)    (al)->last->data
#define FIRST_LIST_ELMT(al)   (al)->first->data
#define LIST_EMPTY(al) ((al)->count == 0)

#define CURR_LIST_NODE(al) ((al)->curr)
#define FIRST_LIST_NODE(al) ((al)->first)
#define LAST_LIST_NODE(al) ((al)->last)
#define PREV_LIST_NODE(al) ((al)->curr->prev)
#define NEXT_LIST_NODE(al) ((al)->curr->next)
#define SET_CURR_LIST_NODE(al, listNode)  ((al)->curr = (listNode))

void  AsnListRemove(AsnList* l);
void* AsnListAdd(AsnList* l);
void* AsnListInsert(AsnList* list);
void  AsnListInit(AsnList* list, int dataSize);
AsnList* AsnListNew(int elmtSize);
void* AsnListPrev(AsnList*);
void* AsnListNext(AsnList*);
void* AsnListLast(AsnList*);
void* AsnListFirst(AsnList*);
void* AsnListPrepend(AsnList*);
void* AsnListAppend(AsnList*);
void* AsnListCurr(AsnList*);
int   AsnListCount(AsnList*);
AsnList* AsnListConcat(AsnList*, AsnList*);
\end{verbatim}
\end{small}

There are a number of macros for dealing with the list routine, the
most important being the list traversal macros.  The
\verb$FOR_EACH_LIST_ELMT$ macro acts like a for statment that
traverses forward through the list.  The first parameter should be a
pointer to the list element type that will be used to hold the current list
element for each iteration of the for loop.  The second parameter is
the list of elements that you wish to traverse.   

The \verb$FOR_EACH_LIST_ELMT_RVS$ macro is identical to the
\verb$FOR_EACH_LIST_ELMT$ macro except that is moves from the back of
the list to the front.   The \verb$FOR_REST_LIST_ELMT$ macro is
similar to the other two but it does not reset the \verb$curr$ pointer
in the \verb$AsnList$ type.  This has the effect of iterating from the
current element to the end of the list.   Look in the generated code
for a better indication of how to use these macros.  The other macros
are straight forward.


\section{\label{any-C-sec}ANY and ANY DEFINED BY}

The ANY and ANY DEFINED BY type are classically the most irritating
ASN.1 types for compiler writers.  They rely on mechanisms outside of
ASN.1 to specify what types they contain.  The 1992 ASN.1 standard has
rectified this by adding much stronger typing semantics and eliminating
macros.

The ANY DEFINED BY type can be handled automatically by snacc
provided you use the SNMP OBJECT-TYPE macro to specify the identifier
to type mapping.  The identifier can be an INTEGER or OBJECT
IDENTIFIER.  Handling ANY types properly will require modifications to
the generated code since there is no identifier associated with the
type.

Look at the C and C++ ANY examples and \verb$any.asn1$ included with
this release for information on using the OBJECT-TYPE macro.  Note
that the OBJECT-TYPE macro has been modified slightly to allow INTEGER
values (identifiers).

The general approach used by snacc to handle ANY DEFINED BY types is
to hash on the identifier value to define the type.  The hash table
entry contains information about the type such as routines to use for
encoding and decoding.

Two hash tables are used, one for INTEGER  to type mappings and the
other for OBJECT IDENTIFIER to type mappings.  Snacc generates an
\verb$InitAny${\em ModuleName} routine to add mappings to the hash
table(s) for each module that uses the OBJECT-TYPE macro to define
identifier to type mappings.  For example with a module named
\verb$"ANY-TEST"$, snacc will generate an \verb$InitAnyANY_TEST()$
routine.  The \verb$InitAny$ routine(s) should be called only once,
before any encoding or decoding is done.  If there is more than one
\verb$InitAny$ routine, the order in which they are called is not
relevant.

The hash tables are constructed such that an INTEGER or OBJECT
IDENTIFIER value will hash to an entry that contains:
\begin{itemize}
\item {the \verb$anyId$}
\item {the INTEGER or OBJECT IDENTIFIER that maps to it}
\item {the size in bytes of the identified data type}
\item {a pointer to the type's PDU encode routine}
\item {a pointer to the type's PDU decode routine}
\item {a pointer to the type's print routine}
\item {a pointer to the type's free routine}
\end{itemize}
The referenced encode and decode routines are PDU oriented in that
they encode the type's tag(s) and length(s) as well as the type's
content. 

Snacc builds an \verb$enum$ called {\em ModuleName}\verb$AnyId$ that
enumerates each mapping defined by the OBJECT-TYPE macros.  The name
of the value associated with each macro is used as part of the
enumerated identifier.  The \verb$anyId$ in the hash table holds the
identified type's enum value.  The \verb$anyId$ is handy for making
decisions based on the received identifier, without comparing OBJECT
IDENTIFIERs.  If the identifiers are INTEGERs then the \verb$anyId$ is
less useful.
 
With ANY DEFINED BY types, it is important to have the identifier
decoded before the ANY DEFINED BY type is decoded.  Hence, you should
not put an ANY DEFINED BY type and identifier in a SET since they are
un-ordered and you definitely should not put an ANY DEFINED BY type
before its identifier in a SEQUENCE. Snacc will print a warning if any
of these situations occur.

The hash tables may be useful to plain ANY types which do not have an
identifier field like the ANY DEFINED BY types; use the OBJECT-TYPE
macro to define the mappings and call the \verb$SetAnyTypeByInt$ or
\verb$SetAnyTypeByOid$ routine with the appropriate identifier value
before encoding or decoding an ANY value.  The compiler will insert
calls to these routines where necessary with some of the arguments
left as ``???''.  There will usually be a ``\verb$/* ANY - Fix me !
*/$'' comment before code that needs to be modified to correctly
handle the ANY type.  The code generated from an ASN.1 module that
uses the ANY type usually will not compile without the above
modifications.

OPTIONAL ANYs and ANY DEFINED BY types that have not been tagged present
special problems for snacc.  Unless they are the last element of a SET
or SEQUENCE, the generated code will need to be modified.  Snacc will
print a warning message when it encounters one of these cases.

To illustrate how things work, here is an brief outline of what
happens when encoding and decoding.  Each ANY or ANY DEFINED BY type
is represented in C by the \verb$AsnAny$ type which contains only a
\verb$void* value$ to hold a pointer to the value and \verb$AnyInfo*
ai$ which points to a hash table entry (as described above).

When encoding, before the ANY or ANY DEFINED BY value is encoded,
\verb$SetAnyTypeByOid$ or \verb$SetAnyTypeByInt$ (depending on the type
of the identifier) is called with the current identifier value to set
the \verb$AsnAny$ value's \verb$ai$ pointer to the proper hash table entry.
Then the encode routine pointed to from the hash table entry is called
with the \verb$void* value$ value from the \verb$AsnAny$ value.  The
\verb$void* value$ in the \verb$AsnAny$ should point to a value of the
correct type for the given identifier, if the user set it up
correctly.  Setting the \verb$void* value$ is not a type safe; you
must make sure that the value's type is the same as indicated by the
identifier.

For decoding, the identifier must have been decoded previously
otherwise the identifier will contain an uninitialized value.  Before
the ANY or ANY DEFINED BY value is decoded, \verb$SetAnyTypeByOid$ or
\verb$SetAnyTypeByInt$ (depending on the type of the identifier) is 
called to set the \verb$AsnAny$ value's \verb$ai$ pointer to the
proper hash table entry.  Then a block of memory of the size
indicated in the hash table entry is allocated, and its pointer stored
in the \verb$AsnAny$ value's \verb$void* value$.  Then the decode routine
pointed to from the hash table entry is called with the newly
allocated block as it value pointer parameter.  The decode routine
fills in the value assuming it is the correct type. Simple! 

Here is some of the \verb$AsnAny$ related code from the header file.  It
should help you understand the way things are done a bit better.  Look
in the \verb$hash.c$ and \verb$hash.h$ files as well.
\begin{small}
\begin{verbatim}
/*
 * 1 hash table for integer keys
 * 1 hash table for oid keys
 */
extern Table* anyOidHashTblG;
extern Table* anyIntHashTblG;

typedef (*EncodeFcn) (BUF_TYPE b, void* value);
typedef void (*DecodeFcn) (BUF_TYPE b, void* value,
                           AsnLen* bytesDecoded, ENV_TYPE env);
typedef void (*FreeFcn)   (void* v);
typedef void (*PrintFcn)  (FILE* f, void* v);

/* 
 * this is put into the hash table with the
 * int or oid as the key
 */
typedef struct AnyInfo
{
    int    anyId;  /* will be a value from the AnyId enum */
    AsnOid oid;    /* will be zero len/null if intId is valid */
    AsnInt intId;
    unsigned int size;  /* size of the C data type (ie as ret'd by sizeof) */
    EncodeFcn Encode;
    DecodeFcn Decode;
    FreeFcn Free;
    PrintFcn Print;
} AnyInfo;

typedef struct AsnAny
{
    AnyInfo* ai; /* point to entry in hash tbl that has routine ptrs */
    void* value; /* points to the value */
} AsnAny;

/*
 * Returns anyId value for the given ANY type.
 * Use this to determine to the type of an ANY after decoding
 * it. Returns -1 if the ANY info is not available
 */
#define GetAsnAnyId(a) (((a)->ai)? (a)->ai->anyId: -1)

/*
 * used before encoding or decoding a type so the proper
 * encode or decode routine is used.
 */
void SetAnyTypeByInt (AsnAny* v, AsnInt id);
void SetAnyTypeByOid (AsnAny* v, AsnOid* id);


/*
 * used to initialize the hash table(s)
 */
void InstallAnyByInt(int anyId,  AsnInt intId,
                     unsigned int size, EncodeFcn encode,
                     DecodeFcn decode, FreeFcn free, PrintFcn print);

void InstallAnyByOid(int anyId, AsnOid* oid, unsigned int size,
                     EncodeFcn encode, DecodeFcn decode, FreeFcn free,
                     PrintFcn print);

/*
 * Standard enc, dec, free, & print routines.
 * for the AsnAny type.
 * These call the routines referenced from the
 * given value's hash table entry.
 */
void FreeAsnAny (AsnAny* v);
AsnLen BEncAsnAny (BUF_TYPE b, AsnAny* v);
void BerDecAsnAny(BUF_TYPE b, AsnAny*  result, AsnLen* bytesDecoded,
                  ENV_TYPE env);
void PrintAsnAny (FILE* f, AsnAny* v, unsigned short indent);


/* AnyDefinedBy is the same as AsnAny */
typedef AsnAny AsnAnyDefinedBy;
#define FreeAsnAnyDefinedBy FreeAsnAny
#define BEncAsnAnyDefinedBy BEncAsnAny
#define BDecAsnAnyDefinedBy BDecAsnAny
#define PrintAsnAnyDefinedBy PrintAsnAny
\end{verbatim}
\end{small}


\section{\label{lib-buf-sec}Buffer Management}

Encoding and decoding performance is heavily affected by cost of
writing to and reading from buffers, thus, efficient buffer management
is necessary.  Flexibility is also important to allow integration of
the generated encoders and decoders into existing environments.  To
provide both of these features, the calls to the buffer routines are
actually macros that can be configured as you want (see
\verb$snacc/c_include/asn_config.h$). Virtually all buffer calls will
be made from the encode/decode library routines.  So macros used in
the generated code will make buffer calls.

If your environment uses a single, simple buffer type, the buffer
routine macros can be defined as the macros for your simple buffer type.
This results in the buffer type being bound at compile time, with no
function call overhead from the encode or decode routines.  This also
mean that the runtime library only works for that buffer type.

If multiple buffer formats must be supported at runtime, the buffer
macros can be defined like the ISODE buffer calls, where a buffer type
contains pointers to the buffer routines and data of the current
buffer type.  This approach will hurt performance since each buffer
operation will be an indirect function call.

The backwards encoding technique requires special buffer primitives
that write from the end of the buffer towards the front.  This
requirement will make it impossible to define buffer primitives that
write directly to stream oriented objects such as TCP connections.  In
cases such as this, you must encode the entire PDU before sending it.
(Or else extend the back-end of the compiler to produce ``forwards''
encoders as well).

Nine buffer primitives are required by the runtime library's encode
and decode routines:
\begin{itemize}
\item \verb$unsigned char BufGetByte(BUF_TYPE b);$
\item \verb$unsigned char BufPeekByte(BUF_TYPE b);$
\item \verb$char* BufGetSeg(BUF_TYPE b, unsigned long int* lenPtr);$
\item \verb$void BufCopy(char* dst, BUF_TYPE b, unsigned long int* lenPtr);$
\item \verb$void BufSkip( BUF_TYPE b, unsigned long int len);$
\item \verb$void BufPutByteRv( BUF_TYPE b,  unsigned char byte);$
\item \verb$void BufPutSegRv( BUF_TYPE b, char* data, unsigned long int len);$
\item \verb$int BufReadError(BUF_TYPE b);$
\item \verb$int BufWriteError(BUF_TYPE b);$
\end{itemize}

These buffer operations are described in the next subsections.  The
\verb$ExpBuf$, \verb$SBuf$ and \verb$MinBuf$ buffer formats that come
with the snacc distribution and how to configure the buffer operations
are discussed following that. 

\subsection{\label{buf-read-c-sec}Buffer Reading Routine Semantics}

The buffer reading routines are called by the decoder routines.  The
following is the list of necessary buffer reading routines and their
semantics.   Be sure to setup the buffer in reading mode before
calling any of these routines.  The means of putting a buffer in
reading mode depends on the buffer type.  

\begin{verbatim}
unsigned char BufGetByte(BUF_TYPE b);
\end{verbatim}
Returns the next byte from the buffer and advances current pointer
such that a subsequent buffer read returns the following byte(s).
This will set the read error flag if an attempt to read past the end
of the data is made.

\begin{verbatim}
unsigned char BufPeekByte(BUF_TYPE b); 
\end{verbatim}
Returns the next byte from the buffer without advancing the current
pointer.

\begin{verbatim}
char* BufGetSeg(BUF_TYPE b, unsigned long int* lenPtr);
\end{verbatim}
Returns a pointer to the next bytes from the buffer and advances the
current pointer. \verb$*lenPtr$ should contain the number of bytes to
read.  If the buffer has a least \verb$*lenPtr$ contiguous bytes
remaining to be read before calling \verb$BufGetSeg$, a pointer to
them will be returned and \verb$*lenPtr$ will be unchanged.  If there
are less than \verb$*lenPtr$ contiguous bytes remaining in the buffer
before the call to \verb$BufGetSeg$, a pointer to them is returned and
\verb$*lenPtr$ is set to the actual number of bytes that are
referenced by the returned pointer.  The current pointer will be
advanced by the value returned in \verb$*lenPtr$ (this may advance to the
next buffer segment if any).  Note that the read error flag is not set
if  \verb$*lenPtr$ is greater than the remaining number of unread
bytes. 

\begin{verbatim}
unsigned long int BufCopy( char* dst, BUF_TYPE b, unsigned long int len)
\end{verbatim}
Copies the next \verb$len$ bytes from the buffer into the \verb$dst
char*$ and advances the current pointer appropriately.  Returns the
number of bytes actually copied.  The number of bytes copied will be
less than requested only if the end of data is reached, in which case
the read error flag is set.


\begin{verbatim}
void BufSkip(BUF_TYPE b, unsigned long int len);
\end{verbatim}
Advances the buffer's current pointer by len bytes.  This will set the
read error flag if less than \verb$len$ unread bytes remain in the
buffer before the call to \verb$BufSkip$.

\begin{verbatim}
int BufReadError(BUF_TYPE b);
\end{verbatim}
Returns non-zero if a read error occurred for the given buffer.
Read errors occur if one of the buffer reading routines attempted to
read past the end of the buffer's data.

\subsection{\label{buf-write-c-sec}Buffer Writing Routine Semantics}

Encoding routines call the buffer writing routines.  Here is a list of
the buffer writing routine and their semantics.  Before calling the
writing routines, you should make sure the buffer is setup for
writing in reverse mode.  The means of doing this depends on the
buffer type.  

\begin{verbatim}
void BufPutByteRvs(BUF_TYPE b, unsigned char byte);
\end{verbatim}
Writes the given byte to the beginning of the data in the given 
buffer.  The newly written byte becomes part of the buffer's data such
that subsequent writes place bytes before the newly written byte.  If
a buffer write error occurs, subsequent writes do nothing.

\begin{verbatim}
void BufPutSegRvs(BUF_TYPE b, char* data, unsigned long int len);
\end{verbatim}
Prepends the given bytes, \verb$data$, of length \verb$len$ to the
beginning of the data in the given buffer \verb$b$.  The \verb$data$
bytes are written such that the first byte in \verb$data$ becomes the
first byte of the buffer's data, followed by the rest. (This means the
bytes in \verb$data$ are not reversed, they are simply prepended as a
unit to the buffer's original data). If a buffer write error occurs,
subsequent writes do nothing.

\begin{verbatim}
int BufWriteError(BUF_TYPE b);
\end{verbatim}
Returns non-zero if a write error occurred for the given buffer.
Write errors occur if the buffer runs out of space for data or cannot
allocate another data block (depends on the buffer type). 

\subsection{Buffer Configuration}

The runtime library's encode and decode routines as well as the
generated code access the buffers via the nine buffer macros
described in the last two sections.  These macros can be defined to
call simple macros for speed or to call functions.  Note that the
buffer configuration is bound at the time the library and generated
code are compiled.

The following is from \verb$snacc/include/asn_config.h$ and shows how to
configure the buffer routines.  This setup will make all calls to
\verb$BufGetByte$ in the library and  generated code call your
\verb$ExpBufGetByte$ routine; the other buffer routines are mapped to
their \verb$ExpBuf$ equivalents in a similar way.

\begin{verbatim}
#include "exp_buf.h"
#define BUF_TYPE ExpBuf**
#define BufGetByte(b)              ExpBufGetByte(b)
#define BufGetSeg(b, lenPtr)       ExpBufGetSeg(b, lenPtr)
#define BufCopy( dst, b, lenPtr)   ExpBufCopy( dst, b, lenPtr)
#define BufSkip( b, len)           ExpBufSkip(b, len)
#define BufPeekByte( b)            ExpBufPeekByte( b)
#define BufPutByteRv( b,  byte)    ExpBufPutByteRv( b, byte)
#define BufPutSegRv( b, data, len) ExpBufPutSegRv( b, data, len)
#define BufReadError(b)            ExpBufReadError(b)
#define BufWriteError(b)           ExpBufWriteError(b)
\end{verbatim}

If you want to use your own buffer type, simply edit the
\verb$asn_config.h$ file such that it includes your buffer's header
file, sets the \verb$BUF_TYPE$ type, and defines the nine buffer
routines (\verb$BufGetByte$ etc.) to call your buffer routines.  Your
buffer routines should have the semantics and prototypes described in
the last two sections (Sections \ref{buf-read-c-sec} and
\ref{buf-write-c-sec}).

\subsection{ExpBuf Buffers}

The \verb$ExpBuf$ buffers are a doubly linked series of buffers that
can be expanded when encoding by adding new buffers as necessary.
Each \verb$ExpBuf$ consists of two blocks of memory, one for the
control and linking information and the other for the data; when
refering to an \verb$ExpBuf$ both parts are included. \verb$ExpBuf$ is
short for ``Expanding Buffer''.  Look in \verb$snacc/c_lib/exp_buf.c$
for an ASCII drawing of the \verb$ExpBuf$ buffers. Take a look a the
\verb$snacc/c_examples/simple/expbuf_ex.c$ file for a quick
introduction on how to use \verb$ExpBufs$.

\verb$ExpBufs$ are fairly general and useful when a reasonable upper
bound can not be put on the size of the encoded values that will be
encountered by the protocol.  The flexibility of these buffer routines
will hurt the performance as many of the \verb$ExpBuf$ calls are not
macros and new buffers may need to be allocated during encoding.

For encoding you need to write into the \verb$ExpBufs$. Start with a
single ExpBuf (or the last one in a list of ExpBufs from a previous
encoding).  Make sure this ExpBuf has been reset is ``Write Reverse''
mode (use \verb$ExpBufResetInWriteRvsMode$).  This clears the write
error flag (and sets the read error flag in case you try a read) and
resets the data start and data end pointers such that the buffer is
empty and ready for writing from the end towards the front.

During encoding, if an \verb$ExpBuf$'s data part fills up, a new
\verb$ExpBuf$ before (since writing in reverse) the current buffer is
needed.  If the \verb$prev$ pointer in the current buffer is non-NULL,
the previous buffer is reset for writing and becomes the current
buffer.  If the \verb$prev$ pointer in the current buffer NULL, a new
buffer is allocated, its pointer is placed in \verb$prev$ and it
becomes the current buffer.  The notion of current buffer is handled
by the parameter to the encoding and decoding routines.  The buffer
parameter is a an \verb$ExpBuf**$ and it always holds the current
\verb$ExpBuf*$ (current buffer).

When encoding is finished and the encoded value has been transmitted,
you have two options.   You can free the entire buffer list or you can
keep them around and re-use them for the next encoding.  Freeing the
buffers after each encoding may be quite slow.  If you re-use the
buffers, the buffer list will grow to the size of the largest encoding
and stay there.  You can easily implement other management schemes.
By default the \verb$ExpBufs$ (both parts) are allocated and freed with
\verb$malloc$ and \verb$free$; you may want to change this to fit you
environment better. If buffer allocation fails during a write, the
writeError flag will be set and subsequent writes will do nothing.

For decoding you will want to put the encoded data into the
\verb$ExpBuf$ format.   For example, if your encoded value is
contiguous in a single block of memory, you could use
\verb$ExpBufInstallDataInBuf$ to attach your data to a single ExpBuf.
Once your data is in the ExpBuf format, you should call
\verb$ExpBufResetInReadMode$ on the first buffer in the list (if more
than one).  Then you can pass it to the desired decode routine.

If a decode routine attempts to read past the end of a buffer (usually
due to an erroneous encoding), the readError flag will be set for the
current \verb$ExpBuf$ in the list.  This error will typically cause
the decoding routine that called the buffer read routine to call
\verb$longjmp$.

The \verb$BUF_TYPE$ is defined as \verb$ExpBuf**$ so that the buffer
parameter \verb$b$ can be set to the next active \verb$ExpBuf$ by the
buffer routines.  This saves having a head of the list type structure
that keeps track of the the first, last and current buffers (the
indirectness of this approach would hurt performance).

There are many routines for administrating the \verb$ExpBufs$ if you
want to treat them like an abstract data type.  Sometimes it may be
easier to skip the utility routines and modify the fields directly.

The following routines are the required nine buffer routines.  Compile
the library and the generated code with the \verb$USE_EXP_BUF$ symbol
defined to map buffer routines that the generated and library code
calls to the \verb$ExpBuf$ routines (see
\verb$snacc/c_include/asn_config.h$). These \verb$ExpBuf$ routines
adhere to the buffer routine prototypes and semantics defined in
Sections \ref{buf-read-c-sec} and \ref{buf-write-c-sec}.

\begin{verbatim}
void          ExpBufSkip(ExpBuf**, unsigned long len);
int           ExpBufCopy(char* dst, ExpBuf** b, unsigned long len);
unsigned char ExpBufPeekByte(ExpBuf** b);
char*         ExpBufGetSeg(ExpBuf** b, unsigned long* len);
void          ExpBufPutSegRvs(ExpBuf** b, char* data, unsigned long len);
unsigned char ExpBufGetByte(ExpBuf** b);
void          ExpBufPutByteRvs(ExpBuf** b, unsigned char byte);
#define       ExpBufReadError(b) ((*b)->readError)
#define       ExpBufWriteError(b) ((*b)->writeError)
\end{verbatim}


The following \verb$ExpBuf$ routines are also provided.  Their
descriptions can be found in the code.
\begin{verbatim}
void ExpBufInit(unsigned long dataBlkSize);
void ExpBufInstallDataInBuf(ExpBuf* b, char* data, unsigned long int len);

void ExpBufResetInReadMode(ExpBuf* b);
void ExpBufResetInWriteRvsMode(ExpBuf* b);

ExpBuf* ExpBufAllocBufAndData();
void ExpBufFreeBufAndData(ExpBuf* b);
void ExpBufFreeBufAndDataList(ExpBuf* b);

ExpBuf* ExpBufNext(ExpBuf* b);
ExpBuf* ExpBufPrev(ExpBuf* b);
ExpBuf* ExpBufListLastBuf(ExpBuf* b);
ExpBuf* ExpBufListFirstBuf(ExpBuf* b);

int ExpBufAtEod(ExpBuf* b);
int ExpBufFull(ExpBuf* b);
int ExpBufHasNoData(ExpBuf* b);

char* ExpBufDataPtr(ExpBuf* b);
unsigned long ExpBufDataSize(ExpBuf* b);
unsigned long ExpBufDataBlkSize(ExpBuf* b);
\end{verbatim}

\subsection{SBuf Buffers}

The \verb$SBufs$ are simple buffers of a fixed size, much like an
\verb$ExpBuf$ that cannot expand.  If you attempt to write
past the end of the buffer, the writeError flag will be set and the
encoding will fail.  If you attempt to read past the end of a buffer
the readError flag will be set and the decoding will fail. 

The \verb$SBufs$ are useful if you can put a reasonable upper bound the
size of the encodings you will dealing with.  The buffer operations
are much simpler because the data is contiguous.  In fact, all of the
\verb$SBuf$ buffer operations are implemented by macros.

Look in \verb$snacc/c_examples/simple/sbuf_ex.c$ for a quick
introduction to using \verb$SBufs$ in your code. The following
operations are defined for the \verb$SBuf$ buffers. 
\begin{verbatim}
/* The nine required buffer operations */
#define SBufSkip(b, skipLen) ...
#define SBufCopy(dst, b, copyLen) ...
#define SBufPeekByte(b) ...
#define SBufGetSeg( b, lenPtr) ...
#define SBufPutSegRvs(b, seg, segLen) ...
#define SBufGetByte(b) ...
#define SBufPutByteRvs(b, byte) ...
#define SBufReadError(b) ...
#define SBufWriteError(b) ...

/* other useful buffer operations */
#define SBufInit(b, data, dataLen) ...
#define SBufResetInReadMode(b) ...
#define SBufResetInWriteRvsMode(b) ...
#define SBufInstallData(b, data, dataLen) ...
#define SBufDataLen(b) ...
#define SBufDataPtr(b) ...
#define SBufBlkLen(b) ...
#define SBufBlkPtr(b) ...
#define SBufEod(b) ...
\end{verbatim}

Snacc is configured to use \verb$SBufs$ by default.  The symbols that
will affect the buffer configuration during compilation of the
libraries and generated code are \verb$USE_EXP_BUF$ and
\verb$USE_MIN_BUF$.

\subsection{MinBuf Buffers}

The \verb$MinBuf$s provide maximum performance but should only be used under
restricted conditions (to avoid segmentation faults etc.).  No checks are
made to determine whether a decoder is reading past the end of the
buffer or if an encoder is writing ``past'' the beginning of the data
block (remember, snacc encoders write backwards).  

A \verb$MinBuf$ is just a \verb$char**$; the referenced \verb$char*$ points
to the next byte to be read or the last byte that was written. The
read routine advance the \verb$char*$ and the write reverse routines
move the \verb$char*$ backwards.

When you start encoding, the \verb$MinBuf$ \verb$char**$ should be a
pointer to a pointer to the byte AFTER the last valid byte in your
buffer. For example the following C fragment would work:
\begin{verbatim}
PersonnelRecord pr;
char blk[128];
char* minBuf;

minBuf = blk + 128; /* start writing a end of block */
BEncPersonnelRecord(&minBuf, pr);
\end{verbatim}

The \verb$MinBufs$ should only be used during encoding if the size of
the \verb$MinBuf$'s buffer is guaranteed to be large enough to hold
the encoded value. Otherwise, the encoder will blindly continue
writing into whatever lies after the \verb$MinBuf$'s buffer.

When you start decoding, the \verb$MinBuf$ value should be a pointer to a
pointer the first byte of the BER value to be decoded.  Look in
\verb$snacc/c_examples/simple/minbuf_ex.c$ for a real example.

The \verb$MinBufs$ should only be used for decoding when the value
being decoded is certain to contain no encoding errors. Otherwise, for
encodings that are incomplete or contain length errors the decoder may
attempt to read the memory that follows the \verb$MinBufs$. If you are
lucky, the decoder will return an error with the \verb$longjmp$
mechanism. If your system has memory protection and you are unlucky
this may abort your program.  If you are really unlucky, the data
following the \verb$MinBuf$ may fool the decoder into thinking that it
is valid and you receive a wrong PDU with no error indication.  This
risky technique has been used successfully in some systems where the
encodings are not guaranteed to be correct.

To configure the generated code to use the \verb$MinBufs$, compile it
with the \verb$USE_MIN_BUF$ symbol defined.

\subsection{Hybrid Buffer Solutions}

The decoding routines only call the buffer reading routines and the
encoding routines only call the buffer writing routines.  You may wish
to choose different a buffer format for the encoding and decoding to
gain performance.  For instance, if you can be sure that the size of
outgoing encodings is less than a certain upper bound, but don't want
to risk segmentation faults when decoding incoming values, you could
use \verb$MinBufs$ for the the buffer writing (encoding) operations
and \verb$SBufs$ or \verb$ExpBufs$ for the buffer reading (decoding)
operations.  

In this case you will need to massage the generated code to achieve
the desired results.

\section{\label{lib-mem-C-sec}Dynamic Memory Management}

Like buffer management, efficient memory management is very important
for efficient decoders.  As a decoder decodes a value, it allocates
memory to hold the internal representation of the value.

The runtime library's and the generated decode routines allocate
memory using the\linebreak \verb$Asn1Alloc$ routine. The runtime library's and the
generated free routines free memory using the \verb$Asn1Free$ routine.
The decoding routines also use \verb$CheckAsn1Alloc$ to make sure that
each allocation succeeded.  These memory routine are defined in the
\verb$asn_config.h$ and have the prototypes:
\begin{verbatim}
void* Asn1Alloc(unsigned long int size);
void  Asn1Free(void* ptr);
int   CheckAsn1Alloc(void* ptr, ENV_TYPE env);
\end{verbatim}

The decoders assume that \verb$Asn1Alloc$ returns a {\em zeroed} block
of memory.  This saves explicit initialization of OPTIONAL elements with
NULL in the generated decoders.  It wouldn't be too hard to modify the
compiler to produce decoders that initialized OPTIONAL elements
explicitly.  

The generated free routines hierarchically free all for a value's
memory using depth first algorithm.  If you use the Nibble Memory
scheme, you will not need the generated free routines.

By default, snacc uses a ``Nibble Memory'' scheme to provide efficient
memory management.  Nibble Memory works by allocating a large block of
memory for allocating from.  When the decoded value has been
processed, you can free the entire value by calling a routine that
simply resets a few pointers.  There is no need to traverse the entire
value freeing a piece at a time.  The following is from
\verb$nibble_alloc.h$. 
\begin{verbatim}
void InitNibbleMem(unsigned long int initialSize, 
                   unsigned long int incrementSize);
void* NibbleAlloc(unsigned long int size);
void ResetNibbleMem();
void ShutdownNibbleMem();
\end{verbatim}

You must explicitly initialize the Nibble Memory with the
\verb$InitNibbleMem$ routine before using a decoder.  You must specify
the initial size of the nibble block and the size that it should grow
by.  If you attempt to allocate a block that is larger that the
initial nibble block or its grow size, a new block of the correct size
will be allocated.  Note that the ``growth'' occurs by linking
separate blocks, not by the potentially slow alternative,
\verb$realloc$. 

When you have processed the decoded value you can free it by calling
\verb$ResetNibbleMem$.  This resets a couple pointers and frees any
extra blocks that were allocated to handle values larger than the
initial block size.  The original memory block is zeroed
using \verb$memset$ so that all allocations will return zeroed values.
This is necessary to support the implicit initialization of OPTIONAL
elements to NULL.  The zeroing is done in this routine instead of
\verb$NibbleAlloc$ under the assumption that zeroing one large block 
is more efficient than zeroing pieces of it as they are allocated. 

When you no longer need the Nibble Memory, you can release it by
using\linebreak \verb$ShutDownNibbleMem$.  This frees all of the
memory associated with Nibble Memory, both the control data and the
block(s) used for allocation.

There are some problems with this memory management scheme.  Currently
the Nibble Memory control information is kept track of via a global
variable that holds a pointer to the control information.  This can
present a problem is separate Nibble Memory contexts are needed, for
example, one to hold one value that will be kept after decoding and
another to hold a decoded value that will soon be discarded.  

The problem of separate contexts could be solved by adding another
layer that would use identifiers for different memory contexts.  This
would require you to set the context using its identifier before
calling a decoding routine and to pass the context identifier to the
\verb$ResetNibbleMem$ routine.

Another problem has to do with building the values to be encoded.
There is no restriction on what allocator you use to build internal
values.  However, it is convenient to use the \verb$AsnListNew$
routine to allocate and initialize a list type.  Unfortunately,
\verb$AsnListNew$ is used by the decoding routines so it uses the
\verb$Asn1Alloc$ routine to allocate the new list.  You should be
aware of this if \verb$Asn1Alloc$ is not what you are using to
allocate the rest of the value. This could be fixed with a different
interface to the \verb$AsnListNew$ routine.

It is possible to change the memory management system without to much
difficulty.  For example if you are not too worried about performance
and want to use \verb$malloc$ and \verb$free$, you could change the
\verb$asn_config.h$ file as follows:
\begin{verbatim}
#include "malloc.h"
#define Asn1Alloc(size)  calloc(1, size)
#define Asn1Free(ptr)    free(ptr)
#define CheckAsn1Alloc(ptr, env)\
   if ((ptr) == NULL)\
      longjmp(env, -27);
\end{verbatim}
If you use \verb$malloc$ based allocators such as \verb$calloc$, you
must use the generated free routines to free your values.  Note that
this example used \verb$calloc$ instead of \verb$malloc$ because
\verb$calloc$ {\em zeroes} each allocated block of memory, as required
by the decoders.   


\section{\label{lib-err-C-sec}Error Management}

The decoding routines use \verb$longjmp$ to handle any errors they
encounter in the value being decoded.  \verb$longjmp$ works by rolling
back the stack to where the \verb$setjmp$ call was made.  Every decode
routine takes a \verb$jmp_buf env$ parameter (initialized by the
\verb$setjmp$ call) that tells the \verb$longjmp$ routine how to
restore the processor to the correct state.  \verb$longjmp$ makes the
error management much simpler since the decoding routines do not have
to pass back error codes or check ones from other decoding routines.

Before a PDU can be decoded, the \verb$jmp_buf env$ parameter to the
decoding routine must be initialized using the \verb$setjmp$ routine.
This should be done immediately and only once before calling the
decoding routine.  This parameter will be passed down to any other
decoding routines called within a decoding routine. The following code
fragment from \verb$snacc/c_examples/simple/exbuf_ex.c$ shows how to
use \verb$setjmp$ before decoding.

\begin{small}
\begin{verbatim}
if ((val = setjmp(env)) == 0)
   BDecPersonnelRecord( &buf, &pr, &decodedLen, env);
else
{
    decodeErr = TRUE;
    fprintf(stderr,"ERROR - Decode routines returned %d\n",val);
}
\end{verbatim}
\end{small}

The code that will signal an error typically looks like:
\begin{small}
\begin{verbatim}
if (mandatoryElmtCount1 != 2)
{
    Asn1Error("BDecChildInformationContent: ERROR - non-optional elmt missing from SET.\n");
    longjmp(env, -108);
}
\end{verbatim}
\end{small}


Most \verb$longjmp$ calls are preceded by a call to \verb$Asn1Error$
which takes a single \verb$char*$ string as a parameter.  The library
routines and the generated code try to use meaningful messages as the
parameter. \verb$Asn1Error$ is defined in \verb$asn_config.h$ and
currently just prints the given string to \verb$stderr$.  You may wish
to make it do nothing, which may shrink the size of your binary
because all of the error strings will be gone.  \verb$Asn1Warning$ is
similar but is not used by the library or generated code anymore. 

The encoding routines do no error checking except for buffer
overflows.  Hence, they do not use the \verb$longjmp$ mechanism and
instead require you to check the status of the buffer after encoding
(use \verb$BufWriteError()$).  If you are not building your values
properly, for example having random pointers for uninitialized
OPTIONAL elements, the encode routines will fail, possibly
catastrophically.


\chapter{\label{cpp-code-gen-chp}C++ Code Generation}

\section{\label{intro-Cpp-sec}Introduction}

The C++ backend of snacc was designed after the C backend had been
written.  The basic model that the generated C++ uses is similar to
that of the generated C, but benefits from the object oriented features of
C++.  This was my first real foray into C++ which may be evident from
some of the design.

As with C, two files are generated for each ASN.1 module, a \verb$.C$
and a \verb$.h$ file. 

Some cleaner designs were rejected either due to their poor
performance or the inability of the available C++ compiler to handle
those features. 

Tags and lengths would be fit nicely into their own classes but
performance was considerably worse than the technique used in the C
environment.  The C design was retained in the C++ model for its
superior performance.

For error management C++'s \verb$try$ and \verb$catch$ are obvious
replacements for the \verb$setjmp$ and \verb$longjmp$ used by the C
decoders. Unfortuantely this is a newer C++ feature and is not yet
supported by gcc.

C++ templates are very attractive for type safe lists (for SET OF and
SEQUENCE OF) without duplicating code.  Template support was shaky in
gcc at the time the generated code was being tested so they were
rejected. Instead, each list generates its own new class with all of
the standard list routines.

As with the C code generation chapter, we will use the EX1 module to
help illustrate some of the code generation.  The following is the
same EX1 module used in the C section.
\begin{small}
\begin{verbatim}
EX1 DEFINITIONS ::= 
BEGIN

anOidVal      OBJECT IDENTIFIER ::= { joint-iso-ccitt 40 foobar(29) } 
theSameOidVal OBJECT IDENTIFIER ::= { 2 40 29 } 
anIntVal      INTEGER ::= 1
aBoolVal      BOOLEAN ::= TRUE

T1 ::= SEQUENCE
{
   INTEGER OPTIONAL,
   OCTET STRING OPTIONAL,
   ENUMERATED { a(0), b(1), c(2) },
   SEQUENCE OF INTEGER,
   SEQUENCE { id OBJECT IDENTIFIER, value OCTET STRING },
   CHOICE { INTEGER, OBJECT IDENTIFIER }
}

END
\end{verbatim}
\end{small}

The C++ backend to snacc is in the \verb$snacc/src/back_ends/c++_gen$
directory if you want to alter it.  Warning! the backend code is
pretty ugly.

\section{\label{naming-Cpp-sec}ASN.1 to C++ Naming Conventions}

The C++ name for a type or value is the same as its ASN.1 name with
any hyphens converted to underscores. 

When an ASN.1 type or value name (after converting any hyphens to
underscores) conflicts with a C++ keyword or the name of a type in
another ASN.1 module (name clashes within the same ASN.1 scope are
considered errors and are detected earlier), the resulting C++ class
name will be the conflicting name with digits appended to it.

Empty field names in SETs, SEQUENCEs, and CHOICEs will be filled.  The
field name is derived from the type name for that field. The library
types such as INTEGER etc. have default field names defined by the
compiler (see \verb$snacc/src/back_ends/c_gen/rules.c$ and \verb$
snacc/src/back_ends/cpp_rules.c$).  The first letter of the field name
is in lower case.  Empty field names should be fixed properly
by adding them to the ASN.1 source.

New type definitions will be generated for SETs, SEQUENCEs, CHOICEs,
ENUMERATED, INTEGERs with named numbers and BIT STRING with named bits
whose definitions are embedded in other SET, SEQUENCE, SET OF,
SEQUENCE OF, or CHOICE definitions.  The name of the new type
is derived from the name of the type in which it was embedded and will
be made unique by appending digits if necessary.

\section{\label{type-gen-Cpp-sec}ASN.1 to C++ Class Translation}

This section describes how C++ classes are used to represent
each ASN.1 type.  First the general characteristics of each ASN.1 type's
C++ class will be discussed followed by how the aggregate types (SETs,
SEQUENCEs, CHOICEs, SET OFs, and SEQUENCE OFs) are represented.  The
representations of non-aggregate types (INTEGER, BOOLEAN, OCTET STRING,
BIT STRING, OBJECT IDENTIFIER) and ANY and ANY DEFINED BY types are
presented in the next chapter since they form part of the C++ ASN.1
runtime library.

Every ASN.1 type is represented by a C++ class with the following
characteristics:
\begin{itemize}
\item[1]{inherits from the \verb$AsnType$ base class}
\item[2]{has a parameterless constructor}
\item[3]{has a clone method, \verb$Clone$}
\item[4]{has a PDU encode and decode method, \verb$BEnc$ and \verb$BDec$}
\item[5]{has a content encode and decode method, \verb$BEncContent$
and \verb$BDecContent$}
\item[6]{has top level interfaces to the PDU encode and decode
methods (handles the \verb$setjmp$ etc.) for the user, \verb$BEncPdu$
and \verb$BDecPdu$}
\item[7]{has print method, \verb$Print$}
\end{itemize}

The following C++ fragment shows the class features listed above in greater
detail.
\begin{small}
\begin{verbatim}
class Foo : public AsnType
{
   ... // data members
   
  public:
    Foo();  // requires parameterless constructor

    // PDU (tags/lengths/content) encode and decode routines    
    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    // content encode and decode routines
    AsnLen BEncContent(BUF_TYPE b);
    void   BDecContent(BUF_TYPE b, AsnTag tag, AsnLen elmtLen, 
                       AsnLen& bytesDecoded, ENV_TYPE env);

    // methods most likely to be used by your code.
    // Returns non-zero for success
    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone();
}
\end{verbatim}
\end{small}

\verb$BEnc$ and \verb$BDec$ are PDU encode and decode methods.  
\verb$BEnc$ encodes the tag and length pairs for the object's type as
well as the  content (the object's value) to the given buffer,
\verb$b$, and returns the number of bytes written to the buffer for
the encoding.

\verb$BDec$ decodes the expected tag and length pairs as well as the
content of the object it is invoked upon from the given buffer,
\verb$b$, and increments \verb$bytesDecoded$  by the byte length of
the tag(s), length(s) and value decoded.  The \verb$env$ parameter
will be used with \verb$longjmp$ if any decoding error occurs.
Decoding errors can be reported via \verb$longjmp$ from any of the
routines that \verb$BDec$ calls, such as \verb$BDecContent$;
\verb$BDec$ will call \verb$longjmp$ directly if the value does not
have the correct tag(s).

\verb$BEncContent$ and \verb$BDecContent$ only deal with the content
of the type their object represents.  \verb$BEncContent$ encodes the
object's value to the given buffer, \verb$b$.  

\verb$BDecContent$ decodes the object's value from the given buffer,
\verb$b$.  The last tag and length pair on the content must be passed in
via the the \verb$tag$ and \verb$elmtLen$ parameters.  The \verb$tag$,
although always present, will only be used when decoding OCTET STRING and
BIT STRING related types, to determine whether the encoding is
constructed.  The \verb$elmtLen$ is the length of the content and may
be the indefinite length form.  \verb$bytesDecoded$ is incremented by
the actual number of bytes in the content; this is normally the same
as \verb$elmtLen$ unless the indefinite length form was decoded.  The
\verb$env$ parameter will be used with \verb$longjmp$ if any decoding
error occurs.  The possible decoding errors depend on the type that is
being decoded.

\verb$BEncPdu$ and \verb$BDecPdu$ are top level interfaces to the PDU
encode and decode routines.  They present the simplest interface;
they return TRUE if the operation succeeded and FALSE if an error
occurred.   Note that the \verb$BDecPdu$ routine sets up the
\verb$env$ parameter using \verb$setjmp$ for any \verb$longjmp$ calls
that may occur.  If you call \verb$BDec$ or \verb$BDecContent$
directly from your code, you must use \verb$setjmp$ to setup the
\verb$env$ parameter.  \verb$BEncPdu$ checks for any buffer writing
errors and \verb$BDecPdu$ checks for any buffer reading errors.

The \verb$Print$ method prints the object's value in ASN.1 value
notation.  When printing SETs and SEQUENCEs, an global variable 
is used for the current indent.

The \verb$AsnType$ base class, parameterless constructor and
\verb$Clone$ method are required by the ANY and ANY DEFINED BY type
handling mechanism explained in Sections \ref{asntype-Cpp-sec} and
\ref{any-Cpp-sec}.  In brief, the \verb$AsnType$ provides a base type
that has virtual \verb$BEnc$, \verb$BDec$ and \verb$Clone$ routines.
The \verb$Clone$ routine is used to generate a new instance (not a
copy) of the object that it is invoked on.  This allows the ANY
DEFINED BY type decoder to create an new object of the correct type
from one stored in a hash table, when decoding (the \verb$Clone$
routine calls the parameterless constructor).  The virtual \verb$BEnc$
and \verb$BDec$ are called from \verb$AsnAny$ \verb$BEnc$ and
\verb$BDec$ methods.


\subsection{\label{Cpp-set-seq-sec}SET and SEQUENCE}

SET and SEQUENCE types generate classes that have their components as
public data members.  This makes accessing the components similar to
referencing the fields of a C struct.  For example the \verb$T1$ type in
module EX1 will produce the following C++ class:

\begin{small}
\begin{verbatim}
class T1 : public AsnType
{
  public:
    AsnInt* integer;
    AsnOcts* octs;
    T1Enum t1Enum;
    T1SeqOf t1SeqOf;
    T1Seq* t1Seq;
    T1Choice* t1Choice;

    T1()
    {
        /* init optional/default elements to NULL */
        integer = NULL;
        octs = NULL;
    }

    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent(BUF_TYPE b);
    void   BDecContent(BUF_TYPE b, AsnTag tag, AsnLen elmtLen,
                       AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone() { return new T1; }
};
\end{verbatim}
\end{small}

All OPTIONAL components in a SET or SEQUENCE are referenced by pointer.
The constructor will automatically set OPTIONAL fields to NULL.  The
other methods are as described at the beginning of this section. 

SETs and SEQUENCEs must contain all non-OPTIONAL components and
SEQUENCEs must be ordered, otherwise an error is reported.  Tagging
errors are also reported.  All detected errors abort the decoding
process via \verb$longjmp$.

\subsection{\label{Cpp-choice-sec}CHOICE}

Each CHOICE type generates a class that has an anonymous union to hold the
components of the CHOICE and a \verb$choiceId$ field to indicate which
component is present.  

Anonymous (un-named) unions allow you to reference the choice components
with just the field name of the component; this makes referencing the
contents of a CHOICE the same a referencing the contents of a SET or
SEQUENCE. 

The \verb$choiceId$ field contains a value in the \verb$ChoiceIdEnum$
that indicates the CHOICE field that is present.  The names in the
enumeration are derived from the field names of the CHOICE components.

When building a local value to be encoded, you must be sure to set the
\verb$choiceId$ such that it corresponds the value in the union.  The
decoder will set the \verb$choiceId$ when decoding incoming values.

Tagging errors are reported and abort the decoding process via
\verb$longjmp$.

The following C++ class is produced for the CHOICE in the EX1 module.

\begin{small}
\begin{verbatim}
class T1Choice : public AsnType
{
  public:
    enum ChoiceIdEnum
    {
        integerCid = 0,
        oidCid = 1
    };
    enum ChoiceIdEnum choiceId;
    union ChoiceUnion
    {
        AsnInt* integer;
        AsnOid* oid;
    };

    T1Choice() {}

    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent(BUF_TYPE b);
    void   BDecContent(BUF_TYPE b, AsnTag tag, AsnLen elmtLen,
                       AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone() { return new T1Choice; }
};
\end{verbatim}
\end{small}



\subsection{\label{Cpp-set-of-sec}SET OF and SEQUENCE OF}

Each SET OF and SEQUENCE OF type produces its own list class, unlike
the C backend which uses a single generic list type for all lists.
This makes the C++ list routines type safe which allows the C++
compiler to detect more programmer errors.  

C++ templates should be used to reduce the code duplication when they
become widespread and reliably implemented.  The duplicated list
handling methods may bloat the size of the generated code.

Any tagging errors are reported and abort the decoding process via
\verb$longjmp$.

From the EX1 ASN.1 module the following list is produced:
\begin{small}
\begin{verbatim}
class T1SeqOf : public AsnType
{
  protected:
    unsigned long int count;
    struct AsnListElmt
    {
        struct AsnListElmt* next;
        struct AsnListElmt* prev;
        AsnInt* elmt;
    } *first, *curr, *last;

  public:
    T1SeqOf();
    void SetCurrElmt(unsigned long int index);
    unsigned long int GetCurrElmtIndex();
    void SetCurrToFirst();
    void SetCurrToLast();

    // reading member fcns
    int Count();
    AsnInt* First();
    AsnInt* Last();
    AsnInt* Curr();
    AsnInt* Next();
    AsnInt* Prev();

    // routines that move the curr elmt
    AsnInt* GoNext();
    AsnInt* GoPrev();

    // write & alloc fcns - returns new elmt
    AsnInt* Append();  // add elmt to end of list
    AsnInt* Prepend(); // add elmt to beginning of list
    AsnInt* InsertBefore(); // insert elmt before current elmt
    AsnInt* InsertAfter();  // insert elmt after current elmt

    // write & alloc & copy - returns list after copying elmt
    T1SeqOf& AppendCopy(AsnInt& elmt);  // add elmt to end of list
    T1SeqOf& PrependCopy(AsnInt& elmt); // add elmt to beginning of list
    T1SeqOf& InsertBeforeAndCopy(AsnInt& elmt); // insert elmt before current elmt
    T1SeqOf& InsertAfterAndCopy(AsnInt& elmt); // insert elmt after current elmt

    // encode and decode routines    
    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent(BUF_TYPE b);
    void   BDecContent(BUF_TYPE b, AsnTag tag, AsnLen elmtLen, 
                       AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone();
};
\end{verbatim}
\end{small}

Each list is doubly linked to allow simple reverse traversal for
backwards encoding.  The \verb$prev$ pointer will be NULL for the
first element of the list and the \verb$next$ pointer will be NULL for
the last element of the list.

Each list maintains a pointer the current element of the list.
Several routines are provide to manipulate the current item.  Since
there is only one current pointer, you may have to save and restore
the current pointer with the \verb$GetCurrElmtIndex$ and
\verb$SetCurrElmt$ methods if you call routines that deal with the
list while iterating through it.

In addition to the standard encode, decode and print methods some list
utility routines are included in each list class.  They are fairly
simple and are described briefly here.

\begin{verbatim}
void SetCurrElmt(unsigned long int index);
\end{verbatim}
This sets the current pointer the element with the given index.
Indexes start at zero, that is, the first element in the list has an
index of zero.  If the given index is greater than or equal the number
of elements in the list, the current pointer is set to the last
element of the list.

\begin{verbatim}
unsigned long int GetCurrElmtIndex();
\end{verbatim}
This returns the index of the current element.  If the current pointer
is NULL (or does not reference an element of the list, which is an
error condition), the index returned will be greater than or equal the
the number of elements in the list (indexes start at zero so this is
an invalid index).   


\begin{verbatim}
void SetCurrToFirst();
\end{verbatim}
This sets the current pointer to the first element of the list. If the
list is empty, it is set to NULL.
\begin{verbatim}
void SetCurrToLast();
\end{verbatim}
This sets the current pointer to the last element of the list. If the
list is empty, it is set to NULL.


\begin{verbatim}
int Count();
\end{verbatim}
This returns number of elements in the list.

\begin{verbatim}
AsnInt* First();
AsnInt* Last();
AsnInt* Curr();
AsnInt* Next();
AsnInt* Prev();
\end{verbatim}
The above routines return a pointer to the list element that the
routine name indicates.  They return NULL if the requested element is
not present.  For example \verb$First$ will return a pointer to the
first element in the list or NULL if the list is empty.  These
routines do not affect the state of the list; the current pointer and
the count remain the same.

\begin{verbatim}
AsnInt* GoNext();
AsnInt* GoPrev();
\end{verbatim}
These routines change the current pointer to the next/previous element
and return a pointer to that element.  If the current element is NULL or
points to the last element, \verb$GetNext$ returns NULL. Similarly, if
the current element is NULL or points to the first element, \verb$GetPrev$
returns NULL.


\begin{verbatim}
AsnInt* Append();
\end{verbatim}
This allocates a new list element, appends it to the end of list and
returns a pointer to the new list element.  Notice that you must set
the value of the returned list element.

\begin{verbatim}
AsnInt* Prepend(); 
\end{verbatim}
This allocates a new list element, prepends it to the beginning of
list and returns a pointer to new the list element. You must set the
value of the returned list element. 

\begin{verbatim}
AsnInt* InsertBefore();
\end{verbatim}
This allocates a new list element, inserts it before the current list
element and returns a pointer to new the list element.  You must set
the value of the returned list element.  If the current pointer is
NULL, the new element is placed at the beginning of the list.

\begin{verbatim}
AsnInt* InsertAfter();
\end{verbatim}
This allocates a new list element, inserts it after the current list
element and returns a pointer to new the list element.  You must set
the value of the returned list element.  If the current pointer is
NULL, the new element is placed at the end of the list.

\begin{verbatim}
T1SeqOf& AppendCopy(AsnInt& elmt);
T1SeqOf& PrependCopy(AsnInt& elmt);
T1SeqOf& InsertBeforeAndCopy(AsnInt& elmt);
T1SeqOf& InsertAfterAndCopy(AsnInt& elmt);
\end{verbatim}
These are similar to the \verb$Append$, \verb$Prepend$,
\verb$InsertBefore$ and \verb$InsertAfter$ routines except that a
copy of the given element's value is placed in the list and the list
itself is returned.

\subsection{\label{Cpp-enumerated-sec}ENUMERATED, Named Numbers and Named Bits}

The C++ type generator encapsulates each ENUMERATED type, INTEGER
with named numbers and BIT STRING with named bits in a new class that
inherits from the proper base class and defines the named elements.
This provides a separate scope for these identifiers so their symbol
will be exactly the same as their ASN.1 counterpart.  Currently these
identifiers are not checked for conflicts with C++ keywords, so you
may have to modify some of them in the ASN.1 modules.

Inheritance is used for attaching ENUMERATED, named number and named
bit information.  ENUMERATED types inherit from the \verb$AsnEnum$ class,
INTEGERs with named number types inherit from the \verb$AsnInt$ class and BIT
STRINGs with named bits inherit from the \verb$AsnBits$ class.

If the tagging on the type is different from the type it inherits
from, the PDU encode and decode methods are re-defined with the
correct tags to override the PDU encode and decode methods of the base
class.

As with the other types, any tagging errors are reported and abort the
decoding process via \verb$longjmp$.  No range checking is done on the
decoded values although it would be easy to provide an new
\verb$BDecContent$ method in the new class that calls the base class's
and then checks the range of the result.

\begin{small}
\begin{verbatim}
/* ENUMERATED { a(0), b(1), c(2) }  */
class T1Enum : public AsnEnum
{
  public:
    T1Enum(): AsnEnum() {}
    T1Enum(int i): AsnEnum(i) {}
    enum
    {
      a = 0,
      b = 1,
      c = 2
    };
};
\end{verbatim}
\end{small}

\section{\label{val-gen-Cpp-sec}ASN.1 to C++ Value Translation}

C++ \verb$const$ values are used to hold ASN.1 defined values.  C++
values will be produced for INTEGER, BOOLEAN and OBJECT IDENTIFIER
ASN.1 values.  An \verb$extern$ declaration for each \verb$const$ value is
written at the end of the header file of the value's module.  The
\verb$const$ values are defined at the beginning of the \verb$.C$ file
of the value's module.  The \verb$extern$ declarations are at the end
of the header file so that any required class definitions are
available.

The following is from the end of the header file generated for the EX1
module: 
\begin{small}
\begin{verbatim}
extern const AsnOid anOidVal;
extern const AsnOid theSameOidVal;
extern const AsnInt anIntVal;
extern const AsnBool aBoolVal;
extern const AsnInt foobar;
\end{verbatim}
\end{small}

The following is from the beginning of the \verb$.C$ file generated
for the EX1 module: 
\begin{small}
\begin{verbatim}
const AsnOid anOidVal(2, 40, 29);
const AsnOid theSameOidVal(2, 40, 29);
const AsnInt anIntVal(1);
const AsnBool aBoolVal(AsnBool::true);
const AsnInt foobar(29);
\end{verbatim}
\end{small}

The C++ constructor mechanism is used to generate these values.  This
mechanism is superior to C static initialization because it allows C++
code to be run to initialize the values. 

\section{\label{compiler-dir-Cpp-sec}Compiler Directives}
Compiler directives are ignored by the C++ backend of snacc.  If you want
to implement them, look at the \verb$FillCppTypeDefInfo$ routine in
file \verb$snacc/src/back_ends/c++_gen/cpp_types.c$.  Then look at the
way it is done for the C backend (file
\verb$snacc/src/back_ends/c_gen/type_info.c$) 

\section{\label{compiling-gen-Cpp-sec}Compiling the Generated C++ Code}

When compiling the generated C++ code you will need:
\begin{itemize}
\item[1] {\verb$snacc/c++_include$ directory in your include path so
the C++ sources can include the library header files. (\verb$-I
snacc/c++_include$)}  
\item[2] {to link with the C++ ASN.1 runtime library,
\verb$snacc/c++_lib/libasn1c++.a$. } 
\item[3] {to link with the math library, since the ASN.1 REAL type's
encode and decode routine use some math routines. (\verb$-lm$)}
\end{itemize}

See the example in \verb$snacc/c++_examples/simple$ for a complete
example. The makefile and main routines are probably the most
important.  There are several other examples in the
\verb$snacc/c++_examples$ directory.  



\chapter{\label{lib-Cpp-chp}C++ ASN.1 Library}
\section{\label{libover-Cpp-sec}Overview}

The following sections describe the C++ representation of the
non-aggregate ASN.1 types, ANY and ANY DEFINED BY types and
the buffer and memory management.  These classes and routines make
up the C++ ASN.1 runtime library. Every aggregate ASN.1 type will be
composed of these library types. The source files for this library
are in \verb$snacc/c++_include$ and \verb$snacc/c++_lib$. 

As mentioned in the last chapter, each ASN.1 type is represented by a
C++ class which inherits from the \verb$AsnType$ base class.  In
addition to the standard encode, decode, print and clone methods
described in the last chapter, each ASN.1 type class in the library
may also have special constructors and other routines that simplify
their use.

Unlike the classes generated for some of the aggregate types such as
SETs and SEQUENCEs, the library types data members are typically
protected and accessed via methods. 

All of the library classes' \verb$BDec$ routines will report tagging errors
via \verb$longjmp$ as described in Section \ref{type-gen-Cpp-sec}.

The top level PDU encode and decode methods are the same for all
library types so they are defined as macros in
\verb$snacc/c++_inlcude/asn_config.h$. For clarity's sake, the macro
that is used to define these methods in the library type class
definitions will be replaced with the actual prototypes.

Run the \verb$test_lib$ program in \verb$snacc/c++_examples/test_lib$
to make sure the library routines are working properly for your
architecture.  The testing is not exhaustive but should point out
obvious problems.


\section{\label{tag-Cpp-sec}Tags}

The C++ tags are identical those used in snacc's C ASN.1 environment.
While it would have been nice define a tag class, the performance cost
would likely have been noticeable.  Also, the snacc users usually do
not have to muck around with tags directly, so the lack of a
class interface will probably not be missed.  The C ASN.1 tags are
described in Section \ref{tag-C-sec}.

Initially I defined a C++ class for tags, but close examination of the
produced assembly code led me to reject it.  The C++ class for tags
used the C tag representation internally and had constructor, encode
and decode methods.  The constructor could not be used as
\verb$switch$ statement case labels like \verb$MAKE_TAG_ID$ because it
did not reduce to an integer constant; this caused problems in the
generated decoders.

As with the C representation of tags, 4 byte long integers limit the
maximum representable tag code to $2^{21}$.  Again, this should not be
a problem. 


\section{\label{len-Cpp-sec}Lengths}

The C++ representation of lengths is the same at the C representation
described in Section \ref{len-C-sec}.  The length type was not given
its own C++ class for reasons similar to those of tags.


\section{\label{asntype-Cpp-sec}The AsnType Base Class}

Every ASN.1 type's C++ class uses the \verb$AsnType$ as its base class.
The \verb$AsnType$ base class is provides the following virtual
functions:
\begin{itemize}
\item{a destructor}
\item{\verb$BDec$}
\item{\verb$BEnc$}
\item{\verb$Print$}
\item{\verb$Clone$}
\end{itemize}

The \verb$AsnType$ class is defined as follows:
\begin{small}
\begin{verbatim}
class AsnType
{
  public:

    virtual ~AsnType() {}

#ifdef SUPPORT_ANY_TYPE
    virtual void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);
    virtual AsnLen BEnc(BUF_TYPE b);
    virtual AsnType* Clone();
#else
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env) {}
    AsnLen BEnc(BUF_TYPE b) { return 0;}
#endif
    virtual void Print(ostream& os);
};
\end{verbatim}
\end{small}

The \verb$AsnType$ class and its virtual functions were added to
support the ANY DEFINED BY type handling mechanism.  This mechanism is
described in Section \ref{any-Cpp-sec}.

Even if you do not use the ANY or ANY DEFINED BY types, the
\verb$AsnType$ base class may be useful for adding features that are
common to all of the types, such as changing the \verb$new$ and
\verb$delete$ functions to improve performance.

Virtual functions provide the simplest method of handling ANY DEFINED
BY and ANY types. Unfortuanately, calls to virtual functions are
slower than calls to normal functions due their indirect nature.  If
you do not need support for the ANY DEFINED BY or ANY types you can
remove most of the virtual functions to improve performance by
undefining the \verb$SUPPORT_ANY_TYPE$ symbol (see the
\verb$asn_type.h$ file).  

Note that a virtual destructor is included in the \verb$AsnType$ base
class as well.  This is done to make sure the \verb$delete$ routine
always gets the correct size.  See pp 215-217 of Stroustrup
\cite{stroustrup} for a discussion of this.

\section{\label{bool-Cpp-sec}BOOLEAN}

The BOOLEAN type is represented by the \verb$AsnBool$ class. The
following is the class definition of \verb$AsnBool$ from the
\verb$snacc/c++_include/asn_bool.h$ file.

\begin{small}
\begin{verbatim}
class AsnBool: public AsnType
{
  protected:
    unsigned char value;

  public:
    enum { false = 0, true = 1};
    operator int() { return value; }
    AsnBool(const int val): value(val != false) {};
    AsnBool() {};

    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent(BUF_TYPE b);
    void BDecContent(BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                          AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone() { return new AsnBool; }
};
\end{verbatim}
\end{small}


The \verb$AsnBool$ class defines the enumerated values of \verb$true$
and \verb$false$ if you want to check to a boolean's value explicitly.
Their enum values are defined inside the \verb$AsnBool$ class to avoid
potential conflicts with other symbols. However, they must by
referenced by \verb$AsnBool::true$ and \verb$AsnBool::false$.

The \verb$operator int()$ is defined such that when an \verb$AsnBool$
value is cast to an integer, it returns the C style integer boolean
value of the \verb$AsnBool$s value.  That is, zero for false and
non-zero for true.  There is also a constructor for \verb$AsnBool$
that builds an \verb$AsnBool$ value from the given C style integer
boolean value. These two methods allow you to manipulate and access
\verb$AsnBool$ values in a straight forward way as the following code
illustrates. 
\begin{small}
\begin{verbatim}
Message::Send()
{
    AsnBool okToSend;
    int connectionOpen;
    int  pduOk;
    . . .
    okToSend = connectionOpen && pduOk; // assign AsnBool from integer bool
    if (okToSend) // cast AsnBool to integer boolean
      . . .
}
\end{verbatim}
\end{small}

The \verb$AsnBool$ class contains the standard encode and decode
methods that were described in chapter \ref{cpp-code-gen-chp}.  

\verb$BDecContent$ will report an error via \verb$longjmp$ if the
length of an encoded BOOLEAN value's content is not exactly 1 octet.

Note that the \verb$Clone$ method returns an \verb$AsnType*$ value
instead of an \verb$AsnBool*$.  It might be more obvious to return an
\verb$AsnBool*$ since due to single inheritance an \verb$AsnBool$ is also
an \verb$AsnType$.  However, it must return an \verb$AsnType*$ for it
to override the virtual function \verb$Clone$ defined in the
\verb$AsnType$. 

The \verb$Print$ method will print either \verb$TRUE$ or \verb$FALSE$
depending on the \verb$AsnBool$ value.  No newline or other formatting
characters are printed.  The global indent information does not affect
the output from this method.


\section{\label{int-Cpp-sec}INTEGER}

The INTEGER type is represented by the \verb$AsnInt$ class. The
following is the class definition of \verb$AsnInt$ from the
\verb$snacc/c++_include/asn_int.h$ file.
\begin{small}
\begin{verbatim}
class AsnInt : public AsnType
{
  protected:
    long int value;

  public:
    operator long int() { return value; }
    AsnInt(long int val): value(val) {}
    AsnInt() {}

    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent(BUF_TYPE b);
    void BDecContent(BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                           AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu(BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu(BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os) { os << value; }
    AsnType* Clone() { return new AsnInt; }
};
\end{verbatim}
\end{small}

The internal representation of an ASN.1 INTEGER value is a \verb$long int$.
This \verb$BDecContent$ routine will signal an error if the integer
value being decoded will not fit into the \verb$long int$
representation.

Unlike the C ASN.1 library, the \verb$unsigned long int$ version of
INTEGER is not provided.  If you need it, it should be relatively
trivial to combine the C unsigned version with the existing C++ class.
The unsigned version of an integer is useful is your ASN.1 source uses
subtyping similar to:
\begin{small}
\begin{verbatim}
Counter ::= [APPLICATION 1] IMPLICIT INTEGER (0..4294967295)
\end{verbatim}
\end{small}



\section{\label{null-Cpp-sec}NULL}

The NULL type is provided by the \verb$AsnNull$ class.  This class has
no data members and includes only the standard methods.
\begin{small}
\begin{verbatim}
class AsnNull : public AsnType
{
  public:
    AsnNull() {}
    AsnLen BEnc( BUF_TYPE b);
    void BDec( BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent( BUF_TYPE b);
    void BDecContent( BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                           AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os) { os << "NULL"; }
    AsnType* Clone() { return new AsnNull; }
};
\end{verbatim}
\end{small}

\section{\label{real-Cpp-sec}REAL}

REAL types are represented by the \verb$AsnReal$ class.  Internally, a
\verb$double$ is used to hold the real value.  The following is from
\verb$snacc/c++_include/asn_real.h$:
\begin{small}
\begin{verbatim}
class AsnReal: public AsnType
{
  protected:
    double value;

  public:
    operator double() const  { return value; }
    AsnReal(): value(0.0) {}
    AsnReal(double val): value(val) { }

    void Print(ostream& os) { os << value; }
    AsnType* Clone() { return new AsnReal; }

    AsnLen BEnc( BUF_TYPE b);
    void BDec( BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent( BUF_TYPE b);
    void BDecContent( BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                          AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);
};

extern const AsnReal PLUS_INFINITY;
extern const AsnReal MINUS_INFINITY;
\end{verbatim}
\end{small}

The \verb$double$ representation and support routines can depend on
the compiler or system you are using so several different encoding
routines are provided.  Even so, you may need to modify the code.  

There are three content encoding routines included and they can be
selected by defining one of \verb$IEEE_REAL_FMT$, \verb$IEEE_REAL_LIB$
or nothing.  Defining \verb$IEEE_REAL_FMT$ uses the encode routine
that assumes the double representation is the standard IEEE double
\cite{68881}.  Defining \verb$IEEE_REAL_LIB$ uses the encode routine
that assumes the IEEE functions library (isinf, scalbn, signbit etc.\
) is available.  If neither are defined, the default encode routine
uses \verb$frexp$.  Look at the related comments in
\verb$snacc/c++_include/asn_config.h$.

\verb$AsnReal$ constants are used to hold \verb$PLUS_INFINITY$ and
\verb$MINUS_INFINITY$ values.  These values are initialized using the
\verb$AsnReal$ constructor mechanism with the \verb$AsnPlusInfinity$
and \verb$AsnMinusInfinity$ routines.  If you do not define
\verb$IEEE_REAL_FMT$ or \verb$IEEE_REAL_LIB$, you should rewrite the
\verb$AsnPlusInfinity$ routine such that it is correct for your system.

There is only one content decoding routine and it builds the value
through multiplication and the \verb$pow$ routine (requires the math
library). The content decoding routine only supports the binary encoding a of
REAL, not the decimal encoding.

\section{\label{bits-Cpp-sec}BIT STRING}

The BIT STRING type is represented by the \verb$AsnBits$ class.  From\linebreak
\verb$snacc/c++_include/asn_bits.h$: 
\begin{small}
\begin{verbatim}
class AsnBits: public AsnType
{
  protected:
    char* bits;
    unsigned long int bitLen;

  public:
    AsnBits() { bits = NULL; bitLen = 0; }
    AsnBits(const unsigned long int numBits) { Set(numBits); }
    AsnBits(const char* bitOcts, const unsigned long int numBits) 
    { 
        Set(bitOcts, numBits);
    }
    AsnBits(const AsnBits& b) { Set(b); }
    ~AsnBits() {delete bits;}                                               

    AsnBits& operator=(const AsnBits& b) { ReSet(b); return *this;}

    // overwrite existing bits and bitLen values
    void Set(unsigned long int numBits);
    void Set(const char* bitOcts, unsigned long int numBits);
    void Set(const AsnBits& b);

    // free old bits value, the reset bits and bitLen values
    void ReSet(const unsigned long int numBits);
    void ReSet(const char* bitOcts, unsigned long int numBits);
    void ReSet(const AsnBits& b);

    operator==(AsnBits& ab)  { return(BitsEquiv(ab)); }
    operator!=(AsnBits& ab)  { return(!BitsEquiv(ab)); }

    void SetBit(unsigned long int);
    void ClrBit(unsigned long int);
    int  GetBit(unsigned long int);

    unsigned long int BitLen() {return bitLen;}

    AsnLen BEnc( BUF_TYPE b);
    void BDec( BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent( BUF_TYPE b);
    void BDecContent( BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                          AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    AsnType* Clone() { return new AsnBits; }
    void Print(ostream& os);

  private:
    int BitsEquiv(AsnBits& ab);
    void BDecConsBits (BUF_TYPE b, AsnLen elmtLen,
                                      AsnLen& bytesDecoded, ENV_TYPE env);
    void FillBitStringStk (BUF_TYPE b, AsnLen elmtLen0, 
                           AsnLen& bytesDecoded, ENV_TYPE env);

};
\end{verbatim}
\end{small}

The \verb$AsnBits$ class contains a pointer to the bits and an
integer that holds the length in bits of the BIT STRING.

In addition to the standard methods, the \verb$AsnBits$ class has
methods for initializing and comparing bit string values and methods
for setting and getting individual bits in a value.

An \verb$AsnBits$ value can created three ways: from the number of
bits needs, from a \verb$char*$ and its bit length or from another
\verb$AsnBits$ value.  Look at the constructors and the \verb$Set$ and
\verb$ReSet$ methods.

\verb$SetBit$ and \verb$ClrBit$ can be used for setting the values of
individual bits in the BIT STRING value.  Given the bit's index,
\verb$SetBits$ sets the that bit to one.  \verb$ClrBit$ sets the bit
of the given index to zero.  The bit indexes start at zero, with zero
being the first (most signficant) bit in the BIT STRING. \verb$GetBit$
will return TRUE if the specified bit is one and FALSE if the bit is
zero.  If the given bit index is too large, \verb$SetBit$ and
\verb$ClrBit$ do nothing and \verb$GetBit$ returns FALSE.

The \verb$==$ and \verb$!=$ operators have been overloaded such that
given two \verb$AsnBits$ values, they will behave as expected.  

Each \verb$AsnBits$ value stores its bit string in a single contiguous
block of memory.  Received BIT STRING values that were encoded in the
constructed form are converted to the simple, flat form (see Section
\ref{bits-C-sec}).  Snacc provides no facility for encoding or
internally representing constructed BIT STRING values.

\section{\label{octets-Cpp-sec}OCTET STRING}
OCTET STRING values are represented with the \verb$AsnOcts$ class.
From\linebreak \verb$snacc/c++_include/asn_octs.h$:
\begin{small}
\begin{verbatim}
class AsnOcts : public AsnType
{
  protected:
    char* octs;
    unsigned long int octetLen;

  public:
    // constructor and Set alway copy strings so destructor can always delete
    AsnOcts(): octs(NULL), octetLen(0) { }
    AsnOcts(const char* str)  { Set(str); }
    AsnOcts(const char* str, const unsigned long int len) { Set(str,len); }
    AsnOcts(const AsnOcts& o) { Set(o); }
    ~AsnOcts() { delete octs; }

    AsnOcts& operator=(const AsnOcts& o) { ReSet(o); return *this; }
    AsnOcts& operator=(const char* str) { ReSet(str); return *this; }

    // these set the octs and octetLen values 
    void Set(const char* str, unsigned long int len);
    void Set(const AsnOcts& o);
    void Set(const char* str);

    // these free the old octs value and 
    // then reset the octs and octetLen values 
    void ReSet(const char* str, unsigned long int len);
    void ReSet(const AsnOcts& o);
    void ReSet(const char* str);

    operator char*() { return octs;}
    unsigned long int Len() {return octetLen;}

    operator==(const AsnOcts& o)  { return(OctsEquiv(o)); }
    operator!=(const AsnOcts& o)  { return(!OctsEquiv(o)); }

    AsnLen BEnc( BUF_TYPE b);
    void BDec( BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent( BUF_TYPE b);
    void BDecContent( BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                           AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone() { return new AsnOcts; }

  private:
    int OctsEquiv(const AsnOcts& o);
    void FillBitStringStk (BUF_TYPE b, AsnLen elmtLen0, 
                           AsnLen& bytesDecoded, ENV_TYPE env);
    void BDecConsOcts (BUF_TYPE b, AsnLen elmtLen,
                                     AsnLen& bytesDecoded,
                                     ENV_TYPE env);
};
\end{verbatim}
\end{small}

The \verb$AsnOcts$ class contains a pointer to the octets and an
integer that holds the length in octets of the OCTET STRING.

There are four constructors for \verb$AsnOcts$.  The parameterless
constructor will initialize the octet string to zero length with a 
NULL octets pointer.  The constructor that takes a single \verb$char*$
assumes that the given string is NULL terminated and initializes the octet
pointer with a pointer to a copy of the given string and sets the
\verb$octetLen$ to the \verb$strlen$ of the the string (this does not usually
included the NULL terminator).  The constructor that takes
\verb$char*$ and a length, \verb$len$, initializes the octets pointer
to point to a copy of \verb$len$ characters from the given string and
sets the \verb$octetLen$ to len.  The last constructor will initialize
an \verb$AsnOcts$ value by copying the given \verb$AsnOcts$ value. 

As with the BIT STRING content decoder, OCTET STRING content decoder
can handle constructed values.  These are handled in the same way as
the constructed BIT STRING values; they are converted to the simple
contiguous representation.  Every OCTET STRING value will
automatically have a NULL terminator appended to it; this extra
character will not be included in the string's length and will make
some strings easier to deal with for printing etc.

The \verb$operator char*()$ is defined for the \verb$AsnOcts$ class to
return a pointer to the octets.  This may be useful for passing the
octets to other functions such as \verb$strcpy$ etc.  The \verb$Len$
method returns the length in bytes of the the string value.

The \verb$==$ and \verb$!=$ operators have been overloaded such that
given two \verb$AsnOcts$ values, they will behave as expected.  



\section{\label{oid-Cpp-sec}OBJECT IDENTIFIER}
OBJECT IDENTIFIER values are represented with the \verb$AsnOid$ class.
From\linebreak \verb$snacc/c++_include/asn_oid.h$:
\begin{small}
\begin{verbatim}
class AsnOid: public AsnType
{
  protected:
    char* oid;
    unsigned long int octetLen;

  public:
    AsnOid(): oid(NULL), octetLen(0) {}
    AsnOid(const char* encOid, unsigned long int len) { Set(encOid, len); }
    AsnOid(const AsnOid& o) { Set(o); }
    AsnOid(unsigned long int a1, unsigned long int a2, 
           long int a3 = -1, long int a4 = -1, long int a5 = -1,
           long int a6 = -1, long int a7 = -1, long int a8 = -1,
           long int a9 = -1, long int a10 = -1, long int a11 = -1);
    ~AsnOid() { delete oid; }

    AsnOid& operator=(const AsnOid& o) { ReSet(o); return *this;}

    // Set methods overwrite oid and octetLen values
    void Set(const char* encOid, const unsigned long int len);
    void Set(const AsnOid& o);

    // first two arc numbers are mandatory.  rest are optional
    // since negative arc nums are not allowed in the encodings,
    // use them to indicate the 'end of arc numbers' in the 
    // optional parameters
    void Set(unsigned long int a1, unsigned long int a2, 
        long int a3 = -1, long int a4 = -1, long int a5 = -1,
        long int a6 = -1, long int a7 = -1, long int a8 = -1,
        long int a9 = -1, long int a10 = -1, long int a11 = -1);


    // ReSet routines are like Set except the old oid value is freed
    void ReSet(const char* encOid, const unsigned long int len);
    void ReSet(const AsnOid& o);
    void ReSet(unsigned long int a1, unsigned long int a2, 
        long int a3 = -1, long int a4 = -1, long int a5 = -1,
        long int a6 = -1, long int a7 = -1, long int a8 = -1,
        long int a9 = -1, long int a10 = -1, long int a11 = -1);

    operator char*() { return oid; }
    unsigned long int Len() {return octetLen;}
    unsigned long int AsnOid::NumArcs();

    operator==(AsnOid& o)  { return(OidEquiv(o)); }
    operator!=(AsnOid& o)  { return(!OidEquiv(o)); }

    AsnLen BEnc( BUF_TYPE b);
    void BDec( BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    AsnLen BEncContent( BUF_TYPE b);
    void BDecContent( BUF_TYPE b, AsnTag tagId, AsnLen elmtLen,
                          AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os);
    AsnType* Clone() { return new AsnOid; }

  private:
    int OidEquiv(AsnOid o);
};
\end{verbatim}
\end{small}

The \verb$AsnOid$ stores OBJECT IDENTIFIER values in their encoded form
to improve performance.  It seems that the most common operation with
OBJECT IDENTIFIERs is to compare for equality, for which the encoded
representation (which is canonical) works well. 

The \verb$AsnOid$ is very similar to the \verb$AsnOcts$ class in all
respects, except that its content decoding routine does not need to
handle constructed encodings.

The \verb$AsnOid$ class has four constructors which are similiar to
those of the \verb$AsnOcts$ class.  A special constructor that takes
arc numbers as parameters and uses default parameters is provided.  An
OBJECT IDENTIFIER value must have at least two arc numbers so the
first two parameters do not have default values.  All of the other
parameters are optional; since their default value of \verb$-1$ is an
invalid arc number (they must be positive) they will not be used in the
value. For example to build the value \verb${1 2 3}$ you simply use
\verb$AsnOid(1,2,3)$.  This constructor is convenient but is more
expensive in terms of CPU time that the others.

The \verb$operator char*()$ is defined for the \verb$AsnOid$ class to
return a pointer to the encoded OBJECT IDENTIFIER value.  This may be
useful for passing the octets to other functions such as \verb$strcpy$
etc.  The \verb$Len$ method returns the length in bytes of the the
encode OBJECT IDENTIFIER value (NOT the number arcs in the value).
\verb$NumArcs$ returns the number of arcs that the value is comprised
of.

The \verb$==$ and \verb$!=$ operators have been overloaded such that
given two \verb$AsnOcts$ values, they will behave as expected.



\section{\label{list-Cpp-sec}SET OF and SEQUENCE OF}

In the C ASN.1 library, the list type was in the library because it
was generic and every SET OF and SEQUENCE OF was defined as an
\verb$AsnList$.  In C++, a new class is defined every list, providing
a type safe list mechanism.  This is described in the previous chapter.


\section{\label{any-Cpp-sec}ANY and ANY DEFINED BY}

The ANY DEFINED BY type can be handled automatically by snacc
provided you use the SNMP OBJECT-TYPE macro to specify the identifier
to type mappings.  The identifier can be an INTEGER or OBJECT
IDENTIFIER.  Handling ANY types properly will require modifications to
the generated code since there is no identifier associated with the
type.  

Look at the C and C++ ANY examples and the \verb$any.asn1$ file
included with this release for information on using the OBJECT-TYPE
macro.  Note that the OBJECT-TYPE macro has been modified slightly to
allow INTEGER values (identifiers).

An ANY DEFINED BY type is represented by the \verb$AsnAny$ class.  
The following is from the \verb$snacc/c++_include/asn_any.h$.
\begin{small}
\begin{verbatim}
/* AnyInfo is a hash table entry */
class AnyInfo
{
  public:
    int    anyId;  /* will be a value from the AnyId enum */
    AsnOid oid;    /* will be zero len/null if intId is valid */
    AsnInt intId;
    AsnType* typeToClone;
};

class AsnAny: public AsnType
{
  public:
    static Table* oidHashTbl;  // all AsnAny class instances
    static Table* intHashTbl;  // share these tables
    AnyInfo* ai; // points to entry in hash tbl for this type
    AsnType* value;

    AsnAny() { ai = NULL; value = NULL; }

    // class level methods
    static void InstallAnyByInt (AsnInt intId, int anyId, AsnType* type);
    static void InstallAnyByOid (AsnOid& oid,  int anyId, AsnType* type);
      
    int GetId() {return((ai)? ai->anyId: -1); }
    void SetTypeByInt(AsnInt id);
    void SetTypeByOid(AsnOid& id);

    AsnLen BEnc(BUF_TYPE b);
    void BDec(BUF_TYPE b, AsnLen& bytesDecoded, ENV_TYPE env);

    int BEncPdu( BUF_TYPE b, AsnLen& bytesEncoded);
    int BDecPdu( BUF_TYPE b, AsnLen& bytesDecoded);

    void Print(ostream& os) { value->Print(os); }
};
\end{verbatim}
\end{small}

The C++ mechanism is similar to the C mechanism which uses hash tables
to hold the identifier to type mappings.  In this section we will
discuss the main differences of the C++ ANY DEFINED BY handling
mechanism.  You should read Section \ref{any-C-sec} for caveats and
other important information.

In C, the hash table entry held the size of the type and pointers to
its encode, decode, free etc. routines to describe the type.  In C++
these have been replaced with a pointer to an instance of the type. A
hash table entry contains: 
\begin{itemize}
\item {the \verb$anyId$}
\item {the INTEGER or OBJECT IDENTIFIER that maps to it}
\item {a pointer to an instance of the identified type}
\end{itemize}

All C++ ASN.1 types use the \verb$AsnType$ base class which
designates the following functions as virtual:
\begin{itemize}
\item{\verb$BDec$}
\item{\verb$BEnc$}
\item{\verb$Print$}
\item{\verb$Clone$}
\end{itemize}

This allows the ANY DEFINED BY handling routines to treat any type as an
\verb$AsnType$.  So, for each type the ANY DEFINED BY handling code
has access to the four virtual methods.  Note that the \verb$value$
field in the \verb$AsnAny$ class and the \verb$typeToClone$ field in
the \verb$AnyInfo$ class are both \verb$AsnType*$.

To build an ANY DEFINED BY value, simply set the value field in the
\verb$AsnAny$ object to point to the object you want to encode.  Then
set the identifier field for that ANY DEFINED BY value to the correct
identifier (as generated for its OBJECT-TYPE macro value).  It is very
important to do this correctly because the encoder will simply call
the virtual \verb$BEnc$ routine for the object pointed to by the
\verb$AsnAny$'s value field.  There is not attempt to make sure that
the identifier field's value matches the object that was encoded.

A potential solution to the last identifier problem is to add a type
id field to the \verb$AsnType$ base class.  Snacc could generate a
unique identifier (that would be stored in the base class) for each
type.  The encoder could then check identifiers between the value
being encoded and the value stored in the hash table.  The identifier
in the base class could easily be automatically set (correctly) from
the constructors for each type (constructors are snacc generated).  It
would be difficult to ensure unique identifiers for each type between
modules if the ASN.1 modules were compiled separately.

Before an ANY DEFINED BY value can be decoded, the field that contains
its identifier must have been decoded and used with the \verb$AsnAny$
value's \verb$SetTypeByInt$ or \verb$SetTypeByOid$ methods.  Then the
ANY DEFINED BY value can be decoded by calling its (\verb$AsnAny$)
\verb$BDec$ routine.  This in turn calls the \verb$Clone$ routine on
the type in the hash table entry to generate the correct object.  Then
the \verb$BDec$ method of the newly created object is called.

When the C ANY DEFINED BY decoder allocates a value, it uses the size
information for the identified type.  This is not safe for C++ so the
virtual \verb$Clone$ routine was added to the \verb$AsnType$ base
class.  This allows the proper constructor mechanism to be used when
allocating the value.

The virtual \verb$Clone$ routine simply calls its type's parameterless
constructor via \verb$new$ (hence every ASN.1 type's class must have a
parameterless constructor).  \verb$Clone$ is a poor name since
the routine only produces a new instance of the given type without
copying the original's data.

The hash tables are automatically initialized using the C++ constructor
mechanism.  You do not need to call any initialization routines as
described in the C chapter.


\section{\label{buffer-Cpp-sec}Buffer Management}

The C++ buffer management provided with snacc is similar to that of
the C \verb$SBuf$s.  The following is from
\verb$snacc/c++_incluce/asn_buf.h$: 
\begin{small}
\begin{verbatim}
class AsnBuf
{
  protected:
    char* dataStart;
    char* dataEnd;
    char* blkStart;
    char* blkEnd;
    char* readLoc;
    int writeError;
    int readError;
    
  public:
    void Init( char* data, unsigned long int dataLen);
    void ResetInReadMode();
    void ResetInWriteRvsMode();
    void InstallData(char* data, unsigned long int dataLen);
    unsigned long int DataLen();
    char* DataPtr();
    unsigned long int BlkLen();
    char* BlkPtr();
    int Eod();

    /* the following 9 methods are required  */
    int ReadError();
    int WriteError();
    void Skip(unsigned long int skipLen)
    unsigned long int Copy( char* dst, unsigned long int copyLen)
    unsigned char PeekByte()                      
    char* GetSeg(unsigned long int* lenPtr)
    void PutSegRvs( char* seg, unsigned long int segLen);
    unsigned char GetByte();
    void PutByteRvs(unsigned char byte);
};
\end{verbatim}
\end{small}

This is the only buffer type provided with the C++ library.

\section{\label{mem-Cpp-sec}Dynamic Memory Management}

The built-in C++ memory management system is used by snacc (\verb$new$
and \verb$delete$).  Better performance might be gained by using an
different management scheme.

To change \verb$new$ and \verb$delete$ to use your own memory
management sheme the best way to start is by defining them as virtual
in the \verb$AsnType$ base class.  More information on providing your
own memory management can be found in Stroustrup \cite{stroustrup}.

\section{\label{error-Cpp-sec}Error Management}

The C++ ASN.1 error management is identical to that of the C ASN.1
model.  C++ exception handling (\verb$try$ and \verb$catch$) were not
used to replace \verb$setjmp$ and \verb$longjmp$ because they were not
implemented by the C++ compiler we used.


\chapter{\label{mod-chp}Modifying the Compiler}

The compiler consists of about 30000 lines of yacc, lex and C code
(another 7000 for the runtime library routines).  The best way to
understand the compiler internals is to understand the module data
structure (\verb$snacc/src/asn1module.h$) and to read the compiler
chapter in this document to gain a conceptual understanding of each
pass of the compiler.

The most common form of modification will likely be for macro
handling.  To understand this, look at the way the OBJECT-TYPE macro is
treated in:
\begin{description}
\item[asn1.lex] {add any new keywords}
\item[asn1.yacc] { parse the macro into the desired data structure.
Use the existing productions as much as possible.}
\item[link\_type.c] { link any type defined or referenced in the
macro}
\item[link\_values.c] { link any value defined or referenced in the
macro}
\item[do\_macros.c] { perform any semantic action for the macro }

\item[normalize.c] { move any type and value definitions in the macro
to the top level so the code generator can generate code for them
(without looking in the macro).}

\item[code generators] { to convert any special semantics into useful
C or C++.  This phase is likely to be dependent on the generated
code's target environment.} 
\end{description}
        
\chapter{\label{concl-chp}Future Work}

There are still many interesting and useful things that can be done to
snacc.  To name a few, snacc could be improved to:
\begin{itemize}
\item{support the new features of 1992 ASN.1}
\item{generate interpreted decoders and encoders}
\item{generate forward encoders that use only the indefinite length
form for constructed BER values}
\item{support new encoding rules such as the variants of Packed
Encoding Rules (PER), Canonical Encoding Rules (CER) and
Distinguished Encoding Rules (DER).}
\item{parse complex values.}
\end{itemize}

1992 ASN.1 has many improvements such as eliminating macros and adding
stronger mechanisms for adding type semantics.  With stronger
semantics, the compiler can do more for the user.

Interpreted encoders and decoders work by interpreting a description
of the type to be encoded or decoded and acting accordingly.  Instead
of generating C or C++ code, the compiler produces a simple
description of each type.  While potentially slower than compiled
encoders and decoders, the interpreted versions would likely take up
less memory and could be easily re-configured during runtime.  The
internal representation (non-encoded) of a value presents and
interesting problem (remember no abstract syntax specific .h files are
available).

New encoding rules such as PER offer improved compression and
possibly better performance in certain cases.  They can be added to
the compiler by adding a new backend to the compiler and providing a
runtime library.

The ability to parse complex ASN.1 value notation values is useful for
protocol testing.  It allows PDUs to be defined in a human readable
form and converted into their encoded representation.  The combination
of an interpreted encoder and decoder with complex value parsing and
support for newer encoding rules would very useful for protocol testing.

\appendix

\chapter{The Module Data Structure ASN.1 Definition}

The ASN.1 modules are parsed into an internal data structure.  The
data structure was initially defined in ASN.1 with the idea that if
we needed to write a parsed module to disk, the ASN.1 encoding
routines could be used.  No file format was needed so the ASN.1 was
merely and exercise.

This procedure highlighted the problem with circular links and index
like links in ASN.1 data structures.  BER does not support this type
of linking; to handle it, the offending links can be made optional
and not encoded.  After decoding, these links need to be
re-established.

The following is the ASN.1 definition of the Module data structure.
The C translation (as generated by an early version of snacc) can be
found in \verb$snacc/src/asn1module.h$.
\begin{small}
\begin{verbatim}
--
-- This module describes the data structure used to represent the 
-- compiled ASN.1. 
-- Using ASN.1 for the internal data structure allows writing
-- (encoding) to disk for storage (not done yet due to recursive
--  refs back to the module)
--
-- Mike Sample 91/08/29
-- Modifed 92/05  MS
--

Asn1Module DEFINITIONS IMPLICIT TAGS ::=
BEGIN 

-- exports everything
-- imports nothing


Modules ::= [APPLICATION 0] IMPLICIT SEQUENCE
{
    creationTime INTEGER, 
    modules ModuleList
}

ModuleList ::= SEQUENCE OF Module


Module ::= SEQUENCE
{
    status ENUMERATED { mod-ok(0), mod-not-linked(1), mod-error(2) },
    modId      ModuleId,
    tagDefault ENUMERATED { explicit-tags(0), implicit-tags(1) },
    exportStatus  ENUMERATED { exports-all(0), exports-nothing(1),
                               exports-some(2) },
    imports    ImportModuleList,
    typeDefs   TypeDefList,
    valueDefs  ValueDefList,
    hasAnys    BOOLEAN,
    
    asn1SrcFileName MyString,
    cHdrFileName    MyString,
    cSrcFileName    MyString,
    cppHdrFileName  MyString,
    cppSrcFileName  MyString
}


ModuleId ::= SEQUENCE
{
    name MyString,
    oid  OBJECT IDENTIFIER OPTIONAL --snacc cTypeName:"OID" isPtr:"TRUE"
}


ImportModuleList ::= SEQUENCE OF ImportModule


ImportModule ::= SEQUENCE
{
    modId       ModuleId,
    importElmts ImportElmtList,
    moduleRef   Module --snacc isEncDec:"FALSE"   
}


ImportElmtList ::= SEQUENCE OF ImportElmt


ImportElmt ::= SEQUENCE
{
    resolvedRef CHOICE
    {
        type  [0] TypeDef,  -- not encoded 
        value [1] ValueDef  -- not encoded 
     } OPTIONAL,
    name MyString,
    privateScope BOOLEAN -- true if from MODNAME.TYPE ref
}

TypeDefList ::= SEQUENCE OF TypeDef

OidOrInt ::= CHOICE
{
    oid OBJECT IDENTIFIER,
    intId INTEGER
}

AnyRef ::= SEQUENCE
{
    anyIdName MyString,
    id OidOrInt
}

AnyRefList ::= SEQUENCE OF AnyRef

TypeDef ::= SEQUENCE
{
    exported     BOOLEAN,
    recursive    BOOLEAN,
    isPdu    BOOLEAN,
    localRefCount     INTEGER,
    importRefCount     INTEGER,
    visited      BOOLEAN,
    definedName  MyString,
    type         Type,
    cTypeDefInfo CTDI,
    cppTypeDefInfo CppTDI,
    attrList     AttributeList,
    refList      TypeDefList,
    anyRefs      AnyRefList
}


Tag ::= SEQUENCE
{
    tclass INTEGER, -- swap this for the BER_CLASS enum from basetypes.h
    form  INTEGER,  -- swap this for the BER_FORM enum
    code  INTEGER,
    explicit BOOLEAN,
    valueRef Value
}

Type ::= SEQUENCE
{
    optional   BOOLEAN,
    implicit   BOOLEAN,
    tags       TagList,
    defaultVal [0] IMPLICIT NamedValue OPTIONAL,
    subtypes   [1] Subtype OPTIONAL,
    basicType  [2] BasicType,
    lineNo     INTEGER,
    cTypeRefInfo CTRI,
    cppTypeRefInfo CppTRI,
    attrList   AttributeList
}

TagList ::= SEQUENCE OF Tag

AttributeList ::= SEQUENCE OF MyString

NamedNumberList ::= ValueDefList


-- BasicTypes with NULL need no more info that which type it is
-- (this is known from the choice id)

BasicType ::= CHOICE
{
    unknown         [0]  IMPLICIT NULL,
    boolean         [1]  IMPLICIT NULL,
    integer         [2]  IMPLICIT NamedNumberList,
    bitString       [3]  IMPLICIT NamedNumberList,
    octetString     [4]  IMPLICIT NULL,
    null            [5]  IMPLICIT NULL,
    oid             [6]  IMPLICIT NULL,
    real            [7]  IMPLICIT NULL,
    enumerated      [8]  IMPLICIT NamedNumberList,
    sequence        [9]  IMPLICIT NamedTypeList,
    sequenceOf      [10] IMPLICIT Type,
    set             [11] IMPLICIT NamedTypeList,
    setOf           [12] IMPLICIT Type,
    choice          [13] IMPLICIT NamedTypeList,
    selection       [14] IMPLICIT SelectionType,
    componentsOf    [15] IMPLICIT Type, --  [Resolved](local/import) type ref  
    any             [16] IMPLICIT NULL,
    anyDefinedBy    [17] IMPLICIT AnyDefinedByType,
    localTypeRef    [19] IMPLICIT TypeRef,
    importTypeRef   [20] IMPLICIT TypeRef,
    macroType       [21] MacroType,
    macroDef        [22] IMPLICIT MacroDef --snacc isPtr:"FALSE"
}

MacroDef ::= MyString -- just keep the text for now 

MacroType ::= CHOICE
{
    rosOperation    [0] IMPLICIT RosOperationMacroType,
    rosError        [1] IMPLICIT RosErrorMacroType,
    rosBind         [2] IMPLICIT RosBindMacroType,
    rosUnbind       [3] IMPLICIT RosBindMacroType,
    rosAse          [4] IMPLICIT RosAseMacroType,
    rosAc           [5] IMPLICIT RosAcMacroType,
    mtsasExtension  [6] IMPLICIT MtsasExtensionMacroType,
    mtsasExtensions [7] IMPLICIT MtsasExtensionsMacroType,
    mtsasExtensionAttribute [8] IMPLICIT MtsasExtensionAttributeMacroType,
    mtsasToken      [9] IMPLICIT MtsasTokenMacroType,
    mtsasTokenData [10] IMPLICIT MtsasTokenDataMacroType,
    mtsasSecurityCategory [11] IMPLICIT MtsasSecurityCategoryMacroType,
    asnObject       [12] IMPLICIT AsnObjectMacroType,
    asnPort         [13] IMPLICIT AsnPortMacroType,
    asnRefine       [14] IMPLICIT AsnRefineMacroType,
    asnAbstractBind      [15] IMPLICIT AsnAbstractBindMacroType,
    asnAbstractUnbind    [16] IMPLICIT AsnAbstractBindMacroType,
    asnAbstractOperation [17] IMPLICIT RosOperationMacroType,
    asnAbstractError     [18] IMPLICIT RosErrorMacroType,
    afAlgorithm     [19] IMPLICIT Type,
    afEncrypted     [20] IMPLICIT Type,
    afProtected     [21] IMPLICIT Type,
    afSignature     [22] IMPLICIT Type,
    afSigned        [23] IMPLICIT Type,
    snmpObjectType  [24] IMPLICIT SnmpObjectTypeMacroType
}


AnyDefinedByType ::= SEQUENCE
{
    fieldName MyString,   -- name of field that its defined by 
    link NamedType OPTIONAL  -- REFERENCE not encoded 
}


SelectionType ::= SEQUENCE
{
    fieldName MyString,   -- name of field in choice
    typeRef   Type,      -- [Resolved](local/import) type ref
    link NamedType OPTIONAL -- REFERENCE not encoded 
}

NamedTypeList ::= SEQUENCE OF NamedType

NamedType ::= SEQUENCE
{
    fieldName  MyString, -- may be empty or NULL str
    type       Type
}


TypeRef ::= SEQUENCE
{
    typeName    MyString,
    moduleName  MyString, -- used for "modname.type" refs(may be null)
    module      Module,   --snacc isEncDec:"FALSE"
    link        TypeDef   --snacc isEncDec:"FALSE"
}



RosOperationMacroType ::= SEQUENCE
{
    arguments NamedType,
    result    NamedType,
    errors    [0] IMPLICIT TypeOrValueList OPTIONAL,
    linkedOps [1] IMPLICIT TypeOrValueList OPTIONAL
}

ValueList ::= SEQUENCE OF Value

TypeOrValueList ::= SEQUENCE OF TypeOrValue

TypeOrValue ::= CHOICE
{
    type  [0] IMPLICIT Type,
    value [1] IMPLICIT Value
}

OidList ::= SEQUENCE OF OBJECT IDENTIFIER


RosErrorMacroType ::= SEQUENCE
{
    parameter NamedType
}

RosBindMacroType ::= SEQUENCE
{
    argument NamedType,
    result   NamedType,
    error    NamedType
}


RosAseMacroType  ::= SEQUENCE
{
    operations       ValueList,
    consumerInvokes  ValueList,
    supplierInvokes  ValueList
}

RosAcMacroType ::= SEQUENCE
{
    nonRoElements       ValueList,
    bindMacroType       Type,
    unbindMacroType     Type,
    remoteOperations    Value,
    operationsOf        ValueList,
    initiatorConsumerOf ValueList,
    responderConsumerOf ValueList,
    abstractSyntaxes    OidList
}


MtsasExtensionMacroType ::=  SEQUENCE
{
    elmtType              [0] IMPLICIT NamedType OPTIONAL,
    defaultValue          [1] IMPLICIT Value    OPTIONAL,
    criticalForSubmission [2] IMPLICIT BOOLEAN  OPTIONAL,
    criticalForTransfer   [3] IMPLICIT BOOLEAN  OPTIONAL,
    criticalForDelivery   [4] IMPLICIT BOOLEAN  OPTIONAL
}


MtsasExtensionsMacroType ::= SEQUENCE
{
    extensions ValueList
}

MtsasExtensionAttributeMacroType ::= SEQUENCE
{
    type Type OPTIONAL
}

MtsasTokenMacroType ::= SEQUENCE
{
    type Type OPTIONAL
}

MtsasTokenDataMacroType ::= SEQUENCE
{
    type Type OPTIONAL
}

MtsasSecurityCategoryMacroType ::= SEQUENCE
{
    type Type OPTIONAL
}

AsnObjectMacroType ::= SEQUENCE
{
   ports  AsnPortList OPTIONAL
}

AsnPortList ::= SEQUENCE OF AsnPort

AsnPort ::= SEQUENCE 
{
    portValue Value,
    portType  ENUMERATED
    {
         consumer-port(0),
         supplier-port(1),
         symmetric-port(2)
    }
}


AsnPortMacroType ::= SEQUENCE
{
    abstractOps     [0] IMPLICIT TypeOrValueList OPTIONAL,
    consumerInvokes [1] IMPLICIT TypeOrValueList OPTIONAL,
    supplierInvokes [2] IMPLICIT TypeOrValueList OPTIONAL
}


AsnRefineMacroType ::= INTEGER

AsnAbstractBindMacroType ::= SEQUENCE
{
    ports [0] IMPLICIT AsnPortList OPTIONAL,
    type  [1] IMPLICIT Type OPTIONAL
}


SnmpObjectTypeMacroType ::= SEQUENCE
{
   syntax Type,
   access ENUMERATED
       { snmp-read-only(0), snmp-read-write(1),
         snmp-write-only(2), snmp-not-accessible(3)},
   status ENUMERATED
       { snmp-mandatory(0), snmp-optional(1),
         snmp-obsolete(2), snmp-deprecated(3)},
   description [0] IMPLICIT Value OPTIONAL,
   reference   [1] IMPLICIT Value OPTIONAL,
   index       [2] IMPLICIT TypeOrValueList OPTIONAL,
   defVal      [3] IMPLICIT Value OPTIONAL
}


Subtype ::= CHOICE
{
    single [0] SubtypeValue,
    and    [1] IMPLICIT SubtypeList,
    or     [2] IMPLICIT SubtypeList,
    not    [3] Subtype
}

SubtypeList ::= SEQUENCE OF Subtype

SubtypeValue ::= CHOICE
{
    singleValue       [0] IMPLICIT Value,
    contained         [1] IMPLICIT Type,
    valueRange        [2] IMPLICIT ValueRangeSubtype,
    permittedAlphabet [3] Subtype, -- only valuerange or singleval
    sizeConstraint    [4] Subtype, -- only single value ints or val range
    innerSubtype      [5] IMPLICIT InnerSubtype
}


ValueRangeSubtype ::= SEQUENCE
{
    lowerEndInclusive BOOLEAN, 
    upperEndInclusive BOOLEAN,
    lowerEndValue Value,
    upperEndValue Value
}


InnerSubtype ::= SEQUENCE
{
    constraintType ENUMERATED { full-ct(0), partial-ct(1), single-ct(2) },
    constraints ConstraintList
}

ConstraintList ::= SEQUENCE OF Constraint

Constraint ::= SEQUENCE
{
    fieldRef MyString,  -- not used if in single-ct, may be null
    presenceConstraint ENUMERATED
    {
        present-ct(0),
        absent-ct(1),
        empty-ct(2),
        optional-ct(3)
    },
    valueConstraints Subtype
}


ValueDefList ::= SEQUENCE OF ValueDef


ValueDef ::= SEQUENCE
{
   exported    BOOLEAN,
   definedName MyString,
   value       Value
}

Value ::= SEQUENCE 
{
    type    Type OPTIONAL,
    valueType  INTEGER,  -- holds one of choiceId's def'd for BasicType
    basicValue BasicValue,
    lineNo     INTEGER
}

BasicValue ::= CHOICE
{
    unknown          [0]  IMPLICIT NULL,
    empty            [1]  IMPLICIT NULL,
    integer          [2]  IMPLICIT INTEGER, 
    specialInteger   [3]  IMPLICIT SpecialIntegerValue,
    longInteger      [4]  IMPLICIT INTEGER,  -- put LONG before INTGEGER
    boolean          [5]  IMPLICIT BOOLEAN,
    real             [6]  IMPLICIT REAL,
    specialReal      [7]  IMPLICIT SpecialRealValue,
    asciiText        [8]  IMPLICIT OCTET STRING,
    asciiHex         [9]  IMPLICIT OCTET STRING,
    asciiBitString   [10] IMPLICIT OCTET STRING,
    oid              [11] IMPLICIT OBJECT IDENTIFIER,
    linkedOid        [12] IMPLICIT OBJECT IDENTIFIER, --snacc cTypeName:"OID"
    berValue         [13] IMPLICIT OCTET STRING,
    perValue         [14] IMPLICIT OCTET STRING,
    namedValue       [15] IMPLICIT NamedValue,
    null             [16] IMPLICIT NULL,
    localValueRef    [17] IMPLICIT ValueRef,
    importValueRef   [18] IMPLICIT ValueRef,
    valueNotation    [19] IMPLICIT OCTET STRING    
}
    


SpecialIntegerValue ::= ENUMERATED { min-int(0), max-int(1) }
SpecialRealValue ::= ENUMERATED { minus-infinity-real(0), plus-infinity-real(1) }


ValueRef ::= SEQUENCE
{
    valueName  MyString,
    moduleName MyString, -- used for "modname.value" refs (may be null)
    link       ValueDef, --snacc isEncDec:"FALSE"
    module     Module    --snacc isEncDec:"FALSE"
}

NamedValue ::= SEQUENCE
{
   fieldName MyString, -- may be null
   value     Value
}

NamedValueList ::= SEQUENCE OF NamedValue

CTypeId  ::= ENUMERATED { c-choice(0), c-list(1), c-any(2), c-anydefinedby(3),
                          c-lib(4), c-struct(5), c-typeref(6), c-no-type(7),
                         c-typedef(8) }

-- C Type Def Info - info used for routine naming
-- and referencing from other types
CTDI ::= SEQUENCE
{
    asn1TypeId         INTEGER, --snacc cTypeName:"enum BasicTypeChoiceId"
    cTypeId            CTypeId,
    cTypeName          MyString,
    isPtrForTypeDef    BOOLEAN,
    isPtrForTypeRef    BOOLEAN,
    isPtrInChoice      BOOLEAN,
    isPtrForOpt        BOOLEAN,

               -- defines these names, used by references
    optTestRoutineName MyString, -- routine/macro to check whether
                                 -- opt type is present
    defaultFieldName   MyString, -- base for generating field names

    printRoutineName   MyString,
    encodeRoutineName  MyString,
    decodeRoutineName  MyString,
    freeRoutineName    MyString,
    isEncDec           BOOLEAN, -- if false, no routines are gen
                                -- and not included in encodings
    genPrintRoutine    BOOLEAN, 
    genEncodeRoutine   BOOLEAN,
    genDecodeRoutine   BOOLEAN,
    genFreeRoutine     BOOLEAN,
    genTypeDef         BOOLEAN
}


-- 
-- CTRI (C Type Ref Info) is used for generating C typedefinitions
-- from the ASN.1 types info
CTRI ::= SEQUENCE
{
    cTypeId CTypeId,
    cFieldName MyString,
    cTypeName  MyString,
    isPtr BOOLEAN,
    isEndCType BOOLEAN,        -- false for struct/union def
    cNamedElmts CNamedElmts OPTIONAL,      -- for C_LIB bits/int/enums 
    choiceIdValue INTEGER,     -- enum value of this c field
    choiceIdSymbol MyString,   -- this fields sym in choiceId enum
    choiceIdEnumName MyString,      
    choiceIdEnumFieldName MyString,
    optTestRoutineName MyString, -- these names are gained from refd type def
    printRoutineName   MyString,   -- or are over-ridden snacc attribute comment
    encodeRoutineName  MyString,
    decodeRoutineName  MyString,
    freeRoutineName    MyString,
    isEncDec           BOOLEAN -- whether part of enc value
}

CNamedElmts ::= SEQUENCE OF CNamedElmt

CNamedElmt ::= SEQUENCE
{
    name MyString,
    value INTEGER
}


CppTDI ::= SEQUENCE
{
    asn1TypeId         INTEGER, --snacc cTypeName:"enum BasicTypeChoiceId"
    className          MyString,
    isEnc              BOOLEAN,
    isPtrForTypeDef    BOOLEAN,
    isPtrForTypeRef    BOOLEAN,
    isPtrInChoice      BOOLEAN,
    isPtrForOpt        BOOLEAN,
    optTestRoutineName MyString,
    defaultFieldName   MyString -- base for generating field names
}



CppTRI ::= SEQUENCE
{
    isEnc BOOLEAN,
    className MyString,
    fieldName MyString,
    isPtr BOOLEAN,
    namedElmts CNamedElmts,
    choiceIdSymbol MyString,
    choiceIdValue INTEGER,
    optTestRoutineName MyString
}

-- use snacc compiler directives to overide the builtin types.
--
-- All strings used in module data struct are null terminated so
-- can just use a char*
-- Note the snacc comments before the PrintableString
-- bind with the MyString TypeDef and the ones after PrintableString
-- bind with the PrintableString Type ref.


MyString ::= --snacc isPtrForTypeDef:"FALSE" 
             --snacc isPtrForTypeRef:"FALSE" 
             --snacc isPtrInChoice:"FALSE" 
             --snacc isPtrForOpt:"FALSE" 
             --snacc optTestRoutineName:"MYSTRING_NON_NULL"
             --snacc genPrintRoutine:"FALSE"
             --snacc genEncodeRoutine:"FALSE"
             --snacc genDecodeRoutine:"FALSE"
             --snacc genFreeRoutine:"FALSE"
             --snacc printRoutineName:"printMyString"
             --snacc encodeRoutineName:"EncMyString"
             --snacc decodeRoutineName:"DecMyString"
             --snacc freeRoutineName:"FreeMyString"
             PrintableString --snacc cTypeName:"char*"

END
\end{verbatim}
\end{small}

\bibliography{asn1}
\bibliographystyle{plain}
\end{document}


