internity 0.1.0

Blazingly fast string interning with compact handles, compact storage, and concurrent fill support
Documentation
# internity TODO

## Durable (`&'static str`) resolution — "static string leak" mode

### Motivation

The [`ustr`](https://github.com/anderslanglands/ustr) crate performs very well
but relies on **leaky process-global state**: its 8-byte `Ustr` handle points
into a bump allocator that is never freed, so `as_str()` yields `&'static str`
and `as_char_ptr()` a permanent null-terminated C pointer. That makes `ustr`
trivially usable for FFI and for anywhere a stable, permanent string reference
is needed.

We want internity to offer an equivalent capability — permanent `&'static str`
references suitable for FFI — **without** hiding a global singleton and without
changing the 4-byte `Sym` handle.

### Proposed design

Add durable resolution to the **frozen `Reader`**, leaking (only) the backing
string payload on demand:

- Keep the handle as the existing 4-byte `Sym`.
- Durable references are available **only from a frozen `Reader`**. The
  lifecycle stays: lexicons intern; readers resolve, iterate, and optionally
  leak immutable payloads.
- Leaking is opt-in and pay-as-you-go:
  - Flat reader: its entire string payload becomes permanent.
  - Sharded reader: only the referenced shard payload becomes permanent.
  - Offset tables and other reader metadata remain reclaimable.
  - Repeated calls return slices from the same payload with no extra
    allocation.

### Minimal API (preferred variant)

Make `LocalLexicon` **not** implement `Reader`, so every `Reader` is necessarily
frozen. Then durable resolution can be added directly to the existing `Reader`
trait, and **no other API needs to change**:

```rust
pub trait Reader: Sealed + Send + Sync {
    fn try_resolve(&self, sym: Sym) -> Option<&str>;

    /// Returns a permanent reference, intentionally leaking its backing buffer.
    fn try_get_durable_str(&self, sym: Sym) -> Option<&'static str>;

    fn get_durable_str(&self, sym: Sym) -> &'static str {
        self.try_get_durable_str(sym)
            .expect("internity: Sym does not belong to this reader")
    }

    // Existing methods...
}
```

Existing freeze signatures remain unchanged:

```rust
LocalLexicon::freeze()   -> impl Reader
ThreadedLexicon::freeze() -> impl Reader
Lexicon::freeze()        -> Box<dyn Reader>
```

Only two public changes are needed:

1. Remove the `Reader` impl from `LocalLexicon`.
2. Extend `Reader` with durable resolution (`try_get_durable_str` /
   `get_durable_str`).

(An alternative sketch introduced a separate `FrozenReader: Reader` trait with
`try_resolve_durable` / `resolve_durable`, but folding it into `Reader` is
simpler once `LocalLexicon` no longer implements `Reader`.)

### Miri considerations

Intentional leaks are memory-safe, but Miri's end-of-program leak checking will
still flag allocations reachable at exit. **Do not** globally disable leak
checking. Instead, durable-reference tests should retain the result in a static
root so the intentional leak is globally reachable while ordinary tests keep
detecting accidental leaks:

```rust
static DURABLE: OnceLock<&'static str> = OnceLock::new();

let value = reader.get_durable_str(sym);
DURABLE.set(value).unwrap();
drop(reader);
assert_eq!(*DURABLE.get().unwrap(), "value"); // survives reader drop
```

### Trade-off vs `ustr` (freeze constraint)

Unlike `ustr` — whose process-global interner accepts new strings indefinitely
and whose values give stable string access immediately — internity requires a
**fill → freeze** boundary: strings are interned during a build/fill phase, the
lexicon is consumed/frozen, and only then can a `Sym` resolve normally or yield
a leaked `&'static str`. No new strings can be interned into a lexicon after
freeze.

This suits **build-then-serve / static-dictionary** workloads (startup-bounded
interning) well, and is a poorer fit for consumers that intern unbounded new
strings throughout steady-state execution. Positioning: a specialized
static-dictionary / build-then-serve alternative to `ustr`, not a drop-in
replacement for its unbounded global interner.

Possible future extensions to widen applicability without abandoning freezing:
multiple frozen epochs/readers, overlay lexicons, or batch rotation — mind the
handle/reader association hazards (`Sym`s are only valid against their own
reader).