/* hash-utils.c - computing hash values
 *
 ****************************************************************
 * Copyright (C) 2000 Thomas Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */



#include "hackerlab/machine/alignment.h"
#include "hackerlab/hash/hash-utils.h"


/************************************************************************
 *(h1 "Hash Utilities"
 *    :includes ("hackerlab/hash/hash-utils.h"))
 * 
 * The functions in this section provide tools useful for computing 
 * hash values.
 */




static unsigned long shuffled_bytes[] =
{
  245, 184, 171, 36, 93, 194, 192, 143, 207, 89, 63, 175, 203, 231, 47, 238,
  103, 67, 176, 102, 80, 133, 24, 155, 91, 141, 234, 58, 44, 191, 218, 157,
  13, 168, 160, 113, 211, 213, 252, 236, 2, 19, 21, 148, 111, 251, 165, 74,
  124, 25, 181, 210, 250, 195, 235, 97, 185, 1, 179, 198, 105, 101, 5, 220,
  35, 162, 142, 41, 200, 209, 224, 71, 201, 134, 69, 48, 65, 170, 72, 167,
  145, 205, 28, 88, 215, 81, 214, 78, 118, 26, 123, 84, 140, 49, 45, 8,
  7, 107, 227, 60, 59, 32, 30, 82, 31, 189, 131, 17, 66, 239, 64, 10,
  149, 40, 130, 146, 54, 147, 9, 114, 4, 254, 241, 116, 110, 249, 57, 233,
  37, 55, 206, 100, 177, 119, 139, 158, 108, 75, 94, 23, 186, 152, 244, 27,
  38, 33, 188, 87, 76, 166, 228, 52, 120, 99, 247, 174, 51, 183, 3, 161,
  246, 135, 14, 178, 11, 216, 77, 172, 122, 154, 39, 253, 104, 34, 164, 230,
  219, 242, 68, 151, 180, 115, 173, 73, 212, 90, 125, 29, 22, 221, 56, 121,
  255, 204, 83, 169, 182, 112, 96, 187, 20, 106, 79, 15, 61, 223, 70, 85,
  53, 197, 217, 232, 196, 95, 136, 150, 243, 109, 129, 202, 208, 237, 144, 156,
  86, 127, 62, 248, 138, 229, 153, 226, 240, 199, 50, 12, 193, 98, 137, 126,
  0, 159, 222, 18, 163, 117, 190, 46, 225, 132, 16, 43, 128, 42, 92, 6
};



/*(c hash_ul)
 * unsigned long hash_ul (unsigned long n);
 * 
 * Generate a hash value from an integer.
 * 
 * This function is slow, but attempts to give a good distribution of
 * hash values even for a series of `n' which are not particularly
 * random.
 */
unsigned long
hash_ul (unsigned long n)
{
  int rounds;
  t_ulong answer = 0;

  for (rounds = 0; rounds < 5; ++rounds)
    {
      int byte;

      for (byte = 0;  byte < sizeof (t_ulong); ++byte)
        {
          t_ulong new_byte;

          new_byte = shuffled_bytes[0xff & (n >> byte)];
          answer ^= (new_byte << byte);
        }

      n = ((n >> 3) ^ (n << 7));
    }

  return answer;
}


/*(c hash_double)
 * unsigned long hash_double (double n);
 * 
 * Generate a hash value from a double precision float.
 * 
 * This function is slow, but attempts to give a good distribution of
 * hash values even for a series of `n' which are not particularly
 * random.
 */
unsigned long
hash_double (double d)
{
  union
  {
    t_uchar raw[sizeof (double)];
    double d;
  } data;

  data.d = d;
  return hash_mem (data.raw, sizeof (double));
}






/*(c hash_pointers)
 * unsigned long hash_pointers (void * elts, size_t n_elts);
 * 
 * Compute a hash value from an array of pointers.
 * 
 * This function is slow, but attempts to give a good distribution of
 * hash values even for a series of pointers which are not
 * particularly random.  Usually, pointers are not particularly
 * random.
 * 
 * `slow' means that the function does roughly `3 * sizeof (n)' array
 * look-ups and lots of bit twiddling, per pointer.
 * 
 */
unsigned long
hash_pointers (void * elts, size_t n_elts)
{
  size_t x;
  unsigned long hash;

  hash = 0xdec0ded;
  for (x = 0; x < n_elts; ++x)
    {
      hash ^= hash_ul (((unsigned long *)elts)[x]);
    }
  return hash;
}




/*(c hash_mem)
 * unsigned long hash_mem (t_uchar * elts, size_t n_bytes);
 * 
 * Compute a hash value from an array of bytes.
 * 
 * This function is slow, but attempts to give a good distribution of
 * hash values even for a series of bytes which are not particularly
 * random.
 * 
 * `slow' means that the function does roughly `3 * sizeof (n)' array
 * look-ups and lots of bit twiddling, per `sizeof (unsigned long)'
 * bytes.
 * 
 */
unsigned long
hash_mem (t_uchar * elts, size_t n_elts)
{
  size_t x;
  unsigned long hash;

  hash = 0xde7a115;

  for (x = 0; (x < n_elts) && (MACHINE_ALIGNMENT - 1); ++x)
    hash ^= hash_ul ((unsigned long)elts[x]);

  while ((n_elts - x) >= sizeof (unsigned long))
    {
      hash ^= hash_ul (*(unsigned long *)(elts + x));
      x += sizeof (unsigned long);
    }
  
  while (x < n_elts)
    {
      hash ^= hash_ul ((unsigned long)elts[x]);
      ++x;
    }
  return hash;
}


