2. Extended Formats

In addition to normal Athapascan-0 datatypes, which can represent C language scalars and arrays, the Athapascan-0 Formats library can also represent structures. Several complex structures can have a format defined in such a way that they are automatically handled by the Athapascan-0 Formats communication functions. There are two forms of representing the extended formats: maps or functions.

The maps are data formats which are described by a list of recursive tuples <basic-format, address-displacement, element-count>. It is enough to describe any statically allocated C language structured variable. For example, the C language type

  typedef struct {
    int a;
    float b;
    float c[3];
  } mystruct_t;
in a machine with 4-byte ints and floats would be represented by a map format with the tuples: (<int, 0, 1>; <float, 4, 1>; <float, 8, 3>). Any C variable defined with the type mystruct_t is represented by the same format, as the address displacements are relative to the beginning of the structure.

Structures dinamically allocated as lists or trees cannot have a fixed displacement associated to every element of the structure. Every element has a different address and it changes as the data structure evolutes. For that type of data, Athapascan-0 Formats implements the function formats. The user must supply the functions that pack and unpack those structures. The Athapascan-0 Formats functions call user-defined functions to transfer data with function formats.

2.1. Basic Formats

The basic formats express the scalar types of C programming language, as well as the Athapascan-0 object types. Any variable or vector having these types can be sent, received, packed, unpacked, put, got, read or written, without creating a new format. They are also used to compose map or function formats.

The example below shows how to receive one integer and send 10 port descriptors. Note that the destination of a message is the same as in Athapascan-0: a given remote port of a receiver node, with a tag. Similarly, at the other side, the receiver of a message choses its origin with a local port, a sender node and a tag.

  int i;
  a0tPort AllPorts[10];
  a0tRequest req;
  ...
  /* receives one integer */
  a0IFReceive(&port, dest, tag, &req, A0FInt, &i, 1);
  ...
  /* send 10 port numbers */
  a0FSend(&port, dest, tag, NULL, A0FPort, &AllPorts, 10);
2

As with regular Athapascan-0 functions, the functions a0FSend() and a0FReceive() and their non blocking versions (a0IFSend() and a0IFReceive()) have also a request descriptor, with the same utility. They exist to wait or test the completion of the communication request if it is non blocking and to hold the status after the communication.

When the programmer is not interested about the termination of the communication, it can pass a NULL pointer as a request. In the case of a non-blocking call, the programmer must have another means to detect the end of the operation. In the case of a blocking call, The NULL request is usual --- a request is only useful in a receive, where the programmer can get the number of effectively received bytes.

2.2. Map Formats

The map formats are structured as lists of tuples <format, displacement>, <count>, called fields. format defines the type of the data elements described by the tuple and it can be a basic format or any other user-defined map format. displacement specifies the distance in bytes of the first data element from the beginning of the structure it belongs. The element count explicits how many contiguous data elements are described. The function a0NewMapFormat() serves to compose new map formats, from a list of fields.

The fields are described with the function a0SetMapField(). This function only fills-in a data structure of type a0tMapField, which contains its format, displacement and count. The caller must pass the address of the structure and the address of the element, in order to evaluate the displacement.

The function a0NewMapFormat() needs to be called with two addresses. They are used by Athapascan-0 Formats to compute the memory extent of the structure, which is the displacement between two structures consecutively strored in memory. The extent is necessary when the user acesses contiguous data elements with the newly created format.

For exemple, suppose the following C structure:

struct bar {
  int a;
  float b[5];
} foo;
struct bar gnats[2];

To compose a map format describing it, two fields must be set. The first field has displacement &foo.a-&foo, format A0FInt and count 1. The second has displacement foo.b-&foo, format A0FFloat and count 5. The map format created has extent of sizeof(struct bar) and two fields.

The example below creates a format to represent a table of floats and another to represent only two fields of a structure. Note that some example data are declared, to supply the addresses needed to compute the relative displacement of the fields in the data structure. When the map represents a table of elements, the programmer can use NULL as the addresses passed to the field, because there is no displacement between the beginning of a table and its first element, in C language.

  struct t_client {
    int  Code;
    char Name[20];
    char Address[40];
    double Credit;
  } Client;
  a0tFormat TableFormat, CodeCreditFormat;
  a0tMapField fields[2];
  ...
  /* create a field of 100 floats */
  a0SetMapField(fields, 0, NULL, A0FFloat, NULL, 100);
  a0NewMapFormat(&TableFormat, 100 * sizeof(float), 1, fields);

  /* create a format to address parts of a structure */
  a0SetMapField(fields, 0, &Client, A0FInt, &Client.Code, 1);
  a0SetMapField(fields, 1, &Client, A0FDouble, &Client.Credit, 1);
  a0NewMapFormat(&CodeCreditFormat, sizeof(struct t_client), 2, fields);
  ...
  /* send all Table */
  a0FSend(&port, &dst, tag, &req, &TableFormat, Table, 1);
  /* send only some fields of Client */
  a0FSend(&port, &dst, tag, &req, &CodeCreditFormat, &Client, 1);

Note that not all the fields of the structure t_client belong to the format CodeCreditFormat. Nevertheless, the size of the structure passed to a0NewMapFormat() is sizeof(struct t_client). In that way, the program can access only their fields of CodeCreditFormat in a table of consecutive structures t_client. For example:

  struct t_client ClientTab[];
  ...
  a0FSend(&port, &dst, tag, NULL, &CodeCreditFormat, ClientTab, 10);

will send only the Code and the Credit fields for the ten first elements of the table ClientTab.

2.3. Function Formats

The function formats exist to represent formats too complexes to express as map formats. They have their own user-defined pack, unpack, size and offset functions. Calls to that Athapascan-0 Formats functions are transfered directly to the user defined ones. Calls to send, receive, put, get, read and write functions will use one or more of the user-defined functions. This allows the programmer to access remote memory or pipes, without rewriting any other function, as they are all based on pack, unpack, size and offset.

To define a function format, the user should define four functions, with the same prototype as a0FPack(), a0FUnpack(), a0GetFormatSize() and a0GetFormatOffset(). They should perform the same operation of their respective functions, for a given function format (see a0NewFunctionFormat()). The programmer is not forced to implement all the four functions, but only those required by the Athapascan-0 Formats functions used in the program1 .

  typedef struct {
  ...
  } list_element_t;
  typedef struct _list_t {
    list_element_t data;
    struct _list_t *next;
  } list_t;

  a0tFormat FormatElement;
  a0tFormat FormatList;

  a0tError PackList(a0tBuffer buffer, a0tFormat *format,
                     void *address, int count) {
    a0tError err;
    list_t *list = (list_t *) address;
    int size = 0;

    while (list) { size++; list = list->next; }

    err = a0FPack(buffer, A0FInt, &size, 1);
    if (err) return err;
    while (size--) {
      err = a0FPack(buffer, (void *)&list->data, &FormatElement, 1);
      if (err) return err;
      list = list->next;
    }
    return err;
  }
  ...
  list_t *MyList;
  ...
  /* create a map format to the elements of the list */
  a0NewMapFormat(&FormatElement, ... );
  /* create an function format with only the function to send it */
  a0NewFunctionFormat(&FormatList, PackList, 0, 0, 0, 0);
  ...
  /* send a list */
  a0FSend(..., &FormatList, MyList, 0);

2.4. Format Compatibility

The format used to send data does not need to be the same of the format used to receive2 them. It's up to the user to certify that the formats used in boths sides of the communication are compatible.

  struct {
    float x,y;    int   a,b;
  } St1;
  struct {
    int   i[2];   float f[2];
  } St2;
  a0tFormat Format1, Format2;
  a0tMapField fields[4];
  ...
  /* create a format to St1 */
  a0SetMapField(fields, 0, &St1, A0FFloat, &St1.x, 1);
  a0SetMapField(fields, 1, &St1, A0FFloat, &St1.y, 1);
  a0SetMapField(fields, 2, &St1, A0FInt, &St1.a, 1);
  a0SetMapField(fields, 3, &St1, A0FInt, &St1.b, 1);
  a0NewMabFormat(&Format1, &St1, (&St1)+1, 4, fields);
  /* create a format to St2 compatible to Format1 */
  a0SetMapField(fields, 0, &St2, A0FFloat, St2.f, 2);
  a0SetMapField(fields, 1, &St2, A0FInt, St2.i, 2);
  a0NewMabFormat(&Format1, &St2, (&St2)+1, 2, fields);
  ...
  /* send from St1 */
  a0FSend(..., &Request, &Format1, &St1, 1);
  ...
  /* receive into St2 */
  a0FReceive(..., &Request, &Format2, &St2, 1);

The example shows two map formats that send two floats and two ints each. One can correctly send data with one format and receive the same data with another. Their field list is identical when expanded to a list of single-element formats.

In the current version, function formats are never compatible to map formats.


1 The dependence will be described here someday.
2 send-receive, put-get, read-write.