Files
zip/fixedptr_test.go
T
zeekay add82e6086 An optional id is read back, instead of panicking on the nil it was
The generated codec two directions disagreed about a POINTER to a fixed array.
The writer guarded the nil and wrote nothing; the reader emitted copy(x.F[:], …)
regardless — through a pointer on a value that had just been allocated, so it
was always nil. Every read of a type carrying an optional id panicked at the far
end of the plane, which is where a panic is a dropped connection and not a
stack trace anyone sees.

The P-Chain L1 validator is exactly that shape: validationID is a *ids.ID, so
get_validator and the L1 arm of get_validators could not be read at all.

An inline slot has no null — the writer leaves an absent pointer bytes zeroed —
so all-zero is what absence looks like coming back. That is already the rule the
pointer SCALARS read by (a zero u64 reads as absent), and reading it here is what
makes the two directions symmetric rather than adding a second convention.
2026-08-29 23:23:11 -07:00

47 lines
1.4 KiB
Go

package zip
import (
"reflect"
"strings"
"testing"
)
// An OPTIONAL id is a pointer to a fixed array, and the two directions of the
// generated codec disagreed about it: the writer guarded the nil and the reader
// did not, so `copy(x.F[:], …)` dereferenced a nil pointer on a value that had
// just been allocated. Every read of a type carrying one panicked at the far
// end of the plane — measured on the P-Chain's L1 validator, whose validationID
// is exactly this shape.
type optionalID struct {
ID *[32]byte `json:"id"`
Name string `json:"name"`
}
func TestAnOptionalIDIsReadBack(t *testing.T) {
written, err := Codecs(reflect.TypeOf(optionalID{}))
if err != nil {
t.Fatal(err)
}
if len(written) != 1 {
t.Fatalf("got %d files, want 1", len(written))
}
src := string(written[0].Source)
// The reader must not write through the pointer before it exists.
if strings.Contains(src, "copy(x.ID[:]") {
t.Errorf("the reader dereferences a nil pointer:\n%s", src)
}
// It allocates, then keeps the pointer only where something arrived — an
// inline slot has no null, so all-zero is what an absent pointer looks
// like, the same rule the pointer scalars already read by.
for _, want := range []string{
"var v [32]uint8",
"copy(v[:], raw)",
"x.ID = &v",
} {
if !strings.Contains(src, want) {
t.Errorf("the reader is missing %q:\n%s", want, src)
}
}
}