10 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | ||
|---|---|---|---|---|---|---|---|---|---|
| typedef/data-access | Implement primitive read/write functions for all 17 TypeDef kinds with endianness support | completed |
|
moderate | medium | component | implementation |
Description
Implement the data access layer in crates/alknet-typedef/src/data_access.rs. This
module provides the primitive typed read/write functions that operate on raw byte
buffers at given offsets. These are the building blocks used by the layout types
(OffsetMap, SequentialReader) and the TypedefEngine.
Per data-access.md.
Fixed-size type read/write
All fixed-size read/write functions take a buffer, an offset, and an Endian, and
return Result<T, TypedefError> (or Result<(), TypedefError> for writes). They
perform bounds checking and return TypedefError::Access with the field path on
failure.
// Signed integers
pub fn read_i8(buffer: &[u8], offset: usize, field_path: &str) -> Result<i8, TypedefError>;
pub fn read_i16(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<i16, TypedefError>;
pub fn read_i32(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<i32, TypedefError>;
// Unsigned integers
pub fn read_u8(buffer: &[u8], offset: usize, field_path: &str) -> Result<u8, TypedefError>;
pub fn read_u16(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<u16, TypedefError>;
pub fn read_u32(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<u32, TypedefError>;
pub fn read_u64(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<u64, TypedefError>;
// Floats
pub fn read_f32(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<f32, TypedefError>;
pub fn read_f64(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<f64, TypedefError>;
// Boolean (0x00 = false, 0x01 = true; other values are errors)
pub fn read_bool(buffer: &[u8], offset: usize, field_path: &str) -> Result<bool, TypedefError>;
// TEnum (u32 index, endian-aware)
pub fn read_enum(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) -> Result<u32, TypedefError>;
Write counterparts:
pub fn write_i8(buffer: &mut [u8], offset: usize, value: i8, field_path: &str) -> Result<(), TypedefError>;
pub fn write_i16(buffer: &mut [u8], offset: usize, value: i16, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_i32(buffer: &mut [u8], offset: usize, value: i32, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_u8(buffer: &mut [u8], offset: usize, value: u8, field_path: &str) -> Result<(), TypedefError>;
pub fn write_u16(buffer: &mut [u8], offset: usize, value: u16, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_u32(buffer: &mut [u8], offset: usize, value: u32, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_u64(buffer: &mut [u8], offset: usize, value: u64, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_f32(buffer: &mut [u8], offset: usize, value: f32, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_f64(buffer: &mut [u8], offset: usize, value: f64, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
pub fn write_bool(buffer: &mut [u8], offset: usize, value: bool, field_path: &str) -> Result<(), TypedefError>;
pub fn write_enum(buffer: &mut [u8], offset: usize, value: u32, field_path: &str, endian: Endian) -> Result<(), TypedefError>;
Variable-length type read/write (inline length-prefixing)
For variable-length types with inline length-prefixing (the default):
/// Read a length-prefixed UTF-8 string. Returns a slice borrowing from the buffer.
/// Format: [length: u32][UTF-8 bytes]
pub fn read_string<'a>(buffer: &'a [u8], offset: usize, field_path: &str, endian: Endian) -> Result<&'a str, TypedefError>;
/// Read length-prefixed raw bytes. Returns a slice borrowing from the buffer.
pub fn read_bytes<'a>(buffer: &'a [u8], offset: usize, field_path: &str, endian: Endian) -> Result<&'a [u8], TypedefError>;
/// Write a length-prefixed UTF-8 string.
pub fn write_string(buffer: &mut [u8], offset: usize, value: &str, field_path: &str, endian: Endian) -> Result<usize, TypedefError>;
// Returns the number of bytes written (4 + value.len()) so the caller can advance.
/// Write length-prefixed raw bytes.
pub fn write_bytes(buffer: &mut [u8], offset: usize, value: &[u8], field_path: &str, endian: Endian) -> Result<usize, TypedefError>;
Variable-length type read (offset indirection)
For offset-indirect types (opt-in, metatensor blob tensor pattern):
/// Read an offset-indirect string. The field at `offset` is a struct
/// {data_offset: u32, data_length: u32}. The consumer provides the
/// separate data region.
pub fn read_string_indirect<'a>(
buffer: &'a [u8], // the index struct buffer
offset: usize, // position of {data_offset, data_length}
data_region: &'a [u8], // the separate data region
field_path: &str,
endian: Endian,
) -> Result<&'a str, TypedefError>;
/// Read offset-indirect raw bytes.
pub fn read_bytes_indirect<'a>(
buffer: &'a [u8],
offset: usize,
data_region: &'a [u8],
field_path: &str,
endian: Endian,
) -> Result<&'a [u8], TypedefError>;
Design notes
- Zero-copy: Read functions for variable-length types return slices borrowing from the input buffer — no allocation.
- Bounds checking: Every function checks that the buffer is large enough for
the requested read/write at the given offset. Returns
TypedefError::Accesswith the field path on failure. - Endianness: Applied at access time based on the schema's
"endian"annotation. The offset computation is endian-agnostic. - No
unwrap: All fallible operations use properResultreturns. The spec pseudocode usesunwrapfor brevity; production code must not. TEnum: Reads/writes au32index. The consumer maps the index to string values using the schema's"enum"array. The engine does not perform this mapping.TBoolean:0x00= false,0x01= true. Other values produceTypedefError::Access.
What this does NOT include
- The offset computation (that's
offset_map.rsandlayout_builder.rs) - TUnion discriminator dispatch (that's
tunion.rs) - The
TypedefEnginestruct (that'sengine.rs) TRecordread/write (deferred — requires count-prefixed sequence walking; can be added when a consumer needs it, or implemented here if straightforward)
Acceptance Criteria
- All fixed-size read functions implemented:
read_i8,read_i16,read_i32,read_u8,read_u16,read_u32,read_u64,read_f32,read_f64,read_bool,read_enum - All fixed-size write functions implemented:
write_i8,write_i16,write_i32,write_u8,write_u16,write_u32,write_u64,write_f32,write_f64,write_bool,write_enum read_stringandread_bytes(inline length-prefixed) implementedwrite_stringandwrite_bytes(inline length-prefixed) implemented, returning bytes writtenread_string_indirectandread_bytes_indirect(offset-indirect) implemented- All functions respect
Endianparameter (little-endian vs big-endian byte order) - All functions perform bounds checking and return
TypedefError::Accesswith field path on failure read_boolrejects values other than0x00and0x01read_stringvalidates UTF-8 and returnsTypedefError::Accesson invalid UTF-8- Zero-copy: read functions for variable-length types return slices, not owned data
- No
unwrap()orexpect()on error paths — all fallible operations useResult - All public functions have doc comments
cargo check -p alknet-typedefsucceedscargo clippy -p alknet-typedefsucceeds with no warningscargo build --workspacestill succeeds
References
- docs/architecture/crates/typedef/data-access.md — read/write model, TEnum access, variable-length handling
- docs/architecture/crates/typedef/schema-layer.md — the 17 TypeDef kinds and their byte sizes
- docs/architecture/decisions/097-schema-annotations.md — ADR-097 (endianness, encoding)
- docs/architecture/decisions/098-error-handling-validation-strategy.md — ADR-098 (error handling)
- /workspace/alknet-typedef-poc/src/lib.rs — POC reference for read/write functions
Notes
This module provides the primitive read/write operations. The layout types (
OffsetMap,SequentialReader) use these to access fields at computed positions. The functions are endian-aware — the caller passes the schema'sEndianand the functions byte-swap accordingly. All functions use properResultreturns with field paths for debugging — nounwrap()in production code. TheTRecordread/write is deferred unless it proves straightforward to implement here.
Summary
Implemented the full data access layer in crates/alknet-typedef/src/data_access.rs: 11 fixed-size read functions, 11 fixed-size write functions, 2 inline length-prefixed variable-length read functions (read_string, read_bytes), 2 inline write functions (write_string, write_bytes), and 2 offset-indirect read functions (read_string_indirect, read_bytes_indirect). All multi-byte types respect the Endian parameter; all functions perform bounds checks and return TypedefError::Access with the field path on failure; read_bool rejects bytes other than 0x00/0x01; read_string/read_string_indirect validate UTF-8. Variable-length read functions return zero-copy slices borrowing from the input buffer. No unwrap()/expect() is used on fallible paths — a private read_array/write_array helper pair propagates try_into failures as TypedefError::Access. The module ships with 27 unit tests covering round-trips, endianness, bounds failures, and zero-copy semantics; all pass under cargo test -p alknet-typedef data_access with cargo clippy -- -D warnings clean.