refactor: apply project style guidelines

This commit is contained in:
2026-10-05 20:18:53 +02:00
parent 6ba23f579a
commit 2648991d04
32 changed files with 495 additions and 266 deletions
+15 -8
View File
@@ -1,5 +1,7 @@
import "@std/mem"
#! owns item storage and retains its allocator; release storage with deinit.
#! items is a borrowed view invalidated by growth or deinit.
ArrayList proc($T type) type {
return struct {
items []mut T
@@ -8,6 +10,7 @@ ArrayList proc($T type) type {
}
}
#! retain allocator for later growth and cleanup; this call does not allocate.
init proc($T type, allocator mem.Allocator) ArrayList(T) {
return ArrayList(T) {
items = mem.empty(T),
@@ -16,6 +19,7 @@ init proc($T type, allocator mem.Allocator) ArrayList(T) {
}
}
#! release owned storage using the retained allocator and reset the list.
deinit proc($T type, list @mut ArrayList(T)) void {
allocation :: list.items.ptr[..list.capacity]
mem.free(list.allocator, allocation)
@@ -23,10 +27,13 @@ deinit proc($T type, list @mut ArrayList(T)) void {
list.capacity = 0
}
reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.AllocError {
if min_capacity <= list.capacity {
return
}
#! grow owned storage with the retained allocator; growth invalidates borrowed item views.
reserve proc(
$T type,
list @mut ArrayList(T),
min_capacity usize,
) void ! mem.AllocError {
if (min_capacity <= list.capacity) return
new_capacity := 8
if list.capacity >= 8 {
@@ -41,9 +48,9 @@ reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
new_capacity = min_capacity
}
length usize :: list.items.len
allocation []mut T :: list.items.ptr[..list.capacity]
grown []mut T :: mem.realloc(list.allocator, allocation, new_capacity) catch |_| {
length :: list.items.len
allocation :: list.items.ptr[..list.capacity]
grown :: mem.realloc(list.allocator, allocation, new_capacity) catch |_| {
return .out_of_memory
}
list.items = grown.ptr[..length]
@@ -52,7 +59,7 @@ reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
}
append proc($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
length usize :: list.items.len
length :: list.items.len
if length == maxval!(usize) {
return .out_of_memory
}
+8 -8
View File
@@ -1,11 +1,11 @@
# Build configuration surface for `brolang build` (v0).
#
# A project's `build.bro` imports this module and declares a top-level constant
# named `config` of type `BuildConfig`. `brolang build [root]` type-checks
# build.bro, reads the config, and writes root/build/name.
#
# Declarative and literal-only: one executable per build. List fields take an
# address-of an array literal (`&["raylib"]`) and default to empty.
#! build configuration surface for `brolang build` (v0).
#!
#! a project's `build.bro` imports this module and declares a top-level constant
#! named `config` of type `BuildConfig`. `brolang build [root]` type-checks
#! build.bro, reads the config, and writes root/build/name.
#!
#! declarative and literal-only: one executable per build. list fields take an
#! address-of an array literal (`&["raylib"]`) and default to empty.
BuildConfig :: struct {
name []u8 # output executable name under root/build
source []u8 # program package directory, relative to build.bro
+5 -11
View File
@@ -17,23 +17,17 @@ print proc($format []u8, $Args type, args Args) void {
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
write = write,
}
io.print(writer, format, Args, args) catch |_| {}
io.print(format, Args, writer, args) catch |_| {}
}
@hide
@hide:file
write proc(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
request := bytes.len
maximum :: usize(maxval!(c_long))
if request > maximum {
request = maximum
}
if (request > maximum) request = maximum
while true {
count :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
if count >= 0 {
return usize(count)
}
if c.__error()?^ != c.EINTR {
return .write_failed
}
if (count >= 0) return usize(count)
if (c.__error()?^ != c.EINTR) return .write_failed
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ EnumMap proc($E, $V type) type {
match typeinfo!(E) {
.enum |info|: return struct {
present [info.fields.len]mut bool # fixme: replace with bitset
values [info.fields.len]mut V
values [info.fields.len]mut V
}
else: compile_error!("EnumMap key must be an enum")
}
+2 -1
View File
@@ -1,6 +1,7 @@
import "@std/mem"
import "@std/testing"
@hide:file
TestEnum :: enum(u8) {
ident = 3
int = 8
@@ -8,7 +9,7 @@ TestEnum :: enum(u8) {
}
handles_sparse_enum_get test {
names EnumMap(TestEnum, []u8) := init({
names EnumMap(TestEnum, []u8) :: init({
ident = "identifier",
int = "integer",
})
+8 -10
View File
@@ -11,6 +11,8 @@ Entry proc($K, $V type) type {
}
}
#! owns entry storage and retains its allocator; release storage with deinit.
#! keys and values are stored by value; any resources they reference remain caller-owned.
HashMap proc(
$K, $V type,
$hash_key proc(key K) usize,
@@ -27,6 +29,7 @@ StringHashMap proc($V type) type {
return HashMap([]u8, V, str_hash, str_eql)
}
#! retain allocator for later growth and cleanup; this call does not allocate.
init proc(
$K, $V type,
$hash_key proc(key K) usize,
@@ -40,7 +43,7 @@ init proc(
}
}
#! free the entries in the hash map.
#! free owned entry storage using the retained allocator, not resources referenced by keys or values.
#! note: this operation invalidates the map.
deinit proc(
$K, $V type,
@@ -81,21 +84,17 @@ put proc(
key K,
value V,
) void ! (PutError | mem.AllocError) {
# check grow
threshold :: map.entries.len - divtrunc!(map.entries.len, 4)
if (map.entries.len == 0 or map.count + 1 > threshold) {
old_entries :: map.entries
new_size :: if (old_entries.len > 0) old_entries.len * 2 else 8
new_entries :: try mem.alloc(Entry(K, V), map.allocator, new_size)
# zero new entries
for (0..new_entries.len) |i| new_entries[i].hash = 0
# move old entries
for old_entries |entry| {
if (entry.hash == 0) continue
# find an empty slot
idx := entry.hash & (new_entries.len - 1)
while new_entries[idx].hash != 0 {
idx = (idx + 1) & (new_entries.len - 1)
@@ -108,7 +107,6 @@ put proc(
mem.free(map.allocator, old_entries)
}
# put new entry
hash :: normalize(hash_key(key))
idx := hash & (map.entries.len - 1)
@@ -132,7 +130,7 @@ put proc(
}
}
@hide
@hide:file
normalize proc(hash usize) usize {
# mapping both 0 and 1 to 1 is safe because equality resolves
# collisions (since hash and key must both be equal).
@@ -142,10 +140,10 @@ normalize proc(hash usize) usize {
#! FNV-1a hash implementation.
#! note: vulnerable to collision attacks.
@hide
@hide:file
str_hash proc(key []u8) usize {
hash u32 := 2166136261 # offset basis
prime u32 := 16777619
prime u32 :: 16777619
for key |byte| {
product u64 :: u64(hash xor u32(byte)) * prime
@@ -155,5 +153,5 @@ str_hash proc(key []u8) usize {
return usize(hash)
}
@hide
@hide:file
str_eql proc(a, b []u8) bool { return mem.eql(a, b) }
+20 -8
View File
@@ -19,15 +19,18 @@ CloseError :: enum {
close_failed
}
#! open a caller-owned handle; release it with close.
open proc(io Io, path [;0]u8, mode FileMode) File ! OpenError {
handle Handle :: try io.vtable.open(io.context, path, mode)
return File{ io = io, handle = handle }
}
#! close the owned handle, invalidating its borrowed readers and writers.
close proc(file File) void ! CloseError {
try file.io.vtable.close(file.io.context, file.handle)
}
#! borrow the file handle; the reader must not outlive the open file.
reader proc(file File) Reader {
return Reader {
context = file.io.context,
@@ -36,6 +39,7 @@ reader proc(file File) Reader {
}
}
#! borrow the file handle; the writer must not outlive the open file.
writer proc(file File) Writer {
return Writer {
context = file.io.context,
@@ -44,7 +48,8 @@ writer proc(file File) Writer {
}
}
@hide system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
@hide:file
system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
request := buffer.len
maximum usize :: usize(maxval!(c_long))
if (request > maximum) request = maximum
@@ -60,7 +65,8 @@ writer proc(file File) Writer {
}
}
@hide system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
@hide:file
system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
request := bytes.len
maximum usize :: usize(maxval!(c_long))
if (request > maximum) request = maximum
@@ -76,7 +82,8 @@ writer proc(file File) Writer {
}
}
@hide system_open proc(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
@hide:file
system_open proc(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
flags := c.O_RDONLY
match mode {
.read_only: flags = c.O_RDONLY
@@ -90,23 +97,28 @@ writer proc(file File) Writer {
}
}
@hide system_close proc(_ ?@mut anyopaque, handle Handle) void ! CloseError {
@hide:file
system_close proc(_ ?@mut anyopaque, handle Handle) void ! CloseError {
if (c.close(handle.file_desc) != 0) return .close_failed
}
@hide system_stdin proc(_ ?@mut anyopaque) Handle {
@hide:file
system_stdin proc(_ ?@mut anyopaque) Handle {
return Handle{ file_desc = c_int(Stream.stdin) }
}
@hide system_stdout proc(_ ?@mut anyopaque) Handle {
@hide:file
system_stdout proc(_ ?@mut anyopaque) Handle {
return Handle{ file_desc = c_int(Stream.stdout) }
}
@hide system_stderr proc(_ ?@mut anyopaque) Handle {
@hide:file
system_stderr proc(_ ?@mut anyopaque) Handle {
return Handle{ file_desc = c_int(Stream.stderr) }
}
@hide system_vtable IoVTable :: IoVTable {
@hide:file
system_vtable IoVTable :: IoVTable {
read = system_read,
write = system_write,
open = system_open,
+33 -14
View File
@@ -1,4 +1,6 @@
import "@ffi/c"
import "@std/mem"
# typeinfo! requires this import even without a qualified meta reference.
import "@std/meta"
ReadError :: enum {
@@ -23,6 +25,7 @@ Stream :: enum(c_int) {
stderr = c.STDERR_FILENO
}
#! context and vtable are borrowed and must outlive derived readers and writers.
Io :: struct {
context ?@mut anyopaque
vtable @IoVTable
@@ -38,12 +41,14 @@ IoVTable :: struct {
stderr @proc(context ?@mut anyopaque) Handle
}
#! borrows its context and handle; it does not close the handle.
Reader :: struct {
context ?@mut anyopaque
handle Handle
read @proc(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
}
#! borrows its context and handle; it does not close the handle.
Writer :: struct {
context ?@mut anyopaque
handle Handle
@@ -101,7 +106,7 @@ stderr proc(io Io) Writer {
}
}
print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
print proc($format []u8, $Args type, output Writer, args Args) void ! WriteError {
inline for parse_format(format.len, format, Args) |token| {
match token.kind {
.unused: break
@@ -120,14 +125,19 @@ print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError
}
@hide:file
write_integer_signed proc(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
write_integer_signed proc(
output Writer,
value i64,
base u64,
uppercase bool,
) void ! WriteError {
buffer [65]mut u8 := undefined
end := buffer.len
current := value
while true {
digit_value i64 :: rem!(current, i64(base))
digit := if (digit_value < 0)
digit :: if (digit_value < 0)
u8(-digit_value)
else
u8(digit_value)
@@ -152,7 +162,12 @@ write_integer_signed proc(output Writer, value i64, base u64, uppercase bool) vo
}
@hide:file
write_integer_unsigned proc(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
write_integer_unsigned proc(
output Writer,
value u64,
base u64,
uppercase bool,
) void ! WriteError {
buffer [65]mut u8 := undefined
end := buffer.len
current := value
@@ -201,9 +216,7 @@ FormatToken :: struct {
@hide:file
parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
tokens [N]mut FormatToken := undefined
for (usize(0))..format.len |index| {
tokens[index] = FormatToken{ kind = .unused, start = 0, end = 0, field = "" }
}
mem.set(FormatToken, tokens[..], FormatToken{ kind = .unused, start = 0, end = 0, field = "" })
field_count := 0
match typeinfo!(Args) {
@@ -314,7 +327,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
if literal_start < format.len {
tokens[token_count] = FormatToken{
kind = .literal,
start = literal_start,
start = literal_start,
end = format.len,
field = "",
}
@@ -351,7 +364,13 @@ scalar_or_distinct_type proc($T type) bool {
}
@hide:file
write_integer proc(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
write_integer proc(
$T type,
output Writer,
value T,
base u64,
uppercase bool,
) void ! WriteError {
match typeinfo!(T) {
.integer: if minval!(T) < 0 {
try write_integer_signed(output, i64(value), base, uppercase)
@@ -369,7 +388,7 @@ write_integer proc(output Writer, $T type, value T, base u64, uppercase bool) vo
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
@hide:file
write_float proc(output Writer, $T type, value T, scientific bool) void ! WriteError {
write_float proc($T type, output Writer, value T, scientific bool) void ! WriteError {
match typeinfo!(T) {
.float: {
buffer [64]mut u8 := undefined
@@ -395,7 +414,7 @@ write_float proc(output Writer, $T type, value T, scientific bool) void ! WriteE
}
@hide:file
write_decimal proc(output Writer, $T type, value T) void ! WriteError {
write_decimal proc($T type, output Writer, value T) void ! WriteError {
match typeinfo!(T) {
.integer: try write_integer(output, value, 10, false)
.float: try write_float(output, value, false)
@@ -409,13 +428,13 @@ write_decimal proc(output Writer, $T type, value T) void ! WriteError {
}
@hide:file
write_character proc(output Writer, $T type, value T) void ! WriteError {
write_character proc($T type, output Writer, value T) void ! WriteError {
match typeinfo!(T) {
.integer: {
if minval!(T) < 0 or maxval!(T) > 255 {
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
}
buffer [1]u8 := [u8(value)]
buffer [1]u8 :: [u8(value)]
try write_all(output, buffer[..])
}
.distinct |backing|: if scalar_or_distinct_type(backing) {
@@ -428,7 +447,7 @@ write_character proc(output Writer, $T type, value T) void ! WriteError {
}
@hide:file
write_default proc(output Writer, $T type, value T) void ! WriteError {
write_default proc($T type, output Writer, value T) void ! WriteError {
match typeinfo!(T) {
.bool: if value {
try write_all(output, "true")
+47 -21
View File
@@ -4,6 +4,7 @@ AllocError :: enum {
out_of_memory
}
#! allocator context and vtable are borrowed and must outlive allocations made through them.
Allocator :: struct {
context ?@mut anyopaque
vtable @AllocatorVTable
@@ -15,23 +16,40 @@ AllocatorVTable :: struct {
free @proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
}
raw_alloc proc(allocator Allocator, size usize, alignment usize) ?*mut u8 {
#! returned storage belongs to the caller; release it with raw_free using the same allocator, size, and alignment.
raw_alloc proc(
allocator Allocator,
size usize,
alignment usize,
) ?*mut u8 {
return allocator.vtable.alloc(allocator.context, size, alignment)
}
raw_realloc proc(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
#! resize caller-owned storage with its original allocator and alignment.
#! on allocation failure with nonzero new_size, original storage remains caller-owned; release the result with raw_free.
raw_realloc proc(
allocator Allocator,
memory ?*mut u8,
old_size usize,
new_size usize,
alignment usize,
) ?*mut u8 {
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
}
raw_free proc(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
#! release caller-owned storage with its original allocator, size, and alignment.
raw_free proc(
allocator Allocator,
memory ?*mut u8,
size usize,
alignment usize,
) void {
allocator.vtable.free(allocator.context, memory, size, alignment)
}
eql proc($T type, left, right []T) bool {
if (left.len != right.len) return false
for (0..left.len) |i| if (left[i] != right[i]) {
return false
}
for (0..left.len) |i| if (left[i] != right[i]) return false
return true
}
@@ -42,17 +60,18 @@ set proc($T type, destination []mut T, value T) void {
}
#! allocate memory for a slice of type `T` with `count` elements.
#! returned storage belongs to the caller; release it with free using the same allocator.
alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
if (count == 0) return empty_slice(T, 0)
element_size usize :: sizeof!(T)
element_size :: sizeof!(T)
if (element_size == 0) return empty_slice(T, count)
if count > divtrunc!(maxval!(usize), element_size) {
return .out_of_memory
}
memory := raw_alloc(allocator, count * element_size, alignof!(T))
memory :: raw_alloc(allocator, count * element_size, alignof!(T))
if memory |bytes| {
pointer *mut T :: ptrcast!(bytes)
return pointer[..count]
@@ -64,17 +83,22 @@ alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
#! reallocate memory for a slice of type `T` with `new_count` elements.
#! reallocating with `new_count == 0` will free the memory and return an empty slice.
#! note: memory must be reallocated with the same allocator that was used to allocate it.
realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
if new_count == memory.len {
return memory
}
#! the result belongs to the caller; release it with free using that allocator.
#! on failure the original slice remains valid and caller-owned.
realloc proc(
$T type,
allocator Allocator,
memory []mut T,
new_count usize,
) []mut T ! AllocError {
if (new_count == memory.len) return memory
if new_count == 0 {
free(allocator, memory)
return empty_slice(T, 0)
}
element_size usize :: sizeof!(T)
element_size :: sizeof!(T)
if (element_size == 0) return empty_slice(T, new_count)
if (new_count > divtrunc!(maxval!(usize), element_size)) return .out_of_memory
@@ -85,7 +109,7 @@ realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mu
old_size = memory.len * element_size
}
resized := raw_realloc(
resized :: raw_realloc(
allocator,
old_memory,
old_size,
@@ -105,8 +129,9 @@ realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mu
#! note: memory must be freed with the same allocator that was used to allocate it.
free proc($T type, allocator Allocator, memory []T) void {
if (memory.len == 0 or sizeof!(T) == 0) return
raw_free(allocator, ptrcast!(
constcast!(memory).ptr),
raw_free(
allocator,
ptrcast!(constcast!(memory).ptr),
memory.len * sizeof!(T),
alignof!(T),
)
@@ -122,14 +147,15 @@ empty_slice proc($T type, count usize) []mut T {
}
#! get an empty slice of type `T` with 0 elements.
#! the view borrows shared empty storage and requires no cleanup.
empty proc($T type) []mut T {
return empty_slice(T, 0)
}
@hide
@hide:file
empty_storage [1]mut u64 := [0]
@hide
@hide:file
malloc_alignment usize :: 16 # note: aarch64-macos libc malloc alignment assumption.
@hide:file
@@ -137,7 +163,7 @@ power_of_two proc(value usize) bool {
if (value == 0) return false
current := value
while current > 1 {
half := divtrunc!(current, 2)
half :: divtrunc!(current, 2)
if (half * 2 != current) return false
current = half
}
@@ -150,7 +176,7 @@ c_alloc proc(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
if (alignment <= malloc_alignment) return ptrcast!(c.malloc(c_ulong(size)))
memory [1]mut ?*mut anyopaque := [null]
status := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
status :: c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if (status != 0) return null
return ptrcast!(memory[0])
@@ -176,7 +202,7 @@ c_realloc proc(
return ptrcast!(c.realloc(old_memory, c_ulong(new_size)))
}
new_memory := c_alloc(null, new_size, alignment)
new_memory :: c_alloc(null, new_size, alignment)
if new_memory |new_bytes| {
copy_size := old_size
if (new_size < copy_size) copy_size = new_size
+4 -1
View File
@@ -15,7 +15,10 @@ fills_typed_values_within_slice_boundaries test {
try testing.expect_equal(u32(305419896), values[2])
try testing.expect_equal(u32(4), values[3])
records [2]mut Record := [Record{ tag = 0, id = 0 }, Record{ tag = 0, id = 0 }]
records [2]mut Record := [
Record{ tag = 0, id = 0 },
Record{ tag = 0, id = 0 },
]
set(records[..], Record{ tag = 3, id = maxval!(u32) })
for records |record| {
try testing.expect_equal(u8(3), record.tag)
+10 -4
View File
@@ -1,18 +1,24 @@
import "@std/testing"
@hide:file
TestTokenKind :: enum(u8) {
ident = 3
int = 8
eof = 21
}
@hide:file
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
@hide:file
TestArrayAlias :: alias [3]u16
@hide:file
TestInner :: distinct u16
@hide:file
TestOuter :: distinct TestInner
@hide:file
TestOuterAlias :: alias TestOuter
@hide
@hide:file
array_info_matches proc($Array, $Child type, $len usize) bool {
return match typeinfo!(Array) {
.array |info|: info.child == Child and info.len == len
@@ -20,7 +26,7 @@ array_info_matches proc($Array, $Child type, $len usize) bool {
}
}
@hide
@hide:file
distinct_info_matches proc($Distinct, $Backing type) bool {
return match typeinfo!(Distinct) {
.distinct |backing|: backing == Backing
@@ -43,7 +49,7 @@ distinct_reflection_exposes_immediate_backing test {
}
enum_field_struct_defaults test {
names TestNames := {
names TestNames :: {
ident = "identifier",
int = "integer",
}
@@ -60,6 +66,6 @@ enum_field_struct_defaults test {
if (field!(names, "eof")) |_| try testing.expect(false)
empty TestNames := {}
empty TestNames :: {}
if (field!(empty, "ident")) |_| try testing.expect(false)
}
+3 -2
View File
@@ -16,6 +16,7 @@ Pair proc($V type) type {
}
#! initializes a static string map from a list of key-value pairs (constructed at compile-time).
#! returned views use compile-time storage and require no cleanup.
init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
if N > usize(maxval!(u32)) {
compile_error!("static string map has too many entries")
@@ -39,7 +40,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
}
if N == 0 {
len_indexes [0]u32 := undefined
len_indexes [0]u32 :: undefined
return StaticStringMap(V){
keys = keys[..],
values = values[..],
@@ -83,7 +84,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
get proc($V type, map @StaticStringMap(V), key []u8) ?V {
if (map.keys.len == 0 or key.len > maxval!(u32)) return null
length := u32(key.len)
length :: u32(key.len)
if (length < map.min_len or length > map.max_len) return null
idx := usize(map.len_indexes[usize(length)])