|
Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
|
Status: Draft Date: 2026-08-02 Applies to: safeAPIFreamwork, include/safeapi/common/sapi_buffer.h
ADR-001 defines the OS Abstraction Layer (L0) and anticipates a future L1 Safety Communication Layer that will move data between application layers (L2 RBC core, L1, L0 services). Several existing L0 services already pass raw (pointer, size) pairs around (sapi_nvm_read/write, sapi_ipc_send/ receive). As more layers and services are added, every one of them would otherwise reinvent its own bounds-checking for "here is some data, here is how much of it is valid."
This ADR defines a single, minimal, cross-layer data buffer abstraction so every layer speaks the same shape when handing data to another layer, without duplicating bounds-checking logic and without introducing dynamic allocation.
This is a generic data buffer, not the L1 Safety Communication Layer itself. It carries no sequence numbers, timeouts, or integrity metadata — those remain scoped to the future L1 ADR (or to services that already own their own integrity mechanism, e.g. sapi_nvm's CRC-checked reads).
sapi_buffer_t is a lightweight view, not a container that owns memory:
Rationale: consistent with ADR-001 section 3.2 (no dynamic allocation after init) and section 3.4 (caller-owned storage). A pool-based alternative (sapi_mem_pool acquire/release per buffer) was considered and rejected for this generic type because it forces every call site to manage pool lifecycle even for simple, short-lived, stack-local exchanges; pools remain available and appropriate for services that need shared/queued ownership (e.g. a future L1 message pool), layered on top of this same view type.
A separate read-only view, sapi_const_buffer_t, is provided so a producer can hand out data without granting the consumer write access:
sapi_buffer_t intentionally carries no CRC/checksum field. Integrity belongs to the service that has the context to define what "valid" means (sapi_nvm already CRC-checks on read; a future L1 layer will define message-level integrity per EN 50159). Baking a generic checksum into every buffer would either be redundant with those mechanisms or, worse, create a false sense of safety at a layer that has no way to act on a mismatch.
All operations are bounds-checked and return sapi_status_t; none allocate.
| Function | Purpose |
|---|---|
| sapi_buffer_init | Bind a buffer view to caller-owned storage + capacity. |
| sapi_buffer_clear | Reset length to 0 (capacity/data unchanged). |
| sapi_buffer_set_length | Mark N bytes of already-written storage as valid. |
| sapi_buffer_copy_in | Bounds-checked copy of external data into the buffer; sets length. |
| sapi_buffer_copy_out | Bounds-checked copy of the buffer's valid bytes to an external destination. |
| sapi_buffer_as_const | Produce a read-only view of the buffer's current valid bytes. |
| sapi_buffer_is_valid | Defensive check: non-null data and length <= capacity. |
sapi_buffer_copy_in/copy_out return SAPI_STATUS_RESOURCE_EXHAUSTED (not a new status code) when the source doesn't fit the destination capacity — reusing the existing "insufficient capacity" semantics from ADR-001's shared status table rather than growing the enum for this.
Superseded by ADR-007. See below for the original path; the current physical layout is include/safeapi/buffer/sapi_buffer.h + src/buffer/sapi_buffer.c, target safeapi::buffer.
include/safeapi/common/sapi_buffer.h + src/common/sapi_buffer.c, built as a new safeapi_common static library target, independent of safeapi_os (no OS dependency — pure data manipulation).