ADR-099: Int64/Uint64 as first-class kinds. The POC targets (SFTP offset: u64, metatensor data_offsets: u64) require 64-bit integers. The prior Uint64 addition was removed because it was half-finished; this ADR specifies the complete addition (layout + validator + API). ADR-100: Reject non-final inline length-prefixed variable fields in aligned mode. The OffsetMap reserves only 4 bytes (the length prefix), but write_string writes prefix+data inline — clobbering subsequent fields. Non-final variable fields must use maxLength or offset-indirect. ADR-101: Packed-mode read API — engine as SequentialReader factory. engine.sequential_reader() returned &SequentialReader but read_next needs &mut self — dead API. Now returns an owned fresh reader. ADR-102: Reject TUnion in aligned mode for v1. The aligned-mode union code had three bugs (no variant offsets, first-variant discriminator offset, misaligned variant region). Unions are the protocol pattern; mmap formats use structs/arrays. Reversible when a consumer needs it.
4.9 KiB
ADR-099: Int64/Uint64 as First-Class Kinds
Status
Accepted
Context
The typedef engine's kind set (ADR-095, ADR-097) tops out at 32-bit
integers. The POC included u64 read/write primitives, and the
call-channels-unification research's own SFTP schema example uses
"TypeDef:Uint64" for the offset field (Read/Write packets have
offset: u64). Metatensor/safetensors data_offsets are also u64.
A TypeDef:Uint64 variant was added to the TypeDefKind enum during
implementation (the task decomposition correctly identified the gap),
but without an ADR the addition was half-finished: type_size()
returned None, the layout engines couldn't compute offsets for it, and
the validator didn't register a TypeDef:Uint64 keyword. The variant
was then removed (commit 14d9cf2) on the grounds that it was
unintended and a latent panic — but the underlying gap is real: SFTP and
metatensor, the two primary POC targets, both require 64-bit integers.
The presumed reason 64-bit integers were left out of the original
specification is a JSON-level concern: serde_json::Number loses
precision past 2^53 when parsing from JSON text. This is a
validation-layer caveat, not a layout-layer one — the binary layout
is 8 raw bytes, and from_le_bytes/from_be_bytes work correctly for
the full u64/i64 range. The validation concern is handled by
accepting integer-form JSON values (the jsonschema crate's
as_i64/as_u64 methods handle the common range; values past 2^53 are
a JSON representation limitation, not a typedef limitation).
Decision
Add TypeDef:Int64 and TypeDef:Uint64 as first-class kinds.
Both are fixed-size (8 bytes), with natural alignment 8. They follow
the schema's endianness annotation like all other fixed-size types.
Read/write is via data_access::read_i64/write_i64/read_u64/
write_u64 (endian-aware, 8 bytes).
Kind table additions
| Kind | TypeBox key | Rust type | Size | Alignment |
|---|---|---|---|---|
TInt64 |
TypeDef:Int64 |
i64 |
8 | 8 |
TUint64 |
TypeDef:Uint64 |
u64 |
8 | 8 |
Validation
The custom keyword validators check:
TypeDef:Int64: value must be an integer ini64::MIN..=i64::MAX(-9223372036854775808to9223372036854775807).TypeDef:Uint64: value must be a non-negative integer in0..=u64::MAX(0to18446744073709551615).
The jsonschema crate's as_i64/as_u64 handle the common range.
JSON numbers past 2^53 lose precision in the JSON representation —
this is a JSON limitation, not a typedef limitation. The binary
representation (8 raw bytes) is always exact. A consumer that needs
to validate the full 64-bit range from JSON should provide the value
as a JSON integer (which serde_json preserves for values up to
u64::MAX/i64::MIN when the arbitrary_precision feature is
enabled, or when the value fits in i64/u64 without the feature).
FieldValue additions
FieldValue::I64(i64) and FieldValue::U64(u64) are added to the
unified return type. The SequentialReader, TypedefEngine::read_field,
and TypedefEngine::write_field dispatch on the new kinds.
Kind count
The engine now has 19 first-class kinds (17 + Int64 + Uint64).
TypeDefKind::is_fixed_size() returns true for both new kinds.
type_size() returns Some(8). natural_alignment() returns 8.
needs_endian() returns true.
Consequences
Positive
- Unblocks the two primary POC targets. SFTP
Read/Writepackets (offset: u64) and metatensordata_offsets(u64) are now expressible in typedef schemas. - Completes the half-finished addition. The
TypeDefKindenum,data_accessprimitives, andFieldValuevariants for 64-bit integers now have matching layout, validator, and engine support. - No new design surface. Int64/Uint64 are fixed-size types that follow all existing patterns (endianness, alignment, zero-copy read/write). They are mechanical additions.
Negative
- JSON precision caveat. Values past 2^53 lose precision in the
JSON representation (not in the binary representation). This is a
JSON limitation, not a typedef limitation, but it means the
validation layer cannot perfectly round-trip the full 64-bit range
through JSON
Numberwithoutarbitrary_precision. In practice, SFTP offsets and tensor data offsets are well within 2^53. - Two more kinds to maintain. The kind table, validator
registration,
FieldValueenum, and dispatch arms all grow by two variants. This is the cost of completeness.
References
docs/research/call-channels-unification/findings.md§"russh-sftp" — the SFTP schema with"offset": { "TypeDef:Uint64": true }docs/research/alknet-typedef/findings.md§"POC 1" — the POC included u64 read/write- ADR-095 — purpose and scope (the kind set)
- ADR-097 — schema annotations (endianness applies to the new kinds)