NAME Data::HashMap - Fast type-specialized hash maps with TTL and LRU, in C SYNOPSIS use Data::HashMap::II; # Keyword API (fastest - bypasses method dispatch) my $map = Data::HashMap::II->new(); hm_ii_put $map, 42, 100; my $val = hm_ii_get $map, 42; # 100 hm_ii_exists $map, 42; # true hm_ii_remove $map, 42; # Method API (convenient - same operations) $map->put(42, 100); $val = $map->get(42); # 100 $map->exists(42); # true $map->remove(42); # Counter operations (integer-value variants only) my $count = hm_ii_incr $map, 1; # 1 $count = hm_ii_incr $map, 1; # 2 $count = hm_ii_decr $map, 1; # 1 # Iteration my @keys = hm_ii_keys $map; my @values = hm_ii_values $map; my @pairs = hm_ii_items $map; # (k1, v1, k2, v2, ...) while (my ($k, $v) = hm_ii_each $map) { print "$k=$v\n" } # Bulk operations my $href = hm_ii_to_hash $map; # Perl hashref snapshot hm_ii_clear $map; # remove all entries # LRU cache (max 1000 entries, evicts least-recently-used) my $lru = Data::HashMap::II->new(1000); # TTL cache (entries expire after 60 seconds) my $ttl = Data::HashMap::II->new(0, 60); # LRU + TTL combined my $both = Data::HashMap::II->new(1000, 60); # Per-key TTL hm_ii_put_ttl $map, 42, 100, 30; # expires in 30 seconds # Get or set default my $v = hm_ii_get_or_set $map, 99, 0; # insert 0 if key 99 absent DESCRIPTION Fourteen hash maps implemented in C, each specialised for one combination of key and value type. Most operations are available both as a keyword, which bypasses method dispatch, and as a method ("$map->get($key)"); a few are methods only. Keywords are enabled by "use Data::HashMap::XX" for the rest of the enclosing lexical scope, normally the file. Each file that calls them needs its own "use" line; "use Data::HashMap::XX ()" does not enable them. Without it a call still compiles, as a method call, and fails when it runs with "Can't locate object method "hm_xx_get" via package "Data::HashMap::XX"". VARIANTS Data::HashMap::I16 - int16 keys, int16 values (4-byte node) Data::HashMap::I16A - int16 keys, any Perl value Data::HashMap::I16S - int16 keys, string values Data::HashMap::I32 - int32 keys, int32 values (8-byte node) Data::HashMap::I32A - int32 keys, any Perl value Data::HashMap::I32S - int32 keys, string values Data::HashMap::II - int64 keys, int64 values (16-byte node) Data::HashMap::IA - int64 keys, any Perl value Data::HashMap::IS - int64 keys, string values Data::HashMap::SA - string keys, any Perl value Data::HashMap::SI16 - string keys, int16 values Data::HashMap::SI32 - string keys, int32 values Data::HashMap::SI - string keys, int64 values Data::HashMap::SS - string keys, string values KEYWORDS Each variant provides the following keywords (replace "xx" with the variant prefix: "i16", "i16a", "i16s", "i32", "i32a", "i32s", "ia", "ii", "is", "sa", "si16", "si32", "si", "ss"): hm_xx_put $map, $key, $value # insert/update, returns bool hm_xx_get $map, $key # lookup, returns value or undef hm_xx_exists $map, $key # returns bool hm_xx_remove $map, $key # returns bool hm_xx_take $map, $key # remove and return value, or undef hm_xx_drain $map, $n # remove up to N entries, returns (k1,v1,...) hm_xx_pop $map # remove+return (key,val): LRU tail or next entry hm_xx_shift $map # remove+return (key,val): LRU head or prev entry hm_xx_reserve $map, $n # pre-allocate capacity for N entries hm_xx_purge $map # reap already-expired TTL entries hm_xx_capacity $map # current internal table capacity hm_xx_persist $map, $key # remove TTL from key (make permanent) hm_xx_swap $map, $key, $new # replace value, return old (undef if missing) Integer-value variants (I16, I32, II, SI16, SI32, SI) also provide: hm_xx_incr $map, $key # +1, returns new value (new keys init to 0) hm_xx_decr $map, $key # -1, returns new value (new keys init to 0) hm_xx_incr_by $map, $key, $n # +N, returns new value (new keys init to 0) hm_xx_cas $map, $key, $expected, $new # compare-and-swap, returns bool All variants also provide: hm_xx_size $map # entry count, including expired entries not yet reaped hm_xx_keys $map # returns list of keys hm_xx_values $map # returns list of values hm_xx_items $map # returns (k1,v1, k2,v2, ...) hm_xx_max_size $map # returns max_size (0 = no LRU) hm_xx_ttl $map # returns default TTL in seconds (0 = no TTL) hm_xx_lru_skip $map # returns lru_skip percentage (0 = strict LRU) hm_xx_clear $map # remove all entries hm_xx_to_hash $map # returns a Perl hashref snapshot hm_xx_each $map # returns (key, value) or empty list hm_xx_iter_reset $map # reset each() iterator to start hm_xx_put_ttl $map, $key, $val, $seconds # insert with per-key TTL ($seconds=0 uses map default) hm_xx_get_or_set $map, $key, $default # get existing or insert default String-value variants (SS, IS, I32S, I16S) also provide: hm_xx_get_direct $map, $key # zero-copy get (read-only, see CAVEATS) Method-only operations (no keyword form): $map->clone # copy the map (SV* values per its copy mode) $map->from_hash(\%h) # bulk-insert from a Perl hashref $map->merge($other_map) # copy in another map's entries; it wins on a conflict $map->freeze # serialize to binary string (non-SV* variants) MyVariant->thaw($data) # reconstruct map from freeze data CONSTRUCTOR my $map = Data::HashMap::II->new(); # plain (no LRU, no TTL) my $lru = Data::HashMap::II->new(1000); # LRU: max 1000 entries my $ttl = Data::HashMap::II->new(0, 60); # TTL: 60-second expiry my $both = Data::HashMap::II->new(1000, 60); # LRU + TTL my $fast = Data::HashMap::II->new(1000, 0, 90); # LRU + 90% skip my $safe = Data::HashMap::SA->new(0, 0, 0, 1); # SV* variants: copy values The arguments are "max_size", "ttl" in seconds, and "lru_skip", each a non-negative number; the SV* variants take a fourth, "copy" (see "CAVEATS"). A reference, a non-numeric string or an extra argument croaks; a "ttl" below one second rounds up to one, and one beyond 2**32 seconds saturates. LRU eviction With "max_size" set, inserting a new key into a full map evicts the least recently used entry. "get", "put" on an existing key, and the counters promote the entry they touch; "exists" does not. "lru_skip" (0-99, larger values clamp to 99) skips promotion on exactly that percentage of the accesses that would promote: 90 promotes one in ten. The least recently used entry is always promoted when touched, so it cannot be starved, and touching the most recently used one never counts. This trades eviction precision for speed on read-heavy workloads with hot keys; 90 suits most caches. TTL expiry With a default "ttl", or a per-key one from "put_ttl" (which also works on a map without a default), entries expire lazily, with one-second resolution: an entry with a TTL of *n* seconds is readable for between *n* and *n*+1 seconds. Iteration ("keys", "values", "items", "each", "to_hash") skips expired entries, and "get", "exists" and the counters remove one they touch; "size" counts expired entries until they are removed. Entries nobody touches again are reaped when the table would otherwise grow, so a map fed never-repeating keys stays bounded, at a few times its live set. "purge" reaps every expired entry on demand and never removes a live one. On a map with a default TTL, "put" and the counters renew an existing entry's lifetime to that default -- even one given its own TTL by "put_ttl" or made permanent by "persist" -- so a counter that keeps being hit never expires. "swap", "cas", "get_or_set" on an existing key, and reads leave the expiry alone. On a map without a default the counters leave it alone too, while "put" clears any "put_ttl" deadline. "get_or_set" inserts with the default TTL; use "put_ttl" for a per-key one. On a map with both "max_size" and a TTL, eviction takes the least recently used entry whether or not it has expired, and an expired entry keeps its slot and its value until it is reaped: call "purge" periodically so that expired entries, not live ones, make room. CAVEATS Keyword syntax A keyword taking two or more arguments takes a plain comma list with no parentheses: "hm_ii_get $m, $k". "hm_ii_get($m, $k)" fails to compile with "Expected ','", and "=>" is not a separator; one-argument keywords such as hm_xx_size($m) do accept parentheses. A keyword parses like a list operator, so everything after the last comma is the last argument: "hm_xx_get $m, $k // $default" looks up "$k // $default". Parenthesise the call when an operator follows it: "(hm_xx_get $m, $k) // $default", "(hm_sa_get $m, $id)->{field}". "hm_xx_keys", "hm_xx_values" and "hm_xx_items" return the entry count in scalar context, which on a TTL map includes expired entries the list form skips. "no Data::HashMap::XX" does not remove the keywords. Integers Keys and values are checked against the variant's range on every call, "from_hash" included, and croak outside it rather than wrap. INT_MIN and INT_MIN+1 are reserved as keys: "put", "put_ttl", "get_or_set" and lookups ignore them, while "incr", "decr" and "incr_by" croak. The counters also croak rather than overflow, leaving the value unchanged, with the same "increment failed" message. The I16 and I32 croaks name the full type range, reserved values included. A 64-bit perl ("use64bitint") is required. String keys Identity is the key's bytes. A UTF-8-flagged key whose characters fit in one byte is downgraded first, so "caf\xe9" and its upgraded form are one key, as in a hash. A key that needs UTF-8 is returned with the flag of its most recent "put", and collides with its own encoded octets: "\x{263a}" and "\xe2\x98\xba" are one key here and two in a hash, so copying a hash that holds both into a map keeps only one. Perl values By default the SV* variants (I16A, I32A, IA, SA) store the SV you pass, not a copy, as 0.08 did. That is the fastest option, but the stored value stays tied to the caller's variable: storing $_ in a "while (<$fh>)" loop leaves every entry holding the loop's final "undef"; a "substr" result, $1 or a "foreach" variable changes after it is stored; "from_hash", "clone", "merge" and "to_hash" share SVs with their source; and a stored literal is read-only. Store a copy ("$_"), or pass a true fourth argument, "copy", to "new": the map then copies each value it stores, as a hash does, and each put costs 1.6 to 2.6 times as much, still less than a Perl hash. In either mode "get", "values", "items" and "each" return the map's own SV, as "values %h" does, and referents are shared. Iteration and order Key order is unspecified and differs between processes, as with Perl's own hashes; sort if you need a stable one. "each" restarts if a "put", "remove", "incr" or "get_or_set" resizes or compacts the table, so do not mutate the map during "each"; in scalar context it returns the key. "drain" removes entries in table order, not LRU order, unlike "pop" and "shift" on an LRU map. "pop" and "shift" skip expired entries; on an LRU map they also reap them, and on a plain map "size" keeps counting them until a lookup reaps them. get_direct Returns a read-only SV that borrows the map's buffer instead of copying it, for immediate use such as comparing or printing. It is valid until that entry is next changed, removed, reaped or evicted, so on a TTL or LRU map treat it as valid only until the next call on the map. Arguments are evaluated before a call: "f($m->get_direct($k), $m->remove($k))" hands "f" freed memory. "my $d = $m->get_direct($k)" copies, so $d is safe to keep. freeze and thaw The format is native-endian and not portable across byte orders. Output is not byte-stable -- table order drifts across rehashes and, with the per-process hash seed, between runs -- so do not use a frozen blob as a digest or cache key; "thaw" round-trips the data regardless. Each entry's remaining lifetime is stored, and "thaw" starts that countdown afresh, so time spent frozen does not count. A "thaw"ed LRU map has lost its recency order. All of this applies to Storable too. from_hash and merge "from_hash" croaks on a key or value the matching "put" would reject, leaving the entries inserted so far in place, skips reserved keys silently, and reads tied hashes. "merge" copies another map's entries in, the other map winning a conflict; they take this map's TTL rules, not the other map's, so merging into a map without a TTL makes them permanent. Capacity A table never shrinks: "remove" and "clear" keep its capacity for reuse; build a new map to release the memory. The table is the next power of two at or above 4/3 of the entries it holds, so a full LRU map's table is between about 1.34 and 4 times "max_size": smaller while filling, larger after a "reserve". Taint The string-value variants (I16S, I32S, IS, SS) keep values in plain C buffers, so a tainted string comes back untainted. Under "-T" do not round-trip untrusted data through them into a sensitive operation; the SV* variants preserve taint. Keys are never tainted, as with Perl's hashes. STORABLE Storable's "freeze", "thaw" and "dclone" work on the ten variants with a native "freeze", through that format, and produce a separate map. The four SV* variants croak with "freeze not supported for SV* variants; use to_hash + Storable". Clone and other deep copiers that duplicate a blessed scalar without a hook are not supported: the copy would share the C table and free it twice. THREADS Maps are not shared between ithreads. Every variant inherits "CLONE_SKIP" from "Data::HashMap", so a child thread keeps the reference but it points at an unblessed "undef", and "DESTROY" never runs on it there; without that, both interpreters would free the same C table. Test with Scalar::Util::blessed($map). A thread that needs a map should build its own, or receive the contents as a plain hash via "to_hash". PERFORMANCE Benchmarks with 100k entries on Linux x86_64, in iterations per second (higher is better). The 16-bit variants (I16, I16A, I16S, SI16) run at 30k entries, since int16 caps them near 65k unique keys, so their rows overstate their speed; at equal size SI16 and SI32 run within a few percent of SI, so choose them for their range checks. INSERT (iterations/sec): Rate perl_ss perl_ii SA SS I32A IA I32S IS SI SI32 I32 II I16A I16S SI16 I16 perl_ss 20.0/s -- -1% -2% -26% -31% -32% -51% -52% -53% -54% -81% -82% -84% -86% -87% -95% perl_ii 20.2/s 1% -- -1% -25% -30% -32% -51% -51% -53% -54% -81% -82% -83% -86% -87% -95% SA 20.4/s 2% 1% -- -25% -29% -31% -50% -51% -53% -53% -81% -82% -83% -86% -87% -95% SS 27.1/s 35% 34% 33% -- -6% -8% -34% -35% -37% -38% -75% -76% -78% -81% -83% -94% I32A 28.8/s 44% 43% 41% 6% -- -3% -30% -30% -33% -34% -73% -74% -76% -80% -82% -93% IA 29.6/s 48% 46% 45% 9% 3% -- -28% -29% -31% -32% -72% -73% -76% -79% -81% -93% I32S 41.0/s 105% 103% 101% 51% 42% 39% -- -1% -5% -6% -62% -63% -66% -71% -74% -90% IS 41.4/s 107% 105% 103% 53% 44% 40% 1% -- -4% -5% -61% -63% -66% -71% -74% -90% SI 42.9/s 115% 113% 111% 59% 49% 45% 5% 4% -- -1% -60% -61% -65% -70% -73% -90% SI32 43.5/s 118% 115% 113% 61% 51% 47% 6% 5% 1% -- -59% -61% -64% -70% -72% -90% I32 107/s 435% 430% 425% 295% 272% 262% 161% 159% 149% 146% -- -3% -12% -25% -32% -75% II 111/s 453% 448% 442% 309% 284% 274% 170% 167% 158% 154% 3% -- -9% -23% -29% -74% I16A 121/s 507% 501% 495% 348% 322% 311% 196% 193% 183% 179% 13% 10% -- -15% -23% -71% I16S 143/s 614% 607% 600% 428% 396% 383% 248% 245% 233% 228% 33% 29% 18% -- -9% -66% SI16 157/s 684% 676% 669% 479% 444% 430% 282% 279% 265% 260% 46% 42% 29% 10% -- -63% I16 424/s 2020% 1999% 1980% 1466% 1372% 1334% 934% 925% 887% 875% 296% 283% 249% 197% 170% -- LOOKUP (iterations/sec): Rate SS SA SS_direct perl_ii perl_ss SI SI32 IS I32S IS_direct I32A IA II I32 SI16 I16A I16S I16 SS 35.0/s -- -2% -3% -7% -12% -19% -20% -43% -45% -47% -47% -47% -72% -75% -83% -88% -89% -93% SA 35.7/s 2% -- -1% -5% -10% -18% -18% -42% -43% -45% -46% -46% -71% -74% -82% -88% -89% -93% SS_direct 36.2/s 3% 1% -- -4% -9% -17% -17% -41% -43% -45% -45% -46% -71% -74% -82% -88% -89% -93% perl_ii 37.7/s 8% 5% 4% -- -5% -13% -13% -39% -40% -42% -43% -43% -70% -73% -82% -87% -89% -92% perl_ss 39.6/s 13% 11% 9% 5% -- -9% -9% -36% -37% -40% -40% -41% -68% -72% -81% -87% -88% -92% SI 43.3/s 24% 21% 20% 15% 10% -- -0% -30% -31% -34% -34% -35% -65% -69% -79% -85% -87% -91% SI32 43.5/s 24% 22% 20% 15% 10% 0% -- -29% -31% -33% -34% -35% -65% -69% -79% -85% -87% -91% IS 61.5/s 76% 72% 70% 63% 56% 42% 41% -- -2% -6% -7% -8% -51% -56% -70% -79% -81% -88% I32S 63.0/s 80% 76% 74% 67% 59% 45% 45% 2% -- -4% -5% -5% -49% -55% -69% -79% -81% -87% IS_direct 65.4/s 87% 83% 81% 74% 65% 51% 50% 6% 4% -- -1% -2% -47% -53% -68% -78% -80% -87% I32A 66.0/s 89% 85% 83% 75% 67% 52% 52% 7% 5% 1% -- -1% -47% -53% -68% -78% -80% -87% IA 66.6/s 90% 86% 84% 77% 68% 54% 53% 8% 6% 2% 1% -- -47% -52% -67% -78% -80% -86% II 124/s 256% 248% 244% 230% 215% 187% 186% 102% 97% 90% 88% 87% -- -11% -39% -58% -62% -75% I32 140/s 300% 291% 287% 271% 253% 223% 221% 127% 122% 114% 112% 110% 12% -- -31% -53% -58% -72% SI16 204/s 483% 470% 463% 441% 415% 370% 368% 231% 223% 211% 208% 206% 64% 46% -- -32% -38% -59% I16A 297/s 751% 733% 723% 690% 652% 586% 584% 384% 372% 355% 350% 347% 139% 113% 46% -- -10% -40% I16S 329/s 841% 821% 810% 774% 732% 659% 657% 435% 422% 403% 398% 394% 164% 135% 62% 11% -- -33% I16 492/s 1308% 1278% 1261% 1207% 1144% 1036% 1032% 700% 681% 653% 645% 639% 296% 252% 142% 65% 50% -- INCREMENT (iterations/sec): Rate perl_ss perl_ii SI SI32 II I32 SI16 I16 perl_ss 31.2/s -- -5% -24% -26% -66% -70% -79% -92% perl_ii 32.9/s 6% -- -19% -22% -64% -68% -78% -91% SI 40.9/s 31% 24% -- -3% -55% -60% -73% -89% SI32 42.1/s 35% 28% 3% -- -54% -59% -72% -89% II 91.0/s 192% 177% 123% 116% -- -11% -40% -76% I32 103/s 229% 212% 151% 144% 13% -- -32% -73% SI16 151/s 386% 360% 271% 260% 66% 48% -- -60% I16 380/s 1118% 1054% 829% 802% 317% 270% 151% -- DELETE (iterations/sec): Rate SS perl_ss SA perl_ii SI SI32 I32A IA I32S IS II I32 I16A SI16 I16S I16 SS 13.0/s -- -11% -15% -25% -39% -41% -48% -49% -53% -53% -78% -81% -87% -87% -89% -95% perl_ss 14.6/s 12% -- -5% -16% -31% -33% -42% -42% -47% -47% -76% -79% -85% -85% -88% -94% SA 15.3/s 18% 5% -- -12% -28% -30% -39% -40% -44% -45% -75% -78% -84% -84% -88% -94% perl_ii 17.4/s 34% 19% 14% -- -18% -21% -31% -31% -36% -37% -71% -75% -82% -82% -86% -93% SI 21.3/s 64% 46% 39% 22% -- -3% -15% -16% -22% -23% -65% -69% -78% -78% -83% -92% SI32 21.9/s 68% 50% 43% 26% 3% -- -13% -13% -20% -21% -64% -68% -78% -78% -82% -91% I32A 25.2/s 94% 73% 65% 45% 18% 15% -- -0% -8% -9% -58% -63% -74% -74% -79% -90% IA 25.3/s 94% 73% 65% 45% 19% 15% 0% -- -8% -9% -58% -63% -74% -74% -79% -90% I32S 27.4/s 111% 88% 79% 57% 29% 25% 9% 8% -- -1% -54% -60% -72% -72% -78% -89% IS 27.8/s 113% 90% 82% 60% 30% 27% 10% 10% 1% -- -54% -60% -72% -72% -77% -89% II 60.3/s 363% 313% 294% 246% 183% 175% 139% 138% 120% 117% -- -13% -38% -38% -51% -76% I32 69.0/s 430% 372% 351% 296% 224% 215% 173% 173% 152% 148% 14% -- -29% -29% -44% -73% I16A 97.6/s 649% 568% 537% 460% 357% 345% 287% 286% 256% 251% 62% 41% -- -0% -21% -62% SI16 97.8/s 651% 569% 539% 461% 359% 346% 287% 286% 256% 252% 62% 42% 0% -- -20% -62% I16S 123/s 843% 740% 702% 605% 476% 460% 387% 385% 348% 342% 104% 78% 26% 26% -- -52% I16 254/s 1852% 1640% 1561% 1359% 1092% 1059% 908% 905% 827% 815% 322% 269% 161% 160% 107% -- LRU / TTL overhead INSERT, II variant (iterations/sec): Rate II_lru_ttl II_lru II II_lru_ttl 75.5/s -- -7% -31% II_lru 81.5/s 8% -- -26% II 110/s 45% 34% -- LOOKUP, II variant (iterations/sec): Rate II_lru_ttl II_lru_s90 II_lru II II_lru_ttl 84.2/s -- -9% -11% -32% II_lru_s90 92.8/s 10% -- -3% -25% II_lru 95.2/s 13% 3% -- -23% II 124/s 47% 33% 30% -- LRU EVICTION CHURN: insert 100k into capacity 50k (iterations/sec): Rate II_lru_ttl II_lru II_lru_ttl 92.2/s -- -9% II_lru 102/s 10% -- Method vs keyword overhead Method calls cost more than keywords: II variant, 100k operations (iterations/sec): keyword method extra time per call LOOKUP 122/s 105/s +16% INSERT 110/s 98.4/s +12% MEMORY Memory usage with 1M entries (fork-isolated measurements): Variant Memory Bytes/entry vs Perl hash ------- ------ ----------- ------------ I16* 0.6 MB 21 8x less I32 29 MB 30 5.5x less II 45 MB 46 3.5x less I32S 73 MB 75 2.2x less IS 73 MB 75 2.2x less SI16 73 MB 75 2.2x less SI32 73 MB 75 2.2x less SI 73 MB 75 2.2x less I16A* 0.6 MB 21 8x less I16S* 0.6 MB 21 8x less I32A 92 MB 95 1.7x less IA 92 MB 95 1.7x less SS 121 MB 124 1.3x less SA 140 MB 144 1.1x less perl %h (int) 159 MB 163 (baseline) perl %h (str) 166 MB 170 (baseline) * I16/I16A/I16S measured at 30k entries (the int16 key range caps unique keys at ~65k). Every row is larger than its node size because the table is a power of two at or above 4/3 of the entries and a growth step transiently holds both tables; the I16 row is the least favourable case, sitting just above a power-of-two boundary. LRU / TTL memory overhead Per-entry cost of LRU (prev/next indices) and TTL (expiry timestamp), at 1M entries. II variant (int64/int64): Variant Bytes/entry LRU overhead +TTL overhead ------- ----------- ------------ ------------- II 46.4 - - II_lru 67.3 +20.9 B - II_lru_ttl 79.9 - +12.6 B SS variant (string/string, various key+value sizes): Variant Bytes/entry LRU overhead +TTL overhead ------- ----------- ------------ ------------- SS 8B keys 123.9 - - SS 8B lru 140.7 +16.8 B - SS 8B lru+ttl 152.2 - +11.5 B SS 16B keys 123.9 - - SS 16B lru 140.7 +16.8 B - SS 16B lru+ttl 152.2 - +11.5 B SS 32B keys 155.9 - - SS 32B lru 172.7 +16.8 B - SS 32B lru+ttl 181.1 - +8.4 B SS 64B keys 220.0 - - SS 64B lru 236.7 +16.8 B - SS 64B lru+ttl 245.1 - +8.4 B IMPLEMENTATION * Open addressing with linear probing; tombstone deletion with automatic compaction * xxHash v0.8.3 ("XXH3_64bits_withSecret") for integer and string keys, keyed by a secret derived at load time from Perl's hash seed for DoS resistance * Resize at 75% load; initial capacity 16 * LRU and TTL state in parallel arrays indexed by slot, allocated only when used, so a map without them pays one never-taken branch * Strings stored as raw C buffers, with the UTF-8 flag in the high bit of the length DEPENDENCIES XS::Parse::Keyword (>= 0.40) AUTHOR vividsnow LICENSE This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.