sub hash {
    my ($key, $type) = @_;
    my ($hash);

    if ($type == 0) {
        $hash = 0;
        # XXX The C version uses the "char" type, which could be either
        # signed or unsigned.  I use signed because so do the two
        # compilers on my system.
        for my $char (unpack ("c*", $key)) {
            $hash = $hash * 33 + $char;
        }
        $hash &= 0x7FFFFFFF;    # limit to 31 bits
        $hash |= 0x40000000;    # set bit 31
        return -$hash;          # return negative int
    }
    elsif ($type == 1) {        # Fowler/Noll/Vo hash
        # see http://www.isthe.com/chongo/tech/comp/fnv/
        require Math::BigInt;   # feel free to reimplement w/o BigInt
        $hash = Math::BigInt->new(0x811c9dc5);
        for my $uchar (unpack ("C*", $key)) {
            # multiply by the 32 bit FNV magic prime mod 2^64
            $hash = ($hash * 0x01000193) & 0xffffffff;
            # xor the bottom with the current octet
            $hash ^= $uchar;
        }
        # cast to int
        return unpack "i", pack "i", $hash;
    }
    else {
        croak("bad hash type $type");
    }
}
