Next: Tables Up: Values, Types, and Constants Previous: Net Type Contents Index
Subsections

- Defining records
- Record Constants
- Accessing Fields Using ```$`''
- Record Assignment

#
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: Tables Up: Values, Types, and Constants Previous: Net Type Contents Index