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.

