From ce83b820cedca374f2cdc90f9a60ed70baab717c Mon Sep 17 00:00:00 2001 From: dmitrii-f-t27 Date: Sat, 17 Oct 2026 19:39:59 -0300 Subject: [PATCH] spec(port): port specs/port/trinity/src/tri/compat/managed_list.t27 (Closes #9860) Port gHashTag/trinity:src/tri/compat/managed_list.zig (at ee512359254b, ~129 lines) as specs/port/trinity/src/tri/compat/managed_list.t27 - Roadmap Stage 5, goal #4547, target t27c gen-rust. The original is a generic wrapper, ManagedList(comptime T: type), restoring the Zig 0.14 allocator-with-list API over Zig 0.15's pass-the-allocator API. Generics cannot be expressed in this language, so the port instantiates the wrapper's decisions for the concrete element type u32 at a fixed capacity of 16 - the generic itself (original line 17) is the named missing piece, everything it wraps carries: the empty init, append within capacity, len, isEmpty, get, set, clearRetainingCapacity (the count drops, the storage stays), the pop pair (an empty list pops nothing, a non-empty one shrinks by one) and the iterator (has-next at the index, next returning the item and the advanced index). One generator quirk is worked around in place and commented: a copy-then-indexed-write-then-return emits the wrong return variable, and a trailing alias keeps the picker on the mutated copy. The allocator that travels with the list is the caller's plumbing here. All four generators (gen, gen-rust, gen-c, gen-verilog) emit this spec before the push. Verified: parse OK, spec-status IMPLEMENTED, typecheck 0 errors, coverage 11/11, test-report 7/7 pass, 0 BLOCKED, gen 158 lines with 0 'not yet implemented', boundary exactly this one file, dupe_scan: all 11 function names unique, bare scan reports zero error lines. Dmitrii Fedorov ([@dmitrii-f-t27](https://github.com/dmitrii-f-t27)) --- .../trinity/src/tri/compat/managed_list.t27 | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 specs/port/trinity/src/tri/compat/managed_list.t27 diff --git a/specs/port/trinity/src/tri/compat/managed_list.t27 b/specs/port/trinity/src/tri/compat/managed_list.t27 new file mode 100644 index 0000000000..d5ce9298a4 --- /dev/null +++ b/specs/port/trinity/src/tri/compat/managed_list.t27 @@ -0,0 +1,181 @@ +module port::trinity::managed_list; + +// Port of gHashTag/trinity:src/tri/compat/managed_list.zig (at ee512359254b, +// ~129 lines) - t27 issue #9860, Roadmap Stage 5 (goal #4547), target t27c +// gen-rust. The original is a GENERIC wrapper, ManagedList(comptime T: type), +// restoring the Zig 0.14 allocator-with-list API over Zig 0.15's +// pass-the-allocator-everywhere API. Generics cannot be expressed in this +// language, so the port instantiates the wrapper's decisions for the concrete +// element type u32 with a fixed capacity of 16; the generic itself (original +// line 17) is the named missing piece - everything it wraps carries. The +// allocator that travels with the list is the caller's plumbing here. + +// The fixed capacity of the port's instantiation. +pub const MANAGED_LIST_CAPACITY: usize = 16; + +// The wrapper's state: the items and the count. +pub struct TrinityManagedList { + items: [16]u32, + len: usize, +} + +// Lines 25-30: init - an empty list. +pub fn ManagedList() -> TrinityManagedList { + const empty = TrinityManagedList { items: [_]u32{0} ** 16, len: 0 }; + return empty; +} + +// Lines 40-43: append - the allocator is provided automatically (the +// wrapper's whole point); the list grows within its capacity. +pub fn managed_list_append(l: TrinityManagedList, item: u32) -> TrinityManagedList { + var next = l; + if (next.len < MANAGED_LIST_CAPACITY) { + next.items[next.len] = item; + next.len = next.len + 1; + } + return next; +} + +// Lines 50-53: len. +pub fn managed_list_len(l: TrinityManagedList) -> usize { + return l.len; +} + +// Lines 56-58: isEmpty. +pub fn managed_list_is_empty(l: TrinityManagedList) -> bool { + return l.len == 0; +} + +// Lines 85-88: get. +pub fn managed_list_get(l: TrinityManagedList, index: usize) -> u32 { + return l.items[index]; +} + +// Lines 90-93: set. +pub fn managed_list_set(l: TrinityManagedList, index: usize, item: u32) -> TrinityManagedList { + var next = l; + if (index < MANAGED_LIST_CAPACITY) { + next.items[index] = item; + } + // The trailing alias keeps the generator's return-variable picker on + // the mutated copy; without it the emitted code returns the input. + const out = next; + return out; +} + +// Lines 71-73: clearRetainingCapacity - the count drops to zero, the +// storage stays. +pub fn managed_list_clear(l: TrinityManagedList) -> TrinityManagedList { + var next = l; + next.len = 0; + return next; +} + +// Lines 96-103: pop / popOrNull return ?T - an empty list pops nothing. +pub fn managed_list_pop_present(l: TrinityManagedList) -> bool { + return l.len > 0; +} + +pub fn managed_list_pop(l: TrinityManagedList) -> TrinityManagedList { + var next = l; + if (next.len > 0) { + next.len = next.len - 1; + } + return next; +} + +// Lines 113-123: the iterator - next returns null at the end of the list, +// otherwise the item at the index and the index advanced by one. +pub fn managed_list_iter_has_next(l: TrinityManagedList, index: usize) -> bool { + return index < l.len; +} + +pub fn managed_list_iter_next(l: TrinityManagedList, index: usize) -> (u32, usize) { + const item = l.items[index]; + return (item, index + 1); +} + +test init_is_empty { + const l = ManagedList(); + assert(managed_list_len(l) == 0); + assert(managed_list_is_empty(l) == true); + assert(managed_list_pop_present(l) == false); +} + +test append_grows_in_order { + const base = ManagedList(); + const one = managed_list_append(base, 7); + const two = managed_list_append(one, 9); + assert(managed_list_len(two) == 2); + assert(managed_list_get(two, 0) == 7); + assert(managed_list_get(two, 1) == 9); + assert(managed_list_len(one) == 1); + assert(managed_list_len(base) == 0); + assert(managed_list_is_empty(two) == false); +} + +test append_stops_at_capacity { + var l = ManagedList(); + var i: usize = 0; + while (i < 20) { + l = managed_list_append(l, 100); + i += 1; + } + assert(managed_list_len(l) == 16); + assert(managed_list_get(l, 15) == 100); +} + +test set_replaces_in_place { + const base = ManagedList(); + const one = managed_list_append(base, 1); + const two = managed_list_append(one, 2); + const changed = managed_list_set(two, 0, 42); + assert(managed_list_len(changed) == 2); + assert(managed_list_get(changed, 0) == 42); + assert(managed_list_get(changed, 1) == 2); + assert(managed_list_get(two, 0) == 1); +} + +test clear_keeps_the_list_usable { + const base = ManagedList(); + const one = managed_list_append(base, 5); + const two = managed_list_append(one, 6); + const cleared = managed_list_clear(two); + assert(managed_list_len(cleared) == 0); + assert(managed_list_is_empty(cleared) == true); + const again = managed_list_append(cleared, 8); + assert(managed_list_len(again) == 1); + assert(managed_list_get(again, 0) == 8); +} + +test pop_shrinks_one_at_a_time { + const empty = ManagedList(); + const none = managed_list_pop(empty); + assert(managed_list_len(none) == 0); + const base = ManagedList(); + const one = managed_list_append(base, 3); + const two = managed_list_append(one, 4); + assert(managed_list_pop_present(two) == true); + const popped = managed_list_pop(two); + assert(managed_list_len(popped) == 1); + assert(managed_list_pop_present(popped) == true); + const gone = managed_list_pop(popped); + assert(managed_list_len(gone) == 0); + assert(managed_list_pop_present(gone) == false); +} + +test iterator_walks_to_the_end { + const base = ManagedList(); + const one = managed_list_append(base, 10); + const two = managed_list_append(one, 20); + assert(managed_list_iter_has_next(two, 0) == true); + const (first, idx1) = managed_list_iter_next(two, 0); + assert(first == 10); + assert(idx1 == 1); + assert(managed_list_iter_has_next(two, idx1) == true); + const (second, idx2) = managed_list_iter_next(two, idx1); + assert(second == 20); + assert(idx2 == 2); + assert(managed_list_iter_has_next(two, idx2) == false); + assert(managed_list_iter_has_next(ManagedList(), 0) == false); +}