NAME

Tie::OrderedHash - Ordered Hashes with a public C ABI

VERSION

Version 0.01

SYNOPSIS

use Tie::OrderedHash;

tie my %h, 'Tie::OrderedHash';
$h{z} = 1;
$h{a} = 2;
$h{m} = 3;
print join(",", keys %h), "\n";   # z,a,m  (insertion order)

# Optional initial list, same as Tie::IxHash:
tie my %g, 'Tie::OrderedHash', alpha => 1, beta => 2, gamma => 3;

# OO interface
my $oh = Tie::OrderedHash->new(first => 1, second => 2);
$oh->Push(third => 3);
my @keys = $oh->Keys;             # ('first','second','third')
my ($k, $v) = $oh->Pop;           # ('third', 3)

STORAGE

Internally each Tie::OrderedHash impl is a blessed AV of four slots:

$self->[0]   # HV mapping key string -> insertion index (IV)
$self->[1]   # AV of keys, in insertion order
$self->[2]   # AV of values, in insertion order
$self->[3]   # IV cursor used by FIRSTKEY/NEXTKEY

This shape is deliberately the same as Tie::IxHash's, so code that poked at $ixhash->[1] for a key list still gets the right answer when migrated to Tie::OrderedHash.

TIED-HASH INTERFACE

The standard perltie hash methods are implemented in XS:

TIEHASH(class, ?LIST)

tie %h, 'Tie::OrderedHash', LIST seeds the hash with LIST as key/value pairs in source order, same as Tie::IxHash.

FETCH, STORE, EXISTS, DELETE, CLEAR

Standard. STORE on an existing key updates the value but preserves that key's position - matches Tie::IxHash's invariant. DELETE shifts all following keys down by one (so iteration order remains contiguous).

FIRSTKEY, NEXTKEY

Walk insertion order. The cursor lives on $self->[3]; only one Perl-level iterator is supported per impl object at a time, same as Tie::IxHash. C-level callers should use the "Public C ABI" below, which uses an external cursor and supports concurrent iteration.

SCALAR

Returns the count of stored pairs (truthy iff non-empty). scalar %h on a Tie::OrderedHash gives the same answer as scalar keys %h.

OO METHODS

These mirror the most-used parts of Tie::IxHash's OO surface. They accept the impl object directly:

my $oh = Tie::OrderedHash->new(...);

or the tied implementation:

tie my %h, 'Tie::OrderedHash';
my $oh = tied %h;
$oh->Push(more => 'data');

new(LIST)

Construct a fresh impl, optionally seeded with LIST as key/value pairs. Returns the blessed impl object directly (no tie magic).

Push(LIST)

Insert each (key, value) pair in LIST, in source order. If a key already exists its value is updated and its position is preserved. Returns the post-insert count.

Pop

Remove the last key/value pair. Returns (key, value), or the empty list if the hash is empty.

Shift

Remove the first key/value pair. Returns (key, value), or the empty list if empty.

Unshift(LIST)

Prepend each (key, value) pair in LIST to the front, in source order. If a key already exists its value is updated in place (no re-positioning), matching Tie::IxHash's documented behaviour. Returns the post-insert count.

Keys(?INDICES)

With no arguments, returns the full key list in insertion order. With one or more numeric indices, returns the keys at those positions (negative indices count from the end).

Values(?INDICES)

Same, for values.

Length

Returns the number of stored pairs.

Clear

Empty the hash.

PUBLIC C ABI

#include "tie_orderedhash.h". Pull it in via ExtUtils::Depends:

my $pkg = ExtUtils::Depends->new('Foo', 'Tie::OrderedHash');

The header declares tie_oh_new, tie_oh_store, tie_oh_fetch, tie_oh_delete, tie_oh_clear, tie_oh_count, tie_oh_iter_init/ tie_oh_iter_next, and tie_oh_is_instance. See the header itself for the full prototypes and ownership conventions.

The intended use is "I have a tied HV and want to write to its underlying Tie::OrderedHash without paying for call_method":

MAGIC *mg = mg_find((SV *)hv, PERL_MAGIC_tied);
if (mg && mg->mg_obj && tie_oh_is_instance(mg->mg_obj)) {
    tie_oh_store(aTHX_ mg->mg_obj, key, klen, value);
} else {
    /* foreign tie class - dispatch via call_method */
}

The iterator is caller-owned (tie_oh_iter_t on the stack), so multiple C-level walks can run concurrently - useful for emitting serialised output while a Perl-level each %h is still running.

PERFORMANCE

Two distinct paths. The tied-hash interface still pays Perl's tie-magic dispatch per operation; the speedup vs Tie::IxHash is modest (~1.2x) because the dispatch cost dominates the body. The public C ABI bypasses the tie-magic dispatch entirely and that's where the big win lives.

If you only need ordering at the Perl level, the gain over Tie::IxHash is real but small. If your callers are a downstream XS module that wants to manipulate the structure from C, the gain is the entire point of this dist.

COMPATIBILITY WITH Tie::IxHash

The standard tied-hash methods plus Push/Pop/Shift/Unshift/ Keys/Values/Length/Clear/new are present.

SEE ALSO

Tie::IxHash

Hash::Ordered

AUTHOR

LNATION, <email@lnation.org>

BUGS

Please report any bugs or feature requests to https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Tie-OrderedHash.

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under the Artistic License 2.0 (GPL Compatible).