Files
herolib/lib/data/tst/tst.v
2025-05-04 08:19:47 +03:00

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
}