Radix (Radix v0.5.0) View Source
A bitwise radix tree for prefix based matching on bitstring keys of any length.
Radix
provides a radix tree whose
radius is 2, has path-compression and no one-way branching. Entries consist of
{key, value}-pairs whose insertion/deletion is always based on an exact
key-match. Retrieval can be either exact or based on a longest prefix match.
Example
iex> t = new()
...> |> put(<<1, 1, 1>>, "1.1.1.0/24")
...> |> put(<<1, 1, 1, 0::6>>, "1.1.1.0/30")
...> |> put(<<1, 1, 1, 1::6>>, "1.1.1.4/30")
...> |> put(<<1, 1, 1, 1::1>>, "1.1.1.128/25")
...> |> put(<<255>>, "255/8")
iex>
iex> # longest prefix match
iex> lookup(t, <<1, 1, 1, 255>>)
{<<1, 1, 1, 1::1>>, "1.1.1.128/25"}
iex>
iex> # more specific matches (includes search key if present)
iex> more(t, <<1, 1, 1>>)
[
{<<1, 1, 1, 1::size(6)>>, "1.1.1.4/30"},
{<<1, 1, 1, 0::size(6)>>, "1.1.1.0/30"},
{<<1, 1, 1>>, "1.1.1.0/24"},
{<<1, 1, 1, 1::size(1)>>, "1.1.1.128/25"}
]
iex> # less specific matches (includes search key if present)
iex> less(t, <<1, 1, 1, 3>>)
[
{<<1, 1, 1, 0::size(6)>>, "1.1.1.0/30"},
{<<1, 1, 1>>, "1.1.1.0/24"}
]
iex> # exact match
iex> get(t, <<1, 1, 1, 0::6>>)
{<<1, 1, 1, 0::6>>, "1.1.1.0/30"}
iex> get(t, <<2, 2, 2, 2>>)
nil
iex> pruner = fn
...> {_k0, _k1, _v1, _k2, _v2} -> {:ok, "new parent"}
...> {_k0, _v0, _k1, _v1, _k2, _v2} -> {:ok, "updated parent"}
...> end
iex> keys(t)
[
<<1, 1, 1, 0::size(6)>>,
<<1, 1, 1>>,
<<1, 1, 1, 1::size(6)>>,
<<1, 1, 1, 1::size(1)>>,
<<255>>
]
iex> prune(t, pruner)
...> |> keys()
[
<<1, 1, 1, 0::size(5)>>,
<<1, 1, 1>>,
<<1, 1, 1, 1::size(1)>>,
<<255>>
]
iex> prune(t, pruner)
...> |> dot()
...> |> (&File.write("assets/readme.dot", &1)).()
The radix tree above, after pruning, looks something like this:
The tree is represented by two types of nodes:
- internal node, as a
{bit, left, right}
-tuple, and - leaf node, which is either
nil
or a non-empty list of{key,value}
-pairs
The bit
denotes the bit position to check in a key during a tree traversal,
where 0
means go left
and 1
means go right
. A bit
beyond a key
's
length is considered to be 0
. Path-compression means not all bits are
checked during tree traversal, only those that differentiate the keys stored
below the current internal
node. Hence, branches are formed as keys with
different patterns are stored in the tree.
The leaf
node can have a list of {key, value}
-pairs where all longer keys
have all shorter keys as their prefix. In other words, they all agree on the
bits that were checked to arrive at that node. The key
is stored alongside
the value
since, due to path-compression, a final match is needed to ensure
a correct match. Hence, retrieval functions return the {key, value}
-pair,
rather than just the value
, since the stored key is not always equal to the
given search key (e.g. when doing a longest prefix match).
Since binaries are bitstrings too, they work as well:
iex> t = new([{"A.new", "new"}, {"A.newer", "newer"}, {"B.newest", "newest"}])
iex> more(t, "A.") |> Enum.reverse()
[{"A.new", "new"}, {"A.newer", "newer"}]
#
iex> lookup(t, "A.newest")
{"A.new", "new"}
#
iex> more(t, "C.")
[]
Link to this section Summary
Types
A user supplied accumulator.
A bitstring used as a key to index into the radix tree.
A radix leaf node.
An internal radix tree node.
Any value to be stored in the radix tree.
Functions
Returns a map where two neighboring key,value-pairs present in tree
, are stored under their
'parent' key.
Counts the number of entries by traversing given tree
.
Deletes the entry from the tree
for a specific key
using an exact match.
Returns a list of lines describing the tree
as a graphviz digraph.
Drops the given keys
from the radix tree
using an exact match.
Returns true if tree
is empty, false otherwise.
Fetches the key,value-pair for a key
in the given tree
.
Fetches the key,value-pair for a specific key
in the given tree
.
Returns the key,value-pair whose key equals the given search key
, or
default
.
Updates a key,value-pair in tree
by invoking fun
with the result of an exact match.
Returns a list of all keys from the radix tree
.
Returns all key,value-pairs whose key is a prefix for the given search key
.
Returns the key,value-pair whose key is the longest prefix of key
, or nil.
Merges two radix trees into one.
Merges two radix trees into one, resolving conflicts through fun
.
Minimizes a radix tree by removing more specific keys and pruning adjacent keys, where allowed.
Returns all key,value-pairs where the given search key
is a prefix for a stored key.
Returns a new, empty radix tree.
Return a new radix tree, initialized using given list of {key
, value
}-pairs.
Removes the value associated with key
and returns the matched
key,value-pair and the new tree.
Prunes given tree
by invoking fun
on adjacent keys.
Stores the key,value-pairs from elements
in the radix tree
.
Stores the key,value-pair under key
in the radix tree
.
Invokes fun
for each key,value-pair in the radix tree
with the accumulator.
Extracts the key,value-pairs associated with keys
from tree
into a new
radix tree.
Returns a new tree with all the key,value-pairs whose key are in keys
.
Returns all key,value-pairs in tree
as a flat list.
Updates a key,value-pair in tree
by invoking fun
after a longest prefix
match lookup.
Looks up the longest prefix match for given search key
in tree
and
updates its value through fun
.
Returns all values stored in the radix tree
.
Invokes fun
on all (internal and leaf) nodes of the radix tree
using either
:inorder
, :preorder
or :postorder
traversal.
Link to this section Types
Specs
acc() :: any()
A user supplied accumulator.
Specs
key() :: bitstring()
A bitstring used as a key to index into the radix tree.
During tree traversals, bit positions in the key are checked in order
to decide whether to go left (0) or right (1). During these checks, bits
beyond the current key's length always evaluate to 0. Bit positions are
zero-indexed, with bit 0
being the most significant bit.
Specs
A radix leaf node.
A leaf is either nil or a list of key,value-pairs sorted on key-length in descending order. All keys in a leaf have the other, shorter keys, as their prefix.
Specs
tree() :: {non_neg_integer(), tree() | leaf(), tree() | leaf()}
An internal radix tree node.
An internal node is a three element tuple: {bit
, left
, right
}, where:
bit
is the bit position to check in a keyleft
is a subtree with keys whosebit
is 0right
is a subtree with keys whosebit
is 1
The keys stored below any given internal
node, all agree on the bits
checked to arrive at that particular node.
Branches in the tree are only created when storing a new key,value-pair in the tree whose key does not agree with the leaf found during traversal.
This path-compression means not all bits in a key are checked while
traversing the tree, only those which differentiate the keys stored below the
current internal
node. Hence, a final match is needed to ensure a correct
match.
Specs
value() :: any()
Any value to be stored in the radix tree.
Link to this section Functions
Specs
Returns a map where two neighboring key,value-pairs present in tree
, are stored under their
'parent' key.
The parent key is 1 bit shorter than that of the two neighboring keys and stores:
{key1, val1, key2, val2}
, or{key1, val1, key2, val2, val3}
If the parent key exists in the tree
as well, its value is included as the
fifth-element in the resulting tuple.
Example
iex> tree = new()
...> |> put(<<1, 1, 1, 0::6>>, "1.1.1.0/30")
...> |> put(<<1, 1, 1, 1::6>>, "1.1.1.4/30")
...> |> put(<<1, 1, 1, 2::6>>, "1.1.1.8/30")
...> |> put(<<1, 1, 1, 3::6>>, "1.1.1.12/30")
...> |> put(<<1, 1, 1, 1::5>>, "1.1.1.8/29")
iex> adjacencies(tree)
%{
<<1, 1, 1, 0::5>> => {
<<1, 1, 1, 0::6>>, "1.1.1.0/30",
<<1, 1, 1, 1::6>>, "1.1.1.4/30"
},
<<1, 1, 1, 1::5>> => {
<<1, 1, 1, 2::6>>, "1.1.1.8/30",
<<1, 1, 1, 3::6>>, "1.1.1.12/30",
"1.1.1.8/29"}
}
Specs
count(tree()) :: non_neg_integer()
Counts the number of entries by traversing given tree
.
Example
iex> new([{<<>>, nil}, {<<1>>, 1}, {<<2>>, 2}])
...> |> count
3
Specs
Deletes the entry from the tree
for a specific key
using an exact match.
If key
does not exist, the tree
is returned unchanged.
Example
iex> elms = [{<<1,1>>, 16}, {<<1,1,0>>, 24}, {<<1,1,1,1>>, 32}]
iex> t = new(elms)
iex> t
{0, {23, [{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}],
[{<<1, 1, 1, 1>>, 32}]
},
nil}
iex> delete(t, <<1, 1, 0>>)
{0, {23, [{<<1, 1>>, 16}],
[{<<1, 1, 1, 1>>, 32}]
},
nil}
Specs
Returns a list of lines describing the tree
as a graphviz digraph.
Options include:
:label
, defaults to "radix"):labelloc
, defaults to "t":rankdir
, defaults to "TB":ranksep
, defaults to "0.5 equally":rootcolor
, defaults to "orange":nodecolor
, defaults to "yellow":leafcolor
, defaults to "green":kv_tostr
, defaults to an internal function that converts key to dotted decimal string (cidr style)
If supplied via :kv_tostr
, the function's signature must be ({key/0
, value/0
}) :: String.t/0
and where the resulting string must be HTML-escaped. See html-entities.
Works best for smaller trees.
Example
iex> t = new()
...> |> put(<<0, 0>>, "left")
...> |> put(<<1, 1, 1::1>>, "left")
...> |> put(<<128, 0>>, "right")
iex> g = dot(t, label: "example")
iex> File.write("assets/example.dot", g)
:ok
which, after converting with dot, yields the following image:
Specs
Drops the given keys
from the radix tree
using an exact match.
Any key
's that don't exist in the tree
, are ignored.
Example
iex> elms = [{<<1, 1>>, 16}, {<<1, 1, 0>>, 24}, {<<1, 1, 1, 1>>, 32}]
iex> t = new(elms)
iex> t
{0, {23, [{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}],
[{<<1, 1, 1, 1>>, 32}]
},
nil}
iex> drop(t, [<<1, 1>>, <<1, 1, 1, 1>>])
{0, [{<<1, 1, 0>>, 24}], nil}
Specs
Returns true if tree
is empty, false otherwise.
Example
iex> new() |> empty?()
true
Specs
Fetches the key,value-pair for a key
in the given tree
.
Returns {:ok, {key, value}}
or :error
when key
is not in the tree
. By
default an exact match is used, specify match: :lpm
to fetch based on a
longest prefix match.
Example
iex> t = new([{<<>>, 0}, {<<1>>, 1}, {<<1, 1>>, 2}])
iex> fetch(t, <<1, 1>>)
{:ok, {<<1, 1>>, 2}}
iex> fetch(t, <<2>>)
:error
iex> fetch(t, <<2>>, match: :lpm)
{:ok, {<<>>, 0}}
Specs
Fetches the key,value-pair for a specific key
in the given tree
.
Returns the {key, value}
-pair itself, or raises a KeyError
if key
is
not in the tree
. By default an exact match is used, specify match: :lpm
to fetch based on a longest prefix match.
Example
iex> t = new([{<<1>>, 1}, {<<1, 1>>, 2}])
iex> fetch!(t, <<1, 1>>)
{<<1, 1>>, 2}
iex> fetch!(t, <<2>>)
** (KeyError) key not found <<0b10>>
iex> fetch!(t, <<1, 1, 1>>, match: :lpm)
{<<1, 1>>, 2}
Specs
Returns the key,value-pair whose key equals the given search key
, or
default
.
If key
is not a bitstring or not present in the radix tree, default
is
returned. If default
is not provided, nil
is used.
Example
iex> elements = [{<<1, 1>>, 16}, {<<1, 1, 1>>, 24}, {<<1, 1, 1, 1>>, 32}]
iex> t = new(elements)
iex> get(t, <<1, 1, 1>>)
{<<1, 1, 1>>, 24}
iex> get(t, <<1, 1>>)
{<<1, 1>>, 16}
iex> get(t, <<1, 1, 0::1>>)
nil
iex> get(t, <<1, 1, 0::1>>, :notfound)
:notfound
Specs
get_and_update( tree(), key(), (nil | {key(), value()} -> {value(), value()} | :pop) ) :: {value(), tree()}
Updates a key,value-pair in tree
by invoking fun
with the result of an exact match.
The callback fun
is called with:
{key, original_value}
if an exact match was found, ornil
, in case the key is not present intree
The callback function should return:
{current_value, new_value}
, or:pop
.
When {current_value, new_value}
is returned, the new_value
is stored
under key
and {current_value, tree}
is returned. When the callback
passes back :pop
, the {key, original_value}
-pair is deleted from the
tree
and {original_value, tree}
is returned.
If the callback passes back :pop
when its argument was nil
then {nil, tree}
is returned, where tree
is unchanged.
If something similar is required, but based on a longest prefix match, perhaps
Radix.update/3
or Radix.update/4
is better suited.
Examples
# update stats, get org value and store new value
iex> count = fn nil -> {0, 1}; {_key, val} -> {val, val+1} end
iex> t = new([{<<1,1,1>>, 1}, {<<2, 2, 2>>, 2}])
iex> {org, t} = get_and_update(t, <<1, 1, 1>>, count)
iex> org
1
iex> get(t, <<1, 1, 1>>)
{<<1, 1, 1>>, 2}
iex> {org, t} = get_and_update(t, <<3, 3>>, count)
iex> org
0
iex> get(t, <<3, 3>>)
{<<3, 3>>, 1}
# modify `count` callback so we get the new value back + updated tree
iex> count = fn nil -> {1, 1}; {_key, val} -> {val+1, val+1} end
iex> t = new([{<<1,1,1>>, 1}, {<<2, 2, 2>>, 2}])
iex> {new, t} = get_and_update(t, <<1, 1, 1>>, count)
iex> new
2
iex> get(t, <<1, 1, 1>>)
{<<1, 1, 1>>, 2}
iex> {new, t} = get_and_update(t, <<3, 3>>, count)
iex> new
1
iex> get(t, <<3, 3>>)
{<<3, 3>>, 1}
# returning :pop deletes the key
iex> once = fn nil -> {0, 1}; {_k, _v} -> :pop end
iex> t = new([{<<1, 1>>, 1}])
iex> {val, t} = get_and_update(t, <<2, 2>>, once)
iex> val
0
iex> get(t, <<2, 2>>)
{<<2, 2>>, 1}
iex> {val, t} = get_and_update(t, <<1, 1>>, once)
iex> val
1
iex> get(t, <<1, 1>>)
nil
Specs
Returns a list of all keys from the radix tree
.
Example
iex> t = new([
...> {<<1, 1, 1, 0::1>>, "1.1.1.0/25"},
...> {<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
...> {<<1, 1, 1>>, "1.1.1.0/24"},
...> {<<3>>, "3.0.0.0/8"},
...> ])
iex> keys(t)
[<<1, 1, 1, 0::1>>, <<1, 1, 1>>, <<1, 1, 1, 1::1>>, <<3>>]
Specs
Returns all key,value-pairs whose key is a prefix for the given search key
.
Collects key,value-pairs where the stored key is the same or less specific.
Optionally exclude the search key from the results by providing option
:exclude
as true.
Example
# include search for less specifics
iex> elements = [
...> {<<1, 1>>, 16},
...> {<<1, 1, 0>>, 24},
...> {<<1, 1, 0, 0>>, 32},
...> {<<1, 1, 1, 1>>, 32}
...> ]
iex> t = new(elements)
iex> less(t, <<1, 1, 1, 1>>)
[{<<1, 1, 1, 1>>, 32}, {<<1, 1>>, 16}]
iex> less(t, <<1, 1, 0>>)
[{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}]
iex> less(t, <<2, 2>>)
[]
#
# exclusive search for less specifics
#
iex> less(t, <<1, 1, 0, 0>>, exclude: true)
[{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}]
#
# search key itself does not have to exist in the tree
iex> less(t, <<1, 1, 0, 25>>)
[{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}]
Specs
Returns the key,value-pair whose key is the longest prefix of key
, or nil.
Returns {key, value}
or nil
if there was no match.
Example
iex> elms = [{<<1, 1>>, 16}, {<<1, 1, 0>>, 24}, {<<1, 1, 0, 0::1>>, 25}]
iex> t = new(elms)
iex> lookup(t, <<1, 1, 0, 127>>)
{<<1, 1, 0, 0::1>>, 25}
iex> lookup(t, <<1, 1, 0, 128>>)
{<<1, 1, 0>>, 24}
iex> lookup(t, <<1, 1, 1, 1>>)
{<<1, 1>>, 16}
iex> lookup(t, <<2, 2, 2, 2>>)
nil
Specs
Merges two radix trees into one.
Adds all key,value-pairs of tree2
to tree1
, overwriting any existing entries.
Example
iex> tree1 = new([{<<0>>, 0}, {<<1>>, 1}])
iex> tree2 = new([{<<0>>, nil}, {<<2>>, 2}])
iex> merge(tree1, tree2)
...> |> to_list()
[{<<0>>, nil}, {<<1>>, 1}, {<<2>>, 2}]
Specs
Merges two radix trees into one, resolving conflicts through fun
.
Adds all key,value-pairs of tree2
to tree1
, resolving conflicts through
given fun
. Its arguments are the conflicting key/0
and the value/0
found in tree1
and the value/0
found in tree2
.
Example
# keep values of tree1, like merge(tree2, tree1)
iex> tree1 = new([{<<0>>, 0}, {<<1>>, 1}])
iex> tree2 = new([{<<0>>, nil}, {<<2>>, 2}])
iex> merge(tree1, tree2, fn _k, v1, _v2 -> v1 end)
...> |> to_list()
[{<<0>>, 0}, {<<1>>, 1}, {<<2>>, 2}]
Specs
Minimizes a radix tree by removing more specific keys and pruning adjacent keys, where allowed.
The radix tree is traversed and a new tree is built. Whenever two keys can
be combined fun/2
is called with the values stored under the keys to be
combined.. If that returns {:ok, value}
that value will be stored on
the least specific key. That means more specific keys are effectively
deleted from the tree and any remaining neighbouring keys are combined to
their parent key and the more specific neighbours are deleted.
Examples
Combine neighbouring keys when they store the same value.
iex> f = fn v1, v2 -> if v1 == v2, do: {:ok, v1} end
iex> [{<<1, 0>>, 0}, {<<1, 1>>, 0}]
...> |> new()
...> |> minimize(f)
...> |> to_list()
[{<<1, 0::size(7)>>, 0}]
Remove more specific keys if they have the same value as some less specific key.
iex> f = fn v1, v2 -> if v1 == v2, do: {:ok, v1} end
iex> [{<<1>>, 0}, {<<1, 0>>, 0}, {<<1, 0, 1>>, 0}]
...> |> new()
...> |> minimize(f)
...> |> to_list()
[{<<1>>, 0}]
Summarize statistics keeping the least specific key.
iex> f = fn v1, v2 -> {:ok, v1 + v2} end
iex> [{<<1>>, 12}, {<<1, 0::size(7)>>, 10}, {<<1, 0>>, 10}, {<<1, 1>>, 10}]
...> |> new()
...> |> minimize(f)
...> |> to_list()
[{<<1>>, 42}]
Specs
Returns all key,value-pairs where the given search key
is a prefix for a stored key.
Collects key,value-pairs where the stored key is the same or more specific.
Example
iex> elements = [
...> {<<1, 1>>, 16},
...> {<<1, 1, 0>>, 24},
...> {<<1, 1, 0, 0>>, 32},
...> {<<1, 1, 1, 1>>, 32}
...> ]
iex> t = new(elements)
iex> more(t, <<1, 1, 0>>)
[{<<1, 1, 0, 0>>, 32}, {<<1, 1, 0>>, 24}]
iex> more(t, <<1, 1, 1>>)
[{<<1, 1, 1, 1>>, 32}]
iex> more(t, <<2>>)
[]
#
# exclusive search for more specifics
#
iex> more(t, <<1, 1, 0>>, exclude: true)
[{<<1, 1, 0, 0>>, 32}]
#
# search key itself does not have to exist
#
iex> more(t, <<1>>)
[{<<1, 1, 1, 1>>, 32}, {<<1, 1, 0, 0>>, 32}, {<<1, 1, 0>>, 24}, {<<1, 1>>, 16}]
Specs
new() :: tree()
Returns a new, empty radix tree.
Example
iex> new()
{0, nil, nil}
Specs
Return a new radix tree, initialized using given list of {key
, value
}-pairs.
Example
iex> elements = [{<<1, 1>>, 16}, {<<1, 1, 1, 1>>, 32}, {<<1, 1, 0>>, 24}]
iex> new(elements)
{0,
{23, [{<<1, 1, 0>>, 24}, {<<1, 1>>, 16}],
[{<<1, 1, 1, 1>>, 32}]},
nil
}
Specs
Removes the value associated with key
and returns the matched
key,value-pair and the new tree.
Options include:
default: value
, returned ifkey
could not be matched (defaults to nil)match: :lpm
, specifies a longest prefix match instead of an exact match
If given search key
was not matched, the tree is unchanged and the
key,value-pair will be the search key
and the default value.
Examples
# pop an existing element
iex> new([{<<0>>, 0}, {<<1>>, 1}, {<<2>>, 2}])
...> |> pop(<<1>>)
{
{<<1>>, 1},
{0, {6, [{<<0>>, 0}], [{<<2>>, 2}]}, nil}
}
# pop non-existing, using a default
iex> new([{<<0>>, 0}, {<<1>>, 1}, {<<2>>, 2}])
...> |> pop(<<3>>, default: :notfound)
{
{<<3>>, :notfound},
{0, {6, {7, [{<<0>>, 0}], [{<<1>>, 1}]}, [{<<2>>, 2}]}, nil}
}
# pop using longest prefix match
iex> new([{<<1, 1, 1>>, "1.1.1.0/24"}, {<<1, 1, 1, 1::1>>, "1.1.1.128/25"}])
...> |> pop(<<1, 1, 1, 255>>, match: :lpm)
{
{<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
{0, [{<<1, 1, 1>>, "1.1.1.0/24"}], nil}
}
Specs
Prunes given tree
by invoking fun
on adjacent keys.
The callback fun
is called with a 5- or 6-element tuple:
{k0, k1, v1, k2, v2}
, for two adjacent keysk1
andk2
and absent parentk0
{k0, v0, k1, v1, k2, v2}
, for two adjacent keysk1
andk2
withv0
as parentk0
's value
If fun
returns {:ok, value}
the children k1
and k2
are deleted from
tree
and value
is stored under the parent key k0
, overwriting any
existing value.
Optionally specify recurse: true
to keep pruning as long as pruning changes
the tree.
Examples
iex> adder = fn {_k0, _k1, v1, _k2, v2} -> {:ok, v1 + v2}
...> {_k0, v0, _k1, v1, _k2, v2} -> {:ok, v0 + v1 + v2}
...> end
iex> t = new()
...> |> put(<<1, 1, 1, 0::1>>, 1)
...> |> put(<<1, 1, 1, 1::1>>, 2)
...> |> put(<<1, 1, 0>>, 3)
iex> # prune, once
iex> prune(t, adder)
{0, {23, [{<<1, 1, 0>>, 3}], [{<<1, 1, 1>>, 3}]}, nil}
iex> # prune, recursively
iex> prune(t, adder, recurse: true)
{0, [{<<1, 1, 0::size(7)>>, 6}], nil}
iex> adder = fn {_k0, _k1, v1, _k2, v2} -> {:ok, v1 + v2}
...> {_k0, v0, _k1, v1, _k2, v2} -> {:ok, v0 + v1 + v2}
...> end
iex> new(for x <- 0..255, do: {<<x>>, x})
...> |> prune(adder, recurse: true)
{0, [{<<>>, 32640}], nil}
iex> Enum.sum(0..255)
32640
Specs
Stores the key,value-pairs from elements
in the radix tree
.
Any existing key
's will have their value
's replaced.
Example
iex> elements = [{<<1, 1>>, "1.1.0.0/16"}, {<<1, 1, 1, 1>>, "1.1.1.1"}]
iex> new()
...> |> put(elements)
{0,
{23, [{<<1, 1>>, "1.1.0.0/16"}],
[{<<1, 1, 1, 1>>, "1.1.1.1"}]},
nil
}
Specs
Stores the key,value-pair under key
in the radix tree
.
Any existing key
will have its value
replaced.
Example
iex> t = new()
...> |> put(<<1, 1>>, "1.1.0.0/16")
...> |> put(<<1, 1, 1, 1>>, "x.x.x.x")
iex> t
{0,
{23, [{<<1, 1>>, "1.1.0.0/16"}],
[{<<1, 1, 1, 1>>, "x.x.x.x"}]},
nil
}
iex> put(t, <<1, 1, 1, 1>>, "1.1.1.1")
{0,
{23, [{<<1, 1>>, "1.1.0.0/16"}],
[{<<1, 1, 1, 1>>, "1.1.1.1"}]},
nil
}
Specs
Invokes fun
for each key,value-pair in the radix tree
with the accumulator.
The initial value of the accumulator is acc
. The function is invoked for
each key,value-pair in the radix tree with the accumulator in a depth-first
fashion. The result returned by the function is used as the accumulator for
the next iteration. The function returns the last accumulator.
fun
's signature is (key/0
, value/0
, acc/0
) -> acc/0
.
Example
iex> t = new([
...> {<<1, 1, 1, 0::1>>, "1.1.1.0/25"},
...> {<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
...> {<<1, 1, 1>>, "1.1.1.0/24"},
...> {<<3>>, "3.0.0.0/8"},
...> ])
iex> f = fn _key, value, acc -> [value | acc] end
iex> reduce(t, [], f) |> Enum.reverse()
["1.1.1.0/25", "1.1.1.0/24", "1.1.1.128/25", "3.0.0.0/8"]
Specs
Extracts the key,value-pairs associated with keys
from tree
into a new
radix tree.
Returns the new tree and the old tree with the key,value-pairs removed.
By default an exact match is used, specify match: :lpm
to match based
on a longest prefix match.
If none of the given keys
match, the new tree will be empty and the old
tree unchanged.
Examples
iex> tree = new([{<<0>>, 0}, {<<1>>, 1}, {<<2>>, 2}, {<<3>>, 3}])
iex> {t1, t2} = split(tree, [<<0>>, <<2>>])
iex> keys(t1)
[<<0>>, <<2>>]
iex> keys(t2)
[<<1>>, <<3>>]
iex> tree = new([{<<0>>, 0}, {<<1>>, 1}, {<<2>>, 2}, {<<3>>, 3}])
iex> {t1, t2} = split(tree, [<<0, 0>>, <<2, 0>>], match: :lpm)
iex> keys(t1)
[<<0>>, <<2>>]
iex> keys(t2)
[<<1>>, <<3>>]
Specs
Returns a new tree with all the key,value-pairs whose key are in keys
.
If a key in keys
does not exist in tree
, it is ignored.
By default keys are matched exactly, use the option match: :lpm
to use
longest prefix matching.
Examples
iex> new([{<<>>, nil}, {<<0>>, 0}, {<<1>>, 1}, {<<128>>, 128}, {<<255>>, 255}])
...> |> take([<<>>, <<1>>, <<255>>])
...> |> to_list()
[{<<>>, nil}, {<<1>>, 1}, {<<255>>, 255}]
# using longest prefix match
iex> new([{<<>>, nil}, {<<0>>, 0}, {<<1>>, 1}, {<<128>>, 128}, {<<255>>, 255}])
...> |> take([<<2, 2, 2, 2>>, <<1, 1, 1, 1>>, <<255, 255, 0, 0>>], match: :lpm)
...> |> to_list()
[{<<>>, nil}, {<<1>>, 1}, {<<255>>, 255}]
Specs
Returns all key,value-pairs in tree
as a flat list.
Example
iex> new([
...> {<<1, 1, 1, 0::1>>, "1.1.1.0/25"},
...> {<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
...> {<<3>>, "3.0.0.0/8"},
...> {<<1, 1, 1>>, "1.1.1.0/24"}
...> ])
...> |> to_list()
[
{<<1, 1, 1, 0::1>>, "1.1.1.0/25"},
{<<1, 1, 1>>, "1.1.1.0/24"},
{<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
{<<3>>, "3.0.0.0/8"}
]
Specs
Updates a key,value-pair in tree
by invoking fun
after a longest prefix
match lookup.
After a longest prefix match lookup for given search key
, the callback fun
is called with:
{matched_key, value}
, in case there was a match{original_key}
, in case there was no match
If the callback fun
returns
{:ok, new_key, new_value}
, then new_value will be stored under new_key in the giventree
- anything else will return the
tree
unchanged.
Note that when new_key
differs from matched_key
, the latter is not
deleted from the tree. Because of the longest prefix match, the
matched_key
is provided to the callback fun
.
The main use case is for when dealing with full keys and doing statistics on
some less specific level. If an exact match is required,
Radix.get_and_update/3
might be a better fit.
Examples
iex> max24bits = fn key when bit_size(key) > 24 ->
...> <<bits::bitstring-size(24), _::bitstring>> = key; <<bits::bitstring>>
...> key -> key
...> end
iex>
iex> counter = fn {k, v} -> {:ok, k, v + 1}
...> {k} -> {:ok, max24bits.(k), 1}
...> end
iex> new()
...> |> update(<<1, 1, 1, 1>>, counter)
...> |> update(<<1, 1, 1, 128>>, counter)
...> |> update(<<1, 1, 1, 255>>, counter)
{0, [{<<1, 1, 1>>, 3}], nil}
# only interested in known prefixes
iex> counter = fn {k, v} -> {:ok, k, v + 1}
...> _discard -> nil
...> end
iex> new()
...> |> put(<<1, 1, 1>>, 0)
...> |> update(<<1, 1, 1, 1>>, counter)
...> |> update(<<1, 1, 1, 2>>, counter)
...> |> update(<<2, 2, 2, 2>>, counter)
{0, [{<<1, 1, 1>>, 2}], nil}
Specs
Looks up the longest prefix match for given search key
in tree
and
updates its value through fun
.
If key
has a longest prefix match in tree
then the associated value is
passed to fun
and its result is used as the updated value of the matching
key. If key
cannot be matched the {key
, default
}-pair is inserted in
the tree
without calling fun
.
Example
iex> t = new()
iex> t = update(t, <<1, 1, 1>>, 1, fn x -> x+1 end)
iex> t
{0, [{<<1, 1, 1>>, 1}], nil}
iex> t = update(t, <<1, 1, 1, 0>>, 1, fn x -> x+1 end)
iex> t
{0, [{<<1, 1, 1>>, 2}], nil}
iex> t = update(t, <<1, 1, 1, 255>>, 1, fn x -> x+1 end)
iex> t
{0, [{<<1, 1, 1>>, 3}], nil}
Specs
Returns all values stored in the radix tree
.
Example
iex> new([
...> {<<1, 1, 1, 0::1>>, "1.1.1.0/25"},
...> {<<1, 1, 1, 1::1>>, "1.1.1.128/25"},
...> {<<1, 1, 1>>, "1.1.1.0/24"},
...> {<<3>>, "3.0.0.0/8"},
...> ])
...> |> values()
["1.1.1.0/25", "1.1.1.0/24", "1.1.1.128/25", "3.0.0.0/8"]
Specs
Invokes fun
on all (internal and leaf) nodes of the radix tree
using either
:inorder
, :preorder
or :postorder
traversal.
fun
should have the signatures:
Examples
Get all values in the tree.
iex> t = new([{<<1>>, 1}, {<<2>>, 2}, {<<3>>, 3}, {<<128>>, 128}])
iex> f = fn
...> (acc, {_bit, _left, _right}) -> acc
...> (acc, leaf) -> acc ++ Enum.map(leaf, fn {_k, v} -> v end)
...> end
iex> walk(t, [], f)
[1, 2, 3, 128]
Get all the keys in the tree
iex> t = new([{<<1>>, 1}, {<<2>>, 2}, {<<3>>, 3}, {<<128>>, 128}])
iex> f = fn
...> (acc, {_bit, _left, _right}) -> acc
...> (acc, leaf) -> acc ++ Enum.map(leaf, fn {k, _v} -> k end)
...> end
iex> walk(t, [], f)
[<<1>>, <<2>>, <<3>>, <<128>>]