A Sequential Format Example(3.1)

The example illustrated in this appendix is a format definition for decoding a shortened version of EWSD AMA (Automatic Message Accounting).

All incoming records are of the same record type, however their content varies. The first four fields are present in all records, the fifth field varies depending on the content of the fieldĀ RecordOwnerTypePresent. The last three fields are optional.

A schematic representation of the example discussed in this section

external

Since the last three optional fields (includingĀ  RecordOwnerTypeĀ and RecordOwnerDN) consist of several fields, each one will be defined as its own type. That is, an external sequential format.


Note!

TheĀ FillerRecord_0x00_EXTĀ construct. This is the padding which may be present between records. Hence, it is defined as a record type identified by the decoder, however not routed on to the subsequent agent (see theĀ in_mapĀ definitions).

If the format is only used for decoding (which is the normal case for switch output formats), the encoding instructions (encode_value) in the following code is skipped.

external AMARecord_EXT:
         identified_by(RecordIdentifier == 0x84),
         dynamic_size(RecordLength)

{
  int(little_endian) RecordIdentifier  :static_size(1),
     external_only, encode_value(0x84);
  int(little_endian) RecordLength      :static_size(2),
     external_only, encode_value(udr_size);

This byte contains several flags, from which only one is of interest. Therefore, ifĀ RecordOwnerTypePresentĀ is set, then theĀ RecordOwnerTypeĀ is present, or else theĀ RecordOwnerDNĀ data is. See the presence specifications in the following code.

  bit_block :static_size(1) {
    int(little_endian)  RecordOwnerTypePresent: msb(7),lsb(7),
         external_only, encode_value(
           (field_present(RecordOwnerType)?1:0));
  };       

Since three bytes of unwanted data is present, it is specified asĀ external_onlyĀ to stop the field from getting automatically generated in the target internal. No encoding is specified (0 padding is used).

byte ignoredFields: static_size(3), external_only;

EitherĀ RecordOwnerTypeĀ orĀ RecordOwnerDNĀ is present. To encode them, it is important to note that exactly one of these fields must be present in the output data.

int(little_endian)  RecordOwnerType: static_size(1), 
  present if(RecordOwnerTypePresent == 1);
  RecordOwnerDN_EXT RecordOwnerDN:
        present if(RecordOwnerTypePresent == 0);

The rest of the record consists of optional packages with additional information. The full AMA format contains lots of other packages (and additional information in the header), however in this example, only three packages are included. Any unrecognized package leads to failure of the decoding, since the size of the set is specified (all the remaining data must be handled by the set decoding).

set: dynamic_size(remaining_size) {
    Package_100_EXT DateTimeDuration : optional;
    Package_101_EXT PartnerDirectoryNumber : optional;
    Package_102_EXT ServiceInfo : optional;
  };
};

external RecordOwnerDN_EXT 
{
     bit_block : static_size(1) {
        int LACLength : msb(7), lsb(4);
        int OwnerIDLength : msb(3), lsb(0),
        external_only, encode_value( strLength( LACAndDN ));
   };

The following is a typical construction for BCD data with a nibble length specification. Both nibble size (native_size) and field size (dynamic_size) must be specified.

bcd LACAndDN : dynamic_size((OwnerIDLength+1)>>1),

 // Alternative syntax: 
 // dynamic_size((OwnerIDLength+1)/2)

     native_size(OwnerIDLength); 
};

//Package with PackageNumber 100 (0x64): 
external Package_100_EXT: identified_by(PackageNumber == 0x64) 
{ 
   int(little_endian) PackageNumber      : static_size(1), 
       external_only, encode_value( 0x64 ); 
   int(little_endian)  Year             : static_size(1); 
   int(little_endian)  Month             : static_size(1); 
   int(little_endian)  Day               : static_size(1); 
   int(little_endian)  Hour              : static_size(1); 
   int(little_endian)  Minutes           : static_size(1); 
   int(little_endian)  Seconds           : static_size(1);
   int(little_endian)  Flags            : static_size(1);
   int(little_endian)  Duration         : static_size(3);
};
// Package with PackageNumber 101 (0x65). 
external Package_101_EXT: identified_by(PackageNumber == 0x65) 
{ 
   int(little_endian) PackageNumber: static_size(1), 
       external_only, encode_value( 0x65 ); 
   int(little_endian) NumberOfDigits:     static_size(1), 
       external_only, encode_value( strLength( Digits ));

    // Again the typical BCD decoding specification

bcd Digits: dynamic_size((NumberOfDigits+1)/2), 
    native_size(NumberOfDigits); 
};

// Package with PackageNumber 102 (0x66). 
external Package_102_EXT: identified_by(PackageNumber == 0x66) 
{ 
   int(little_endian)  PackageNumber            : static_size(1), 
       external_only, encode_value( 0x66 ); 
   int(little_endian)  ServiceIndicator         : static_size(1); 
   int(little_endian)  AdditionalInformation    : static_size(1); 
   int(little_endian)  Flags                    : static_size(1); 
};

Note!

TheĀ identified_byĀ expression, which must be specified for any format used in aĀ setĀ construct.

/ Filler needed to be able to recognize on the input stream:
external FillerRecord_0x00_EXT: 
      identified_by(RecordIdentifier == 0x00), static_size(32)
{
   int(little_endian) RecordIdentifier      : static_size(1),
      external_only, encode_value(0x00);

   // Note that no other fields are specified.
   // The UDR size (32) will be consumed and discarded (see in_map).

};

Alternative Syntax

An alternative to use the setĀ construct, in the external definition for AMARecord_ext,Ā switched_setĀ could be used. This will impact the syntax for the Package_*_EXTĀ types. Only the syntax differing from the original example is shown. The main reason for using switched_setĀ instead of setĀ is when performance must be increased.

external AMARecord_EXT:

// ...
// All preceding fields according to the original specification.
// Only the set construct is replaced with switched_set.
// ...

switched_set(PackageNumber): dynamic_size(remaining_size) {
    case( 0x64 ) {
       Package_100_EXT DateTimeDuration;
    };
    case( 0x65 ) {
      int(little_endian)  NumberOfDigits_101:   static_size(1),
         external_only, encode_value( strLength( Digits_101 ));

      // Again the typical BCD decoding specification.

      bcd Digits_101: dynamic_size((NumberOfDigits_101+1)/2),
         native_size(NumberOfDigits_101);
    };
    case( 0x66 ) {
      int(little_endian)  ServiceIndicator_102 : static_size(1);
      int(little_endian)  AdditionalInformation_102 : static_size(1);
      int(little_endian)  Flags_102 : static_size(1);
    };
  };
};


external RecordOwnerDN_EXT 
{
   bit_block    : static_size(1) {
      int LACLength         : msb(7), lsb(4);
      int OwnerIDLength     : msb(3), lsb(0),
      external_only, encode_value( strLength( LACAndDN ));
   };

The following is a typical construction for BCD data with a nibble length specification. Both nibble size (native_size) and field size (dynamic_size) must be specified.

bcd LACAndDN : dynamic_size((OwnerIDLength+1)>>1),
                  native_size(OwnerIDLength);
};

// Package with PackageNumber 100 (0x64). 
external Package_100_EXT
{
   int(little_endian)  Year             : static_size(1);
   int(little_endian)  Month            : static_size(1);
   int(little_endian)  Day              : static_size(1);
   int(little_endian)  Hour             : static_size(1);
   int(little_endian)  Minutes          : static_size(1);
   int(little_endian)  Seconds          : static_size(1);
   int(little_endian)  Flags            : static_size(1);
   int(little_endian)  Duration         : static_size(3);
};  

Note!

NoĀ  identified_by Ā or PackageNumber is present, since this is handled in the containingĀ  switched_set .

internal

NoĀ  internal Ā is used. In this case, theĀ  target_internal Ā is sufficient; all field names and field types are in order and there is only one type of record present in the input, and no additional fields are required.

in_map

The padding in the records is recognized by the decoder, however it is not actually mapped in to the system due to the use of theĀ  discard_output Ā flag.

TheĀ AMARecord_MapĀ contains sub-automatic specifications (theĀ target_internalĀ specifications within theĀ automaticĀ block), which will give five additionalĀ internalĀ formats (other than theĀ AMARecord). This is useful when you want to route them as individual records.

in_map FillerRecord_0x00_Map : 
                 external(FillerRecord_0x00_EXT),
                 target_internal(FillerRecord_0x00), 
                 discard_output { 
                               automatic; 
                 }; 
in_map AMARecord_Map : external(AMARecord_EXT), 
                       target_internal( AMARecord ) { 
  automatic { 
    RecordOwnerType_EXT : target_internal( RecordOwnerType ); 
    RecordOwnerDN_EXT : target_internal( RecordOwnerDN ); 
    Package_100_EXT : target_internal( Package_100 ); 
    Package_101_EXT : target_internal( Package_101 ); 
    Package_102_EXT : target_internal( Package_102 ); 
  }; 
};

decoder

The padding pseudo-records and data records can arrive in any order, therefore there is no need to define a constructed decoder.

decoder AMAFile   :   in_map( AMARecord_Map ),
                      in_map( FillerRecord_0x00_Map );