This commit is contained in:
2025-03-24 06:44:39 +01:00
parent 0df10f5cb3
commit 598b312140
371 changed files with 8238 additions and 9082 deletions

View File

@@ -23,7 +23,8 @@ fn test_basic_operations() ! {
// Test delete
rt.delete('test')!
mut ok := false
if _ := rt.get('test') {ok = true
if _ := rt.get('test') {
ok = true
} else {
ok = false
}
@@ -101,7 +102,7 @@ fn test_update_metadata() ! {
// Set initial entry
rt.set(prefix, initial_metadata)!
// Verify initial value
value := rt.get(prefix)!
assert value.bytestr() == 'metadata_initial'

View File

@@ -3,17 +3,17 @@ module radixtree
import freeflowuniverse.herolib.ui.console
fn test_getall() ! {
//console.print_debug('Starting 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'
'user_1': 'data1'
'user_2': 'data2'
'user_3': 'data3'
'admin_1': 'admin_data1'
'admin_2': 'admin_data2'
'guest': 'guest_data'
}
// Set all test data
@@ -22,57 +22,57 @@ fn test_getall() ! {
}
// Test getall with 'user_' prefix
//console.print_debug('Testing getall with prefix "user_"')
// console.print_debug('Testing getall with prefix "user_"')
user_values := rt.getall('user_')!
//console.print_debug('user_values count: ${user_values.len}')
// 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_"')
// console.print_debug('Testing getall with prefix "admin_"')
admin_values := rt.getall('admin_')!
//console.print_debug('admin_values count: ${admin_values.len}')
// 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')
// console.print_debug('Testing getall with empty prefix')
all_values := rt.getall('')!
//console.print_debug('all_values count: ${all_values.len}')
// 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"')
// 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}')
// 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')
// console.print_debug('test_getall completed successfully')
}

View File

@@ -3,19 +3,19 @@ module radixtree
import freeflowuniverse.herolib.ui.console
fn test_list() ! {
//console.print_debug('Starting 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'
'apple': 'fruit1'
'application': 'software1'
'apply': 'verb1'
'banana': 'fruit2'
'ball': 'toy1'
'cat': 'animal1'
'car': 'vehicle1'
'cargo': 'shipping1'
}
// Set all test data
@@ -24,56 +24,56 @@ fn test_list() ! {
}
// Test prefix 'app' - should return apple, application, apply
//console.print_debug('Testing prefix "app"')
// console.print_debug('Testing prefix "app"')
app_keys := rt.list('app')!
//console.print_debug('app_keys: ${app_keys}')
// 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"')
// console.print_debug('Testing prefix "ba"')
ba_keys := rt.list('ba')!
//console.print_debug('ba_keys: ${ba_keys}')
// 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"')
// console.print_debug('Testing prefix "car"')
car_keys := rt.list('car')!
//console.print_debug('car_keys: ${car_keys}')
// 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"')
// console.print_debug('Testing prefix "z"')
z_keys := rt.list('z')!
//console.print_debug('z_keys: ${z_keys}')
// 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')
// console.print_debug('Testing empty prefix')
all_keys := rt.list('')!
//console.print_debug('all_keys: ${all_keys}')
// 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"')
// console.print_debug('Testing exact key as prefix "apple"')
exact_key := rt.list('apple')!
//console.print_debug('exact_key: ${exact_key}')
// console.print_debug('exact_key: ${exact_key}')
assert exact_key.len == 1
assert exact_key[0] == 'apple'
//console.print_debug('test_list completed successfully')
// console.print_debug('test_list completed successfully')
}
fn test_list_with_deletion() ! {
//console.print_debug('Starting 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
@@ -83,115 +83,115 @@ fn test_list_with_deletion() ! {
rt.set('other', 'value4'.bytes())!
// Initial check
//console.print_debug('Testing initial prefix "test"')
// console.print_debug('Testing initial prefix "test"')
test_keys := rt.list('test')!
//console.print_debug('test_keys: ${test_keys}')
// 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"')
// console.print_debug('Deleting key "test2"')
rt.delete('test2')!
// Check after deletion
//console.print_debug('Testing prefix "test" 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}')
// 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')
// console.print_debug('Testing empty prefix')
all_keys := rt.list('')!
//console.print_debug('all_keys: ${all_keys}')
// 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')
// console.print_debug('test_list_with_deletion completed successfully')
}
fn test_list_edge_cases() ! {
//console.print_debug('Starting 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"')
// console.print_debug('Testing empty tree with prefix "any"')
empty_result := rt.list('any')!
//console.print_debug('empty_result: ${empty_result}')
// console.print_debug('empty_result: ${empty_result}')
assert empty_result.len == 0
// Set a single key
//console.print_debug('Setting single key "single"')
// 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"')
// console.print_debug('Testing prefix longer than any key "singlelonger"')
long_prefix := rt.list('singlelonger')!
//console.print_debug('long_prefix: ${long_prefix}')
// 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"')
// console.print_debug('Testing partial prefix match "sing"')
partial := rt.list('sing')!
//console.print_debug('partial: ${partial}')
// 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')
// 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')
// console.print_debug('Testing long prefix')
long_prefix_result := rt.list('a'.repeat(100))!
//console.print_debug('long_prefix_result: ${long_prefix_result}')
// 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')
// console.print_debug('test_list_edge_cases completed successfully')
}
fn test_list_performance() ! {
//console.print_debug('Starting 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')
// 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)')
// console.print_debug('Setting 500 keys (100 for each prefix)')
for prefix in prefixes {
for i in 0..100 {
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')
// console.print_debug('Testing retrieval by each prefix')
for prefix in prefixes {
//console.print_debug('Testing prefix "${prefix}"')
// console.print_debug('Testing prefix "${prefix}"')
keys := rt.list(prefix)!
//console.print_debug('Found ${keys.len} keys for prefix "${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')
// console.print_debug('Testing retrieval of all keys')
all_keys := rt.list('')!
//console.print_debug('Found ${all_keys.len} total keys')
// console.print_debug('Found ${all_keys.len} total keys')
assert all_keys.len == 500
//console.print_debug('test_list_performance completed successfully')
// console.print_debug('test_list_performance completed successfully')
}

View File

@@ -43,9 +43,9 @@ pub fn new(args NewArgs) !RadixTree {
)!
mut root_id := u32(1) // First ID in ourdb is now 1 instead of 0
//console.print_debug('Debug: Initializing root node')
// console.print_debug('Debug: Initializing root node')
if db.get_next_id()! == 1 {
//console.print_debug('Debug: Creating new root node')
// console.print_debug('Debug: Creating new root node')
root := Node{
key_segment: ''
value: []u8{}
@@ -53,14 +53,14 @@ pub fn new(args NewArgs) !RadixTree {
is_leaf: false
}
root_id = db.set(data: serialize_node(root))!
//console.print_debug('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 {
//console.print_debug('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 :=
// root_node :=
deserialize_node(root_data)!
//console.print_debug('Debug: Root node has ${root_node.children.len} children')
// console.print_debug('Debug: Root node has ${root_node.children.len} children')
}
return RadixTree{
@@ -104,56 +104,56 @@ pub fn (mut rt RadixTree) set(key string, value []u8) ! {
children: []NodeRef{}
is_leaf: true
}
//console.print_debug('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))!
//console.print_debug('Debug: Created node ID ${new_id}')
// console.print_debug('Debug: Created node ID ${new_id}')
// Create new child reference and update parent node
//console.print_debug('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)!)!
//console.print_debug('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
}
//console.print_debug('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
//console.print_debug('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)
//console.print_debug('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
//console.print_debug('Debug: Verifying serialization...')
// console.print_debug('Debug: Verifying serialization...')
if _ := deserialize_node(parent_data) {
//console.print_debug('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 {
//console.print_debug('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
//console.print_debug('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
//console.print_debug('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)!
//console.print_debug('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 {
//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}')
// 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
@@ -339,7 +339,7 @@ pub fn (mut rt RadixTree) delete(key string) ! {
}
}
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 {
@@ -368,7 +368,7 @@ pub fn (mut rt RadixTree) list(prefix string) ![]string {
// 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
@@ -377,7 +377,7 @@ fn (mut rt RadixTree) find_keys_with_prefix(node_id u32, current_path string, pr
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
@@ -386,20 +386,21 @@ fn (mut rt RadixTree) find_keys_with_prefix(node_id u32, current_path string, pr
}
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) {
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)!
}
}
@@ -409,12 +410,12 @@ fn (mut rt RadixTree) find_keys_with_prefix(node_id u32, current_path string, pr
// 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
@@ -427,7 +428,7 @@ fn (mut rt RadixTree) collect_all_keys(node_id u32, current_path string, mut res
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 {
@@ -435,7 +436,7 @@ pub fn (mut rt RadixTree) getall(prefix string) ![][]u8 {
values << value
}
}
return values
}