433 lines
14 KiB
V
433 lines
14 KiB
V
module tst
|
|
|
|
import freeflowuniverse.herolib.data.ourdb
|
|
|
|
// Represents a node in the ternary search tree
|
|
struct Node {
|
|
mut:
|
|
character u8 // The character stored at this nodexs
|
|
is_end_of_string bool // Flag indicating if this node represents the end of a key
|
|
value []u8 // The value associated with the key (if this node is the end of a key)
|
|
left_id u32 // Database ID for left child (character < node.character)
|
|
middle_id u32 // Database ID for middle child (character == node.character)
|
|
right_id u32 // Database ID for right child (character > node.character)
|
|
}
|
|
|
|
// TST represents a ternary search tree data structure
|
|
@[heap]
|
|
pub struct TST {
|
|
mut:
|
|
db &ourdb.OurDB // Database for persistent storage
|
|
root_id u32 // Database ID of the root node
|
|
}
|
|
|
|
@[params]
|
|
pub struct NewArgs {
|
|
pub mut:
|
|
path string
|
|
reset bool
|
|
}
|
|
|
|
// Creates a new ternary search tree with the specified database path
|
|
pub fn new(args NewArgs) !TST {
|
|
println('Creating new TST with path: ${args.path}, reset: ${args.reset}')
|
|
mut db := ourdb.new(
|
|
path: args.path
|
|
record_size_max: 1024 * 1024 // 1MB
|
|
incremental_mode: true
|
|
reset: args.reset
|
|
)!
|
|
|
|
mut root_id := u32(1) // First ID in ourdb is now 1 instead of 0
|
|
|
|
if db.get_next_id()! == 1 {
|
|
// Create a new root node if the database is empty
|
|
// We'll use a null character (0) for the root node
|
|
println('Creating new root node')
|
|
root := Node{
|
|
character: 0
|
|
is_end_of_string: false
|
|
value: []u8{}
|
|
left_id: 0
|
|
middle_id: 0
|
|
right_id: 0
|
|
}
|
|
root_id = db.set(data: serialize_node(root))!
|
|
println('Root node created with ID: ${root_id}')
|
|
assert root_id == 1 // First ID is now 1
|
|
} else {
|
|
// Database already exists, just get the root node
|
|
println('Database already exists, getting root node')
|
|
root_data := db.get(1)! // Get root node with ID 1
|
|
root := deserialize_node(root_data)!
|
|
println('Root node retrieved: character=${root.character}, is_end=${root.is_end_of_string}, left=${root.left_id}, middle=${root.middle_id}, right=${root.right_id}')
|
|
root_id = 1 // Ensure we're using ID 1 for the root
|
|
}
|
|
|
|
return TST{
|
|
db: &db
|
|
root_id: root_id
|
|
}
|
|
}
|
|
|
|
// Sets a key-value pair in the tree
|
|
pub fn (mut self TST) set(key string, value []u8) ! {
|
|
normalized_key := namefix(key)
|
|
println('Setting key: "${key}" (normalized: "${normalized_key}")')
|
|
|
|
if normalized_key.len == 0 {
|
|
return error('Empty key not allowed')
|
|
}
|
|
|
|
// If the tree is empty, create a root node
|
|
if self.root_id == 0 {
|
|
println('Tree is empty, creating root node')
|
|
root := Node{
|
|
character: 0
|
|
is_end_of_string: false
|
|
value: []u8{}
|
|
left_id: 0
|
|
middle_id: 0
|
|
right_id: 0
|
|
}
|
|
self.root_id = self.db.set(data: serialize_node(root))!
|
|
println('Root node created with ID: ${self.root_id}')
|
|
}
|
|
|
|
// Insert the key-value pair
|
|
mut last_node_id := self.insert_recursive(self.root_id, normalized_key, 0, value)!
|
|
println('Key "${normalized_key}" inserted to node ${last_node_id}')
|
|
|
|
// Make sure the last node is marked as end of string with the value
|
|
if last_node_id != 0 {
|
|
node_data := self.db.get(last_node_id)!
|
|
mut node := deserialize_node(node_data)!
|
|
|
|
// Ensure this node is marked as the end of a string
|
|
if !node.is_end_of_string {
|
|
println('Setting node ${last_node_id} as end of string')
|
|
node.is_end_of_string = true
|
|
node.value = value
|
|
self.db.set(id: last_node_id, data: serialize_node(node))!
|
|
}
|
|
}
|
|
|
|
println('Key "${normalized_key}" inserted successfully')
|
|
}
|
|
|
|
// Recursive helper function for insertion
|
|
fn (mut self TST) insert_recursive(node_id u32, key string, pos int, value []u8) !u32 {
|
|
// Check for position out of bounds
|
|
if pos >= key.len {
|
|
println('Position ${pos} is out of bounds for key "${key}"')
|
|
return error('Position out of bounds')
|
|
}
|
|
|
|
// If we've reached the end of the tree, create a new node
|
|
if node_id == 0 {
|
|
println('Creating new node for character: ${key[pos]} (${key[pos].ascii_str()}) at position ${pos}')
|
|
|
|
// Create a node for this character
|
|
new_node := Node{
|
|
character: key[pos]
|
|
is_end_of_string: pos == key.len - 1
|
|
value: if pos == key.len - 1 { value.clone() } else { []u8{} }
|
|
left_id: 0
|
|
middle_id: 0
|
|
right_id: 0
|
|
}
|
|
new_id := self.db.set(data: serialize_node(new_node))!
|
|
println('New node created with ID: ${new_id}, character: ${key[pos]} (${key[pos].ascii_str()}), is_end: ${pos == key.len - 1}')
|
|
|
|
// If this is the last character in the key, we're done
|
|
if pos == key.len - 1 {
|
|
return new_id
|
|
}
|
|
|
|
// Otherwise, create the next node in the sequence and link to it
|
|
next_id := self.insert_recursive(0, key, pos + 1, value)!
|
|
|
|
// Update the middle link
|
|
node_data := self.db.get(new_id)!
|
|
mut updated_node := deserialize_node(node_data)!
|
|
updated_node.middle_id = next_id
|
|
self.db.set(id: new_id, data: serialize_node(updated_node))!
|
|
|
|
return new_id
|
|
}
|
|
|
|
// Get the current node with error handling
|
|
node_data := self.db.get(node_id) or {
|
|
println('Failed to get node data for ID ${node_id}')
|
|
return error('Node retrieval error: ${err}')
|
|
}
|
|
|
|
mut node := deserialize_node(node_data) or {
|
|
println('Failed to deserialize node with ID ${node_id}')
|
|
return error('Node deserialization error: ${err}')
|
|
}
|
|
|
|
println('Node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}')
|
|
|
|
// Compare the current character with the node's character
|
|
if key[pos] < node.character {
|
|
println('Going left for character: ${key[pos]} (${key[pos].ascii_str()}) < ${node.character} (${node.character.ascii_str()})')
|
|
// Go left
|
|
node.left_id = self.insert_recursive(node.left_id, key, pos, value)!
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
} else if key[pos] > node.character {
|
|
println('Going right for character: ${key[pos]} (${key[pos].ascii_str()}) > ${node.character} (${node.character.ascii_str()})')
|
|
// Go right
|
|
node.right_id = self.insert_recursive(node.right_id, key, pos, value)!
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
} else {
|
|
// Equal - go middle
|
|
if pos == key.len - 1 {
|
|
println('End of key reached, setting is_end_of_string=true')
|
|
// We've reached the end of the key
|
|
node.is_end_of_string = true
|
|
node.value = value
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
} else {
|
|
println('Going middle for next character: ${key[pos + 1]} (${key[pos + 1].ascii_str()})')
|
|
// Move to the next character in the key
|
|
node.middle_id = self.insert_recursive(node.middle_id, key, pos + 1, value)!
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
}
|
|
}
|
|
|
|
return node_id
|
|
}
|
|
|
|
// Gets a value by key from the tree
|
|
pub fn (mut self TST) get(key string) ![]u8 {
|
|
normalized_key := namefix(key)
|
|
println('Getting key: "${key}" (normalized: "${normalized_key}")')
|
|
|
|
if normalized_key.len == 0 {
|
|
return error('Empty key not allowed')
|
|
}
|
|
|
|
if self.root_id == 0 {
|
|
return error('Tree is empty')
|
|
}
|
|
|
|
return self.search_recursive(self.root_id, normalized_key, 0)!
|
|
}
|
|
|
|
// Very simple recursive search with explicit structure to make the compiler happy
|
|
fn (mut self TST) search_recursive(node_id u32, key string, pos int) ![]u8 {
|
|
// Base cases
|
|
if node_id == 0 {
|
|
println('Node ID is 0, key not found')
|
|
return error('Key not found')
|
|
}
|
|
|
|
if pos >= key.len {
|
|
println('Position ${pos} out of bounds for key "${key}"')
|
|
return error('Key not found - position out of bounds')
|
|
}
|
|
|
|
// Get the node
|
|
node_data := self.db.get(node_id) or {
|
|
println('Failed to get node ${node_id}')
|
|
return error('Node not found in database')
|
|
}
|
|
|
|
node := deserialize_node(node_data) or {
|
|
println('Failed to deserialize node ${node_id}')
|
|
return error('Failed to deserialize node')
|
|
}
|
|
|
|
println('Searching node ${node_id}: char=${node.character}, pos=${pos}, key_char=${key[pos]}')
|
|
|
|
mut result := []u8{}
|
|
|
|
// Left branch
|
|
if key[pos] < node.character {
|
|
println('Going left')
|
|
result = self.search_recursive(node.left_id, key, pos) or { return error(err.str()) }
|
|
return result
|
|
}
|
|
|
|
// Right branch
|
|
if key[pos] > node.character {
|
|
println('Going right')
|
|
result = self.search_recursive(node.right_id, key, pos) or { return error(err.str()) }
|
|
return result
|
|
}
|
|
|
|
// Character matches
|
|
println('Character match')
|
|
|
|
// At end of key
|
|
if pos == key.len - 1 {
|
|
if node.is_end_of_string {
|
|
println('Found key')
|
|
// Copy the value
|
|
for i in 0 .. node.value.len {
|
|
result << node.value[i]
|
|
}
|
|
return result
|
|
} else {
|
|
println('Character match but not end of string')
|
|
return error('Key not found - not marked as end of string')
|
|
}
|
|
}
|
|
|
|
// Not at end of key, go to middle
|
|
if node.middle_id == 0 {
|
|
println('No middle child')
|
|
return error('Key not found - no middle child')
|
|
}
|
|
|
|
println('Going to middle child')
|
|
result = self.search_recursive(node.middle_id, key, pos + 1) or { return error(err.str()) }
|
|
return result
|
|
}
|
|
|
|
// Deletes a key from the tree
|
|
pub fn (mut self TST) delete(key string) ! {
|
|
normalized_key := namefix(key)
|
|
println('Deleting key: "${key}" (normalized: "${normalized_key}")')
|
|
|
|
if normalized_key.len == 0 {
|
|
return error('Empty key not allowed')
|
|
}
|
|
|
|
if self.root_id == 0 {
|
|
return error('Tree is empty')
|
|
}
|
|
|
|
self.delete_recursive(self.root_id, normalized_key, 0)!
|
|
println('Key "${normalized_key}" deleted successfully')
|
|
}
|
|
|
|
// Recursive helper function for deletion
|
|
fn (mut self TST) delete_recursive(node_id u32, key string, pos int) !bool {
|
|
if node_id == 0 {
|
|
println('Node ID is 0, key not found')
|
|
return error('Key not found')
|
|
}
|
|
|
|
// Check for position out of bounds
|
|
if pos >= key.len {
|
|
println('Position ${pos} is out of bounds for key "${key}"')
|
|
return error('Key not found - position out of bounds')
|
|
}
|
|
|
|
// Get the current node with error handling
|
|
node_data := self.db.get(node_id) or {
|
|
println('Failed to get node data for ID ${node_id}')
|
|
return error('Node retrieval error: ${err}')
|
|
}
|
|
|
|
mut node := deserialize_node(node_data) or {
|
|
println('Failed to deserialize node with ID ${node_id}')
|
|
return error('Node deserialization error: ${err}')
|
|
}
|
|
|
|
println('Deleting from node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, pos=${pos}')
|
|
mut deleted := false
|
|
|
|
if key[pos] < node.character {
|
|
println('Going left: ${key[pos]} (${key[pos].ascii_str()}) < ${node.character} (${node.character.ascii_str()})')
|
|
// Go left
|
|
if node.left_id == 0 {
|
|
println('Left child is null, key not found')
|
|
return error('Key not found')
|
|
}
|
|
|
|
deleted = self.delete_recursive(node.left_id, key, pos)!
|
|
if deleted && node.left_id != 0 {
|
|
// Check if the left child has been deleted
|
|
if _ := self.db.get(node.left_id) {
|
|
// Child still exists
|
|
println('Left child still exists')
|
|
} else {
|
|
// Child has been deleted
|
|
println('Left child has been deleted, updating node')
|
|
node.left_id = 0
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
}
|
|
}
|
|
} else if key[pos] > node.character {
|
|
println('Going right: ${key[pos]} (${key[pos].ascii_str()}) > ${node.character} (${node.character.ascii_str()})')
|
|
// Go right
|
|
if node.right_id == 0 {
|
|
println('Right child is null, key not found')
|
|
return error('Key not found')
|
|
}
|
|
|
|
deleted = self.delete_recursive(node.right_id, key, pos)!
|
|
if deleted && node.right_id != 0 {
|
|
// Check if the right child has been deleted
|
|
if _ := self.db.get(node.right_id) {
|
|
// Child still exists
|
|
println('Right child still exists')
|
|
} else {
|
|
// Child has been deleted
|
|
println('Right child has been deleted, updating node')
|
|
node.right_id = 0
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
}
|
|
}
|
|
} else {
|
|
// Equal
|
|
println('Character matches: ${key[pos]} (${key[pos].ascii_str()}) == ${node.character} (${node.character.ascii_str()})')
|
|
if pos == key.len - 1 {
|
|
// We've reached the end of the key
|
|
if node.is_end_of_string {
|
|
// Found the key
|
|
println('End of key reached and is_end_of_string=true, found the key')
|
|
if node.left_id == 0 && node.middle_id == 0 && node.right_id == 0 {
|
|
// Node has no children, delete it
|
|
println('Node has no children, deleting it')
|
|
self.db.delete(node_id)!
|
|
return true
|
|
} else {
|
|
// Node has children, just mark it as not end of string
|
|
println('Node has children, marking it as not end of string')
|
|
node.is_end_of_string = false
|
|
node.value = []u8{}
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
return false
|
|
}
|
|
} else {
|
|
println('End of key reached but is_end_of_string=false, key not found')
|
|
return error('Key not found')
|
|
}
|
|
} else {
|
|
// Move to the next character in the key
|
|
println('Moving to next character: ${key[pos + 1]} (${key[pos + 1].ascii_str()})')
|
|
if node.middle_id == 0 {
|
|
println('Middle child is null, key not found')
|
|
return error('Key not found')
|
|
}
|
|
|
|
deleted = self.delete_recursive(node.middle_id, key, pos + 1)!
|
|
if deleted && node.middle_id != 0 {
|
|
// Check if the middle child has been deleted
|
|
if _ := self.db.get(node.middle_id) {
|
|
// Child still exists
|
|
println('Middle child still exists')
|
|
} else {
|
|
// Child has been deleted
|
|
println('Middle child has been deleted, updating node')
|
|
node.middle_id = 0
|
|
self.db.set(id: node_id, data: serialize_node(node))!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this node has no children and is not the end of a string, delete it
|
|
if node.left_id == 0 && node.middle_id == 0 && node.right_id == 0 && !node.is_end_of_string {
|
|
println('Node has no children and is not end of string, deleting it')
|
|
self.db.delete(node_id)!
|
|
return true
|
|
}
|
|
|
|
return deleted
|
|
}
|