Skip to content

niso-structured-clone

Generated from crates/niso-structured-clone/CONTRACT.md when the site was built at 7aa23e2.

HTML’s structured serialization as bytes: the format the glue’s SER and DESER (glue/structured-clone.js) write and read, its decoder and encoder in Rust, and the graph inspection the core needs on a serialized value (own_property and create_data_property for IndexedDB key paths and key injection, buffer_source, js_string for string keys). The format is specified in docs/structured-clone.md.

It does not walk JS values (only an engine can; the glue does), run StructuredSerialize or StructuredDeserialize, or own transfer: a transferred object is the index of something carried beside the bytes (MessagePorts in niso-core’s messaging). It is pure: no host, no state, no threads.

Platform objects are its host-object hook. A serializable platform object (0x1E, Record::Platform) is its interface’s name and one data value, and the crate reads and writes that pair without knowing the interface: the interface’s owner runs its serialization steps, chooses the data, and on deserialization checks the data and throws DataCloneError for data it would not write or a realm the interface is not exposed in. DOMException, DOMRect, ImageData, Blob, File and CryptoKey use it today, with their owners’ steps in their glue sections (docs/structured-clone.md lists each one’s data). So Blob and File bytes, CryptoKey algorithms and key material, and secure contexts are not this crate’s concern, and an interface added later needs no change here.

  • Rust: VERSION; JsString, js_string, to_rust_string; VIEW_KINDS, ERROR_NAMES; BigInt, Value, Record; Serialized with primitive, decode, encode, add, record, own_property, create_data_property, buffer_source; array_index. testing (below) and, behind the contract-tests feature, contract.
  • C: niso_structured_clone_decode, _free, _value, _entry (include/structured-clone.h, its section of the composed niso_runtime.h). They are implemented in niso-capi’s clone.rs over this crate’s API until step 6c generates per-crate shims.
  • Format: docs/structured-clone.md, the byte format this crate and the glue read and write, with each platform object’s data. It is part of this contract and one of the crate’s sources: a change to it is a contract change, reviewed with this file.
  • IDL: none.
  • Glue: glue/structured-clone.js (SC), which writes and reads this format, composed into the glue in the monolith’s order until glue modules exist (step 6c).

niso-core re-exports the crate as niso_core::structured_clone.

None.

  • niso-base: ErrorCode, Result, err.
  1. Round trip: every corpus value (testing::values()) decodes to the graph it was written from, and encoding that again gives the same bytes. Objects are numbered in the order the format writes them (a view after its buffer). contract::round_trip.
  2. Input that ends early, has bytes past the value or another version is rejected with an Error whose code is DataClone, never a panic: it is bad input the caller can act on, and Internal is kept for broken invariants. contract::rejects_truncated_input.
  3. Decoding arbitrary bytes returns Ok or a DataClone error and never panics. contract::never_panics (corrupted corpus values).
  4. Objects nest at most 256 deep when read; deeper input is rejected, and that depth decodes on a 512 KB thread. contract::depth_limit, and the unit test max_depth_fits_a_512_kb_thread.
  5. js_string gives a string’s UTF-16 code units, so JsString order is JS’s string order (not Rust’s str order), and to_rust_string turns code units back with lone surrogates as U+FFFD. contract::js_strings.
  6. A platform object decodes to its interface name and its data as written, whatever the data is; checking it is the interface owner’s step (see Purpose). contract::platform_objects_are_opaque, on the Blob, File and CryptoKey records the glue writes.

All types are plain data and Send; every function may be called from any thread. Workers’ messages are decoded on the receiving agent’s thread.

Decoding allocates the decoded graph and nothing proportional to an unchecked length: every length is checked against the input first. About 1.6 KB of stack per nesting level in a debug build (cargo test -p niso-structured-clone --lib stack_per_level -- --ignored). cargo test -p niso-structured-clone runs in under a second after an edit.

None needed: the crate is pure. testing::values() is a corpus of graphs for this crate’s suite and for crates that store or send serialized values: every primitive and record kind, cycles, back-references, a view of each kind, length-tracking views, transferred buffers, Map and Set with object keys, errors with causes, sparse arrays, platform objects (a DOMException, and a Blob, File and CryptoKey with the data the glue writes), the deepest nesting and strings that are not well-formed UTF-16. testing::nested_arrays builds a graph of a given depth.

Read or write structured serialization bytes from any Rust program, for example to inspect what a page stored in IndexedDB: Serialized::decode(&bytes)?.own_property(&v.value, &js_string("id")).