Documentation
¶
Overview ¶
Package codegen emits per-schema ZAP v2 accessors that match v1's hand-rolled inline-everything performance.
Why codegen ¶
Go's generic dispatch and inlining cost budget combine to put a hard ceiling on how fast a generic API can be: any function whose body is larger than the inliner's 80-cost budget cannot inline into its caller, so the call site pays a function-call cost (~5-7 ns on modern hardware). The work performed by a "Wrap" function (parse the ZAP wire frame, validate the kind discriminator, build a typed view) exceeds that budget. The generic [zapv2.Wrap[S]] function therefore costs one function call per Wrap, even when every other primitive in its body would inline.
Hand-written per-schema [WrapX] shims (e.g. [examples.WrapAdvanceTime]) have the same problem — even though they pin S{}.Kind() and S{}.Size() as constants, the body is still too large to inline.
Codegen solves this by emitting Wrap/Build/Read/Write functions that DON'T live inside a function at all — they expand inline at the call site through Go templates instantiated at build time. The user writes a single schema declaration; the codegen tool produces a *_zap.go file whose functions match v1's hand-rolled pattern byte-for-byte.
Output shape ¶
For a schema declared as:
type AdvanceTimeSchema struct{}
func (AdvanceTimeSchema) Kind() zapv2.KindByte { return 1 }
func (AdvanceTimeSchema) Size() int { return 9 }
func (AdvanceTimeSchema) Name() string { return "AdvanceTimeTx" }
//zap:field Time uint64 @1
the codegen tool emits a sibling file (advance_time_zap.go) with:
const sizeAdvanceTimeTx = 9
const kindAdvanceTimeTx uint8 = 1
const offsetAdvanceTimeTx_Time = 1
func WrapAdvanceTime(b []byte) (zapv2.View[AdvanceTimeSchema], error) {
msg, err := zap.Parse(b)
...
}
The emitted code uses v1 primitives directly (zap.Parse, msg.Root, root.Uint8) so that the inliner folds them into the caller's frame, matching v1 hand-rolled performance.
When to use codegen ¶
- Hot-path schemas where the function-call overhead matters (per-tx assembly, per-block validation).
- When you have a schema description in a declarative form (a YAML file, a Cap'n-Proto-style schema file, struct tags) and want ZAP v2 accessors emitted automatically.
For cold paths and ad-hoc schemas, the generic [zapv2.Wrap[S]] / [zapv2.Build[S]] are equally correct and one extra function call slower — which is rarely the bottleneck.
Status ¶
This package is the entry point. The current implementation emits the canonical AdvanceTimeTx fast-path file (used as the canary in the bench suite); adding new schemas means feeding the codegen tool a schema declaration in one of the supported forms.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Emit ¶
Emit writes the per-schema *.go file to w. Returns an error if the schema is invalid (unknown field type, offset overlap, fields past declared Size, etc.).
The emitted code follows the v1 hand-rolled pattern: every step (Parse, Root, Uint reads, kind check, View compose) uses v1 primitives that inline into the caller's frame. The result matches v1 hand-rolled performance to within compiler noise — verified by the bench suite.
Scalar fields go through the [zapv2.Field][S, T] generic handle (declared in the per-schema <GoName>Fields var). Fixed-width byte-array fields ("bytes<N>") are emitted as standalone typed accessor functions that call v1's SetBytesFixed / BytesFixedSlice — they do NOT appear in the Fields struct because [N]byte is not a [zapv2.FieldKind] member. Both forms produce the same wire layout.
Types ¶
type Field ¶
type Field struct {
// Name is the Go-visible field name (e.g. "Time"). Emitted into
// the schema's Fields struct as `<SchemaGoName>Fields.Name`.
Name string
// Type is the Go type as a string. See Field doc for the supported
// set: scalar FieldKind types or "bytes<N>" for fixed byte arrays.
Type string
// Offset is the byte position within the fixed-size payload.
Offset uint32
}
Field describes one fixed-size field in a schema.
Two field kinds are supported:
Scalar fields. Type is one of the [zapv2.FieldKind] members (bool, int8/16/32/64, uint8/16/32/64, float32/64). The emitted code uses a [zapv2.Field][S, T] handle and the standard zapv2.Read/Write generic functions.
Fixed-width byte-array fields. Type is "bytes<N>" where N is a positive integer (e.g., "bytes20" for NodeID, "bytes32" for hashes, "bytes16" for session IDs). The emitted code uses the v1 ObjectBuilder.SetBytesFixed / Object.BytesFixedSlice accessors and returns the value as a [N]byte. Byte-array fields do NOT use the zapv2.Field generic handle because [N]byte is not a [zapv2.FieldKind] member; instead they get a typed accessor function emitted alongside the schema.
Variable-length tail fields (lists, bytes, sub-objects) are NOT declared here. They use the generic [zapv2.ListAt] / out-of-line pointer machinery and require a hand-written accessor.
type Schema ¶
type Schema struct {
// GoName is the Go type name for the schema marker struct (e.g.
// "AdvanceTimeSchema"). Used as the View[S] type parameter.
GoName string
// WireName is the human-readable name of the schema (e.g.
// "AdvanceTimeTx"). Used in error messages and Registry lookup.
// Stable across versions — part of the schema's identity.
WireName string
// Kind is the discriminator byte at object offset 0.
Kind uint8
// Size is the fixed object payload in bytes (excluding header).
Size int
// Package is the Go package the emitted file should live in.
Package string
// Fields is the ordered list of fixed-size fields. Variable-length
// tails (lists, bytes) are not declared here; they use the
// generic [zapv2.ListAt] / [zapv2.WriteList] machinery.
Fields []Field
// SkipRegistry suppresses the emit of `init() { zapv2.Register[S]
// (zapv2.DefaultRegistry) }`. Use for schema families that have a
// PRIVATE Kind namespace (the discriminator byte is unique only
// within a per-package registry, not globally). Examples:
//
// - LP-186 chains-VM wire: each <vm>wire package has its own
// KindBlock=0x01 / KindTx=0x02 / etc. Registering all twelve
// KindBlock=0x01 implementations into a single global registry
// would panic on duplicate kind byte at init time.
// - LP-182 consensus wire: the 0x01..0x0D kind bytes are local to
// the consensus-wire registry (`pkg/wire/zap/schemas.go`), not
// the global zapv2.DefaultRegistry shared with P2P/light-client
// schemas at 0xD0+/0xF0+.
//
// Default is false — schemas with globally-unique Kind bytes
// (LP-201, LP-208, LP-211, LP-214, LP-218) DO register at init.
SkipRegistry bool
}
Schema is the declarative description of a ZAP v2 schema. The codegen tool consumes a Schema and emits a per-schema *.go file with the Wrap/Build/Read/Write functions hand-rolled to inline.
One schema description, one emitted file, one and only one way to access the wire format from Go code — Hickey-style.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
zapgen
command
Command zapgen emits per-schema ZAP v2 accessors from a declarative schema description.
|
Command zapgen emits per-schema ZAP v2 accessors from a declarative schema description. |
|
zapgen-all
command
Command zapgen-all bulk-emits per-schema ZAP v2 accessor files from a directory of JSON schema declarations.
|
Command zapgen-all bulk-emits per-schema ZAP v2 accessor files from a directory of JSON schema declarations. |