The writer appends canonical records to any seekable binary port. Finalization writes a canonical structural index, a completion footer, and a fixed-size trailer. The reader validates the container structure before exposing records and supports offline lookup by stream, generation, tick, or simulation time.
Distribution version 0.3.3 writes FSR binary 1.2 with
structural-validation by default. The hot write path does not hash payloads. Opening
a recording validates frame magic and lengths, exact offsets and bounds, canonical
metadata, index projection, global and per-stream sequence continuity, generation and
clock ordering, footer counts, and truncation while seeking over payload bytes. The
index and footer remain compactly checksummed; stream inventory never contains a fake
or placeholder payload digest.
Payload bytes are read only by read_payload, read_record, or typed decoding.
FSRReader.file_sha256 keeps its public str result but computes and caches the full
file digest only when that property is explicitly requested. For digest-bearing files,
select integrity="fast" to emit binary 1.1 or integrity="strong" to emit binary
1.0. The reader remains compatible with both released layouts.
from io import BytesIO
from fastsim_recording_format import FSRReader, FSRWriter
port = BytesIO()
writer = FSRWriter(port)
writer.write_manifest({"run_id": "example"})
writer.define_schema("joint-state/1", {"type": "object"})
writer.define_stream("robot.joints", "joint-state/1", required=True)
writer.write_record(
"robot.joints",
record_sequence=1,
source_sequence=1,
generation=0,
tick=0,
sim_time_s=0.0,
attributes={"dtype": "float32"},
payload=b"binary sample",
)
writer.finalize(completion={"status": "succeeded"}, last_record_sequence=1)
reader = FSRReader(port)
record = reader.seek("robot.joints", generation=0, tick=0, mode="exact")
assert record is not None
assert reader.read_payload(record) == b"binary sample"
# Optional: requesting this property performs one lazy full-file SHA-256 pass.
print(reader.file_sha256)
Immutable embedded assets
Version 0.3.3 adds optional asset frames for non-temporal data such as a scene
geometry index and chunked mesh buffers. write_asset is valid only before the first
temporal record; asset IDs are unique, bounded, indexed, and available through
FSRReader.assets and FSRReader.read_asset. Ordinary recordings that write no assets
retain the previous frame layout. Assets do not consume record sequence numbers and
therefore cannot disturb control, observation, event, or camera ordering.
Typed offline reads
FSRTypedReader adds simulator-free decoding for the portable JSON, raw RGB24, and
little-endian float64 codecs emitted by the official recorder. With the optional
images extra it also validates and decodes JPEG/PNG RGB payloads to the same
RGBFrame type. It borrows a fully
validated FSRReader: construction does not read the file again, create a second
reader, or take ownership of the caller's binary port.
from fastsim_recording_format import FSRReader, FSRTypedReader
reader = FSRReader(port) # structural validation does not scan record payloads
typed = FSRTypedReader.from_validated_reader(reader)
record = typed.seek("robot.joints", generation=0, tick=0, mode="exact")
assert record is not None and record.decoded == b"binary sample"
# A validated reference can be decoded directly, which is useful for bounded caches.
reference = reader.records("robot.joints")[0]
decoded = typed.read(reference)
The public typed surface also includes DecodedRecord, StreamKind,
classify_stream, decode_payload, and FSRCodecError. JSON decoding rejects
duplicate keys and non-finite numbers and enforces node/depth limits. RGB24 and
float64 payloads must match their declared shape, dtype, layout, color space, and
finite-value requirements. Unknown codecs fail closed; opaque raw payloads remain
bytes unless the stream claims to be RGB or calibration data.
The package never extracts files, follows embedded paths, or inventories directories. Artifact selection, path policy, and output transactions belong to FastSim Core and the Record/Replay plugins.
Canonical metadata profile
FSR v1 fixes its metadata profile as fastsim-fsr-cjson/1. This profile is separate
from FastSim Core's simulation-contract-digest canonicalizer. It uses UTF-8, no byte
order mark or whitespace, mapping keys in Unicode code-point order, source array order,
unescaped non-ASCII text, JSON's mandatory string escapes, decimal integer tokens, and
finite floating-point tokens in Python 3.11/3.12 json shortest-round-trip form.
Consequently the authoring distinction is retained for 1.0 and -0.0, and exponent
padding such as 1e-07 is normative in FSR v1. Non-Python implementations must
reproduce the package's published golden vectors byte for byte. A different numeric
profile requires a new FSR format version; it cannot silently change v1 bytes.
Installation and verification
Python 3.11 and 3.12 are supported. The base package has no runtime dependency on FastSim, UniRoboSim, a simulator SDK, NumPy, or an image library. Install the image extra when decoding compressed RGB streams:
python -m pip install "fastsim-recording-format[images]==0.3.3"
Without that extra, container validation and raw/JSON/numeric access remain available;
attempting typed JPEG/PNG decode fails with an actionable FSRCodecError.
From this repository:
python -m pytest packages/fastsim-recording-format/tests -q
python -m ruff check \
packages/fastsim-recording-format/src \
packages/fastsim-recording-format/tests
cd packages/fastsim-recording-format
python -m mypy --config-file pyproject.toml src/fastsim_recording_format
python -m build
License and authorship
Author: Hofee lexhofee@gmail.com
The package currently uses LicenseRef-Pending until the repository license is
finalized.