Peta::Dict#
Peta::Dict - a fast dictionary bound to a Perl hash with tie.
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 directtied(%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)copiesvalin (asv_setsvwithout get-magic and without stealing:valis the throwaway proxy, or the already-copied RHS of%h = (...), and$x = $h{$k} = 5reads 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/SCALARare each exactly one synchronous call from the responsible C function (magic_existspack,magic_clearpack,magic_wipepack,magic_nextpack,magic_scalarpack).deletereturns the value. Iteration order is whatever this class produces (perl imposes none on a tied hash);keys %hnever 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.