niso-storage
Generated from crates/niso-storage/CONTRACT.md when the site was built at 7aa23e2.
Purpose
Section titled “Purpose”Ordered, transactional storage at the level IndexedDB needs: per origin
and storage endpoint, named databases with a version, object stores and
indexes, records ordered by encoded key, and transactions that commit or
abort. It never sees JS values: keys arrive encoded so that memcmp orders
them as IndexedDB orders keys (key), values as structured-serialization
bytes. It ships three drivers, SQLite (the default, one file per
database), memory (sessionStorage and tests) and the adapter for a host’s
C driver, and the configuration that picks one (directory, driver, app
origin).
niso’s document uses it through niso_core::storage, a re-export: the
core’s IndexedDB and Web Storage, and the Dom setters behind
niso_dom_set_storage_*, are its users until steps 9 and 10.
Public surface
Section titled “Public surface”- Rust:
Driverand its types:Endpoint,Mode,Durability,DriverError,DriverResult,DbId,TxnId,StoreId,IndexId,StoreInfo,IndexInfo,DatabaseInfo,Source,End,Range,Entry.- The drivers:
sqlite::SqliteDriver,memory::MemoryDriver(with a quota),ffi::CDriveroverffi::NisoStorageDriver. key::Key(encode, decode, from and to structured-clone values).- Configuration:
Config(Directory,Driver),Storage(configureandset_app_origin, both InvalidState once in use;in_use,app_origin,driver,driver_for,session),default_app_origin,default_directory. From<DriverError> for niso_base::Error.- Behind the
testingfeature,testing(below); behindcontract-tests(which turns ontesting),contract:suiteand its checks (exercise,edges,names,beside,aborted_upgrade,deletes,all_or_nothing),snapshots,configuration,k.
- C: include/storage-driver.h and include/storage-config.h, its sections
of the composed niso_runtime.h (
niso_storage_driverand its structs,niso_dom_set_storage_driver,niso_dom_set_storage_directory,niso_dom_set_app_origin);ffi’s#[repr(C)]types are their Rust side. The setters stay in niso-capi, unchanged.
Host requirements
Section titled “Host requirements”None: it needs no host. A user keeps one Storage (or one driver), on one
thread, and makes the calls Driver documents in the order it documents:
schema changes only in a “versionchange” transaction, reads and writes only
in a transaction’s scope, at most one “readwrite” or “versionchange”
transaction per database at a time, and read-only ones beside it only on
stores it does not use.
May assume from dependencies
Section titled “May assume from dependencies”niso-base: Error, ErrorCode, Result, err. niso-structured-clone:
Value, Record, Serialized, JsString, js_string, for key
conversion.
Guarantees
Section titled “Guarantees”What the contract suite checks, on the memory driver, the SQLite driver
and both behind the C vtable (tests/contract.rs). suite runs every check
that needs one driver; snapshots needs two on shared storage, and runs
on SQLite and C over SQLite; configuration is of Storage and runs once.
- A database opens at version 0 with no stores; a committed transaction’s schema, version, records, index entries and key generator are what the next open and transaction see; an aborted “readwrite” transaction’s records and generator are undone. exercise.
- An aborted “versionchange” transaction leaves the version and the schema as they were (stores made, deleted, renamed; indexes deleted), records and index entries included. aborted_upgrade.
- A transaction the caller aborts after one of its calls failed (a put, or the commit itself) leaves nothing behind. all_or_nothing, with FailingDriver making the failures. A driver’s own failure in the middle of its commit is not something the suite can cause; SQLite’s commit is one SQLite transaction.
- Scans visit a store’s records, or an index’s (key, primary) entries, in
memcmp order of the encoded keys, forwards or in reverse, within a range
whose ends may be open and may name a (key, primary) pair;
countagrees with the scan. exercise, edges. - Replacing or deleting records drops their index entries;
clearempties a store and its indexes and no other store;delete_storetakes the store and its indexes, and a store made again under the same name and id starts empty, with none of the deleted one’s indexes. exercise, edges, deletes. - A read-only transaction on one store runs beside a “readwrite” one on another, and the writer’s commit lands. beside.
- Two drivers on shared storage: a read-only transaction begun before the other’s commit does not see it, and one begun after does. snapshots. One driver alone never shows this (the core never runs a reader beside a writer on the same store), and memory drivers share nothing, so it runs on SQLite only, directly and through the vtable.
databaseslists committed versions per origin and endpoint; deleting one database keeps the others, names of any length work, and a new database takes a free place. names.- The driver and the app origin are settable until the first driver call, then InvalidState. configuration.
- A write past the space a driver has fails with
DriverError::quota, which the C adapter carries asNISO_QUOTA_EXCEEDEDboth ways. (tests/contract.rs, and the memory driver’s quota test.) - The C adapter (
CDriver) lets a panic inscan’s visitor stop the scan and go on once the host’s driver has returned, never unwinding through the host’s C frames. (tests/contract.rs.)
SQLite alone also detects another document deleting or replacing a
database’s file under an open one (its readers and writers then fail
rather than read or lose data): unit tests in lib.rs, #[cfg(unix)], as
they rely on unix file semantics; they are SQLite’s, not the contract’s.
Differences from architecture.md
Section titled “Differences from architecture.md”- Configuration is a plain
Storagevalue the document holds in a field, not an extension slot: niso-storage has no host (2.2), and the slot comes with the crates that install it (niso-indexeddb, niso-webstorage, steps 9 and 10; architecture.md 11). - The Web Storage areas loaded so far stay in niso-core, beside
Storage, until step 10.
Threads
Section titled “Threads”Not Send: a driver lives on the document’s thread. SQLite drivers on different threads (several documents, workers) may share files; SQLite’s locking serializes them, and a read-only transaction reads one snapshot.
Performance and memory
Section titled “Performance and memory”The move changes no code path: the document calls the same drivers through one more field. The memory driver keeps everything in maps, up to its quota (256 MiB by default).
memory::MemoryDriver is the fake driver, and exact. Behind the testing
feature, so the shipped build leaves them out: testing::FailingDriver
wraps a driver and fails its n-th fallible call (or every one from it), as
a quota error or not, for dependents’ error paths; testing::vtable puts
any driver behind the C vtable, as a host’s C driver is, its functions
catching panics (the Internal status) and refusing bad mode, durability or
endpoint bytes (the Type status).
Miri runs the contract suite’s memory and C-vtable tests (tests/check.sh):
MIRIFLAGS=-Zmiri-ignore-leaks cargo +nightly miri test -p niso-storage --test contract. The SQLite tests are cfg_attr(miri, ignore), as SQLite
is C.
Standalone use
Section titled “Standalone use”MemoryDriver::default() or SqliteDriver::new(dir), then open,
begin, create_store, put, scan and commit with keys from
Key::encode.