The C# class variables corresponding to each of the ASN.1 types and method of population are the same as they were in the BER encoding case. See the section on BER encoding for instructions on how to populate the variables prior to encoding.
Once an object’s member variables have been populated, the object’s encode method can be invoked to encode the value. The general procedure to do this involves the following three steps:
Create an encode message buffer object into which the value will be encoded
Invoke the encode method
Invoke encode message buffer methods to access the encoded message component
The first step is the creation of an encode message buffer object. For PER encoding, this is an object of the Asn1PerEncodeBuffer class. The following constructors are available for creating a PER encode buffer object:
public Asn1PerEncodeBuffer (bool aligned); public Asn1PerEncodeBuffer (bool aligned, int sizeIncrement);
The first argument indicates whether PER aligned or unaligned encoding should be done. The second form of the constructor contains a size increment argument. This argument will determine how often the buffer will need to be resized to hold large messages. If you know that you will be encoding large messages, then this object should be constructed with a large value for the increment. If you know that you will be encoding small messages in a constrained environment, then this value can be set very low. The default constructor sets the value to a reasonable mid-range value (see SIZE_INCREMENT in Asn1EncodeBuffer.cs, as of this writing the value was set to 1024).
The second step is the invocation of the encode method. The calling arguments were described earlier. As per the C# standard, this method must be invoked from within a try/catch block to catch the possible exceptions that may be thrown. Alternatively, the method from which the encode method is called can declare that it throws an Asn1Exception leaving it to be dealt with at a higher level.
Finally, encode buffer methods can be called to access the encoded message component. The C# API provides an object called a ByteArrayInputStream that provides a way to look at the encoded component as a stream. The encode buffer object provides a method called GetInputStream that returns a byte array input stream representing the message component. This is the preferred way to access the encoded component.
In addition to GetInputStream there is a MsgCopy property that will retrieve a copy of the generated message into a byte array object. This is somewhat slower because a copy needs to be done. Another option that is only available when doing PER encoding is the Buffer property. This returns a reference to the actual message buffer into which the message was encoded. Since a PER message is encoded front-to-back (unlike the back-to-front used in BER/DER encoding), the buffer reference returned will point to the start of the encoded message. The MsgByteCnt property can then be used to get the message length in bytes or the MsgBitCnt property can be called to get the length in bits.
The encode buffer class also contains other methods for operating directly on the encoded component (for example, the write method can be used to write it to a file or other medium). And of course, one could derive their own special encode buffer class from this class to add more functionality. See the description of the Asn1PerEncodeBuffer class in the runtime section for a full description of the available methods.
A complete example showing how to invoke a PER encode method is as follows:
// Note: personnelRecord object was previously populated with data // Step 1: Create a message buffer object. This object uses the // default size increment for buffer expansion.. Asn1PerEncodeBuffer encodeBuffer = new Asn1PerEncodeBuffer(); // Step 2: Invoke the encode method. Note that it must be done // from within a try/catch block.. try { personnelRecord.Encode (encodeBuffer); if (trace) { System.Console.Out.WriteLine ("Encoding was successful"); System.Console.Out.WriteLine ("Hex dump of encoded record:"); encodeBuffer.HexDump (); System.Console.Out.WriteLine ("Binary dump:"); encodeBuffer.BinDump (“personnelRecord”); } // Step 3: Access the encoded message component. In this // case, we use methods in the class to write the component // to a file and output a formatted dump to the message.dmp // file.. // Write the encoded record to a file encodeBuffer.Write(new System.IO.FileStream( filename, System.IO.FileMode.Create)); // Generate a dump file for comparisons System.IO.StreamWriter messagedmp = new System.IO.StreamWriter( new System.IO.FileStream( "message.dmp", System.IO.FileMode.Create)); messagedmp.AutoFlush = true; encodeBuffer.HexDump(messagedmp); // We can also directly access the buffer as follows: byte[] buffer = encodeBuffer.Buffer; int msglen = encodeBuffer.MsgByteCnt; } catch (Exception e) { System.Console.Out.WriteLine (e.Message); Asn1Util.WriteStackTrace(e, Console.Error); return; } |
If you compare this example with the BER encoding example in Figure 1, you will see the encoding procedure is almost identical. This makes it very easy to switch encoding methods should the need arise. All you need to do is change Asn1BerEncodeBuffer to Asn1PerEncodeBuffer and remove the explicit argument from the encode method call.