Tables
Keywords: doc-html

Next: Sets Up: Values, Types, and Constants Previous: Records Contents Index
Subsections

- Declaring Tables
- Initializing Tables
- Table Attributes
- Accessing Tables
- Table Assignment
- Deleting Table Elements

#
Tables

`table`'s provide associative arrays: mappings from one set of values to another. The values being mapped are termed the index (or indices, if they come in groups of more than one) and the results of the mapping the yield.

Tables are quite powerful, and indexing them is very efficient, boiling down to a single hash table lookup. So you should take advantage of them whenever appropriate.

##
Declaring Tables

You declare tables using the following syntax:

> `table [` type `] of` type

where type is one or more types, separated by commas.

The indices can be of the following scalar types: numeric, temporal, enumerations, `string`, `port`, `addr`, or `net`. The yield can be of any type. So, for example:

        global a: table[count] of string;

declares `a` to be a table indexed by a `count` value and yielding a `string` value, similar to a regular array in a language like C. The yield type can also be more complex:

        global a: table[count] of table[addr, port] of conn_id;

declares `a` to be a table indexed by `count` and yielding another table, which itself is indexed by an `addr` and a `port` to yield a `conn_id` record.

This second example illustrates a multi-dimensional table, one indexed not by a single value but by a tuple of values.

##
Initializing Tables

You initialize tables by enclosing a set of initializers within braces. Each initializer looks like:

> `[` expr-list `] =` expr

where expr-list is a comma-separated list of expressions corresponding to an index of the table (so, for a table indexed by `count`, for example, this would be a single expression of type `count`) and expr is the yield value to assign to that index.

For example,

        global a: table[count] of string = {
            [11] = "eleven",
            [5] = "five",
        };

initializes the table `a` to have two elements, one indexed by `11` and yielding the string `"eleven"` and the other indexed by `5` and yielding the string `"five"`. (Note the comma after the last list element; it is optional, similar to how C allows final commas in declarations.)

You can also group together a set of indices together to initialize them to the same value:

        type HostType: enum { DeskTop, Server, Router };
        global a: table[addr] of HostType = {
            [[155.26.27.2, 155.26.27.8, 155.26.27.44]] = Server,
        };

is equivalent to:

        type HostType: enum { DeskTop, Server, Router };
        global a: table[addr] of HostType = {
            [155.26.27.2] = Server,
            [155.26.27.8] = Server,
            [155.26.27.44] = Server,
        };

This mechanism also applies to hostnames, which can be used in table initializations for any indices of type `addr`. For example, if `www.my-server.com` corresponded to the addresses 155.26.27.2 and 155.26.27.44, then the above could be written:

        global a: table[addr] of HostType = {
            [[www.my-server.com, 155.26.27.8]] = Server,
        };

and if it corresponded to all there, then:

        global a: table[addr] of HostType = {
            [www.my-server.com] = Server,
        };

You can also use multiple index groupings across different indices:

        global access_allowed: table[addr, port] of bool = {
            [www.my-server.com, [21/tcp, 80/tcp]] = T,
        };

is equivalent to:

        global access_allowed: table[addr, port] of bool = {
            [155.26.27.2, 21/tcp] = T,
            [155.26.27.2, 80/tcp] = T,
            [155.26.27.8, 21/tcp] = T,
            [155.26.27.8, 80/tcp] = T,
            [155.26.27.44, 21/tcp] = T,
            [155.26.27.44, 80/tcp] = T,
        };

Fix me: add example of cross-product initialization of sets

##
Table Attributes

When declaring a table, you can specify a number of attributes that affect its operation:

  [`&default`] Specifies a value to yield when an index does not appear in the table. Syntax:

  > `&default = expr`

  expr can have one of two forms. If it's type is the same as the table's yield type, then expr is evaluated and returned. If it's type is a `function` with arguments whose types correspond left-to-right with the index types of the table, and which returns a type the same as the yield type, then that function is called with the indices that yielded the missing value to compute the default value.

  For example:

          global a: table[count] of string &default = "nothing special";

  will return the string `"nothing special"` anytime `a` is indexed with a `count` value that does not appear in `a`.

  A more dynamic example:

          function nothing_special(): string
              {
              if ( panic_mode )
                  return "look out!";
              else
                  return "nothing special";
              }

          global a: table[count] of string &default = nothing_special;

  An example of using a function that computes using the index:

          function make_pretty(c: count): string
              {
              return fmt("**%d**", c);
              }

          global a: table[count] of string &default = make_pretty;

  [`&create_expire`] Specifies that elements in the table should be automatically deleted after a given amount of time has elapsed since they were first entered into the table. Syntax:

  > `&create_expire = expr`

  where expr is of type `interval`.

  [`&read_expire`] The same as `&create_expire` except the element is deleted when the given amount of time has lapsed since the last time the element was accessed from the table.

  [`&write_expire`] The same as `&create_expire` except the element is deleted when the given amount of time has lapsed since the last time the element was entered or modified in the table.

  [`&expire_func`] Specifies a function to call when an element is due for expression because of `&create_expire`, `&read_expire`, or `&write_expire`. Syntax:

  > `&expire_func = expr`

  expr must be a function that takes an argument with the same type as the table's yield type and returns an `interval` value. The `interval` indicates for how much longer the element should remain in the table; returning `0 secs` or a negative value instructs Bro to go ahead and delete the element.

  Deficiency: There should be a version of `&expire_func` that is passed the index of the element as well as its yield value.

You specify multiple attributes by listing one after the other, without commas between them:

        global a: table[count] of string &default="foo" &write_expire=5sec;

Note that you can specify each type of attribute only once. You can, however, specify more than one of `&create_expire`, `&read_expire`, and `&write_expire`. In that case, whenever any of the corresponding timers expires, the element will be deleted.

##
Accessing Tables

As usual, you access the values in tables by indexing them with a value (for a single index) or list of values (multiple indices) enclosed in `[]`'s. Deficiency: Presently, when indexing a multi-dimensional table you must provide all of the relevant indices; you can't leave one out in order to extract a sub-table.

You can also index arrays using `record`'s, providing the record is comprised of values whose types match that of the table's indices. (Any record fields whose types are themselves records are recursively unpacked to effect this matching.) For example, if we have:

        local b: table[addr, port] of conn_id;
        local c = 131.243.1.10;
        local d = 80/tcp;

then we could index `b` using `b[c, d]`, but if we had:

        local e = [$field1 = c, $field2 = d];

we could also index it using `a[d]`

You can test whether a table holds a given index using the `in` operator. For example:

        [131.243.1.10, 80/tcp] in b

or

        e in b

per the examples above. In addition, if the table has only a single index (not multi-dimensional), then you can omit the `[]`'s:

        local active_connections: table[addr] of conn_id;
        ...
        if ( 131.243.1.10 in active_connections )
            ...

##
Table Assignment

An indexed can be the target of an assignment:

        b[131.243.1.10, 80/tcp] = c$id;

You can also assign to an entire table. For example, suppose we have the global:

        global active_conn_count: table[addr, port] of count;

then we could later clear the contents of the table using:

        local empty_table: table[addr, port] of count;
        active_conn_count = empty_table;

Here the first statement declares a local variable `empty_table` with the same type as `active_conn_count`. Since we don't initialize the table, it starts out empty. Assigning it to `active_conn_count` then replaces the value of `active_conn_count` with an empty table. Note: As with `record`'s, assigning `table` values results in a shallow copy.

In addition to directly accessing an element of a table by specifying its index, you can also loop over all of the indices in a table using the `for` statement.

##
Deleting Table Elements

You can remove an individual element from a table using the `delete` statement:

        delete active_host[c$id];

will remove the element in `active_host` corresponding to the connection identifier `c$id` (which is a `conn_id` record). If the element isn't present, nothing happens.

Next: Sets Up: Values, Types, and Constants Previous: Records Contents Index