```{index} single: Peta::Dict ``` # Peta::Dict ```{pperl-module-badges} Peta::Dict ``` Peta::Dict - a fast dictionary bound to a Perl hash with `tie`. ```perl use Peta::Dict; tie my %count, 'Peta::Dict'; $count{$_}++ for qw(a b a c a); print join(", ", map { "$_=$count{$_}" } sort keys %count), "\n"; # a=3, b=1, c=1 delete $count{b}; print exists $count{b} ? "b still there\n" : "b gone\n"; # b gone ``` It behaves as a plain hash does - the same keys, values, `exists`, `delete`, `each` and `keys` - with its storage built for speed: under the JIT an element access compiles to a direct table probe. Iteration order is the table's own. Peta::Dict is pperl's own module, version 0.2.0. `%h` itself stays a faithful perl5 hash; what `tie` binds it to is a separate stratum: an open-addressed table (node's `Map` class, ~one cache line per access) whose element accesses the JIT compiles to an in-register probe. That is the same "native store + JIT probe" shape that carries Memoize, applied to a general container - a dictionary is a language facility every pole ships, and this one is stratified beside `%h` rather than changing it. The tie protocol is perl5's, untouched (`pp_sys.c pp_tie`, the `PERL_MAGIC_tied` node, the per-access `PERL_MAGIC_tiedelem` proxy, `mg.c Perl_magic_methcall`); the class merely implements the methods natively, so even the interpreted path never runs Perl code: * `FETCH(obj, key)` returns a copy of the stored value, as the pure-Perl class does - so a direct `tied(%h)->FETCH($k)` cannot change the entry through its result. A missing key returns undef, unconditionally (perl calls FETCH for every rvalue read; there is no existence pre-check to skip). * `STORE(obj, key, val)` copies `val` in (a `sv_setsv` without get-magic and without stealing: `val` is the throwaway proxy, or the already-copied RHS of `%h = (...)`, and `$x = $h{$k} = 5` reads the proxy again after the STORE). `$h{$k}++` is exactly one FETCH and one STORE on one proxy (`pp_preinc` -> `sv_inc` -> `SvSETMAGIC`). * `EXISTS`/`DELETE`/`CLEAR`/`FIRSTKEY`/`NEXTKEY`/`SCALAR` are each exactly one synchronous call from the responsible C function (`magic_existspack`, `magic_clearpack`, `magic_wipepack`, `magic_nextpack`, `magic_scalarpack`). `delete` returns the value. Iteration order is whatever this class produces (perl imposes none on a tied hash); `keys %h` never FETCHes. KEYS. A key is the integer or the string perl would stringify it to, so an integer key and its decimal string name the same entry, exactly as in a plain hash (`$h{5}` is `$h{"5"}`; `"05"` is another key). An IOK-only key is taken as its integer; anything else is stringified by perl's own `sv_2pv_flags` (which warns for undef as perl does) and an exact canonical decimal (no sign on zero, no leading zero, in IV range) is folded back to an integer. Integer keys live in the open-addressed table the JIT probes; string keys live in a side map that is correct and native but not JIT-probed. Iteration returns integer keys as IVs, whose string form is that same decimal. A string key is held as a hash holds it: a UTF-8 key that fits in Latin-1 is the same entry as its byte string, and one that does not keeps its UTF-8 flag through `keys`. The pure-Perl `assets/modules/user/Peta/Dict.pm` is the fallback perl5 (the t/TEST oracle) loads: a plain hash behind the same protocol, identical output, merely not fast. It is not embedded: pperl's registry answers the `require` first, and a build without this class stops there with the capability hint. The JIT seam: [`DictTable`] is `#[repr(C)]` with the probed fields first (`slots`/`state`/`mask`/`shift`), so compiled code reaches an entry with offset arithmetic from the table pointer. A HIT (an existing integer key) is served in-register; a MISS (an insert, which may grow the table) goes through the same Rust functions the methods use, after which the probe reloads the arrays from the struct.