rax-lpm derived from rax, it implemented a widely used longest prefix match algorithm in radix tree.
The primary goal of this project is to implement the longest prefix match algorithm by providing raxFind
-like functionality without intervention of rax iterators. It's extremely useful when you do a path prefix match, or embed the rax into embedded device by removing rax iterators.
For more information, please refers to rax project.
rax itself is a full-featured radix tree implementation, for basic API usage, please see Basic API.
There're two APIs used for radix tree longest prefix match:
/**
* Find longest prefix match in a radix tree
*
* @tree the tree
* @key key to match
* @len length of the key
* @pos [OUT] position(index) in the key
* ranged [-1, len]
* -1 indicate mismatch(will return raxNotFound)
* [0, len) indicate a submatch
* len indicate a full match
* @return value associated to the longest prefix match node
* raxNotFound if not match
*
* see: rax/rax.c#raxFind
*/
void *raxLongestPrefixMatch(
rax *tree,
unsigned char *key,
size_t len,
ssize_t * __nullable pos);
/**
* Find longest prefix match in a radix tree
*
* @tree the tree
* @key key to match
* @len length of the key
* @pos [OUT] position(index) in the key
* ranged [-1, len]
* -1 indicate mismatch(will return 0)
* [0, len) indicate a submatch
* len indicate a full match
* @data [OUT] value associated to the longest prefix match node if found
* @return 1 if found any match 0 o.w.
*
* see: rax/rax.c#raxFind
*/
int raxLongestPrefixMatch2(
rax *tree,
unsigned char *key,
size_t len,
ssize_t * __nullable pos,
void * __nullable * __nullable data);
The __nullable
keyword annotates that left hand side type can be NULL
, i.e. it's optional. by default, pointer type shouldn't be NULL
.
Those two APIs used in different scenarios, they're the same per se, choose whichever you like.
When you insert any key into the radix tree in the following form:
raxInsert(rax, (unsigned char *) _whatever1_, /* n.b. */ 0, (void *) _whatever2_, NULL);
e.g. the len
parameter equals to zero, there'll always be a zero-length longest prefix match.
Thus, following longest prefix match assertion will always proceed:
/* Assume `rax' contains only the zero-length key */
ssize_t sz;
void *data = raxLongestPrefixMatch(rax, (unsigned char *) "foobar", 6, &sz);
assert(data == (void *) _whatever2_);
assert(sz == 0);
It simply because when you insert a zero-length key, rax->head->iskey
always yields 1, hence there is a match.
Similar situations applys to raxFind
, raxRemove
:
int ok;
void *data;
ok = raxInsert(rax, (unsigned char *) _whatever1_, /* n.b. */ 0, (void *) _whatever2_, NULL);
assert(ok == 1);
data = raxFind(rax, (unsigned char *) _whatever3_, 0);
assert(data == (void *) _whatever2_);
ok = raxRemove(rax, (unsigned char *) _whatever4_, 0, NULL);
assert(ok == 1);
This project submoduled rax source, so it can stay up-to-date automatically. Please specify --recurse-submodules
when cloning this repository.
Before compile the source, you should first do:
git apply --directory rax rax.c.patch
To patch rax/rax.c(only once)
And then run the tests:
# `release' target for release build
make
./test_lpm
Please see Debugging Rax.
[sic] Rax is an open source project, released under the BSD two clause license.
rax-lpm is also an open source project, released under the BSD 3-clause license.
Created 180309+0800