diff --git a/source/strpool.hon b/source/strpool.hon index e69de29..678bb27 100644 --- a/source/strpool.hon +++ b/source/strpool.hon @@ -0,0 +1,80 @@ +import "@std/mem" +import "@std/arraylist" +import "@std/hashmap" +import "@std/testing" + +StringId :: alias usize + +StringPool :: struct { + ids hashmap.StringHashMap(StringId) # string → id + strings arraylist.ArrayList([]u8) # id → owned string + allocator mem.Allocator +} + +init func(allocator mem.Allocator) StringPool { + return StringPool{ + ids = hashmap.init(allocator), + strings = arraylist.init(allocator), + allocator = allocator, + } +} + +deinit func(pool @mut StringPool) void { + hashmap.deinit(&pool.ids) + for pool.strings.items |str| { + mem.free(pool.allocator, str) + } + arraylist.deinit(&pool.strings) +} + +intern func(pool @mut StringPool, str []u8) StringId ! mem.AllocError { + if hashmap.get(&pool.ids, str) |id| return id + + id :: pool.strings.items.len + owned_str []mut u8 :: try mem.alloc(u8, pool.allocator, str.len) + errdefer mem.free(pool.allocator, owned_str) + memcopy!(owned_str, str) + + try arraylist.append(&pool.strings, owned_str) + errdefer _ = arraylist.pop(&pool.strings) + + hashmap.put(&pool.ids, owned_str, id) catch |err| { + match err { + .key_exists: unreachable + else: return err + } + } + + return id +} + +get_str func(pool @StringPool, id StringId) ?[]u8 { + if (id >= pool.strings.items.len) return null + return pool.strings.items[id] +} + +get_id func(pool @StringPool, str []u8) ?StringId { + return hashmap.get(&pool.ids, str) +} + +handles_intern test { + pool StringPool = init(mem.c_allocator) + defer deinit(&pool) + + input [5]mut u8 = ['h', 'e', 'l', 'l', 'o'] + id :: try intern(&pool, input[..]) + duplicate :: try intern(&pool, "hello") + input[0] = 'j' + + same_id :: get_id(&pool, "hello") + mutated_id :: get_id(&pool, input[..]) + hello_str :: get_str(&pool, id) + invalid_str :: get_str(&pool, id + 1) + + try testing.expect_equal(0, id) + try testing.expect_equal(id, duplicate) + try testing.expect_equal(id, same_id?) + try testing.expect_equal(null, mutated_id) + try testing.expect_equal(null, invalid_str) + try testing.expect_equal("hello", hello_str?) +} diff --git a/std/arraylist/arraylist.hon b/std/arraylist/arraylist.hon index 9088da7..4eb28d1 100644 --- a/std/arraylist/arraylist.hon +++ b/std/arraylist/arraylist.hon @@ -62,6 +62,13 @@ append func($T type, list @mut ArrayList(T), value T) void ! mem.AllocError { return } +pop func($T type, list @mut ArrayList(T)) ?T { + if (list.items.len == 0) return null + value :: list.items[list.items.len - 1] + list.items = list.items.ptr[..list.items.len - 1] + return value +} + clear func($T type, list @mut ArrayList(T)) void { list.items = list.items.ptr[..0] } diff --git a/std/testing/testing.hon b/std/testing/testing.hon index 964dab3..e5e08c0 100644 --- a/std/testing/testing.hon +++ b/std/testing/testing.hon @@ -38,11 +38,9 @@ expect_equal func($T type, expected, actual T, location SourceLocation) void ! E debug.print("{s}:{d}:{d}: expected and actual slices differ\n", {location.file, location.line, location.column}) return .expectation_failed } - else: { - if expected != actual { - debug.print("{s}:{d}:{d}: expected {}, found {}\n", {location.file, location.line, location.column, expected, actual}) - return .expectation_failed - } + else: if expected != actual { + debug.print("{s}:{d}:{d}: expected {}, found {}\n", {location.file, location.line, location.column, expected, actual}) + return .expectation_failed } } }