Casts
Values can be cast from one data type to another. However, this
cast only changes the type of the data, it does not change the data’s
value. That is, a cast allows you to convert an octets
data type
to a string
, but the resulting string
may still contain
non-printable characters.
Casting is used when the server is unable to automatically figure out
the correct data type to use. For example, in the following
expansion, it is not immediately obvious that the right side of the
expression is an IP address. It could instead be interpreted as a string
"192.0.2.1"
.
if (%sql("SELECT ipaddress FROM table WHERE user=%{User-Name}") == 192.0.2.1) }
....
}
Since there is no attribute reference on either side of the ==
operator, the interpreter has no way of knowing that the string
192.0.2.1
is an IP address. In most cases, the interpreter will
guess correctly. However, there is no way of always parsing
strings in order to automatically determine which data type to use.
Any such automatic parsing works most of the time, but it does have
error cases where the parsing may be incorrect.
The solution to resolving these ambiguities is by allowing the values
to be cast
to a particular type. Casting a value to a type tells
the interpreter how that value should be parsed. Casting is done by
prefixing a value with the type name, surrounded by brackets; (…)
.
(...)value
We can add a cast to the above example, as follows:
if (%sql("SELECT ipaddress FROM table WHERE user=%{User-Name}") == (ipaddr)192.0.2.1) }
....
}
In this example, we prefix the IP address with the string (ipaddr)
.
The interpreter then knows that the value 192.0.2.1
should be
interpreted as the data type ipaddr
, and not as the literal string
"192.0.2.1"
.
In most cases, the server can automatically determine what data type to use. The cast syntax is used when either the data type is ambiguous, or when data should be normalized prior to comparison, or when a specific data type is required.
For a full list of data types which can be used in a cast, please see the list of data types page, and the "Basic Type Types" section.
Casting Behavior
In general, casting data types of different sizes will fail. For
example, casting an octet
string of length 3
to uint32
is
impossible, because uint32
data types are 4 bytes in length.
However, in many cases casting from one data type will "just work".
When the cast fails, the result is a NULL
or empty value.
IP Addresses
Casting ipv4addr
to ipv6addr
will return an IPv6 address which
contains the IPv4 address, with the upper bits as ::ffff
. This
result is then a valid "IPv4-mapped IPv6 address".
Casting ipv6addr
to ipv4addr
will work if the IPv6 address is a
valid "IPv4-mapped IPv6 address".
Similar rules apply to casting between IPv4 and IPv6 prefixes.
Casting an IPv4 address to an IPv4 prefix is allowed, and will return
a prefix of using a /32
. Casting the other way is also allowed, but
only if the prefix is /32
.
Numbers
Numbers can generally be cast from one data type to another, if the input value can be represented in the new data type.
For example, casting uint8
to uint16
will always succeed.
However, casting a signed int8
to uint16
may fail, as negative
values cannot be represented in the uint16
data type.
Octets
Casting any data type to octets
means returning the "raw" value of
the underlying data type. For example, casting an IPv4 address of
value 127.0.0.1
to octets
will return the octets
string
0x7f000001
.
Casting an octets
value to any other data type means interpreting
the raw value as that data type, generally as if the data had been
received from the network. For example, casting the octets
string
0x7f000001
to ipvaddr
will return the IPv4 address 127.0.0.1
.
Strings
Casting any data type to string
means printing the data type to a
string, and assigning the resulting value to the string
. For a
value of type octets
, this means that the output is a hex string,
prefixed with 0x
. In order to get the "raw" hex values of an
octets
data type, the %hex(…)
expansion is used. It prints out
the hex value of its input, without the leading 0x
characters.
Casting a string
value to another data type means parsing the
string as that data type.
See double-quoted strings for examples of how double-quoted strings are used.
Lists
When a cast is applied to a list, the cast is applied individually to each entry in the list. This behavior is most noticeable when calling a function, or looping over a set of attributes.
The following example will first create two Reply-Message
attributes
in the request. It will then copy all of those attributes to the
reply.
&control.Reply-Message := { "one", "two" }
&reply.Reply-Message := &control.Reply-Message[*]
The following example will take an input string "192.168.0.1"
, split
it on the '.'
character, and then assign it to the Tmp-Integer-0
attribute. The result will be four copies of the Tmp-Integer-0
attribute, which each carry one octet of the IP address.
&Tmp-Integer-0 := %explode("192.168.0.1", '.')
If you need to cast an entire list to a value, then the value being cast should be surrounded by brackets.
In the following example, the %explode()
function will return a list
of four values: {"192", "168", "0", "1"}
. Casting that to a string
causes the values to be merged together, The resulting string is
"19216801"
.
If you need to add text in between each list entry, see the
%concat()
function in the built-in
expansions list.
&reply.Reply-Message := (string) (%explode("192.168.0.1", '.'))
Expressions
Unlang expressions can use casts, too, as in the following example:
(string)(5 + 6)
The output of this cast will be the string value "11"
.
Casting expressions can also be used to "force" specific data types. For example, if we want to get the network byte-order value of a 16-bit integer, we can do the following:
(octets)((uint16) 258)
The output of this cast will be an octet
string having value
0x0103
. This kind of casting can be used to create and pack "ad
hoc" data structures for sending in a packet:
(octets)((uint16) 258) + (octets)((uint16) 4) + (octets)((ipv4addr) 127.0.0.1)
will result in the octet
string value 0x010300047f000001
.