radix tree has now prefix

This commit is contained in:
2025-03-10 09:05:57 +01:00
parent f773ce168e
commit 46afb63f31
21 changed files with 500 additions and 105 deletions

View File

@@ -40,17 +40,27 @@ The radix tree uses OurDB as its persistent storage backend:
### Key Operations
#### Insertion
#### Set (formerly Insertion)
1. Traverse the tree following matching prefixes
2. Split nodes when partial matches are found
3. Create new nodes for unmatched segments
4. Update node values and references in OurDB
#### Search
#### Get (formerly Search)
1. Start from the root node
2. Follow child nodes whose key segments match the search key
3. Return the value if an exact match is found at a leaf node
#### List (formerly Search by Prefix)
1. Start from the root node
2. Find all keys that start with the given prefix
3. Return a list of matching keys
#### GetAll
1. Find all keys that start with the given prefix using List
2. Retrieve the value for each matching key
3. Return a list of values for all matching keys
#### Deletion
1. Locate the node containing the key
2. Remove the value and leaf status
@@ -65,14 +75,20 @@ import freeflowuniverse.herolib.data.radixtree
// Create a new radix tree
mut tree := radixtree.new('/path/to/storage')!
// Insert key-value pairs
tree.insert('hello', 'world'.bytes())!
tree.insert('help', 'me'.bytes())!
// Set key-value pairs
tree.set('hello', 'world'.bytes())!
tree.set('help', 'me'.bytes())!
// Search for values
value := tree.search('hello')! // Returns 'world' as bytes
// Get values by key
value := tree.get('hello')! // Returns 'world' as bytes
println(value.bytestr()) // Prints: world
// List keys by prefix
keys := tree.list('hel')! // Returns ['hello', 'help']
// Get all values by prefix
values := tree.getall('hel')! // Returns ['world'.bytes(), 'me'.bytes()]
// Delete keys
tree.delete('help')!
```

View File

@@ -1,61 +1,64 @@
module radixtree
import freeflowuniverse.herolib.ui.console
fn test_basic_operations() ! {
mut rt := new(path: '/tmp/radixtree_test', reset: true)!
// Test insert and search
rt.insert('test', 'value1'.bytes())!
value1 := rt.search('test')!
// Test set and get
rt.set('test', 'value1'.bytes())!
value1 := rt.get('test')!
assert value1.bytestr() == 'value1'
// Test updating existing key
rt.insert('test', 'value2'.bytes())!
value2 := rt.search('test')!
rt.set('test', 'value2'.bytes())!
value2 := rt.get('test')!
assert value2.bytestr() == 'value2'
// Test non-existent key
if _ := rt.search('nonexistent') {
if _ := rt.get('nonexistent') {
assert false, 'Expected error for non-existent key'
}
// Test delete
rt.delete('test')!
mut ok := false
if _ := rt.search('test') {
ok = true
if _ := rt.get('test') {ok = true
} else {
ok = false
}
assert ok
assert !ok, 'Expected error for deleted key'
}
fn test_prefix_matching() ! {
mut rt := new(path: '/tmp/radixtree_test_prefix')!
// Insert keys with common prefixes
rt.insert('team', 'value1'.bytes())!
rt.insert('test', 'value2'.bytes())!
rt.insert('testing', 'value3'.bytes())!
// Set keys with common prefixes
rt.set('team', 'value1'.bytes())!
rt.set('test', 'value2'.bytes())!
rt.set('testing', 'value3'.bytes())!
// Verify each key has correct value
value1 := rt.search('team')!
value1 := rt.get('team')!
assert value1.bytestr() == 'value1'
value2 := rt.search('test')!
value2 := rt.get('test')!
assert value2.bytestr() == 'value2'
value3 := rt.search('testing')!
value3 := rt.get('testing')!
assert value3.bytestr() == 'value3'
// Delete middle key and verify others still work
rt.delete('test')!
if _ := rt.search('test') {
if _ := rt.get('test') {
assert false, 'Expected error after deletion'
}
value1_after := rt.search('team')!
value1_after := rt.get('team')!
assert value1_after.bytestr() == 'value1'
value3_after := rt.search('testing')!
value3_after := rt.get('testing')!
assert value3_after.bytestr() == 'value3'
}
@@ -63,28 +66,28 @@ fn test_edge_cases() ! {
mut rt := new(path: '/tmp/radixtree_test_edge')!
// Test empty key
rt.insert('', 'empty'.bytes())!
empty_value := rt.search('')!
rt.set('', 'empty'.bytes())!
empty_value := rt.get('')!
assert empty_value.bytestr() == 'empty'
// Test very long key
long_key := 'a'.repeat(1000)
rt.insert(long_key, 'long'.bytes())!
long_value := rt.search(long_key)!
rt.set(long_key, 'long'.bytes())!
long_value := rt.get(long_key)!
assert long_value.bytestr() == 'long'
// Test keys that require node splitting
rt.insert('test', 'value1'.bytes())!
rt.insert('testing', 'value2'.bytes())!
rt.insert('te', 'value3'.bytes())!
rt.set('test', 'value1'.bytes())!
rt.set('testing', 'value2'.bytes())!
rt.set('te', 'value3'.bytes())!
value1 := rt.search('test')!
value1 := rt.get('test')!
assert value1.bytestr() == 'value1'
value2 := rt.search('testing')!
value2 := rt.get('testing')!
assert value2.bytestr() == 'value2'
value3 := rt.search('te')!
value3 := rt.get('te')!
assert value3.bytestr() == 'value3'
}
@@ -96,18 +99,18 @@ fn test_update_metadata() ! {
initial_metadata := 'metadata_initial'.bytes()
new_metadata := 'metadata_updated'.bytes()
// Insert initial entry
rt.insert(prefix, initial_metadata)!
// Set initial entry
rt.set(prefix, initial_metadata)!
// Verify initial value
value := rt.search(prefix)!
value := rt.get(prefix)!
assert value.bytestr() == 'metadata_initial'
// Update metadata while keeping the same prefix
rt.update(prefix, new_metadata)!
// Verify updated value
updated_value := rt.search(prefix)!
updated_value := rt.get(prefix)!
assert updated_value.bytestr() == 'metadata_updated'
// Test updating non-existent prefix
@@ -119,15 +122,15 @@ fn test_update_metadata() ! {
fn test_multiple_operations() ! {
mut rt := new(path: '/tmp/radixtree_test_multiple')!
// Insert multiple keys
// Set multiple keys
keys := ['abc', 'abcd', 'abcde', 'bcd', 'bcde']
for i, key in keys {
rt.insert(key, 'value${i + 1}'.bytes())!
rt.set(key, 'value${i + 1}'.bytes())!
}
// Verify all keys
for i, key in keys {
value := rt.search(key)!
value := rt.get(key)!
assert value.bytestr() == 'value${i + 1}'
}
@@ -140,14 +143,14 @@ fn test_multiple_operations() ! {
expected := ['value1', 'value3', 'value4']
for i, key in remaining {
value := rt.search(key)!
value := rt.get(key)!
assert value.bytestr() == expected[i]
}
// Verify deleted keys return error
deleted := ['abcd', 'bcde']
for key in deleted {
if _ := rt.search(key) {
if _ := rt.get(key) {
assert false, 'Expected error for deleted key: ${key}'
}
}

View File

@@ -0,0 +1,78 @@
module radixtree
import freeflowuniverse.herolib.ui.console
fn test_getall() ! {
//console.print_debug('Starting test_getall')
mut rt := new(path: '/tmp/radixtree_getall_test', reset: true)!
// Set up test data with common prefixes
test_data := {
'user_1': 'data1',
'user_2': 'data2',
'user_3': 'data3',
'admin_1': 'admin_data1',
'admin_2': 'admin_data2',
'guest': 'guest_data'
}
// Set all test data
for key, value in test_data {
rt.set(key, value.bytes())!
}
// Test getall with 'user_' prefix
//console.print_debug('Testing getall with prefix "user_"')
user_values := rt.getall('user_')!
//console.print_debug('user_values count: ${user_values.len}')
// Should return 3 values
assert user_values.len == 3
// Convert byte arrays to strings for easier comparison
mut user_value_strings := []string{}
for value in user_values {
user_value_strings << value.bytestr()
}
// Check all expected values are present
assert 'data1' in user_value_strings
assert 'data2' in user_value_strings
assert 'data3' in user_value_strings
// Test getall with 'admin_' prefix
//console.print_debug('Testing getall with prefix "admin_"')
admin_values := rt.getall('admin_')!
//console.print_debug('admin_values count: ${admin_values.len}')
// Should return 2 values
assert admin_values.len == 2
// Convert byte arrays to strings for easier comparison
mut admin_value_strings := []string{}
for value in admin_values {
admin_value_strings << value.bytestr()
}
// Check all expected values are present
assert 'admin_data1' in admin_value_strings
assert 'admin_data2' in admin_value_strings
// Test getall with empty prefix (should return all values)
//console.print_debug('Testing getall with empty prefix')
all_values := rt.getall('')!
//console.print_debug('all_values count: ${all_values.len}')
// Should return all 6 values
assert all_values.len == test_data.len
// Test getall with non-existent prefix
//console.print_debug('Testing getall with non-existent prefix "xyz"')
non_existent_values := rt.getall('xyz')!
//console.print_debug('non_existent_values count: ${non_existent_values.len}')
// Should return empty array
assert non_existent_values.len == 0
//console.print_debug('test_getall completed successfully')
}

View File

@@ -0,0 +1,197 @@
module radixtree
import freeflowuniverse.herolib.ui.console
fn test_list() ! {
//console.print_debug('Starting test_list')
mut rt := new(path: '/tmp/radixtree_prefix_test', reset: true)!
// Insert keys with various prefixes
test_data := {
'apple': 'fruit1',
'application': 'software1',
'apply': 'verb1',
'banana': 'fruit2',
'ball': 'toy1',
'cat': 'animal1',
'car': 'vehicle1',
'cargo': 'shipping1'
}
// Set all test data
for key, value in test_data {
rt.set(key, value.bytes())!
}
// Test prefix 'app' - should return apple, application, apply
//console.print_debug('Testing prefix "app"')
app_keys := rt.list('app')!
//console.print_debug('app_keys: ${app_keys}')
assert app_keys.len == 3
assert 'apple' in app_keys
assert 'application' in app_keys
assert 'apply' in app_keys
// Test prefix 'ba' - should return banana, ball
//console.print_debug('Testing prefix "ba"')
ba_keys := rt.list('ba')!
//console.print_debug('ba_keys: ${ba_keys}')
assert ba_keys.len == 2
assert 'banana' in ba_keys
assert 'ball' in ba_keys
// Test prefix 'car' - should return car, cargo
//console.print_debug('Testing prefix "car"')
car_keys := rt.list('car')!
//console.print_debug('car_keys: ${car_keys}')
assert car_keys.len == 2
assert 'car' in car_keys
assert 'cargo' in car_keys
// Test prefix 'z' - should return empty list
//console.print_debug('Testing prefix "z"')
z_keys := rt.list('z')!
//console.print_debug('z_keys: ${z_keys}')
assert z_keys.len == 0
// Test empty prefix - should return all keys
//console.print_debug('Testing empty prefix')
all_keys := rt.list('')!
//console.print_debug('all_keys: ${all_keys}')
assert all_keys.len == test_data.len
for key in test_data.keys() {
assert key in all_keys
}
// Test exact key as prefix - should return just that key
//console.print_debug('Testing exact key as prefix "apple"')
exact_key := rt.list('apple')!
//console.print_debug('exact_key: ${exact_key}')
assert exact_key.len == 1
assert exact_key[0] == 'apple'
//console.print_debug('test_list completed successfully')
}
fn test_list_with_deletion() ! {
//console.print_debug('Starting test_list_with_deletion')
mut rt := new(path: '/tmp/radixtree_prefix_deletion_test', reset: true)!
// Set keys with common prefixes
rt.set('test1', 'value1'.bytes())!
rt.set('test2', 'value2'.bytes())!
rt.set('test3', 'value3'.bytes())!
rt.set('other', 'value4'.bytes())!
// Initial check
//console.print_debug('Testing initial prefix "test"')
test_keys := rt.list('test')!
//console.print_debug('test_keys: ${test_keys}')
assert test_keys.len == 3
assert 'test1' in test_keys
assert 'test2' in test_keys
assert 'test3' in test_keys
// Delete one key
//console.print_debug('Deleting key "test2"')
rt.delete('test2')!
// Check after deletion
//console.print_debug('Testing prefix "test" after deletion')
test_keys_after := rt.list('test')!
//console.print_debug('test_keys_after: ${test_keys_after}')
assert test_keys_after.len == 2
assert 'test1' in test_keys_after
assert 'test2' !in test_keys_after
assert 'test3' in test_keys_after
// Check all keys
//console.print_debug('Testing empty prefix')
all_keys := rt.list('')!
//console.print_debug('all_keys: ${all_keys}')
assert all_keys.len == 3
assert 'other' in all_keys
//console.print_debug('test_list_with_deletion completed successfully')
}
fn test_list_edge_cases() ! {
//console.print_debug('Starting test_list_edge_cases')
mut rt := new(path: '/tmp/radixtree_prefix_edge_test', reset: true)!
// Test with empty tree
//console.print_debug('Testing empty tree with prefix "any"')
empty_result := rt.list('any')!
//console.print_debug('empty_result: ${empty_result}')
assert empty_result.len == 0
// Set a single key
//console.print_debug('Setting single key "single"')
rt.set('single', 'value'.bytes())!
// Test with prefix that's longer than any key
//console.print_debug('Testing prefix longer than any key "singlelonger"')
long_prefix := rt.list('singlelonger')!
//console.print_debug('long_prefix: ${long_prefix}')
assert long_prefix.len == 0
// Test with partial prefix match
//console.print_debug('Testing partial prefix match "sing"')
partial := rt.list('sing')!
//console.print_debug('partial: ${partial}')
assert partial.len == 1
assert partial[0] == 'single'
// Test with very long keys
//console.print_debug('Testing with very long keys')
long_key1 := 'a'.repeat(100) + 'key1'
long_key2 := 'a'.repeat(100) + 'key2'
rt.set(long_key1, 'value1'.bytes())!
rt.set(long_key2, 'value2'.bytes())!
//console.print_debug('Testing long prefix')
long_prefix_result := rt.list('a'.repeat(100))!
//console.print_debug('long_prefix_result: ${long_prefix_result}')
assert long_prefix_result.len == 2
assert long_key1 in long_prefix_result
assert long_key2 in long_prefix_result
//console.print_debug('test_list_edge_cases completed successfully')
}
fn test_list_performance() ! {
//console.print_debug('Starting test_list_performance')
mut rt := new(path: '/tmp/radixtree_prefix_perf_test', reset: true)!
// Insert a large number of keys with different prefixes
//console.print_debug('Setting up prefixes')
prefixes := ['user', 'post', 'comment', 'like', 'share']
// Set 100 keys for each prefix (500 total)
//console.print_debug('Setting 500 keys (100 for each prefix)')
for prefix in prefixes {
for i in 0..100 {
key := '${prefix}_${i}'
rt.set(key, 'value_${key}'.bytes())!
}
}
// Test retrieving by each prefix
//console.print_debug('Testing retrieval by each prefix')
for prefix in prefixes {
//console.print_debug('Testing prefix "${prefix}"')
keys := rt.list(prefix)!
//console.print_debug('Found ${keys.len} keys for prefix "${prefix}"')
assert keys.len == 100
// Verify all keys have the correct prefix
for key in keys {
assert key.starts_with(prefix)
}
}
// Test retrieving all keys
//console.print_debug('Testing retrieval of all keys')
all_keys := rt.list('')!
//console.print_debug('Found ${all_keys.len} total keys')
assert all_keys.len == 500
//console.print_debug('test_list_performance completed successfully')
}

View File

@@ -1,6 +1,7 @@
module radixtree
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.ui.console
// Represents a node in the radix tree
struct Node {
@@ -42,9 +43,9 @@ pub fn new(args NewArgs) !&RadixTree {
)!
mut root_id := u32(1) // First ID in ourdb is now 1 instead of 0
println('Debug: Initializing root node')
//console.print_debug('Debug: Initializing root node')
if db.get_next_id()! == 1 {
println('Debug: Creating new root node')
//console.print_debug('Debug: Creating new root node')
root := Node{
key_segment: ''
value: []u8{}
@@ -52,13 +53,13 @@ pub fn new(args NewArgs) !&RadixTree {
is_leaf: false
}
root_id = db.set(data: serialize_node(root))!
println('Debug: Created root node with ID ${root_id}')
//console.print_debug('Debug: Created root node with ID ${root_id}')
assert root_id == 1 // First ID is now 1
} else {
println('Debug: Using existing root node')
//console.print_debug('Debug: Using existing root node')
root_data := db.get(1)! // Get root node with ID 1
root_node := deserialize_node(root_data)!
println('Debug: Root node has ${root_node.children.len} children')
//console.print_debug('Debug: Root node has ${root_node.children.len} children')
}
return &RadixTree{
@@ -67,8 +68,8 @@ pub fn new(args NewArgs) !&RadixTree {
}
}
// Inserts a key-value pair into the tree
pub fn (mut rt RadixTree) insert(key string, value []u8) ! {
// Sets a key-value pair in the tree
pub fn (mut rt RadixTree) set(key string, value []u8) ! {
mut current_id := rt.root_id
mut offset := 0
@@ -102,56 +103,56 @@ pub fn (mut rt RadixTree) insert(key string, value []u8) ! {
children: []NodeRef{}
is_leaf: true
}
println('Debug: Creating new leaf node with key_part "${key_part}"')
//console.print_debug('Debug: Creating new leaf node with key_part "${key_part}"')
new_id := rt.db.set(data: serialize_node(new_node))!
println('Debug: Created node ID ${new_id}')
//console.print_debug('Debug: Created node ID ${new_id}')
// Create new child reference and update parent node
println('Debug: Updating parent node ${current_id} to add child reference')
//console.print_debug('Debug: Updating parent node ${current_id} to add child reference')
// Get fresh copy of parent node
mut parent_node := deserialize_node(rt.db.get(current_id)!)!
println('Debug: Parent node initially has ${parent_node.children.len} children')
//console.print_debug('Debug: Parent node initially has ${parent_node.children.len} children')
// Add new child reference
parent_node.children << NodeRef{
key_part: key_part
node_id: new_id
}
println('Debug: Added child reference, now has ${parent_node.children.len} children')
//console.print_debug('Debug: Added child reference, now has ${parent_node.children.len} children')
// Update parent node in DB
println('Debug: Serializing parent node with ${parent_node.children.len} children')
//console.print_debug('Debug: Serializing parent node with ${parent_node.children.len} children')
parent_data := serialize_node(parent_node)
println('Debug: Parent data size: ${parent_data.len} bytes')
//console.print_debug('Debug: Parent data size: ${parent_data.len} bytes')
// First verify we can deserialize the data correctly
println('Debug: Verifying serialization...')
//console.print_debug('Debug: Verifying serialization...')
if test_node := deserialize_node(parent_data) {
println('Debug: Serialization test successful - node has ${test_node.children.len} children')
//console.print_debug('Debug: Serialization test successful - node has ${test_node.children.len} children')
} else {
println('Debug: ERROR - Failed to deserialize test data')
//console.print_debug('Debug: ERROR - Failed to deserialize test data')
return error('Serialization verification failed')
}
// Set with explicit ID to update existing node
println('Debug: Writing to DB...')
//console.print_debug('Debug: Writing to DB...')
rt.db.set(id: current_id, data: parent_data)!
// Verify by reading back and comparing
println('Debug: Reading back for verification...')
//console.print_debug('Debug: Reading back for verification...')
verify_data := rt.db.get(current_id)!
verify_node := deserialize_node(verify_data)!
println('Debug: Verification - node has ${verify_node.children.len} children')
//console.print_debug('Debug: Verification - node has ${verify_node.children.len} children')
if verify_node.children.len == 0 {
println('Debug: ERROR - Node update verification failed!')
println('Debug: Original node children: ${node.children.len}')
println('Debug: Parent node children: ${parent_node.children.len}')
println('Debug: Verified node children: ${verify_node.children.len}')
println('Debug: Original data size: ${parent_data.len}')
println('Debug: Verified data size: ${verify_data.len}')
println('Debug: Data equal: ${verify_data == parent_data}')
//console.print_debug('Debug: ERROR - Node update verification failed!')
//console.print_debug('Debug: Original node children: ${node.children.len}')
//console.print_debug('Debug: Parent node children: ${parent_node.children.len}')
//console.print_debug('Debug: Verified node children: ${verify_node.children.len}')
//console.print_debug('Debug: Original data size: ${parent_data.len}')
//console.print_debug('Debug: Verified data size: ${verify_data.len}')
//console.print_debug('Debug: Data equal: ${verify_data == parent_data}')
return error('Node update failed - children array is empty')
}
return
@@ -195,8 +196,8 @@ pub fn (mut rt RadixTree) insert(key string, value []u8) ! {
}
}
// Searches for a key in the tree
pub fn (mut rt RadixTree) search(key string) ![]u8 {
// Gets a value by key from the tree
pub fn (mut rt RadixTree) get(key string) ![]u8 {
mut current_id := rt.root_id
mut offset := 0
@@ -316,28 +317,127 @@ pub fn (mut rt RadixTree) delete(key string) ! {
return error('Key not found')
}
// Remove the leaf node
// Get the node to delete
mut last_node := deserialize_node(rt.db.get(path.last().node_id)!)!
last_node.is_leaf = false
last_node.value = []u8{}
// If the node has children, just mark it as non-leaf
if last_node.children.len > 0 {
last_node.is_leaf = false
last_node.value = []u8{}
rt.db.set(id: path.last().node_id, data: serialize_node(last_node))!
return
}
// If node has no children, remove it from parent
if last_node.children.len == 0 {
if path.len > 1 {
mut parent_node := deserialize_node(rt.db.get(path[path.len - 2].node_id)!)!
for i, child in parent_node.children {
if child.node_id == path.last().node_id {
parent_node.children.delete(i)
break
}
if path.len > 1 {
mut parent_node := deserialize_node(rt.db.get(path[path.len - 2].node_id)!)!
for i, child in parent_node.children {
if child.node_id == path.last().node_id {
parent_node.children.delete(i)
break
}
rt.db.set(id: path[path.len - 2].node_id, data: serialize_node(parent_node))!
}
rt.db.set(id: path[path.len - 2].node_id, data: serialize_node(parent_node))!
// Delete the node from the database
rt.db.delete(path.last().node_id)!
} else {
// If this is a direct child of the root, just mark it as non-leaf
last_node.is_leaf = false
last_node.value = []u8{}
rt.db.set(id: path.last().node_id, data: serialize_node(last_node))!
}
}
// Lists all keys with a given prefix
pub fn (mut rt RadixTree) list(prefix string) ![]string {
mut result := []string{}
// Handle empty prefix case - will return all keys
if prefix.len == 0 {
rt.collect_all_keys(rt.root_id, '', mut result)!
return result
}
// Start from the root and find all matching keys
rt.find_keys_with_prefix(rt.root_id, '', prefix, mut result)!
return result
}
// Helper function to find all keys with a given prefix
fn (mut rt RadixTree) find_keys_with_prefix(node_id u32, current_path string, prefix string, mut result []string) ! {
node := deserialize_node(rt.db.get(node_id)!)!
// If the current path already matches or exceeds the prefix length
if current_path.len >= prefix.len {
// Check if the current path starts with the prefix
if current_path.starts_with(prefix) {
// If this is a leaf node, add it to the results
if node.is_leaf {
result << current_path
}
// Collect all keys from this subtree
for child in node.children {
child_path := current_path + child.key_part
rt.find_keys_with_prefix(child.node_id, child_path, prefix, mut result)!
}
}
return
}
// Current path is shorter than the prefix, continue searching
for child in node.children {
child_path := current_path + child.key_part
// Check if this child's path could potentially match the prefix
if prefix.starts_with(current_path) {
// The prefix starts with the current path, so we need to check if
// the child's key_part matches the next part of the prefix
prefix_remainder := prefix[current_path.len..]
// If the prefix remainder starts with the child's key_part or vice versa
if prefix_remainder.starts_with(child.key_part) ||
(child.key_part.starts_with(prefix_remainder) && child.key_part.len >= prefix_remainder.len) {
rt.find_keys_with_prefix(child.node_id, child_path, prefix, mut result)!
}
}
}
}
// Helper function to recursively collect all keys under a node
fn (mut rt RadixTree) collect_all_keys(node_id u32, current_path string, mut result []string) ! {
node := deserialize_node(rt.db.get(node_id)!)!
// If this node is a leaf, add its path to the result
if node.is_leaf {
result << current_path
}
// Recursively collect keys from all children
for child in node.children {
child_path := current_path + child.key_part
rt.collect_all_keys(child.node_id, child_path, mut result)!
}
}
// Helper function to get the common prefix of two strings
// Gets all values for keys with a given prefix
pub fn (mut rt RadixTree) getall(prefix string) ![][]u8 {
// Get all matching keys
keys := rt.list(prefix)!
// Get values for each key
mut values := [][]u8{}
for key in keys {
if value := rt.get(key) {
values << value
}
}
return values
}
// Helper function to get the common prefix of two strings
fn get_common_prefix(a string, b string) string {
mut i := 0

View File

@@ -1,48 +1,49 @@
module radixtree
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.ui.console
// Gets a node from the database by its ID
pub fn (mut rt RadixTree) get_node_by_id(id u32) !Node {
node_data := rt.db.get(id)!
node := deserialize_node(node_data)!
println('Debug: Retrieved node ${id} with ${node.children.len} children')
//console.print_debug('Debug: Retrieved node ${id} with ${node.children.len} children')
return node
}
// Logs the current state of a node
pub fn (mut rt RadixTree) debug_node(id u32, msg string) ! {
node := rt.get_node_by_id(id)!
println('Debug: ${msg}')
println(' Node ID: ${id}')
println(' Key Segment: "${node.key_segment}"')
println(' Is Leaf: ${node.is_leaf}')
println(' Children: ${node.children.len}')
//console.print_debug('Debug: ${msg}')
//console.print_debug(' Node ID: ${id}')
//console.print_debug(' Key Segment: "${node.key_segment}"')
//console.print_debug(' Is Leaf: ${node.is_leaf}')
//console.print_debug(' Children: ${node.children.len}')
for child in node.children {
println(' - Child ID: ${child.node_id}, Key Part: "${child.key_part}"')
//console.print_debug(' - Child ID: ${child.node_id}, Key Part: "${child.key_part}"')
}
}
// Prints the current state of the database
pub fn (mut rt RadixTree) debug_db() ! {
println('\nDatabase State:')
println('===============')
//console.print_debug('\nDatabase State:')
//console.print_debug('===============')
mut next_id := rt.db.get_next_id()!
for id := u32(0); id < next_id; id++ {
if data := rt.db.get(id) {
if node := deserialize_node(data) {
println('ID ${id}:')
println(' Key Segment: "${node.key_segment}"')
println(' Is Leaf: ${node.is_leaf}')
println(' Children: ${node.children.len}')
//console.print_debug('ID ${id}:')
//console.print_debug(' Key Segment: "${node.key_segment}"')
//console.print_debug(' Is Leaf: ${node.is_leaf}')
//console.print_debug(' Children: ${node.children.len}')
for child in node.children {
println(' - Child ID: ${child.node_id}, Key Part: "${child.key_part}"')
//console.print_debug(' - Child ID: ${child.node_id}, Key Part: "${child.key_part}"')
}
} else {
println('ID ${id}: Failed to deserialize node')
//console.print_debug('ID ${id}: Failed to deserialize node')
}
} else {
println('ID ${id}: No data')
//console.print_debug('ID ${id}: No data')
}
}
}
@@ -68,7 +69,7 @@ pub fn (mut rt RadixTree) print_tree_from_node(node_id u32, indent string) ! {
}
node_info += ']'
}
println(node_info)
//console.print_debug(node_info)
// Print children recursively with increased indentation
for i, child in node.children {
@@ -84,8 +85,8 @@ pub fn (mut rt RadixTree) print_tree_from_node(node_id u32, indent string) ! {
// Prints the entire tree structure starting from root
pub fn (mut rt RadixTree) print_tree() ! {
println('\nRadix Tree Structure:')
println('===================')
//console.print_debug('\nRadix Tree Structure:')
//console.print_debug('===================')
rt.print_tree_from_node(rt.root_id, '')!
}