The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define INIT32 2166136261U
#define MULT32 16777619U
#define INIT64 14695981039346656037U
#define MULT64 1099511628211U

static UV
fnv1_32 (SV *data, U32 h)
{
  STRLEN l;
  U8 *p = (U8 *)SvPVbyte (data, l);

  while (l--)
    {
      h *= MULT32;
      h ^= *p++;
    }

  return h;
}

static UV
fnv1a_32 (SV *data, U32 h)
{
  STRLEN l;
  U8 *p = (U8 *)SvPVbyte (data, l);

  while (l--)
    {
      h ^= *p++;
      h *= MULT32;
    }

  return h;
}

#if UVSIZE >= 8

static UV
fnv1_64 (SV *data, UV h)
{
  STRLEN l;
  U8 *p = (U8 *)SvPVbyte (data, l);

  while (l--)
    {
      h *= MULT64;
      h ^= *p++;
    }

  return h;
}

static UV
fnv1a_64 (SV *data, UV h)
{
  STRLEN l;
  U8 *p = (U8 *)SvPVbyte (data, l);

  while (l--)
    {
      h ^= *p++;
      h *= MULT64;
    }

  return h;
}

#endif

#define fnv0_32 fnv1_32
#define fnv0_64 fnv1_64

static UV
xorfold (UV hash, int bits, int max)
{
  if (bits < max)
    hash = ((hash >> (max - bits)) ^ hash) & ((1 << bits) - 1);

  return hash;
}

#define xorfold_32(hash,bits) xorfold (hash, bits, 32)
#define xorfold_64(hash,bits) xorfold (hash, bits, 64)

static UV
reduce_32 (U32 hash, U32 range)
{
  U32 retry =         0xffffffffU / range * range;

  while (hash >= retry)
    hash = hash * MULT32 + INIT32;

  return hash % range;
}

static UV
reduce_64 (UV hash, UV range)
{
  U32 retry = 0xffffffffffffffffU / range * range;

  while (hash >= retry)
    hash = hash * MULT64 + INIT64;

  return hash % range;
}

MODULE = Digest::FNV::XS		PACKAGE = Digest::FNV::XS

PROTOTYPES: ENABLE

UV fnv0_32  (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvUV (init)

UV fnv1_32  (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvOK (init) ? SvUV (init) : INIT32

UV fnv1a_32 (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvOK (init) ? SvUV (init) : INIT32

#if UVSIZE >= 8

UV fnv0_64  (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvUV (init)

UV fnv1_64  (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvOK (init) ? SvUV (init) : INIT64

UV fnv1a_64 (SV *data, SV *init = &PL_sv_undef)
	C_ARGS: data, SvOK (init) ? SvUV (init) : INIT64

#endif

UV xorfold_32 (UV hash, int bits)

UV xorfold_64 (UV hash, int bits)

UV reduce_32 (UV hash, UV range)

UV reduce_64 (UV hash, UV range)