Files

88 lines
3.7 KiB
Markdown
Raw Permalink Normal View History

# zapcodec — ZAP-native little-endian reflection codec
## What this module is
`github.com/luxfi/zapcodec` is the standalone reflection-driven codec
that emits the **ZAP-native little-endian wire layout** consumed by
proto/p, proto/x, proto/p/warp, and the proto/zap_codec Manager wired
into the SDK wallet.
It originated inside `github.com/luxfi/codec` (as `codec/zapcodec`) and
was extracted into its own top-level module by the Wave 2G-Archive sweep
once every production caller of `luxfi/codec` had been migrated away.
The codec module itself is **archived** at tag `v1.1.5`; any historical
bench fixture or test that wants to compare against the legacy
linearcodec output pins that frozen tag.
## Wire format
| Field shape | Encoding |
|---|---|
| `uint8`/`int8`/`bool` | 1 byte |
| `uint16`/`int16` | 2 bytes, little-endian |
| `uint32`/`int32` | 4 bytes, little-endian |
| `uint64`/`int64` | 8 bytes, little-endian |
| `string` | `uint16` LE length prefix + UTF-8 body |
| `[]byte` | `uint32` LE length prefix + raw bytes |
| `[N]byte` | N raw bytes, no prefix |
| `[]T` (T ≠ byte) | `uint32` LE length prefix + N marshalled elements |
| `[N]T` (T ≠ byte) | N marshalled elements, no prefix |
| `struct` | fields emitted in `serialize:"true"` declaration order |
| `interface` | `uint32` LE registered-type-id + concrete value |
| `map[K]V` | `uint32` LE entry count + key-sorted entries |
| `*T` | the marshalled `T` (no nil sentinel — nil is rejected) |
Slice/map length prefixes cap at `math.MaxInt32`. Interface registration
is sequential — `RegisterType` assigns IDs 0, 1, 2, … in call order.
`SkipRegistrations(n)` bumps the counter by `n` without registering, so
callers can preserve historical slot layouts during a migration.
## API
```go
import (
"github.com/luxfi/zapcodec"
"github.com/luxfi/utils/wrappers"
)
c := zapcodec.NewDefault() // honours `serialize:"true"`
_ = c.RegisterType(&MyConcrete{})
p := &wrappers.Packer{MaxSize: 1 << 20}
_ = c.MarshalInto(value, p)
// p.Bytes[:p.Offset] is the wire payload (no outer version prefix)
q := &wrappers.Packer{Bytes: p.Bytes[:p.Offset], MaxSize: 1 << 20}
var out MyType
_ = c.UnmarshalFrom(q, &out)
```
For the canonical version-prefix outer layer the SDK wallet / proto
consumers use, see `github.com/luxfi/proto/zap_codec.NewVersionedManager`.
## Module boundary
- **No `Manager` type here.** Callers compose the version-prefix outer
layer themselves. The canonical Manager wrapper lives at
`proto/zap_codec`.
- **No comparative tests here.** Head-to-head benches vs linearcodec
live at `bench/modes/zap_vs_codec`. The standalone module is testable
without pulling in the archived codec module.
- **Single external dependency: `luxfi/utils/wrappers`.** That's the
canonical home of the `Packer` type that crosses module boundaries on
MarshalInto/UnmarshalFrom. Everything else (sentinels, struct-field
discovery, interface dispatch) is internal to this module.
## History
| Wave | Outcome |
|---|---|
| 2A | proto/p, proto/x deleted codec.Manager — wire format moved to ZAP-native |
| 2D | node/vms/* funneled through node/vms/pcodecs (codec.Manager type alias) |
| 2E | vm/chains/atomic deleted codec — hand-rolled big-endian binary |
| 2F | codec/wrappers → luxfi/utils/wrappers; codec/jsonrpc → luxfi/utils/json; codec/hierarchycodec deleted |
| 2G-Wallet | proto v1.2.1 + sdk v1.17.5 — proto/zap_codec canonical site established; wallet migrated |
| 2G-Genesis | genesis v1.13.12 — genesis builder ZAP-native via staging shim |
| 2G-Internal | proto v1.3.0 + node 1201692215 + coreth 7f195c239 — pcodecs + test mocks + secpfx migrated |
| **2G-Archive** | **codec/zapcodec → luxfi/zapcodec; codec module archived at v1.1.5** |