= The switch Statement

.Syntax
[source,unlang]
----
switch <expansion> {
    case <match-1> {
        [ statements-1 ]
    }
    case <match-2> {
        [ statements-2 ]
    }
    default {
        [ statements-3 ]
    }
}
----

A `switch` statement causes the server to evaluate _<expansion>_,
which can be an xref:unlang/attr.adoc[&Attribute-Name] or
xref:unlang/condition/operands.adoc[data].  The result is compared against
_<match-1>_ and _<match-2>_, etc. in order to find a match. If no
match is found, then the server looks for the `default`
xref:unlang/case.adoc[case] statement.

The matching is done via equality comparison.  The `switch`
statement is functionally equivalent to the following use of
xref:unlang/if.adoc[if] statements:

.Nearly equivalent syntax
[source,unlang]
----
if (<expansion> == <match-1>) {
    [ statements-1 ]
}
elsif (<expansion> == <match-2>) {
    [ statements-2 ]
}
else {
    [ statements-3 ]
}
----

However, there are some differences from a series of
xref:unlang/if.adoc[if] statements.  For a `switch` statement, the
_<expansion>_ is evaluated only once.  For the equivalent
xref:unlang/if.adoc[if] statement, the _<expansion>_ is evaluated
again for every xref:unlang/if.adoc[if].  The other difference
performance.  Multiple xref:unlang/if.adoc[if] statements are slow,
and are evaluated in turn.  In contrast, xref:unlang/case.adoc[case]
statements are evaluated only once, and are put into an optimized data
structure.

If a matching xref:unlang/case.adoc[case] is found, the statements
within that xref:unlang/case.adoc[case] are evaluated. If no matching
xref:unlang/case.adoc[case] is found, the `default` section is
evaluated.  If there is no `default`, then the `switch` statement
behaves as if the `default` section was empty.

== Performance and Data Types

The `switch` keyword will automatically choose an efficient
representation for the set of xref:unlang/case.adoc[case] statements,
depending on the data type of the _<expansion>_.

For `string` and `octets` data, the xref:unlang/case.adoc[case]
statements are place into a
https://en.wikipedia.org/wiki/Red%E2%80%93black_tree[Red-black tree].

For IP address data types (`ipv4addr`, `ipv6addr`, `ipv4prefix`, and
`ipv6prefix`), the xref:unlang/case.adoc[case] statements are placed
into a https://en.wikipedia.org/wiki/Radix_tree[Patricia / Radix
tree].  The Patricia Trie allows for an IP address to match exactly,
or to match a particular network.  Multiple networks can be given,
including nested networks, so long as there are no duplicates.

Data types which are of the various "integer" types, or `ethernet`, or
`ifid` are put into a hash table.

Other data types such as `vsa` or `group` are not permitted in the
_<expansion>_ field of a `switch` statement.

These data structures mean that the _<match>_ lookups are generally
`O(lg(N))` in the number of entries.  In contrast, a `if` / `elsif`
chain is much slower, because it is linear in the number of entries.
This efficiency means that it's possible to create a `switch`
statement which has a thousands to hundreds of thousands of entries,
with minimal performance overhead.  The only cost of having 10,000
entries in a `switch` statement is that the server will use more
memory.

== Limitations

The _match_ text for the xref:unlang/case.adoc[case] statement _must_
be static data.  That is, the "thing to match"
cannot be an attribute reference, or a dynamic expansion.

Duplicate xref:unlang/case.adoc[case] statements are forbidden and
will produce an error.

No statement other than xref:unlang/case.adoc[case] can appear in a
`switch` statement, and the xref:unlang/case.adoc[case] statement
cannot appear outside of a `switch` statement.

For compatibility with version 3, and empty `case` statement can also
be used instead of `default`.  This compatibility will be removed in a
future release.

== Examples

.Switch over a User-Name
[source,unlang]
----
switch &User-Name {
    case "bob" {
        reject
    }

    default {
        ok
    }
}
----

.Switch over IP prefixes
[source,unlang]
----
switch &Framed-IP-Address {
    case 192.168/16 {
        accept
    }

    case 192.168.2.0/24 {
        reject
    }

    default {
        ok
    }
}
----

// Copyright (C) 2021 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
// This documentation was developed by Network RADIUS SAS.
