Add a trie lookup handler from a byte sequence key

* This is intended for write-only so does not support removing keys at all.
This commit is contained in:
baldurk
2025-06-06 12:16:54 +01:00
parent 93617f3d11
commit 81d5ca7c35
7 changed files with 998 additions and 0 deletions
+358
View File
@@ -34,10 +34,30 @@
#include "common/formatting.h"
#include "common/globalconfig.h"
#include "common/timing.h"
#include "core/rdcbytetrie.h"
#include "os/os_specific.h"
#include "catch/catch.hpp"
struct TrieValue
{
TrieValue() : id() {}
TrieValue(uint32_t id) : id(id) {}
bool operator==(const TrieValue &o) const { return id == o.id; }
uint32_t id;
uint16_t _trie;
};
template <>
rdcstr DoStringise<TrieValue>(const TrieValue &v)
{
return DoStringise(v.id);
}
using KV = rdcpair<bytebuf, TrieValue>;
template <typename inner>
void TestInsert()
{
@@ -2500,4 +2520,342 @@ TEST_CASE("Test rdcfixedarray type", "[basictypes][rdcfixedarray]")
};
};
template <typename container>
void CheckKeyValues(container &trie, KV *keyVals, size_t numKeyVals, const bytebuf &missing)
{
for(size_t i = 0; i <= numKeyVals; i++)
{
// check all keys added so far are contained and lookup if so
for(size_t j = 0; j < numKeyVals; j++)
{
if(j < i)
{
CHECK(trie.contains(keyVals[j].first));
CHECK(trie.lookup(keyVals[j].first) == keyVals[j].second);
}
else
{
CHECK_FALSE(trie.contains(keyVals[j].first));
}
}
CHECK_FALSE(trie.contains(missing));
CHECK(trie.lookup(missing) == TrieValue());
// add the key
if(i < numKeyVals)
CHECK(trie.insert(keyVals[i].first, keyVals[i].second));
}
// duplicate insertion is fine
CHECK(trie.insert(keyVals[0].first, keyVals[0].second));
CHECK(trie.lookup(keyVals[0].first) == keyVals[0].second);
// differing values is the problem
EXPECT_ERROR();
CHECK_FALSE(trie.insert(keyVals[0].first, keyVals[0].second.id + 1));
CHECK(DID_ERROR_HAPPEN());
CHECK(trie.lookup(keyVals[0].first) == keyVals[0].second);
}
TEST_CASE("Test rdcbytetrie type", "[basictypes][rdcbytetrie]")
{
rdcbytetrie<TrieValue> trie;
SECTION("Simple lookups")
{
KV keyVals[] = {
{{1, 1}, 1},
{{1, 2}, 2},
{{3, 2}, 3},
};
bytebuf d = {1, 3};
CheckKeyValues(trie, keyVals, ARRAY_COUNT(keyVals), d);
}
SECTION("different sized keys, expanding length")
{
KV keyVals[] = {
//
{{1}, TrieValue(1)},
//
{{1, 2}, TrieValue(2)},
//
{{1, 2, 3}, TrieValue(3)},
//
{{1, 2, 4}, TrieValue(4)},
//
{{2, 1, 1}, TrieValue(5)},
//
{{2, 1, 6}, TrieValue(6)},
};
bytebuf d = {1, 3};
CheckKeyValues(trie, keyVals, ARRAY_COUNT(keyVals), d);
}
SECTION("different sized keys, contracting length")
{
KV keyVals[] = {
//
{{1, 1, 1, 2, 3}, TrieValue(1)},
//
{{1, 1, 1, 2, 4}, TrieValue(2)},
//
{{1, 1, 1, 3}, TrieValue(3)},
//
{{1, 2}, TrieValue(4)},
//
{{2, 1, 1}, TrieValue(5)},
//
{{2, 1, 6}, TrieValue(6)},
//
{{1}, TrieValue(7)},
};
bytebuf d = {1, 3};
CheckKeyValues(trie, keyVals, ARRAY_COUNT(keyVals), d);
}
SECTION("Invalid key size")
{
bytebuf a;
a.resize(512);
TrieValue val(1);
EXPECT_ERROR();
CHECK_FALSE(trie.contains(a));
CHECK(DID_ERROR_HAPPEN());
EXPECT_ERROR();
CHECK_FALSE(trie.insert(a, val));
CHECK(DID_ERROR_HAPPEN());
EXPECT_ERROR();
CHECK_FALSE(trie.contains(a));
CHECK(DID_ERROR_HAPPEN());
}
SECTION("long common prefix with last byte varying")
{
bytebuf keys[80];
TrieValue vals[80];
keys[0].fill(200, 1);
for(uint8_t i = 1; i < 80; i++)
keys[i] = keys[0];
for(uint8_t i = 0; i < 80; i++)
keys[i].push_back(2 + i);
for(uint8_t i = 0; i < 80; i++)
CHECK_FALSE(trie.contains(keys[i]));
for(uint8_t i = 0; i < 80; i++)
{
vals[i] = 5 + i;
CHECK(trie.insert(keys[i], vals[i]));
}
for(uint8_t i = 0; i < 80; i++)
{
CHECK(trie.contains(keys[i]));
CHECK(trie.lookup(keys[i]) == vals[i]);
}
}
SECTION("first byte varying with long suffix")
{
bytebuf keys[80];
TrieValue vals[80];
for(uint8_t i = 0; i < 80; i++)
{
keys[i].fill(200, 1);
keys[i].insert(0, 2 + i);
}
for(uint8_t i = 0; i < 80; i++)
CHECK_FALSE(trie.contains(keys[i]));
for(uint8_t i = 0; i < 80; i++)
{
vals[i] = 5 + i;
CHECK(trie.insert(keys[i], vals[i]));
}
for(uint8_t i = 0; i < 80; i++)
{
CHECK(trie.contains(keys[i]));
CHECK(trie.lookup(keys[i]) == vals[i]);
}
}
SECTION("middle varying byte with medium prefix/suffix")
{
bytebuf keys[80];
TrieValue vals[80];
for(uint8_t i = 0; i < 80; i++)
{
keys[i].fill(200, 1);
keys[i].insert(100, 2 + i);
}
for(uint8_t i = 0; i < 80; i++)
CHECK_FALSE(trie.contains(keys[i]));
for(uint8_t i = 0; i < 80; i++)
{
vals[i] = 5 + i;
CHECK(trie.insert(keys[i], vals[i]));
}
for(uint8_t i = 0; i < 80; i++)
{
CHECK(trie.contains(keys[i]));
CHECK(trie.lookup(keys[i]) == vals[i]);
}
}
SECTION("middle shared sequences with prefix/suffix")
{
bytebuf keys[80];
TrieValue vals[80];
for(uint8_t i = 0; i < 80; i++)
{
keys[i].fill(150, 1);
keys[i].insert(100, i >> 3);
keys[i].insert(101, i >> 3);
keys[i].insert(102, i >> 2);
keys[i].insert(103, i >> 2);
keys[i].insert(104, i);
}
for(uint8_t i = 0; i < 80; i++)
CHECK_FALSE(trie.contains(keys[i]));
for(uint8_t i = 0; i < 80; i++)
{
vals[i] = 5 + i;
CHECK(trie.insert(keys[i], vals[i]));
}
for(uint8_t i = 0; i < 80; i++)
{
CHECK(trie.contains(keys[i]));
CHECK(trie.lookup(keys[i]) == vals[i]);
}
}
SECTION("'real' descriptors of palletised indices")
{
rdcarray<KV> keyVals;
struct indices
{
uint32_t a : 20;
uint32_t b : 12;
};
bytebuf k;
k.resize(sizeof(indices));
indices *i = (indices *)k.data();
for(uint32_t b = 0; b < 2000; b += 50 + (rand() & 0x3f))
{
for(uint32_t a = 0; a < 1000000; a += 1000 + (rand() & 0x3fff))
{
i->a = a;
i->b = b;
keyVals.push_back({k, TrieValue(b + a)});
}
}
CheckKeyValues(trie, keyVals.data(), keyVals.size(), bytebuf());
}
SECTION("'real' descriptors of pointer + size")
{
rdcarray<KV> keyVals;
struct pointerSize
{
uint64_t ptr;
uint64_t sz;
};
bytebuf k;
k.resize(sizeof(pointerSize));
pointerSize *p = (pointerSize *)k.data();
for(uint32_t b = 0; b < 0xfffff; b += 0x8000 + (rand() & 0xfff))
{
for(uint32_t a = 0; a < 0xfffffff; a += 0x600000 + (rand() & 0xfffff))
{
p->ptr = 0xdeadbeef + a;
p->sz = 0xf00b + b;
keyVals.push_back({k, TrieValue(b + a)});
}
}
CheckKeyValues(trie, keyVals.data(), keyVals.size(), bytebuf());
}
SECTION("'real' descriptors which are large with pointer + size")
{
rdcarray<KV> keyVals;
struct pointerSize
{
uint64_t padding1;
uint64_t padding2;
uint64_t ptr;
uint64_t padding3;
uint32_t sz;
uint32_t padding4;
uint64_t padding5;
uint64_t padding6;
uint64_t padding7;
};
bytebuf k;
k.resize(sizeof(pointerSize));
pointerSize *p = (pointerSize *)k.data();
p->padding1 = 0xDBBAE716BAB4;
p->padding2 = 0x3F097C6CC886;
p->padding3 = 0xD8D877E8012C;
p->padding4 = 0xFB97503B;
p->padding5 = 0x43C5E4856EED;
p->padding6 = 0x530E8ACF5452;
p->padding7 = 0x93D8819988A2;
for(uint32_t b = 0; b < 0xfffff; b += 0x8000 + (rand() & 0xfff))
{
for(uint32_t a = 0; a < 0xfffffff; a += 0x600000 + (rand() & 0xfffff))
{
p->ptr = 0xdeadbeef + a;
p->sz = 0xf00b + b;
keyVals.push_back({k, TrieValue(b + a)});
}
}
CheckKeyValues(trie, keyVals.data(), keyVals.size(), bytebuf());
}
}
#endif // ENABLED(ENABLE_UNIT_TESTS)