ASN.1
Previous: Macros Up: Additional Features Next: Parameterization


Table Constraints

The Concept

To introduce the concept of a table constraint, let's say we have a protocol where message types are identified by a message code and some messages might not be understood or supported by the recipient. Therefore, each message includes a criticality code, telling the recipient how to respond if a message cannot be handled: whether to ignore the message, to send back a rejection message, or to send back a notification message.

In ASN.1, we might define our messages like so:

      Message ::= SEQUENCE {
         code INTEGER(0..255),
         criticality ENUMERATED {reject, notify, ignore},
         data CHOICE {
            setup Setup,
            event Event,
            info Info
         }
      }
   

Now suppose message criticality is not arbitrary but is specified by the protocol. The protocol designers might use the following table to list the various message codes, the criticality, and the data that goes with that type of message:

message code criticality message data
1 reject Setup
2 notify Event
3 ignore Info

Our goal is to use a table constraint to ensure that messages conform to the information represented by this table - for example, that a message with code 1 is sent with criticality "reject" and that it contains Setup data.

The Implementation

The first step is to specify an information object class, frequently referred to simply as a "class". The class describes the format of the table by defining the columns. A CLASS definition uses a syntax similar to that of a SEQUENCE. Here's our class definition:

      MESSAGE-CLASS ::= CLASS {
          &code        INTEGER(0..255)   UNIQUE,
          &criticality ENUMERATED {reject, notify, ignore} DEFAULT ignore,
          &Data
      }  
      WITH SYNTAX {
        MESSAGE      &Data
        CODE          &code
        [CRITICALITY            &criticality]
     }
   
A few things to notice in the class definition:

Now that we have the format of our table, we need to specify some rows. An information object is an instance of an information object class, and it represents a row in the table. The WITH SYNTAX clause in our class definition specifies the syntax for defining information objects of that class. We'll define three information objects, one for each row of our table:

      setupMessage MESSAGE-CLASS ::= { 
         MESSAGE Setup 
         CODE 1 
         CRITICALITY reject 
      }
      
      eventMessage MESSAGE-CLASS ::= {
         MESSAGE Event
         CODE 2
         CRITICALITY notify
      }
      
      infoMessage MESSAGE-CLASS ::= {
         MESSAGE Info
         CODE 3
         CRITICALITY ignore
      }
   

On the left, the information object definition has the name of the object and the name of its class. On the right, we have the actual object. Note:

Next, we put our information objects together into an information object set. The set will function as the table in our table constraint. An information object set consists of information objects just as a table consists of rows. Because field &code was defined as UNIQUE, each information object in a given information object set must have a unique value for &code. Here is the definition for our set:

      Messages MESSAGE-CLASS ::= { setupMessage | eventMessage | infoMessage }
   

The notation for the information object set is similar to the notation for a value set. On the left, we have the set name and the class name; on the right, the elements of the set are listed. The name of an information object set MUST begin with an uppercase letter.

Finally, we can now rewrite Message to use table constraints:

      Message ::= SEQUENCE {
         code        MESSAGE-CLASS.&code          ({Messages}),
         criticality MESSAGE-CLASS.&criticality   ({Messages}{@code}),
         data        MESSAGE-CLASS.&Data          ({Messages}{@code})
      }
   

First, notice that the type for each of our components is now specified by reference to a field in class MESSAGE-CLASS. The type of the component is the type of the referenced field. Thus, component code has type INTEGER(0..255) and component data is open type.

Next, notice that each component has a constraint, specified inside parentheses as always; each of these constraints is a table constraint. The "table" of the table constraint is specified as an information object set inside curly braces. In our case, the "table" is the information object set Messages. Messages therefore essentially gives us a table that our SEQUENCE values will have to conform to; it's the table we saw in the concept section. The constraining information object set for a table constraint MUST have objects of the class referenced by the constrained component. Thus, Messages MUST contain objects of class MESSAGE-CLASS.

Not only does each component's type depend on which class field is referenced, but this also establishes the correspondence between the SEQUENCE components and the columns in the constraining table. For example, component code refers to field &code, so it is constrained by the table constraint to being one of the values given for &code in the information objects in set Messages. This means code is constrained to the values 1, 2, or 3.

As already mentioned, data is open type. Without the table constraint, data could have a value of literally any type, but the table constraint changes that. Look again at data's constraint:

     data        MESSAGE-CLASS.&Data          ({Messages}{@code})

Inside the first set of curly braces, we have the constraining information object set. If we stopped there and omitted the next set of curly braces, then that would mean data would be constrained to a value of any of the types that are specified for &Data in the set Messages, i.e. types Setup, Event, or Info. That wouldn't be very useful (the exact type would be unknown). So, inside the second set of curly braces, we have an "at-notation" that refers to another component, namely, code. This means that the value of code and the type of value must correspond to the same row in the constraining table, or, in other words, to the same information object. So, if code is 1, then data must be a value of type Setup, in accordance with object setupMessage, in set Messages. The same constraint and logic applies to component criticality. Thus, if code is 1, then criticality must be reject.

You now know how table constraints work.

Extensibility

A final thing to know is that information object sets can be extensible. If we wanted Messages to be extensible, we would have written:

      Messages MESSAGE-CLASS ::= { setupMessage | eventMessage | infoMessage, ... }
      

When an extensible information object set is used in a table constraint, it basically means that there are unspecified legal values. In our case, it means that it is NOT an error if code does not take on a value found in the table (i.e. is not equal to the &code value of some object in the set). However, in that case, the type for component data would be unknown. It is not a problem for an open type to have an unknown type, because open types are encoded such that a decoder can always locate the end of the open type data.

Previous: Macros Up: Additional Features Next: Parameterization


This site was developed from:
Computer Networks and Open Systems
An Application Development Perspective

by
Lillian N. Cassel
Richard H. Austing

Jones & Bartlett Publisher
ISBN 0-7637-1122-5

This site is hosted by:


Real World ASN.1 and XML Solutions