fix(typedef): resolve spec inconsistencies from sanity check
- Remove TEnum 'always LE' exception (no POC basis, contradicts ADR-097). TEnum now follows schema endianness like all other fixed-size types. - Document TEnum design change: u32 index is a deliberate deviation from TypeBox's string enum for binary efficiency. - Document TBytes as alknet-typedef addition (not in TypeBox typedef.ts). - Fix kind count inconsistency: all docs now consistently say 17 kinds. - Fix TTimestamp validation contradiction: clarify data-access layer vs jsonschema validator responsibility. - Add TEnum read/write coverage to data-access.md. - Add TEnum endianness cross-reference to layout-engine.md. - Clarify TBytes binary-vs-JSON representation in validation.md.
This commit is contained in:
1 parent
a941d86c3a
commit
c6ab00d141
6 files changed
+58
-11
No files matched your search
@@ -18,7 +18,7 @@ format definition; the engine is generic.
|
||||
| [schema-layer.md](schema-layer.md) | draft | The 17 `TypeDef:*` kinds, jsonschema custom keyword integration, TypeBox interop, schema annotations |
|
||||
| [layout-engine.md](layout-engine.md) | draft | Offset computation, the two layout modes (packed sequential vs aligned static), alignment, endianness, variable-length handling |
|
||||
| [data-access.md](data-access.md) | draft | Read/write functions, TUnion dispatch, field paths, zero-copy access, length-prefix reading |
|
||||
| [validation.md](validation.md) | draft | Custom keyword validators for all 16 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time validation, `TypedefEngine` |
|
||||
| [validation.md](validation.md) | draft | Custom keyword validators for all 17 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time validation, `TypedefEngine` |
|
||||
|
||||
## Applicable ADRs
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ typed access at the computed positions.
|
||||
|
||||
### Fixed-size types
|
||||
|
||||
Fixed-size types (`TFloat32`, `TInt32`, `TUint8`, etc.) are accessed via
|
||||
Fixed-size types (`TFloat32`, `TInt32`, `TUint8`, `TEnum`, etc.) are accessed via
|
||||
zero-copy pointer casts:
|
||||
|
||||
```rust
|
||||
@@ -48,6 +48,28 @@ The engine applies endianness at access time based on the schema's
|
||||
`"endian"` annotation (ADR-097). The offset computation is
|
||||
endian-agnostic.
|
||||
|
||||
### TEnum access
|
||||
|
||||
`TEnum` is a fixed-size type (4 bytes, `u32` index). Read/write follows
|
||||
the same pattern as other fixed-size types — the engine reads/writes a
|
||||
`u32` at the field's computed offset, applying the schema's endianness:
|
||||
|
||||
```rust
|
||||
fn read_enum(buffer: &[u8], offset: usize, endian: Endian) -> u32 {
|
||||
let bytes: [u8; 4] = buffer[offset..offset+4].try_into().unwrap();
|
||||
match endian {
|
||||
Endian::Little => u32::from_le_bytes(bytes),
|
||||
Endian::Big => u32::from_be_bytes(bytes),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The consumer maps the `u32` index back to the enum's string values using
|
||||
the schema's `"enum"` array (index 0 → first value, index 1 → second
|
||||
value, etc.). The engine does not perform this mapping — it operates on
|
||||
the raw `u32` index. The jsonschema validator checks that the index
|
||||
corresponds to a valid enum value at the JSON level.
|
||||
|
||||
### Variable-length types (inline length-prefixing)
|
||||
|
||||
For variable-length types with inline length-prefixing (the default):
|
||||
|
||||
@@ -207,7 +207,8 @@ Endianness is per-schema (ADR-097). The offset computation is
|
||||
endian-agnostic — it computes byte positions, not byte values. The
|
||||
read/write functions apply endianness when converting between bytes and
|
||||
typed values. The engine reads the `"endian"` annotation from the schema
|
||||
and byte-swaps accordingly.
|
||||
and byte-swaps accordingly. All fixed-size types — including `TEnum`
|
||||
(u32 index) — follow the schema's endianness.
|
||||
|
||||
## Mode Selection
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ These boundaries are decided in [ADR-095](../../decisions/095-alknet-typedef-pur
|
||||
dispatch, field paths, zero-copy access for fixed-size types,
|
||||
length-prefix reading for variable-length types.
|
||||
- **[validation.md](validation.md)** — custom keyword validators for all
|
||||
16 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time
|
||||
17 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time
|
||||
validation, `TypedefEngine` as the compiled form of a schema.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
@@ -5,7 +5,7 @@ last_updated: 2026-07-20
|
||||
|
||||
# alknet-typedef — Schema Layer
|
||||
|
||||
The schema layer: the 16 `TypeDef:*` custom type kinds, their mapping to
|
||||
The schema layer: the 17 `TypeDef:*` custom type kinds, their mapping to
|
||||
Rust types and byte sizes, the `jsonschema` custom keyword integration,
|
||||
TypeBox interop, and the concrete JSON shapes for schema-level annotations.
|
||||
|
||||
@@ -53,8 +53,17 @@ second is index 1, etc. The enum's values are declared via the standard
|
||||
JSON Schema `"enum"` keyword (e.g., `"enum": ["read", "write", "execute"]`).
|
||||
The `TypeDef:Enum` custom keyword signals that the type is an enum for
|
||||
layout purposes; the built-in `enum` keyword provides the value list.
|
||||
The `u32` index is always little-endian (enum indices are not protocol
|
||||
data — they are internal to the schema). See [ADR-097](../../decisions/097-schema-annotations.md).
|
||||
|
||||
**Design note:** TypeBox's `TEnum` is a string enum (variable-length). The
|
||||
typedef engine uses a `u32` index instead — a deliberate deviation from
|
||||
TypeBox fidelity in favor of binary efficiency. Most enums have a small
|
||||
number of variants (e.g., the call protocol's 5 event types); a `u32`
|
||||
index is compact, fixed-size, and sufficient for any realistic enum. The
|
||||
JSON representation (for validation) remains a string; the binary
|
||||
representation is the `u32` index.
|
||||
The `u32` index follows the schema's endianness annotation (ADR-097), like
|
||||
all other fixed-size types. In little-endian mode the index is
|
||||
`u32::from_le_bytes`; in big-endian mode it is `u32::from_be_bytes`.
|
||||
|
||||
### Variable-length types
|
||||
|
||||
@@ -115,6 +124,14 @@ consistent byte order for both field values and length prefixes.
|
||||
**`TBytes`:** Raw bytes — no UTF-8 constraint. The payload is `&[u8]`.
|
||||
Otherwise identical to `TString` in layout (same three strategies).
|
||||
|
||||
**Design note:** `TypeDef:Bytes` is an alknet-typedef addition — it does
|
||||
not exist in TypeBox's `typedef.ts` (which defines 16 kinds). It is
|
||||
included because raw byte arrays are a common binary protocol primitive
|
||||
(SFTP data payloads, channels payloads, tensor data) and are semantically
|
||||
distinct from UTF-8 strings. In the binary representation, TBytes is raw
|
||||
bytes with no encoding (not base64, not hex). In the JSON representation
|
||||
(for validation), TBytes is a string (JSON has no native byte type).
|
||||
|
||||
**`TRecord`:** A string-keyed map. Binary layout is a count-prefixed
|
||||
sequence of `(key, value)` pairs: `[count: u32][key_len: u32][key_bytes]
|
||||
[value_len: u32][value_bytes]...`. The count is the number of entries.
|
||||
@@ -126,9 +143,11 @@ entire record is reserved at `maxLength` bytes (zero-padded).
|
||||
|
||||
**`TTimestamp`:** An RFC 3339 timestamp string (the internet profile of
|
||||
ISO 8601). Stored as a length-prefixed UTF-8 string (strategy 1) or
|
||||
fixed-size reservation (strategy 2 with `maxLength`). The engine does not
|
||||
parse or validate the timestamp format beyond UTF-8 — the jsonschema
|
||||
validator checks RFC 3339 conformance at the JSON level.
|
||||
fixed-size reservation (strategy 2 with `maxLength`). The data-access
|
||||
layer treats timestamps as opaque length-prefixed strings — it does not
|
||||
parse or validate the timestamp format. The jsonschema custom keyword
|
||||
validator checks RFC 3339 conformance at the JSON level (see
|
||||
[validation.md](validation.md)).
|
||||
|
||||
`TArray` is variable-length when the element type is variable-length or
|
||||
when the count is not known at schema time. For fixed-size element arrays
|
||||
|
||||
@@ -104,8 +104,13 @@ type constraints; `jsonschema` handles all structural validation.
|
||||
must not exceed it.
|
||||
|
||||
**`TypeDef:Bytes`:**
|
||||
- Value must be a string (JSON represents binary data as a string).
|
||||
- Value must be a string (JSON represents binary data as a string — JSON
|
||||
has no native byte type).
|
||||
- If `maxLength` is specified, the byte length must not exceed it.
|
||||
- **Binary representation:** In the binary layout, `TBytes` is raw bytes
|
||||
with no encoding (not base64, not hex). The JSON representation (for
|
||||
validation) uses a string; the binary representation (for data access)
|
||||
uses `&[u8]` directly.
|
||||
|
||||
**`TypeDef:Enum`:**
|
||||
- The `TypeDef:Enum` custom keyword signals that the type is an enum for
|
||||
|
||||
Reference in new issue
Block a user