// to data chunks.
All association functions (this and the ones definedfollowing) overrides previously defined associations if invoked for the same container instance and field name.
Field names are case-sensitive. The field name that is passed must match a field name of a container in a case-sensitive way. If the field name that was passed does not match any of the field names in the container, an execution exception will be thrown.
A field is decoded from a given source, and several fragments of that source could have been used to decode that field. The source is set at the field level, and that implies that a container (that is a message or reference type) can have fields decoded from different data sources.
Chunks;
}
Observe that a source can be from any type. Nevertheless, there are three expected cases, representing the three most common sources: a binary stream, a character stream, and an XML structure. This is not enforced by the API and it is left as a contract between the decoder writer and the consumer, which is usually the UI. Therefore, a Chunk will represent the fragments of each case in a different way.
interface Chunk{}
type BinaryChunk: Chunk
{
int bitPosition where value >= 0;
int bitLength where value >= 0;
}
type CharacterChunk: Chunk
{
int charPosition where value >= 0;
int charLength where value >= 0;
}
type XMLChunk: Chunk
{
string xpath;
}
The following describes each type of Chunk:
The BinaryChunk is a pair representing a position in the binary stream and the number of bytes from that position.
The CharacterChunk is a pair representing a position in the character stream and the number of characters from that position. Observe that this type is equivalent to BinaryChunk from a structural perspective, but they are modeled separately to make the distinction explicit: magnitudes are different, and the way to measure bits is not the same as the way to measure characters.
The XMLChunk is a string representing an XPath that selects from a source with XML type a fragment of XML that was used to decode the field.
In case of a mismatch between the Chunk subtype and the type of information decoded (for example an XMLChunk is associated to binary data, decoded by BinaryDecoder) an execution exception is thrown.
For an example that uses this function consider this declared message.
message MyMessage
{
string UserName;
int Flags;
}
A user writing a custom parser to decode this message can invoke the association function in this way.
MyMessage customDecoder(stream s)
{
MyMessage m = new MyMessage();
int currentPosition = s.BitPosition;
// Decode the username and associate the corresponding data chunks
// and source.
m.UserName = decodeUserName(s);
DataChunks usernameChunk = new DataChunks();
usernameChunk.DataSource = s;
usernameChunk.Chunks = {new BinaryChunk{bytePosition = currentPosition,
byteLenght = s.BitPosition - currentPosition}}
m.AssociateField("UserName", usernameChunk);
// Decode the Flags and associate the corresponding data chunks
// and source.
int currentPosition = s.BitPosition;
m.Flags = decodeFlags(s);
DataChunks flagsChunk = new DataChunks();
flagsChunk.DataSource = s;
flagsChunk.Chunks = [new BinaryChunk{
bytePosition = currentPosition, byteLenght = s.BitPosition - currentPosition}]
m.AssociateField("Flags", flagsChunk);
return m;
}
In the preceding example we assumed that the stream is treated as a binary stream, and that each field of the message has a unique chunk of data.
Associating Collections
It is also possible to associate data to collections.
Arrays
The first one deals with arrays specificallythose that are the most common case for collections.
// Associates an array that is part of a container
// (message or reference type) to an array of data chunks.
// There is an implicit one-to-one association between each
// element of the array field and each element of the array of chunks.
void AssociateArrayField(this any containerInstance, string arrayFieldName, array arrayOfChunks);
The following are some restrictions that will be enforced at execution time.
The provided arrayFieldName should be a valid field name of containerInstance and must have type array.
The size of arrayOfChunks must be the same as the size of arrayFieldName.
The other available function is the following.
// Selectively associates some elements of an array that is part of
// a container (message or reference type) to data chunks.
// The map keys stores positions of the array,
// and the map values of the associated chunks.
void AssociateArrayField(this any containerInstance, string arrayFieldName,
map mapOfChunks);
The previous function allows associating just some positions of the array by providing a map from positions (zero-based) to data chunks. This case is useful when the decoded array is big and the data to associate to is only present for sparse elements.
Observe that in this way, for each array element, a different source and data chunks can be associated to it,for example.
message MyMessage
{
array Options;
}
MyMessage MyMessageDecoder(stream s)
{
MyMessage m = new MyMessage();
int currentPosition = s.CurrentByte;
// Decodes the array of options.
m.Options = decodeArray(s);
// Assumes each decoded element is a 4 byte integer.
array chunks = [];
for(int i = 0; i < chunks.Count; i++)
{
chunks+= new DataChunks{DataSource = s, Chunks =
[new BinaryChunk{bytePosition = currentPosition + i*4,
byteNumber = 4}]};
}
m.AssociateArrayField("Options", chunks);
return m;
}
Sets
To deal with the case of sets the following function is provided.
// Similar as the preceding example, but for sets. Each element of the set
// that the user wants to associate has to be the key of the chunk map in
// order to establish the association.
void AssociateSetField(this any containerInstance, string collectionFieldName, map mapOfChunks);
The following are the restrictions:
The provided collectionFieldName should be a valid field name of containerInstance and must have type set.
The provided map of chunks must have type map.
Not all elements in the set need to be associated. For each element e in the specified container set, an association is made if e is a key of mapOfChunks. Take the previous example and use this function to associate data chunks to the first element of the array.
MyMessage MyMessageDecoder(stream s)
{
MyMessage m = new MyMessage();
int currentPosition = s.CurrentByte;
// Decodes a set of options.
m.Options = decodeSet(s);
// Creates a data chunk for the first element.
DataChunks chunks = new DataChunks{DataSource = s, Chunks =
[new BinaryChunk{bytePosition = currentPosition, byteNumber = 4}]};
// Associates just the element MAIN_ELEMENT of the set, if that exists.
if (m[MAIN_ELEMENT])
{
m.AssociateSetField("Options", {MAIN_ELEMENT -> chunks);
}
return m;
}
Maps
Finally, a function to deal with maps is provided.
// Similar as the preceding example, but for maps. Each element of the
// decoded map is a key-value pair, so two maps are provided, one for keys
// and the other for pairs.
void AssociateMapField(this any containerInstance, string collectionFieldName, map mapOfChunksForKeys, map mapOfChunksForValues);
The following are the restrictions:
The provided collectionFieldName should be a valid field name of containerInstance and must have type map.
The provided map of chunks mapOfChunksForKeys must have type map.
The provided map of chunks mapOfChunksForValues must have type map.
The decoded keys and values that the user wants to associate should be keys of mapOfChunksForKeys and mapOfChunksForValues respectively.
Retrieving Associations
Associated chunks can be retrieved in the following way.
// Retrieve the array of data chunks associated to a value.
// Returns null if no values are associated yet.
any GetFieldAssociation(this any containerInstance, string fieldName);
One of the following is the returning type of the function:
The DataChunks if AssociateField was used for the given container instance and field name.
The array if AssociateArrayField was used for the given container instance and field name.
A pair of map (containing one map for keys and another one for values) if AssociateMapField was used for the given container instance and field name.
If no association was made for that particular container and field, null is returned.
// Associate a field that is part of a container (message or reference type)
// to data chunks.
void AssociateField(this any type containerInstance, string fieldName,
DataChunks chunks);
// Associate an array that is part of a container (message or reference type)
// to a array of data chunks. There is an implicit one-to-one
// association between each element of the array field and each element of
// the array of chunks.
void AssociateArrayField(this any containerInstance, string arrayFieldName, array arrayOfChunks);
// Selectively associates some elements of an array that is part of
// a container (message or reference type) to data chunks.
// The map keys stores positions of the array,
// and the map values of the associated chunks.
void AssociateArrayField(this any containerInstance, string arrayFieldName,
map mapOfChunks);
// Similar as the preceding example but for sets. Each element of the
// collection that the user wants to associate has to be the key of the
// chunk map in order to establish the association.
void AssociateSetField(this any containerInstance, string collectionFieldName, map mapOfChunks);
// Similar as the preceding example but for maps. Each element of the decoded
// map is a key-value pair, so two maps are provided, one for keys and
// the other for pairs.
void AssociateMapField(this any containerInstance, string collectionFieldName, map mapOfChunksForKeys, map mapOfChunksForValues);
// Retrieves the array of data chunks associated to a value. Returns null
// if no values are associated yet.
any GetFieldAssociation(this any containerInstance, string fieldName);
// Data chunks has a data source and a set of chunks.
type DataChunks
{
any DataSource;
array Chunks;
}
// A Chunk is just an interface, and we currently provide Binary,
// Character and XML chunk type.
interface Chunk{}
type BinaryChunk: Chunk
{
int bitPosition where value >= 0;
int bitLength where value >= 0;
}
type CharacterChunk: Chunk
{
int charPosition where value >= 0;
int charLength where value >= 0;
}
type XMLChunk: Chunk
{
string xpath;
}