Skip to content

bimap: add compact canonical maps - #261

Draft
TurkeyMan wants to merge 3 commits into
masterfrom
ow/bimap
Draft

TurkeyMan wants to merge 3 commits into
masterfrom
ow/bimap

Conversation

@TurkeyMan

@TurkeyMan TurkeyMan commented Aug 24, 2026

Copy link
Copy Markdown
Member

Adds a compact canonical-first BiMap, compile-time StaticBiMap, and packed runtime synthesis.

  • BiMap!(K, V = void, has_reverse = true, has_ack = false) keeps unique K and V arrays independently sorted when reverse lookup is enabled.
  • Repeated endpoints retain their first opposite endpoint as canonical.
  • One-way maps contain only sorted K, parallel K-sorted V, and one count.
  • Reverse maps use one non-template BiMapIndexes for byte/ushort cross-index storage, widening, insertion, adjustment, and acknowledgement packing.
  • V=void maps K directly to a linear or adopted ordinal without value storage.
  • Ack methods are present only when has_ack is enabled. Bit 0 stores acknowledgement and the ordinal occupies the remaining bits.
  • String-like endpoints retain a const(char)[] API while storing sorted ushort offsets into aligned, length-prefixed packed character buffers.
  • Endpoint lookup uses urt.algorithm.binary_search; repeated strings are deduplicated before packing.

StaticBiMap!entries accepts either KVP!(K, V)[N] or plain K[N]; the latter emits an immutable BiMap!(K, void) whose ordinals follow declaration order. The template itself is the generated public scope: .map is the immutable map and manifest .k2decl is exact-width sorted-key-to-declaration metadata. Key and value strings share one aligned string block, and equal-width k2v/v2k tables share one concatenated map block.

synthesise_bimap(entries, scratch = null) builds the same canonical representation from a complete runtime table. It qsorts source ordinals, deduplicates endpoints, performs one exact persistent allocation laid out as the BiMap, sorted endpoint arrays, packed strings, and cross maps, then returns a const(BiMap*). Callers may supply the reusable ushort source-index workspace; the convenience path uses temporary memory. release_synthesised_bimap() releases the whole owned block once. Mutating APIs are unavailable through the const result.

Static and runtime construction share source-index qsort, deduplication, string packing, and integer-map helpers. Endpoint-aware work instantiates only per endpoint type; integer-only helpers are non-template. The embedded build emits no runtime-synthesis functions when the runtime constructor is unused.

urt.si.unit now uses StaticBiMap!(KVP!(string, ScaledUnit)[]) for spelling lookup and canonical reverse lookup. The map owns its packed spelling strings; manifest .k2decl is consumed only at CTFE to produce a 10-byte packed prefixability bitmap. The manifest spelling specs remain the CTFE parsing path because D cannot dereference pointers stored in an immutable static object during evaluation.

On the BL808 M0 release build, .text is 0xe7fae, .rodata is 0x239e8, and m0fw.bin is 1,129,696 bytes. Relative to the pre-migration build, that is 88 bytes less text, 64 bytes less rodata, and a 192-byte smaller firmware image. The small lookup-layer inline hints are size-significant under LDC -Oz: removing them increased .text by 684 bytes and the image by 704 bytes, while restoring them returned the image exactly to this baseline.

Tests cover dynamic, static, and runtime-synthesised maps; KVP and ordinal-only construction; narrow/wide and mixed-width storage; concatenated equal-width maps; canonical duplicate endpoints; acknowledgements; sparse ordinals; copied packed strings; embedded NULs; empty strings; CTFE construction; pointer keys; declaration-order output; const runtime ownership; and the complete unit parsing/formatting table. The forced Linux DMD suite passes 72/72 modules, and the BL808 M0 release build passes.

@TurkeyMan
TurkeyMan marked this pull request as draft September 14, 2026 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant