next up previous contents index
Next: Tables Up: Values, Types, and Constants Previous: Net Type   Contents   Index

Subsections


Records

A record is a collection of values. Each value has a name, referred to as one of the record's fields, and a type. The values do not need to have the same type, and there is no restriction on the allowed types (i.e., each field can be any type).


Defining records

A definition of a record type has the following syntax:
record { field$^+$ }
(that is, the keyword record followed by one-or-more field's enclosed in braces), where a field has the syntax:
identifier : type field-attributes$^*$ ; identifier : type field-attributes$^*$ ,
Each field has a name given by the identifier (which can be the same as the identifier of an existing variable or a field in another record). Field names must follow the same syntax as that for Bro variable names (see § ), namely they must begin with a letter or an underscore (``_'') followed by zero or more letters, underscores, or digits. Bro reserved words such as if or event cannot be used for field names. Field names are case-sensitive.

Each field holds a value of the given type. We discuss the optional field-attributes below. Finally, you can use either a semicolon or a comma to terminate the definition of a record field.

For example, the following record type:

    type conn_id: record {
        orig_h: addr;  # Address of originating host.
        orig_p: port;  # Port used by originator.
        resp_h: addr;  # Address of responding host.
        resp_p: port;  # Port used by responder.
    };
is used throughout Bro scripts to denote a connection identifier by specifying the connections originating and responding addresses and ports. It has four fields: orig_h and resp_h of type addr, and orig_p of resp_p of type port.


Record Constants

You can initialize values of type record using either assignment from another, already existing record value; or element-by-element; or using a record constructor.

In a Bro function or event handler, we could declare a local variable the conn_id type given above:

    local id: conn_id;
and then explicitly assign each of its fields:
    id$orig_h = 207.46.138.11;
    id$orig_p = 31337/tcp;
    id$resp_h = 207.110.0.15;
    id$resp_p = 22/tcp;
Deficiency: One danger with this initialization method is that if you forget to initialize a field, and then later access it, you will crash Bro.

Or we could use:

    id = [$orig_h = 207.46.138.11, $orig_p = 31337/tcp,
          $resp_h = 207.110.0.15, $resp_p = 22/tcp];

This second form is no different from assigning a record value computed in some other fashion, such as the value of another variable, a table element, or the value returned by a function call. Such assignments must specify all of the fields in the target (i.e., in id in this example), unless the missing field has the or attribute.


Accessing Fields Using ``$''

You access and assign record fields using the ``$'' (dollar-sign) operator. As indicated in the example above, for the record id we can access its orig_h field using:

    id$orig_h
which will yield the addr value 207.46.138.11.


Record Assignment

You can assign one record value to another using simple assignment:
    local a: conn_id;
    ...
    local b: conn_id;
    ...
    b = a;
Doing so produces a shallow copy. That is, after the assignment, b refers to the same record as does a, and an assignment to one of b's fields will alter the field in a's value (and vice versa for an assignment to one of a's fields). However, assigning again to b itself, or assigning to a itself, will break the connection.

Deficiency: Bro lacks a mechanism for specifying a deep copy, in which no linkage is connected between b and a. Consequently, you must be careful when assigning records to ensure you account for the shallow-copy semantics.

You can also assign to a record another record that has fields with the same names and types, even if they come in a different order. For example, if you have:

    local b: conn_id;
    local c: record {
        resp_h: addr, orig_h: addr;
        resp_p: port, orig_p: port;
    };
then you can assign either b to c or vice versa.

You could not, however, make the assignment (in either direction) if you had:

    local b: conn_id;
    local c: record {
        resp_h: addr, orig_h: addr;
        resp_p: port, orig_p: port;
        num_alerts: count;
    };
because the field num_alerts would either be missing or excess.

However, when declaring a record you can associate attributes with the fields. The relevant ones are &optional, which indicates that when assigning to the record you can omit the field, and &default = expr, which indicates that if the field is missing, then a reference to it returns the value of the expression expr. So if instead you had:

    local b: conn_id;
    local c: record {
        resp_h: addr, orig_h: addr;
        resp_p: port, orig_p: port;
        num_alerts: count &optional;
    };
then you could execute c = b even though num_alerts is missing from b. You still could not execute b = c, though, since in that direction, num_alerts is an extra field (regardless of whether it has been assigned to or not--the error is a type-checking error, not a run-time error).

The same holds for:

    local b: conn_id;
    local c: record {
        resp_h: addr, orig_h: addr;
        resp_p: port, orig_p: port;
        num_alerts: count &default = 0;
    };
I.e., you could execute c = b but not b = c. The only difference between this example and the previous one is that for the previous one, access to c$num_alerts without having first assigned to it results in a run-time error, while in the second, it yields 0.

You can test for whether a record field exists using the ?$ operator.

Finally, all of the rules for assigning records also apply when passing a record value as an argument in a function call or an event handler invocation.


next up previous contents index
Next: Tables Up: Values, Types, and Constants Previous: Net Type   Contents   Index
Vern Paxson 2002-11-17