Repository navigation
v0.14.0
Added
C handles for the shared string, bytes, ordered-bytes and blob maps, in every binding. SyncExpanseStrMap, SyncExpanseBytesMap, SyncExpanseOrderedBytesMap and SyncExpanseBlobMap gain C handles in include/expanse.h: an owning handle, a borrowed reader, point operations, ordered reads (on the string map's reader; on the ordered-bytes map's handle, where they exclude writers for the call), blob reads into caller-owned buffers, and compare_exchange / get_or_insert on all four (#1544, #1545, #1546, #1547, #1549). Go, Java, .NET, Node.js and Python wrap them (#1552, #1551, #1553, #1555, #1556). scripts/check_abi_parity.py reports all 234 C symbols covered in every binding, with no deferrals.
Read-modify-write on the shared u64 map from C and the bindings. expanse_sync_map_compare_exchange and expanse_sync_map_get_or_insert, with compare_exchange, get_or_insert and update in Python, Node.js, Go, Java and .NET (#1531, #1533, #1534, #1535, #1536).
SyncExpanseOrderedBytesMap: get_or_insert, get_or_insert_with, owned_reader and detached_reader, with the reader identity check the other readers carry (#1562).
expanse_blob_status_t gains EXPANSE_BLOB_NOT_FOUND = 1, EXPANSE_BLOB_BUFFER_TOO_SMALL = 2, EXPANSE_BLOB_MISMATCH = 3 and EXPANSE_BLOB_NO_META_FIELD = 20; existing values are unchanged.
Changed
Blob maps keep hot_meta for short payloads (#1557). A payload of 7 bytes or fewer (3 or fewer on ExpanseBlobMap32) written with non-zero metadata used to be stored inline and lose its metadata; it is now an arena record that reads back and filters by its metadata. This applies to ExpanseBlobMap, SyncExpanseBlobMap, ExpanseBlobMap32, both C surfaces and every binding. Values written with metadata 0 keep their representation and memory.
- Memory (measured by #1560 with
examples/bytes_per_key.rsandbytes_per_key_32.rs): each such value costs a 16-byte arena record on the 64-bit maps in addition to its slot word (ExpanseBlobMapbytes per key 8.56 to 24.56); onExpanseBlobMap32it costs 24 bytes plus the payload and one arena entry, which counts against the 4,096 live entries that map can address, so a workload storing many short payloads with metadata can now reachOffsetOverflow. hot_metawider than 24 bits (12 onExpanseBlobMap32) is refused withMetaOverflow/EXPANSE_BLOB_META_OVERFLOWat every payload length, short payloads included, and the key is left unchanged.- A compare-and-swap that expected metadata 0 for such a value now reports a mismatch carrying the stored metadata.
- The image format is unchanged (version 2).
expanse_blob_map_get_into copies the whole payload or nothing (#1539). It used to copy what fit into a short buffer and return true. Now false with *out_len > buf_len means the buffer is too short (buf untouched, *out_len the required length); false with *out_len == 0 means absent; NULL with length 0 queries the length. The signature is unchanged. A caller that relied on truncation must size buf from *out_len and retry.
Documentation. SyncExpanseBlobMap::mem_used and mem_held wait for a running compaction and make writers wait; mem_used_estimate does neither. This is now stated on the C declarations and in the Node.js and Python docs (#1567).
Compatibility
- Public Rust API: additions only (
.github/public-api/expanse-trie.txt, 30 lines added, none removed). include/Judy.h: unchanged.include/expanse.h: additions only; no existingexpanse_*signature or status value changed.
Supply chain
deny.toml accepts the yanked zip 8.6.0 and RUSTSEC-2026-0367, -0368 and -0369 (zip before 9: extraction can keep SUID/SGID bits and write through symlinks outside the destination). zip reaches the tree only as the Windows build dependency of ext-php-rs, whose build script extracts the PHP development pack; no shipped artifact links it, and no released ext-php-rs accepts zip 9 yet (#1550, #1558).
What's Changed
📦 Packaging & Distribution
Other Changes
- bench(concurrency): the set ordered-readers verdict honours the host-guard record (Refs #1194) by @orieg in #1528
- feat(capi): expanse_sync_map_compare_exchange and get_or_insert; update stays a binding-level loop (Refs #1194) by @orieg in #1511
- docs(sync): state the measured set ordered-read result in the reader rustdoc (Refs #1194) by @orieg in #1529
- ci(deps): keep the Python 3.8 requirements out of the main Dependabot pip entry by @orieg in #1530
- feat(python): compare_exchange, get_or_insert and update on the shared map (Refs #1194) by @orieg in #1531
- feat(node): compare_exchange, get_or_insert and update on the shared map (Refs #1194) by @orieg in #1533
- feat(go): compare_exchange, get_or_insert and update on the shared map (Refs #1194) by @orieg in #1534
- feat(java): compare_exchange, get_or_insert and update on the shared map (Refs #1194) by @orieg in #1535
- feat(dotnet): compare_exchange, get_or_insert and update on the shared map (Refs #1194) by @orieg in #1536
- docs(compat): plan C handles for the shared string, bytes, ordered-bytes and blob maps (Refs #1538 #1194) by @orieg in #1537
- fix(capi): expanse_blob_map_get_into refuses a short buffer instead of truncating by @orieg in #1539
- ci(bench): restore the host-guard record on the set ordered-readers artifact (Refs #1194) by @orieg in #1541
- bench(concurrency): reclaim_stall driver probes the build and refuses a child off its pin by @orieg in #1542
- bench(concurrency): reclaim_stall reports the wait counters per stall window against thread CPU time by @orieg in #1543
- feat(capi): shared string map C handle with reader and point operations (Refs #1538) by @orieg in #1544
- feat(capi): ordered reads on the shared string map's reader handle (Refs #1538) by @orieg in #1545
- feat(capi): shared bytes and ordered-bytes map C handles (Refs #1538) by @orieg in #1546
- feat(capi): shared blob map C handle with caller-buffer reads (Refs #1538) by @orieg in #1547
- chore(deny): allow the yanked zip 8.6.0 build dependency of ext-php-rs by @orieg in #1550
- feat(capi): read-modify-write on the shared string, bytes, ordered-bytes and blob maps (Refs #1538) by @orieg in #1549
- feat(go): wrap the shared string, bytes, ordered-bytes and blob maps by @orieg in #1552
- feat(java): wrap the shared string, bytes, ordered-bytes and blob maps by @orieg in #1551
- feat(dotnet): wrap the shared string, bytes, ordered-bytes and blob maps by @orieg in #1553
- docs(compat): list the Java shared-map classes in the container table by @orieg in #1554
- feat(node): wrap the shared string, bytes, ordered-bytes and blob maps by @orieg in #1555
- feat(py): wrap the shared string, bytes, ordered-bytes and blob maps by @orieg in #1556
- chore(deny): ignore the zip < 9 extraction advisories for the ext-php-rs build dependency by @orieg in #1558
- fix(blob): keep hot_meta for payloads of 7 bytes or fewer by @orieg in #1557
- bench(blob): measure the memory cost of metadata on short blob payloads (Refs #1548) by @orieg in #1560
- docs(blob32): state the 4096-entry arena cap consistently by @orieg in #1561
- feat(sync): get_or_insert and owned/detached readers on SyncExpanseOrderedBytesMap by @orieg in #1562
- bench(sync): contended update cells for per-call reader registration (Refs #1417) by @orieg in #1564
- docs(blob): state the compaction wait on every blob mem_used/mem_held surface by @orieg in #1567
- docs(bench): record the METHODOLOGY section 39 pair for contended update cells by @orieg in #1568
Full Changelog: v0.13.0...v0.14.0