/*///---------------------------------------------------------------------\\\*/
/*// Copyright (c)               (_)/=\~/_(_)         All rights reserved. \\*/
/*/  2005-2006                (_)  /=\  ~/_  (_)        Released under the  \*/
/*\  Jim Peters            (_)    /=\    ~/_    (_)             GNU GPL v2  /*/
/*\\ http://uazu.net    (_) ____ /=\ ____ ~/_ ____ (_)      http://fsf.org //*/
/*\\\---------------------------------------------------------------------///*/

//
//      Support code
//

#ifdef HEADER

#define ALLOC(type) ((type*)Alloc(sizeof(type)))
#define ALLOC_ARR(cnt, type) ((type*)Alloc((cnt) * sizeof(type)))
#define DIE die(__FILE__, __LINE__)
extern void die(char*,int) __attribute__((noreturn));
extern void error(char*,...) __attribute__((noreturn));
extern void verror(char*,va_list) __attribute__((noreturn));

typedef struct List List;
struct List {
   List *nxt;
   char *str;
};

// For hash function
typedef  u_int32_t  ub4;   /* unsigned 4-byte quantities */
typedef  u_int8_t   ub1;   /* unsigned 1-byte quantities */

#else

#include "all.h"

int fatal_to_stdout= 0;

void 
error(char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);	
   verror(fmt, ap);
}

// Use convention that prefix of 'symirr:' indicates remote symirr error
void
verror(char *fmt, va_list ap) {
   status(0);
   if (!o_local) fprintf(stderr, "symirr: ");
   vfprintf(stderr, fmt, ap);
   fprintf(stderr, "\n");
   if (fatal_to_stdout) {
      printf("ERR FATAL: ");
      vprintf(fmt, ap);
      printf("\n");
   }
   pipe_disconn();	// Disconnect if we are connected
   exit(1);
}

void
warn(char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   status(0);
   vfprintf(stderr, fmt, ap);
   fprintf(stderr, "\n");
   fflush(stderr);
}

void 
die(char *fil, int lin) {
   // Include version number so that bug reports more likely to be useful
   error("ABORT: unexpected condition at %s line %d (version " VERSION ")", fil, lin);
}

void
usage() {
   extern char usage_text[];
   o_local= 1;
   error("%s", usage_text);
}

void *
Alloc(int size) {
   void *vp= calloc(1, size);
   if (!vp) error("Out of memory");
   return vp;
}

void *
StrDup(char *str) {
   char *cp= strdup(str);
   if (!cp) error("Out of memory");
   return cp;
}

void *
StrDupR(char *str, char *end) {
   int len= end-str;
   char *cp= malloc(len+1);
   if (!cp) error("Out of memory");
   memcpy(cp, str, len);
   cp[len]= 0;
   return cp;
}

// Scratch buffer and StrDupF/StrDupT not thread-safe
static char *scratch= 0;
static int scr_len= 0;

#if 1
// GCC-specific code that initialises scratch automatically
static void scratch_init() __attribute__((constructor));
static void scratch_init() { scratch= Alloc(scr_len= 1024); } // MEMDEBUG-IGNORE
#define SCR_INIT
#define SCR_GROW { free(scratch); scratch= Alloc(scr_len *= 2); }
#else
#define SCR_INIT if (!scratch) scratch= Alloc(scr_len= 1024)
#define SCR_GROW { free(scratch); scratch= Alloc(scr_len *= 2); }
#endif


char *
StrDupF(char *fmt, ...) {
   va_list ap;
   char *rv;
   int len;
   SCR_INIT;
 retry:
   va_start(ap, fmt);
   len= vsnprintf(scratch, scr_len, fmt, ap);
   va_end(ap);
   if (len < 0 || len >= scr_len-1) { SCR_GROW; goto retry; }
   rv= strdup(scratch);
   if (!rv) error("Out of memory");
   return rv;
}

// Tab-separates the list of strings passed in the argument list,
// terminated by a 0 pointer
char *
StrDupT(char *str, ...) {
   va_list ap;
   char *rv, *p, *txt;
   SCR_INIT;
 retry:
   p= scratch;
   va_start(ap, str);
   txt= str;
   while (txt) {
      int len= strlen(txt);
      if (len + 2 > scratch + scr_len - p) {
	 va_end(ap); SCR_GROW; goto retry;
      }
      *p++= '\t'; 
      memcpy(p, txt, len); p += len;
      *p= 0;

      txt= va_arg(ap, char*);
   }
   rv= strdup(scratch+1);
   if (!rv) error("Out of memory");
   return rv;
}

// Format to scratch buffer; must be really careful where the result
// is used.  If the result is passed to a routine, you have to be
// really sure that routine isn't going to use the scratch buffer.
char *
StrF(char *fmt, ...) {
   va_list ap;
   int len;
   SCR_INIT;
 retry:
   va_start(ap, fmt);
   len= vsnprintf(scratch, scr_len, fmt, ap);
   va_end(ap);
   if (len < 0 || len >= scr_len-1) { SCR_GROW; goto retry; }
   return scratch;
}

// Format to scratch buffer a tab-separated list of strings passed in
// the argument list, terminated by a 0 pointer.  If the result is
// passed to a routine, you have to be really sure that routine isn't
// going to use the scratch buffer.
char *
StrT(char *str, ...) {
   va_list ap;
   char *p, *txt;
   SCR_INIT;
 retry:
   p= scratch;
   va_start(ap, str);
   txt= str;
   while (txt) {
      int len= strlen(txt);
      if (len + 2 > scratch + scr_len - p) {
	 va_end(ap); SCR_GROW; goto retry;
      }
      if (txt != str) *p++= '\t';
      memcpy(p, txt, len); p += len;
      *p= 0;
      txt= va_arg(ap, char*);
   }
   return scratch;
}

// Skip over a TAB-separated-value in string, returning pointer to the
// next value, and mark the previous off with a NUL; returns 0 if the
// current is the last value.  Usage: val0= str; val1= skipTSV(val0);
// val2= skipTSV(val1); val3= skipTSV(val2);
char *
skipTSV(char *p) {
   if (p && (p= strchr(p, '\t'))) {
      *p++= 0;
      return p;
   }
   return 0;
}


char *
ends_with(char *p, char *q) {
   int len= strlen(q);
   char *end= strchr(p, 0);
   return (end - len < p) || 0 != strcmp(end-len, q) ? 0 : end-len;
}

int
starts_with(char *p, char *q) {
   int len= strlen(q);
   return 0 == memcmp(p, q, len);
}

int 
str_eq(char *p, char *q) {
   return 0 == strcmp(p, q);
}

int 
str_ne(char *p, char *q) {
   return 0 != strcmp(p, q);
}

int 
enc_cc(char *p) {
   switch (strlen(p)) {
    case 1:
       return p[0];
    case 2:
       return (p[0] * ('10'-'00') +
	       p[1] * ('01'-'00'));
    case 3:
       return (p[0] * ('100'-'000') +
	       p[1] * ('010'-'000') +
	       p[2] * ('001'-'000'));
    case 4:
       return (p[0] * ('1000'-'0000') +
	       p[1] * ('0100'-'0000') +
	       p[2] * ('0010'-'0000') +
	       p[3] * ('0001'-'0000'));
   }
   return 0;
}

int 
enc_ccN(int ch, ...) {
   char tmp[20], *p;
   va_list ap;
   va_start(ap, ch);
   
   p= tmp;
   while (ch) {
      *p++= ch;
      ch= va_arg(ap, int);
   }
   *p= 0;
   return enc_cc(tmp);
}
   

//
//	File modification support
//


// Decode mode/user/group; returns SYMLNK as *modp == -1
void 
decode_mug(char *mug, int *modp, int *uidp, int *gidp) {
   char *mode= StrDup(mug);
   char *user, *group;
   char *p;

   // Cached user/group names (cached between calls)
   static int c_uid, c_gid;
   static char *c_user= 0, *c_group= 0;

   if (!(p= strchr(mode, '/'))) goto bad;
   *p++= 0; user= p;
   if (!(p= strchr(p, '/'))) goto bad;
   *p++= 0; group= p;

   if (str_eq(mode, "SYMLNK")) {
      *modp= -1;
   } else {
      *modp= strtol(mode, &p, 8);
      if (*p) goto bad;
   }

   *uidp= strtol(user, &p, 10);
   if (*p) {
      if (!c_user || str_ne(c_user, user)) {
	 if (my_uid != ~0 && str_eq(user, "_"))
	    c_uid= my_uid;
	 else {
	    struct passwd *pw;
	    free(c_user); c_user= StrDup(user);	// MEMDEBUG-IGNORE
	    if (!(pw= getpwnam(user))) 		// MEMDEBUG-IGNORE
	       error("User '%s' is not known on this system", user);
	    c_uid= pw->pw_uid;
	 }
      }
      *uidp= c_uid;
   }

   *gidp= strtol(group, &p, 10);
   if (*p) {
      if (!c_group || str_ne(c_group, group)) {
	 if (my_gid != ~0 && str_eq(group, "_")) 
	    c_gid= my_gid;
	 else {
	    struct group *gr;
	    free(c_group); c_group= StrDup(group);	// MEMDEBUG-IGNORE
	    if (!(gr= getgrnam(group))) 		// MEMDEBUG-IGNORE
	       error("Group '%s' is not known on this system", group);
	    c_gid= gr->gr_gid;
	 }
      }
      *gidp= c_gid;
   }
   
   free(mode);
   return;

 bad:
   error("Badly formed mode/user/group in decode_mug(): %s", mug);
}


// Decode size/mtime
void 
decode_szt(char *szt, int *sizp, int *mtimp, int *symp) {
   char dmy;
   if (2 == sscanf(szt, "%u/#%x %c", sizp, mtimp, &dmy))
      *symp= 1; 
   else if (2 == sscanf(szt, "%u/%x %c", sizp, mtimp, &dmy))
      *symp= 0; 
   else 
      error("Bad size/mtime in decode_szt(): %s", szt);
}


// Create directory with given mode/user/group
void 
do_mkdir(char *path, char *mug) {
   int mode, uid, gid;
   
   decode_mug(mug, &mode, &uid, &gid);
   if (mode < 0) error("Shouldn't have a directory with SYMLNK flag"); 
   
   if (mkdir(path, mode)) 
      error("Couldn't create directory (%s): %s", strerror(errno), path);

   if (lchown(path, uid, gid))
      error("Couldn't set owner/group (%s): %s", strerror(errno), path);
}


// Set mode/user/group
void 
do_chmug(char *path, char *mug) {
   int mode, uid, gid;
   struct stat sb;
   
   decode_mug(mug, &mode, &uid, &gid);

   // Some sanity checks
   if (0 != lstat(path, &sb))
      error("do_chmug attempted on a missing file/dir: %s", path);
   if ((mode < 0) != (S_ISLNK(sb.st_mode)))
      error("do_chmug cannot change symlink to non-symlink or vice versa (%d/%d):\n  %s %s", 
	    mode < 0, S_ISLNK(sb.st_mode), mug, path);

   if (mode >= 0 && 	// Don't attempt chmod() for symlinks
       chmod(path, mode))
      error("Couldn't set file mode to %06o (%s): %s", mode, strerror(errno), path);
   
   if (lchown(path, uid, gid))
      error("Couldn't set owner/group (%s): %s", strerror(errno), path);
}   


// Set mtime
void 
do_mtime(char *path, int mtime) {
   struct utimbuf utb;
   utb.actime= mtime;
   utb.modtime= mtime;

   if (utime(path, &utb))
      error("Error setting mtime of file (%s): %s", strerror(errno), path);
}


// Create a symlink, deleting any existing file with that name
void 
do_symlink(char *path, char *val) {
   struct stat sb;

   if (0 == lstat(path, &sb)) {
      if (o_backup) {
	 if (!tgz_out) tgz_open(tgz_pathname);
	 tgz_append(path);
      }
      if (0 != unlink(path))
	 error("do_symlink, error deleting existing file (%s): %s", strerror(errno), path);
   }

   if (0 != symlink(val, path))
      error("do_symlink, error creating symlink (%s): %s", strerror(errno), path);
}


// Recursive removal of files/directories
void 
do_rm(char *path) {
   DIR *dir;
   struct dirent *ent;

   if (o_backup) {
      // The tgz_* functions don't touch scratch[]
      if (!tgz_out) tgz_open(tgz_pathname);
      tgz_append(path);
   }

   if (0 == remove(path)) return;

   // Have to go through directory, recursively
   path= StrDup(path);
   dir= opendir(path);
   if (!dir) error("Can't remove, and can't open as directory: %s", path);

   while ((ent= readdir(dir))) {
      if (str_eq(ent->d_name, ".")) continue;
      if (str_eq(ent->d_name, "..")) continue;
      do_rm(StrF("%s/%s", path, ent->d_name));
   }

   closedir(dir);
   if (0 != rmdir(path))
      error("Can't remove directory (%s): %s", strerror(errno), path);
   free(path);
}


// Backup a file/dir that is just about to be overwritten, if it
// exists
void 
do_backup(char *path) {
   struct stat sb;

   if (!o_backup) return;

   if (0 == lstat(path, &sb)) {
      if (!tgz_out) tgz_open(tgz_pathname);
      tgz_append(path);
   }
}

// Expand a path that has a symlink at the end, to the full path of
// the pointed-to file or directory; returns strdup'd string, or 0 if
// the path did not refer to a symbolic link
char *
expand_symlink(char *path) {
   char buf[16384];
   int plen, slen;
   char *p, *q, *end;

   p= strrchr(path, '/');
   plen= p ? p+1-path : 0;
   if (plen >= sizeof(buf)) 
      error("Buffer too small for path (expand_symlink(), length %d):\n  %s", strlen(path), path);
   memcpy(buf, path, plen);
   p= buf + plen;
   
   slen= readlink(path, p, sizeof(buf)-plen);
   if (slen < 0) {
      if (errno == EINVAL) return 0;
      error("Error reading symlink (%s):\n  %s", strerror(errno), path);
   }
   if (slen+plen >= sizeof(buf))
      error("Symlink is too long for buffer (in expand_symlink()):\n  %s", path);
   buf[plen+slen]= 0;
   
   if (buf[plen] == '/') return StrDup(buf+plen);
   
   // Scan through handling /./ and /../
   end= buf + plen + slen;
   p= buf;
   while (p<end) {
      if (starts_with(p, "/./")) {
	 memmove(p, p+2, end-(p+2));
	 end -= 2;
	 continue;
      }
      if (starts_with(p, "/../")) {
	 for (q= p; q > buf && q[-1] != '/'; q--) ;
	 p += 4;
	 memmove(q, p, end-p);
	 end -= (p-q);
	 p= q-1;
	 if (p < buf) p= buf;
	 continue;
      }
      p++;
   }
   *end= 0;
   return StrDup(buf);
}
   

//
//	Glob-style matching:
//	  ?  match any character except '/'
//	  *  match any number of characters except '/'
//	  ** match any number of characters including '/'
//	  \  escape any single character
//

int 
glob_match(char *patt, char *str) {
   while (1) {
      if (*patt == 0) return *str == 0;
      if (*patt == '*') break;
      if (*patt == '?') {
	 if (*str == 0 || *str == '/') return 0;
	 str++; patt++;
	 continue;
      }
      if (*patt == '\\') {
	 patt++;
	 if (!*patt) return 0;
      }
      if (*str++ != *patt++) return 0;
   }
      
   // we have a '*' or '**', recurse
   {
      patt++;
      if (*patt == '*') { 
	 patt++;
	 while (1) {
	    if (glob_match(patt, str)) return 1;
	    if (*str == 0) break;
	    str++;
	 }
      } else {
	 while (1) {
	    if (glob_match(patt, str)) return 1;
	    if (*str == 0 || *str == '/') break;
	    str++;
	 }
      }
      return 0;
   }
}


// Get time in micro-seconds, modulus 2^32
int
time_us() {
   struct timeval tv;
   gettimeofday(&tv, NULL);
   return tv.tv_sec * 1000000 + tv.tv_usec;
}
      

//
//	Status display; shows a string on the terminal stderr followed
//	by CR.  Remembers the length to clear it correctly when the
//	next status message is written.  Does nothing if stderr isn't
//	a tty.  status(0) or status("") clear the status string if
//	there is one.  (error() and warn() do this automatically)
//
//	For status lines that may update very fast (too fast), start
//	the format with '!' (which is not output) and the code will
//	check the time and rate-limit to about 10 updates a second.
//      

static int status_on= -1;
static int status_len= 0;
static int status_rlmax= 0;	// How many input strings to read to output just one, or 0 if rate-limiting disabled
static int status_rlcnt;	// Rate limit count -- how many lines we have received
static int status_rltim;	// Time we started our rate limit counter
#define RT_TARG 100000		// Target output rate, in micro-seconds

void 
status(char *fmt, ...) {
   va_list ap;
   int len;
   char buf[1024], *p;

   if (status_on < 0) status_on= isatty(2);
   if (!status_on) return;

   // Rate-limiting for status messages
   if (fmt && fmt[0] == '!') {
      fmt++;
      if (!status_rlmax) {
	 status_rlmax= 1;
	 status_rlcnt= 0;
	 status_rltim= time_us();
      } else {
	 int now, period, targ;
	 status_rlcnt++;
	 if (status_rlcnt < status_rlmax) return;       // Skip outputting this line
	 now= time_us();
	 period= now-status_rltim;
	 targ= status_rlmax * RT_TARG / period;
	 if (period < RT_TARG)  
	    //Alt: status_rlmax= (status_rlmax*2 + targ) / 3; // Slowing down: move 33% closer
	    status_rlmax= (status_rlmax * 3 + 1) / 2;   // Slow down exponentially
	 else 			
	    status_rlmax= targ;			        // Speed up instantly
	 status_rlcnt= 0;
	 status_rltim= now;
      }
   } else {
      status_rlmax= 0;
   }

   if (!fmt || !fmt[0]) {
      if (status_len)
	 fprintf(stderr, "%*s\r", status_len, "");
      status_len= 0;
   } else {
      va_start(ap, fmt);
      len= vsnprintf(buf, sizeof(buf), fmt, ap);
      if (len >= sizeof(buf)) buf[len= sizeof(buf)-1]= 0;
      for (p= buf; *p; p++) if (*p == '\t') *p= ' ';
      len= p-buf;
      fprintf(stderr, "%s%*s\r", buf, status_len > len ? status_len - len : 0, "");
      status_len= len;
   }
   fflush(stderr);
}
	 
	 



//
//	list-handling support
//
//	  list_add	Add a string to the top of the list
//	  list_rem	Remove a string from the bottom of the list (FIFO)
//	  list_push	Add a string to the top of the list (same as list_add)
//	  list_pop	Pop a string from the top of the list (LIFO)
//	  list_sort	Sort in binary order, with first at top, last at bottom
//	  list_rsort	Reverse sort in binary order, with last at top, first at bottom
//	  list_string	Create a single allocated string containing the whole list
//	  list_free	Completely deallocate the list and all strings within it
//

void 
list_add(List **lp, char *str) {
   List *tmp= ALLOC(List);
   tmp->str= str;
   tmp->nxt= *lp;
   *lp= tmp;
}

void 
list_push(List **lp, char *str) {
   List *tmp= ALLOC(List);
   tmp->str= str;
   tmp->nxt= *lp;
   *lp= tmp;
}

char *
list_rem(List **lp) {
   char *rv;
   List **prvp= lp;
   if (!*prvp) return 0;
   while ((*prvp)->nxt) 
      prvp= &((*prvp)->nxt);
   rv= (*prvp)->str;
   free(*prvp);
   *prvp= 0;
   return rv;
}

char *
list_pop(List **lp) {
   List *tmp= *lp;
   char *rv;
   if (!tmp) return 0;
   *lp= tmp->nxt;
   rv= tmp->str;
   free(tmp);
   return rv;
}

static int 
list_cmp(const void *p, const void *q) {
   List *pp= *(List**)p;
   List *qq= *(List**)q;
   return strcmp(pp->str, qq->str);
}

void 
list_sort(List **lp) {
   int cnt= 0;
   int a;
   List *p;
   List **arr;
   List **prvp;

   for (cnt=0, p= *lp; p; p= p->nxt) cnt++;
   if (cnt < 2) return;

   arr= ALLOC_ARR(cnt, List*);
   for (a=0, p= *lp; p; p= p->nxt) arr[a++]= p;

   qsort(arr, cnt, sizeof(List*), list_cmp);

   prvp= lp;
   for (a=0; a<cnt; a++) {
      *prvp= arr[a];
      arr[a]->nxt= 0;
      prvp= &(arr[a]->nxt);
   }
   free(arr);
}

void 
list_rsort(List **lp) {
   int cnt= 0;
   int a;
   List *p;
   List **arr;
   List **prvp;

   for (cnt=0, p= *lp; p; p= p->nxt) cnt++;
   if (cnt < 2) return;

   arr= ALLOC_ARR(cnt, List*);
   for (a=0, p= *lp; p; p= p->nxt) arr[a++]= p;

   qsort(arr, cnt, sizeof(List*), list_cmp);

   prvp= lp;
   for (a=cnt-1; a>=0; a--) {
      *prvp= arr[a];
      arr[a]->nxt= 0;
      prvp= &(arr[a]->nxt);
   }
   free(arr);
}

void 
list_free(List **lp) {
   while (*lp) 
      free(list_pop(lp));
}

// rev: 0 bottom of list is start of string, 1 top of list is start of string
// sep: string to use to separate items of list
char *
list_string(List **lp, int rev, char *sep) {
   int len= 0;
   int cnt= 0;
   List *p;
   char *rv;
   char *cp;
   int sepsiz;

   for (p= *lp; p; p= p->nxt) {
      cnt++;
      len += strlen(p->str);
   }
   len += (cnt-1) * (sepsiz= strlen(sep));
   if (len < 0) len= 0;
   
   rv= cp= Alloc(len+1);
   if (!rev) {
      cp += len;
      for (p= *lp; p; p= p->nxt) {
	 int siz= strlen(p->str);
	 cp -= siz;
	 memcpy(cp, p->str, siz);
	 if (p->nxt) {
	    cp -= sepsiz;
	    memcpy(cp, sep, sepsiz);
	 }
      }
   } else {
      for (p= *lp; p; p= p->nxt) {
	 int siz= strlen(p->str);
	 memcpy(cp, p->str, siz);
	 cp += siz;
	 if (p->nxt) {
	    memcpy(cp, sep, sepsiz);
	    cp += sepsiz;
	 }
      }
   }

   return rv;
}
   

   


//
//	Generate a 32-bit 'random' number by hashing the current time
//	(including microseconds)
//

ub4
hash_of_time() {
   struct timeval tv;
   if (gettimeofday(&tv, NULL)) DIE;
   return do_hash((char*)&tv, sizeof(tv), 0);
}


//
//	Hashing function.  See below for credits.
//

// Moved to header
// typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
// typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */

#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)

/*
--------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
For every delta with one or two bits set, and the deltas of all three
  high bits or all three low bits, whether the original value of a,b,c
  is almost all zero or is uniformly distributed,
  * If mix() is run forward or backward, at least 32 bits in a,b,c
  have at least 1/4 probability of changing.
  * If mix() is run forward, every bit of c will change between 1/3 and
  2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
mix() was built out of 36 single-cycle latency instructions in a
  structure that could supported 2x parallelism, like so:
      a -= b;
      a -= c; x = (c>>13);
      b -= c; a ^= x;
      b -= a; x = (a<<8);
      c -= a; b ^= x;
      c -= b; x = (b>>13);
      ...
  Unfortunately, superscalar Pentiums and Sparcs can't take advantage
  of that parallelism.  They've also turned some of those single-cycle
  latency instructions into multi-cycle latency instructions.  Still,
  this is the fastest good hash I could find.  There were about 2^^68
  to choose from.  I only looked at a billion or so.
--------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
  a -= b; a -= c; a ^= (c>>13); \
  b -= c; b -= a; b ^= (a<<8); \
  c -= a; c -= b; c ^= (b>>13); \
  a -= b; a -= c; a ^= (c>>12);  \
  b -= c; b -= a; b ^= (a<<16); \
  c -= a; c -= b; c ^= (b>>5); \
  a -= b; a -= c; a ^= (c>>3);  \
  b -= c; b -= a; b ^= (a<<10); \
  c -= a; c -= b; c ^= (b>>15); \
}

/*
--------------------------------------------------------------------
hash() -- hash a variable-length key into a 32-bit value
  k       : the key (the unaligned variable-length array of bytes)
  len     : the length of the key, counting by bytes
  initval : can be any 4-byte value
Returns a 32-bit value.  Every bit of the key affects every bit of
the return value.  Every 1-bit and 2-bit delta achieves avalanche.
About 6*len+35 instructions.

The best hash table sizes are powers of 2.  There is no need to do
mod a prime (mod is sooo slow!).  If you need less than 32 bits,
use a bitmask.  For example, if you need only 10 bits, do
  h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.

If you are hashing n strings (ub1 **)k, do it like this:
  for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);

By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
code any way you wish, private, educational, or commercial.  It's free.

See http://burtleburtle.net/bob/hash/evahash.html
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable.  Do NOT use for cryptographic purposes.
--------------------------------------------------------------------
*/

ub4 
do_hash(register ub1 *k,        /* the key */
	register ub4  length,   /* the length of the key */
	register ub4  initval)  /* the previous hash, or an arbitrary value */
{
   register ub4 a,b,c,len;

   /* Set up the internal state */
   len = length;
   a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
   c = initval;         /* the previous hash value */

   /*---------------------------------------- handle most of the key */
   while (len >= 12)
      {
	 a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
	 b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
	 c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
	 mix(a,b,c);
	 k += 12; len -= 12;
      }

   /*------------------------------------- handle the last 11 bytes */
   c += length;
   switch(len)              /* all the case statements fall through */
      {
       case 11: c+=((ub4)k[10]<<24);
       case 10: c+=((ub4)k[9]<<16);
       case 9 : c+=((ub4)k[8]<<8);
	  /* the first byte of c is reserved for the length */
       case 8 : b+=((ub4)k[7]<<24);
       case 7 : b+=((ub4)k[6]<<16);
       case 6 : b+=((ub4)k[5]<<8);
       case 5 : b+=k[4];
       case 4 : a+=((ub4)k[3]<<24);
       case 3 : a+=((ub4)k[2]<<16);
       case 2 : a+=((ub4)k[1]<<8);
       case 1 : a+=k[0];
	  /* case 0: nothing left to add */
      }
   mix(a,b,c);
   /*-------------------------------------------- report the result */
   return c;
}

#endif

/*///---------------------------------------------------------------------\\\*/
/*// )`( )()/)(`/\`//\`          (_)/=\~/_(_)           '/\\'/\')(\()( )'( \\*/
/*/ ()(')()\)(/`/\\ `         (_)  /=\  ~/_  (_)          ' //\'\)(/()(`)() \*/
/*\  )`()( )(/'            (_)    /=\    ~/_    (_)             `\)( )()'(  /*/
/*\\  ()()(`)(/         (_) ____ /=\ ____ ~/_ ____ (_)          \)(')()()  //*/
/*\\\---------------------------------------------------------------------///*/
