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 capacity usize allocator mem.Allocator } } #! 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 = &[], capacity = 0, allocator = allocator, } } #! 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) list.items = &[] list.capacity = 0 } #! 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 { half :: divtrunc!(list.capacity, 2) if list.capacity > maxval!(usize) - half { new_capacity = min_capacity } else { new_capacity = list.capacity + half } } if new_capacity < min_capacity { new_capacity = min_capacity } 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] list.capacity = new_capacity return } append proc($T type, list @mut ArrayList(T), value T) void ! mem.AllocError { length :: list.items.len if length == maxval!(usize) { return .out_of_memory } try reserve(list, length + 1) list.items = list.items.ptr[..length + 1] list.items[length] = value return } pop proc($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 proc($T type, list @mut ArrayList(T)) void { list.items = list.items.ptr[..0] }