Files
zeekay 9d9e477978 v1/codegen: emit REPEATED nested objects (repeated message, zero-copy)
The repeated-message wire shape: a list whose elements carry their own
variable-length tails (string/bytes/nested), which the fixed-stride inline
List cannot hold. Completes proto field-shape coverage in the runtime —
map<k,v> reduces to a repeated {key,value} entry, so this is its keystone too.

Composition, not a new wire concept: ListNested = List ∘ NestedAt. The list
slots are a contiguous stride-4 object-pointer array; each pointer is
followed exactly as a singular nested pointer to an out-of-line tailed View.

Runtime:
- builder.go: ListBuilder.AddObjectPtr(targetPos) — appends a signed 4-byte
  relative object pointer (the repeated-message element kind).
- v1/list_nested.go: ListNested[N] (Len/At/All/IsZero), ListNestedAt[S,N]
  (read), WriteListNested[S,N] + NestedElemSetter[N] (write). Same audited
  bounds discipline as Object.Object / ListStride (RED-HIGH-1/2 inherited).

Codegen:
- elemIsTailed: a list element with a string/bytes/nested/list sub-field
  auto-routes to ListNested/WriteListNested/ListNestedAt; scalar-only
  elements stay in the tighter inline List/WriteList/ListAt. One Elem
  declaration, the generator picks the correct encoding.

Proven end-to-end: runtime roundtrip (3 elements each with its OWN string
tail, via At and All, + empty), emit routing test (tailed->ListNested,
scalar-only->List), and a generated-code wire roundtrip (testpkg/repeatwire).
Root zap + full v1+codegen suite green; scalar listwire unchanged (no regression).
2026-06-23 16:38:05 -07:00

64 lines
1.7 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package repeatwire
import (
"testing"
zapv1 "github.com/luxfi/zap/v1"
)
// TestRoundTrip_Repeated proves the GENERATED repeated-message code works
// on the wire: a Parent with a repeated Child where every Child carries its
// own string tail — the case the codegen routes to ListNested. Build, wrap,
// read each element's scalar + string zero-copy via the generated accessor.
func TestRoundTrip_Repeated(t *testing.T) {
t.Parallel()
children := []Child{
{ID: 11, Name: "one"},
{ID: 22, Name: "two"},
{ID: 33, Name: "three-is-a-longer-string"},
}
_, buf := NewParent(0x7, children)
got, err := WrapParent(buf)
if err != nil {
t.Fatalf("WrapParent: %v", err)
}
if c := zapv1.Read(got, ParentSchemaFields.Count); c != 0x7 {
t.Errorf("Count = %d, want 7", c)
}
list := ParentChildren(got)
if list.Len() != len(children) {
t.Fatalf("len = %d, want %d", list.Len(), len(children))
}
for i, want := range children {
ch := list.At(i)
if ch.IsZero() {
t.Fatalf("child[%d] zero", i)
}
if id := zapv1.Read(ch, ChildSchemaFields.ID); id != want.ID {
t.Errorf("child[%d].ID = %d, want %d", i, id, want.ID)
}
if nm := ChildName(ch); nm != want.Name {
t.Errorf("child[%d].Name = %q, want %q", i, nm, want.Name)
}
}
}
// TestRoundTrip_RepeatedEmpty proves the empty repeated field round-trips.
func TestRoundTrip_RepeatedEmpty(t *testing.T) {
t.Parallel()
_, buf := NewParent(0, nil)
got, err := WrapParent(buf)
if err != nil {
t.Fatalf("WrapParent: %v", err)
}
if n := ParentChildren(got).Len(); n != 0 {
t.Errorf("empty len = %d, want 0", n)
}
}