text: // Spooky Hash
  // A 128-bit noncryptographic hash, for checksums and table lookup
  // By Bob Jenkins.  Public domain.
  //   Oct 31 2010: published framework, disclaimer ShortHash isn't right
  //   Nov 7 2010: disabled ShortHash
  //   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
  //   April 10 2012: buffer overflow on platforms without unaligned reads
  //   July 12 2012: was passing out variables in final to in/out in short
  //   July 30 2012: I reintroduced the buffer overflow
  //   August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
  
  #include "spooky_hash_private.hpp"
  #include <memory.h>
  
  #define ALLOW_UNALIGNED_READS 1
  
  //
  // short hash ... it could be used on any message, 
  // but it's used by Spooky just for short messages.
  //
  void spooky_hash_context::Short(
      const void *message,
      size_t length,
      uint64 *hash1,
      uint64 *hash2)
  {
      uint64 buf[2*sc_numVars];
      union 
      { 
          const uint8 *p8; 
          uint32 *p32;
          uint64 *p64; 
          size_t i; 
      } u;
  
      u.p8 = (const uint8 *)message;
      
      if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
      {
          memcpy(buf, message, length);
          u.p64 = buf;
      }
  
      size_t remainder = length%32;
      uint64 a=*hash1;
      uint64 b=*hash2;
      uint64 c=sc_const;
      uint64 d=sc_const;
  
      if (length > 15)
      {
          const uint64 *end = u.p64 + (length/32)*4;
          
          // handle all complete sets of 32 bytes
          for (; u.p64 < end; u.p64 += 4)
          {
              c += u.p64[0];
              d += u.p64[1];
              ShortMix(a,b,c,d);
              a += u.p64[2];
              b += u.p64[3];
          }
          
          //Handle the case of 16+ remaining bytes.
          if (remainder >= 16)
          {
              c += u.p64[0];
              d += u.p64[1];
              ShortMix(a,b,c,d);
              u.p64 += 2;
              remainder -= 16;
          }
      }
      
      // Handle the last 0..15 bytes, and its length
      d += ((uint64)length) << 56;
      switch (remainder)
      {
      case 15:
      d += ((uint64)u.p8[14]) << 48;
      case 14:
          d += ((uint64)u.p8[13]) << 40;
      case 13:
          d += ((uint64)u.p8[12]) << 32;
      case 12:
          d += u.p32[2];
          c += u.p64[0];
          break;
      case 11:
          d += ((uint64)u.p8[10]) << 16;
      case 10:
          d += ((uint64)u.p8[9]) << 8;
      case 9:
          d += (uint64)u.p8[8];
      case 8:
          c += u.p64[0];
          break;
      case 7:
          c += ((uint64)u.p8[6]) << 48;
      case 6:
          c += ((uint64)u.p8[5]) << 40;
      case 5:
          c += ((uint64)u.p8[4]) << 32;
      case 4:
          c += u.p32[0];
          break;
      case 3:
          c += ((uint64)u.p8[2]) << 16;
      case 2:
          c += ((uint64)u.p8[1]) << 8;
      case 1:
          c += (uint64)u.p8[0];
          break;
      case 0:
          c += sc_const;
          d += sc_const;
      }
      ShortEnd(a,b,c,d);
      *hash1 = a;
      *hash2 = b;
  }
  
  // do the whole hash in one call
  void spooky_hash_context::Hash128(
      const void *message, 
      size_t length, 
      uint64 *hash1, 
      uint64 *hash2)
  {
      if (length < sc_bufSize)
      {
          Short(message, length, hash1, hash2);
          return;
      }
  
      uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
      uint64 buf[sc_numVars];
      uint64 *end;
      union 
      { 
          const uint8 *p8; 
          uint64 *p64; 
          size_t i; 
      } u;
      size_t remainder;
      
      h0=h3=h6=h9  = *hash1;
      h1=h4=h7=h10 = *hash2;
      h2=h5=h8=h11 = sc_const;
      
      u.p8 = (const uint8 *)message;
      end = u.p64 + (length/sc_blockSize)*sc_numVars;
  
      // handle all whole sc_blockSize blocks of bytes
      if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
      {
          while (u.p64 < end)
          { 
              Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  	    u.p64 += sc_numVars;
          }
      }
      else
      {
          while (u.p64 < end)
          {
              memcpy(buf, u.p64, sc_blockSize);
              Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  	    u.p64 += sc_numVars;
          }
      }
  
      // handle the last partial block of sc_blockSize bytes
      remainder = (length - ((const uint8 *)end-(const uint8 *)message));
      memcpy(buf, end, remainder);
      memset(((uint8 *)buf)+remainder, 0, sc_blockSize-remainder);
      ((uint8 *)buf)[sc_blockSize-1] = remainder;
      
      // do some final mixing 
      End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
      *hash1 = h0;
      *hash2 = h1;
  }
  
  
  
  // init spooky state
  void spooky_hash_context::Init(uint64 seed1, uint64 seed2)
  {
      m_length = 0;
      m_remainder = 0;
      m_state[0] = seed1;
      m_state[1] = seed2;
  }
  
  
  // add a message fragment to the state
  void spooky_hash_context::Update(const void *message, size_t length)
  {
      uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
      size_t newLength = length + m_remainder;
      uint8  remainder;
      union 
      { 
          const uint8 *p8; 
          uint64 *p64; 
          size_t i; 
      } u;
      const uint64 *end;
      
      // Is this message fragment too short?  If it is, stuff it away.
      if (newLength < sc_bufSize)
      {
          memcpy(&((uint8 *)m_data)[m_remainder], message, length);
          m_length = length + m_length;
          m_remainder = (uint8)newLength;
          return;
      }
      
      // init the variables
      if (m_length < sc_bufSize)
      {
          h0=h3=h6=h9  = m_state[0];
          h1=h4=h7=h10 = m_state[1];
          h2=h5=h8=h11 = sc_const;
      }
      else
      {
          h0 = m_state[0];
          h1 = m_state[1];
          h2 = m_state[2];
          h3 = m_state[3];
          h4 = m_state[4];
          h5 = m_state[5];
          h6 = m_state[6];
          h7 = m_state[7];
          h8 = m_state[8];
          h9 = m_state[9];
          h10 = m_state[10];
          h11 = m_state[11];
      }
      m_length = length + m_length;
      
      // if we've got anything stuffed away, use it now
      if (m_remainder)
      {
          uint8 prefix = sc_bufSize-m_remainder;
          memcpy(&(((uint8 *)m_data)[m_remainder]), message, prefix);
          u.p64 = m_data;
          Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
          Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
          u.p8 = ((const uint8 *)message) + prefix;
          length -= prefix;
      }
      else
      {
          u.p8 = (const uint8 *)message;
      }
      
      // handle all whole blocks of sc_blockSize bytes
      end = u.p64 + (length/sc_blockSize)*sc_numVars;
      remainder = (uint8)(length-((const uint8 *)end-u.p8));
      if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
      {
          while (u.p64 < end)
          { 
              Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  	    u.p64 += sc_numVars;
          }
      }
      else
      {
          while (u.p64 < end)
          { 
              memcpy(m_data, u.p8, sc_blockSize);
              Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  	    u.p64 += sc_numVars;
          }
      }
  
      // stuff away the last few bytes
      m_remainder = remainder;
      memcpy(m_data, end, remainder);
      
      // stuff away the variables
      m_state[0] = h0;
      m_state[1] = h1;
      m_state[2] = h2;
      m_state[3] = h3;
      m_state[4] = h4;
      m_state[5] = h5;
      m_state[6] = h6;
      m_state[7] = h7;
      m_state[8] = h8;
      m_state[9] = h9;
      m_state[10] = h10;
      m_state[11] = h11;
  }
  
  
  // report the hash for the concatenation of all message fragments so far
  void spooky_hash_context::Final(uint64 *hash1, uint64 *hash2)
  {
      // init the variables
      if (m_length < sc_bufSize)
      {
          *hash1 = m_state[0];
          *hash2 = m_state[1];
          Short( m_data, m_length, hash1, hash2);
          return;
      }
      
      const uint64 *data = (const uint64 *)m_data;
      uint8 remainder = m_remainder;
      
      uint64 h0 = m_state[0];
      uint64 h1 = m_state[1];
      uint64 h2 = m_state[2];
      uint64 h3 = m_state[3];
      uint64 h4 = m_state[4];
      uint64 h5 = m_state[5];
      uint64 h6 = m_state[6];
      uint64 h7 = m_state[7];
      uint64 h8 = m_state[8];
      uint64 h9 = m_state[9];
      uint64 h10 = m_state[10];
      uint64 h11 = m_state[11];
  
      if (remainder >= sc_blockSize)
      {
          // m_data can contain two blocks; handle any whole first block
          Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
          data += sc_numVars;
          remainder -= sc_blockSize;
      }
  
      // mix in the last partial block, and the length mod sc_blockSize
      memset(&((uint8 *)data)[remainder], 0, (sc_blockSize-remainder));
  
      ((uint8 *)data)[sc_blockSize-1] = remainder;
      
      // do some final mixing
      End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  
      *hash1 = h0;
      *hash2 = h1;
  }
  
  SpookyHash::SpookyHash(const void* data, size_t len, uint64 s1, uint64 s2)
  {
      _hash_1 = s1;
      _hash_2 = s2;
      spooky_hash_context::Hash128(data, len, &_hash_1, &_hash_2);
  }
  
  bool SpookyHash::operator==(const SpookyHash& other) const{
      return _hash_1 == other._hash_1 && _hash_2 == other._hash_2;
  }
  bool SpookyHash::operator<(const SpookyHash& other) const{
      if(_hash_1 < other._hash_1) return true;
      return _hash_2 < other._hash_2;
  }
  
  std::ostream& operator<<(std::ostream& s, const SpookyHash& h) {
      char hex[33] = {};
      h.toString(hex);
      s << hex;
      return s;
  }
  SpookyHash& SpookyHash::operator=(const SpookyHash& other){
      _hash_1 = other._hash_1;
      _hash_2 = other._hash_2;
      return *this;
  }