++ed by:

3 non-PAUSE users.

Sergey Aleynikov


Panda::Lib - Collection of useful functions and classes with Perl and C interface.


Panda::Lib contains a number of very fast useful functions, written in C. You can use it from Perl or directly from XS code.


    use Panda::Lib qw/ hash_merge merge compare clone fclone crypt_xor string_hash string_hash32 /;
    $result = hash_merge($dest, $source, $flags);
    $result = merge($dest, $source, $flags);
    $is_equal = compare($hash1, $hash2);
    $is_equal = compare($array1, $array2);
    $cloned = lclone($data);
    $cloned = fclone($data);
    $crypted = crypt_xor($data, $key);
    $val = string_hash($str);
    $val = string_hash32($str);


    #include <xs/lib.h>
    using namespace xs::lib;
    HV* result = hash_merge(hvdest, hvsource, flags);
    SV* result = merge(hvdest, hvsource, flags);
    bool is_equal = hash_cmp(hv1, hv2);
    bool is_equal = av_cmp(av1, av2);
    SV* cloned = clone(sv, with_cross_checks);
    // for XS code
        /* code that can throw c++ exceptions */;
    #include <panda/lib.h>
    #include <panda/lib/hash.h>
    using namespace panda::lib;
    char* crypted = crypt_xor(source, slen, key, klen);
    uint32_t val = hash32(str, len);
    uint64_t val = hash64(str, len);


hash_merge (\%dest, \%source, [$flags])

Merges hash $source into $dest. Merge is done extremely fast. $source and $dest must be HASHREFS or undefs. New keys from source are added to dest. Existing keys(values) are replaced. If a key contains HASHREF both in source and dest, they are merged recursively. Otherwise it gets replaced by value from source. Returns resulting hashref (it may or may not be the the same ref as $dest, depending on $flags provided).

$flags is a bitmask of these flags:


By default, if a key contains ARRAYREF both in source and dest, it gets replaced by array from source. If you enable this flag, such arrays will be concatenated (like: push @{$dest->{key}}, @{$source->{key}).


If a key contains ARRAYREF both in source and dest, it gets merged. It means that $dest->{key}[0] is merged with $source->{key}[0], and so on. Values are merged using following rules: if both are hashrefs or arrayrefs, they are merged recursively, otherwise value in dest gets replaced.


If you set this flag, merge process won't override any existing and defined values in dest. Keep in mind that if you also set MERGE_ARRAY_MERGE, then the same is in effect while merging array elements.

    my $hash1 = {a => 1, b => undef};
    my $hash2 = {a => 2, b => 3, c => undef };
    hash_merge($hash1, $hash2, MERGE_LAZY);
    # $hash1 is {a => 1, b => 3, c => undef };

If enabled, values from source that are undefs won't replace anything in dest.

    my $hash1 = {a => 1};
    my $hash2 = {a => undef, b => undef, c => 2};
    hash_merge($hash1, $hash2, MERGE_SKIP_UNDEF);
    # $hash1 is {a => 1, c => 2};

If enabled, values from source that are undefs acts as a 'deleters', i.e. the corresponding values get deleted from dest.

    my $hash1 = {a => 1, b => 2};
    my $hash2 = {a => undef};
    hash_merge($hash1, $hash2, MERGE_DELETE_UNDEF);
    # $hash1 is {b => 2};

Makes deep copy of $dest, merges it with source and returns this new hashref.


By default, if any value from source replaces value from dest, it doesn't get deep copied. For example:

    my $hash1 = {};
    my $hash2 = {a => [1,2]};
    hash_merge($hash1, $hash2);
    shift @{$hash1->{a}};
    say scalar @{$hash2->{a}}; # prints 1

Moreover, even primitive values are not copied, instead they get aliased for speed. For example:

    my $hash1 = {};
    my $hash2 = {a => 'mystring'};
    hash_merge($hash1, $hash2);
    substr($hash1->{a}, 0, 2);
    say $hash2->{a}; # prints 'string'

If you enable this flag, replacing values from source will be copied (references - deep copied).



This is how undefined $source or undefined $dest are handled:

If $source is undef

Nothing is merged, however if MERGE_COPY_DEST is set, deep copy of $dest is still returned. If $dest is also undef, then regardless of MERGE_COPY_DEST flag, empty hashref is returned.

If $dest is undef

Empty hashref is created, merged with $source and returned.

merge ($dest, $source, [$flags])

Acts much like 'hash_merge', but receives any scalar as $dest and $source, not only hashrefs. Returns merged value which may or may not be the same scalar (modified or not) as $dest.

This function does the same work as 'hash_merge' does for its elements. I.e. if both $dest and $source are HASHREFs then they are merged via 'hash_merge'. If both are ARRAYREFs, then depending on $flags, $dest are either replaced, concatenated or merged. Otherwise $source replaces $dest following the rules described in 'hash_merge' function with respect to flags MERGE_COPY_DEST, MERGE_COPY_SOURCE and MERGE_LAZY.

For example, if $source and $dest are scalars (not refs), and no flags provided, then $dest becomes equal $source. If MERGE_LAZY is provided and $dest is not an undef, $dest is unchanged. If MERGE_COPY_DEST is provided then $dest is unchaged and the result is returned in a new scalar. And so on.

However there is one difference: if $dest and $source are primitive scalars, instead of creating an alias, the $source variable is copied to $dest (or new result). If MERGE_COPY_SOURCE is disabled, copying is not deep, like $dest = $source.

lclone ($source)

Light clone: makes a deep copy of $source and returns it.

Does not handle cross-references: references to the same data will be different references. If a cycled reference is present in $source, it will croak.

Handles CODEREFs and IOREFs, but doesn't clone it, just copies pointer to the same CODE and IO into new reference. All other data types are cloned normally.

If clone encounters a blessed object and it has a HOOK_CLONE method, the return value of this method is used instead of a default behaviour. You can call [lf]clone($self) again from HOOK_CLONE if you need to, for example to prevent cloning some of your properties:

    sub HOOK_CLONE {
        my $self = shift;
        my $tmp = delete local $self->{big_obj_backref};
        my $ret = lclone($self);
        $ret->{big_obj_backref} = $tmp;
        return $ret;

In this case second lclone() call won't call HOOK_CLONE again and will clone $self in a standart manner.

fclone ($source)

Full clone: same as lclone() but handles cross-references: references to the same data will be the same references. If a cycled reference is present in $source, it will remain cycled in cloned data.

If and object being cloned via fclone() and has HOOK_CLONE which calls lclone($self) / clone($self) / fclone($self), then it will act like fclone($self) with the same dictinonary as the top call.

clone ($source, [$with_cross_checks])

If $with_cross_checks is false or omitted, behaves like lclone(), otherwise like fclone()

compare ($data1, $data2)

Performs deep comparison and returns true if every element of $data1 is equal to corresponding element of $data2.

The rules of equality for two elements (including the top-level $data1 and $data2 itself):

If any of two elements is a reference.
If any of elements is a blessed object

If they are not objects of the same class, they're not equal

If class has overloaded '==' operation, it is used for checking equality. If not, objects' underlying data structures are compared.

If both elements are hash refs.

Equal if all of the key/value pairs are equal.

If both elements are array refs.

Equal if corresponding elements are equal (a[0] equal b[0], etc).

If both elements are code refs.

Equal if they are references to the same code.

If both elements are IOs (IO refs)

Equal if both IOs contain the same fileno.

If both elements are typeglobs

Equal if both are references to the same glob.

If both elements are refs to anything.

They are dereferenced and checked again from the beginning.

Otherwise (one is ref, another is not) they are not equal
If both elements are not references

Equal if perl's 'eq' or '==' (depending on data type) returns true.

crypt_xor ($string, $key)

Performs round-robin XOR $string with $key. Algorithm is symmetric, i.e.:

    crypt_xor(crypt_xor($string, $key), $key) eq $string

string_hash ($string)

Calculates 64-bit hash value for $string. Currently uses MurMurHash64A algorithm (very fast).

string_hash32 ($string)

Calculates 32-bit hash value for $string. Currently uses jenkins_one_at_a_time_hash algorithm.


Functions marked with [pTHX] must receive aTHX_ as a first arg.

HV* xs::lib::hash_merge (HV* dest, HV* source, IV flags = 0) [pTHX]

SV* xs::lib::merge (SV* dest, SV* source, IV flags = 0) [pTHX]

SV* xs::lib::clone (SV* source, bool cross_references = false) [pTHX]

bool xs::lib::hv_compare (HV*, HV*) [pTHX]

bool xs::lib::av_compare (AV*, AV*) [pTHX]

bool xs::lib::sv_compare (SV*, SV*) [pTHX]

uint64_t panda::lib::hash64 (const char* str, size_t len)

uint64_t panda::lib::hash64 (const char* str)

uint32_t panda::lib::hash32 (const char* str, size_t len)

uint32_t panda::lib::hash32 (const char* str)

All functions above behaves like its perl equivalents. See PERL FUNCTIONS docs.

char* panda::lib::crypt_xor (const char* source, size_t slen, const char* key, size_t klen, char* dest = NULL)

Performs XOR crypt. If 'dest' is null, mallocs and returns new buffer. Buffer must be freed by user manually via 'free'. If 'dest' is not null, places result into this buffer. It must have enough space to hold the result.

panda::lib::h2be16, h2le16, be2h16, le2h16, h2be32, h2le32, be2h32, le2h32, h2be64, h2le64, be2h64, le2h64

Endianess convertation functions. They use __builtin_bswap*, _byteswap_*, etc if possible. Otherwise they use code likely to be compiled as a single bswap or rol instruction.

    #include <panda/lib/endian.h>
    uint64_t network_order = h2be64(host_order);


Pronin Oleg <syber@crazypanda.ru>, Crazy Panda, CP Decision LTD


You may distribute this code under the same terms as Perl itself.