AGENTS.md@runtimes/go · git:20261031.084a463 · 2026-10-31 · sha256 cbab42427c87f5ba

AGENTS.md@runtimes/go git:20261031.084a463A

Immutable. This exact content never changes and is served at /api/v1/blob/cbab42427c87f5ba.

# Go runtime: file map

> Read when: reading or changing the Go runtime (runtimes/go, package nino), its string kernels, struct tags, sample codecs or Go benchmark.

Module `github.com/JasonXuDeveloper/Nino/runtimes/go/v4` (Go 1.22, no dependencies): the Reader and Writer
generated Go code calls, struct tags as a reflection-based alternative, and the corpus tests; README.md documents
the API and its guarantees. nino-gen's Go target (`src/Nino.Codegen`) writes code in the style of
`internal/sample`. `audit-runtimes` audits it, `audit-security` its read paths.

```
README.md                    the API, guarantees, struct tags, tags on generated structs, strings, tests, benchmarks
go.mod                       the module (no requirements)
.gitignore                   test binaries and profiles
doc.go                       the package documentation: what it is and its guarantees
api.go                       Deserialize / DeserializeExact / Serialize / Append with a pooled reader or writer
reader.go                    Reader: bounds-checked reads, sticky errors (zeros after the first failure)
writer.go                    Writer: appends to a doubling buffer, sticky errors, Reset for reuse
errors.go                    Error (the one error type) and its Code
wire.go                      the wire constants (Nino.Core's NinoWire and NinoDepth)
types.go                     .NET value types without a Go equivalent (Decimal, Guid, DateTime, TimeSpan, DateOnly,
                             TimeOnly, DateTimeOffset, Int128, Half, Version, BigInteger), read and written as .NET does
raw.go                       blocks of primitives as one copy (ReadPrimitives / WritePrimitives), bools checked 0 or 1
block.go                     ReadBlock / WriteBlock: lists of dates, Decimal, Guid and Int128 (BlockValue) as one block,
                             each value checked as when read alone
endian_little.go             littleEndian = true on little-endian architectures (blocks copied as memory)
endian_other.go              littleEndian = false elsewhere (blocks converted element by element)
unique.go                    duplicate keys of sets and maps read in data order: numbers and strings compared as .NET
                             does, pairwise or in a seeded open-addressing table (the reader's scratch), no map
collections.go               slices, maps, sets, entries, fixed-size runs, multidimensional shapes (the func-value
                             helpers unexported, for the runtime's rare paths); counts checked against the bytes left
depth.go                     class bodies (Class, ReadClass, WriteClass), regions, deferral past 128, the 512 limit
poly.go                      polymorphism: TypeID (FNV-1a), Subtype, TypeSet, ReadAny / WriteAny (allowed ids only)
refs.go                      reference tracking (wire-format spec §7.9): reference headers, Track, Back
layout.go                    layout strings: a type's NinoLayout in the [NinoTypeLayout] grammar (spec §8), parsed
strings.go                   strings: the default encoding's header, Latin-1 / UTF-16 / [NinoUtf8], the readers
strings_write.go             writing strings straight into the buffer, eight bytes at a time
strings_wire.go              what the string paths' Go code and kernels share (constants for go_asm.h)
strings_wire_amd64.go        x64 kernel tiers (SSSE3, AVX2, AVX-512) and their detection; go:generate for internal/asm
strings_wire_amd64.s         generated by internal/asm (avo): do not edit
strings_wire_arm64.go        arm64 NEON kernels' Go side
strings_wire_arm64.s         generated by internal/asm: do not edit
strings_wire_cpu_amd64.go    osAVX512 from XCR0 (not darwin)
strings_wire_cpu_darwin_amd64.go  osAVX512 on macOS (sysctl), as golang.org/x/sys/cpu decides it
strings_wire_tables.go       shuffle tables of the vector string paths
strings_wire_other.go        no vector string paths (other architectures, or the purego tag)
strings_asm.go               asciiCopy / widenASCII declarations (amd64, arm64)
strings_amd64.s              asciiCopy / widenASCII for amd64 (hand-written)
strings_arm64.s              asciiCopy / widenASCII for arm64 (hand-written)
strings_noasm.go             their no-op forms elsewhere
tagged.go                    struct tags: Go structs mapped to [NinoType]s without generated code (overview)
tagged_plan.go               struct plans from the tags and the declared layout
tagged_build.go              the builder: the codec of each Go type a plan reaches (TagError for what cannot map)
tagged_codec.go              the codecs plans are made of
tagged_cache.go              built codecs cached per reflect.Type (safe and unsafe field access)
marshal.go                   Marshal / Unmarshal through the tag plans
block_test.go                block sizes and ReadBlock / WriteBlock against element-by-element reads; BenchmarkBlocks
                             (checked blocks: dates, decimals, bools, raw structs with a bool; Guids as the copy alone)
raw_test.go                  bool blocks, raw struct blocks and padded structs
unique_test.go               duplicate keys: integer keys, large sets, primitive entries
collections_ref_test.go      the func-value collection helpers the runtime dropped, as the tests' references
reader_test.go               the Reader and Writer against hand-written bytes
race_test.go, norace_test.go  raceEnabled: allocation counts are checked without the race detector (sync.Pool drops)
strings_test.go              strings against a plain char-by-char reference; BenchmarkWriteString, BenchmarkUTF16
strings_utf8_test.go         the [NinoUtf8] paths against the plain ones
strings_wire_test.go         each vector tier against the Go loops, byte for byte
strings_wire_amd64_test.go   tier detection (NINO_WANT_TIERS) and darwin
strings_wire_linux_test.go   strings ending or starting at an unreadable page
tagged_test.go               struct tags (the spec's appendix A types)
tagged_bench_test.go         the tag path against hand-written code
fuzz_test.go                 FuzzReader: programs of reads over random bytes
tagged_fuzz_test.go          FuzzTagged: random bytes into tag-mapped types
internal/
  asm/                       command (own module, avo) writing strings_wire_amd64.s and strings_wire_arm64.s;
                             main_test.go checks the committed files are current
  corpustest/                runs the conformance corpus (valid, malicious, truncations) over a set of codecs;
                             render.go: values to the corpus's JSON (reflection, test code only)
  sample/                    the corpus's types (conformance/Nino.Conformance/Types.cs) with hand-written codecs in the
                             style a generator emits (codec_*.go; their collection loops and raw values in loops.go, as
                             the template writes them), their ids, layouts and tags; conformance, deep, registry and
                             tagged tests; FuzzDecode, FuzzTaggedDecode
  generated/                 nino_types.go: the corpus's types as nino-gen writes them (do not edit), its corpus test and
                             into_test.go (DeserializeInto: objects, slices, slots, failures, deferral);
                             shapes/: tests/Nino.Codegen.Tests/Shapes.cs as nino-gen writes it, round-tripped
bench/                       module of its own: the runtime against Protocol Buffers (benchpb/, buf), MessagePack,
                             encoding/json and encoding/gob on the .NET benchmark's SimpleClass, strings, sets
                             (sets.go) and a scattered list of 1,000,000 records (scattered.go);
                             tiers_amd64_test.go checks tier detection against golang.org/x/sys/cpu
```

Generated files: `internal/generated` is rewritten by `NINO_UPDATE_GENERATED=1 dotnet test tests/Nino.Codegen.Tests`;
the `.s` kernels by `go generate` (internal/asm).

## Go runtime

`runtimes/go` (package `nino`, see its README.md) reads and writes the wire format for Go; `internal/sample` holds the
corpus's types with codecs in the style a generator would write. Both performance and safety on untrusted input are
requirements:
- Never panic on any input, and never rely on `recover`: check every bound, and keep errors sticky (after a failure
  the reader returns zeros, empty slices and null, so every loop ends). Check a length against the bytes left
  before allocating, with .NET's caps (`SliceCap`, `capacity`); grow, never presize, what the data only claims.
- Count depth exactly as .NET does (`ReadClass`, `WriteClass`, regions and sections): the bytes must match, and
  the stack must stay bounded however deep the data. A polymorphic slot accepts only the ids of the types it allows.
- Hot paths: no reflection, no boxing into `any` outside polymorphic slots; a run of fixed-size members is one
  `Take`/`Extend` and `binary.LittleEndian` at constant offsets; blocks of primitives are one copy (guarded for
  big-endian hosts). Keep rare paths (`Fail`, growth, deferral) in `//go:noinline` functions. Check
  `go build -gcflags=-d=ssa/check_bce/debug=1` and `-gcflags=-m` on what you change, and keep writing a value free
  of allocations (`bench`, `-benchmem`). A collection of fixed-size elements takes a block helper
  (`ReadPrimitives`, `ReadPrimitiveEntries`, `ReadPrimitiveMap`), never the generic helpers that take element
  functions: those are an indirect call per element (`(*Reader).I32` is never inlined through a func value) and a
  bounds check per value. Any other collection loop in generated code is written out with the element's call made directly
  (nino-gen's `read_loop` / `write_loop`), never a helper taking a func value (the runtime keeps `readSlice` and the like unexported, for its own rare paths) (`optimize-runtimes`, "Dispatch
  outside .NET"). `ReadPrimitiveMap` read a 10-pair `map[int32]int32` 31 % faster than `ReadMap` (now unexported), and
  SimpleClass 4-7 % faster on runners (7ac8b46). Walk a block with a cursor (`for len(b) >= wire { ...; b =
  b[wire:] }`): indexing `b[i*wire:]` kept a bounds check per pair. A runner A/B can move code you did not touch by
  layout: the first map round left the untouched tag write 1.5-7.7 % slower on x64 only; make that path faster too
  (it then measured 7-14 % faster) rather than land a loss.
- Checks over a block (ranges of dates and decimals, the bools of raw structs) and UTF-16 copies: what measured
  faster and slower is in the `optimize-runtimes` skill, "Checking blocks in Go"; time them with BenchmarkBlocks and BenchmarkUTF16.
- Struct tags (`tagged.go`, `layout.go`, `marshal.go`) are the one place reflection is allowed: a plan built once
  per type, then walked. Anything the wire format cannot express is a `*TagError` naming the field when the plan
  is built, never a silent default; reads go through the same `Reader` calls as generated code, so the guarantees
  above hold there too. The sample types carry the tags and `NinoLayout` a generator emits (README, "Tags on
  generated structs"); keep them and `internal/sample/tagged_test.go` in step with the hand-written codecs.
- Before committing: `go vet ./...`, `go test -race ./...` with the conformance corpus present, a short run of
  `FuzzReader`, `FuzzDecode`, `FuzzTagged` and `FuzzTaggedDecode`, and the benchmarks back to back against the
  previous commit for a change to a read or write path. The runtime's module has no dependencies; competitors live in the `bench` module.
- The module says Go 1.22 and CI builds and tests it with Go 1.22: no API newer than that outside the `bench` module,
  tests and benchmarks included (`testing.B.Loop` is Go 1.24: loop with `for range b.N`). Check with
  `GOTOOLCHAIN=go1.22.12 go vet ./...` before pushing. CI also tests 386, Arm32 and big-endian s390x: run
  `GOARCH=386 go test ./...` (a constant such as `1<<31` compared with an `int` does not build there, and a test that
  hard-codes a pointer's size as 8 reads past its slice; both reached main in r101) and, with qemu-user,
  `GOARCH=arm GOMAXPROCS=1 go test -exec qemu-arm ./...` (several Ps crash Go 1.27 under qemu: `platforms`) and `GOARCH=s390x go test -exec qemu-s390x ./...`.