/* Implement hash tables.  This is -*- C++ -*- code.
   Copyright (C) 1992 Per Bothner.

This file is part of Q.

Q is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Q is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#ifdef __GNUG__
#pragma implementation
#endif
#include <stdio.h>
#include "types.h"
#include "hashtab.h"
#include "procs.h"
#include <Qcompile.h>
#include <debug.h>
#include <exceptions.h>
#include <expression.h>
#include <newtype.h>
#include <std.h>

void *
SymbolLookup(HashArray *tab, void *arg)
{
    unsigned short hash = (*tab->f->hash_it)(arg);
#define HASH_ELEMENT_TYPE Object
#define HASH_LENGTH_LOG tab->tab_len_log
#define HASH_ELEMENT_LOG tab->element_len_log
#define HASH_EQUAL(element,arg) (*element==(Object)arg)
#define HASH_NULL(element) (*element==HashNull)
#define HASH_DATA tab->data
#define HASH_DELETED(element)  (*element == HashDeleted)
/* the mask is redundant if masking is done before indexing */
#include "hashfunc.h"
}

unsigned HashSymbol(void * addr) { return (unsigned)((Symbol*)addr)->Hash(); }
unsigned HashAddr(void * addr)
 { return (unsigned)addr ^ ((unsigned)addr >> 15);}

struct HashFuncs StdHashFuncs = {NULL, SymbolLookup, HashSymbol};
struct HashFuncs IdentHashFuncs = {NULL, SymbolLookup, HashAddr};

void *Search(HashTable *tab, RootPtr arg)
  {
    Object *element = (*tab->f->lookup)(tab, arg);
    if (!HashNone(*element))
	return (void*)element[1];
#if 0
    if ((tab->flags & HashDefaultIsProc) != 0)
	return (void*)Apply(arg, tab->f->defaultVal);
    else
#endif
	return (void *)tab->f->defaultVal;
  }

#if 0
Func
SearchOrRaise(tab, arg)
    HashTable *tab; Object arg;
  { extern Func FuncTabSearch();
    Func f = FuncTabSearch(tab, arg);
    if (f == NULL) RAISE(Undefined, arg);
    return f;
  }

Object
_Search(Object arg)
  {
    register HashTable *tab = (HashTable *)GetProcData;
    register int i;
    register MapPair *pp;
    Object o;
  retry:
    for (i = 0, pp = &tab->tab[0]; i < tab->tabLen; i++, pp++)
        if (pp->arg.O == arg)
	     return pp->val.O;
    o = tab->f->defaultVal;
/* next line may crash if o is non-object
    if (HasHType(o, HashTab)) { tab = (HashTable*)o; goto retry; }
 */
    return o;
  }
#endif

Object SearchTab(HashTable *tab, Object arg)
  {
    return Search(tab, arg);
#if 0
    register int i;
    register MapPair *pp;
    Object o;
  retry:
    for (i = 0, pp = &tab->tab[0]; i < tab->tabLen; i++, pp++)
        if (pp->arg.O == arg)
	     return pp->val.O;
    o = tab->f->defaultVal;
    if (HasHType(o, HashTab)) { tab = (HashTable*)o; goto retry; }
 /*RAISE(Undefined, arg);*/
    return o;
#endif
  }

RootPtr
TestTab(HashTable *tab, RootPtr arg)
  { Object x = (Object)SearchTab(tab, arg);
/*    if (x == NoValue || x == Undefined) RAISE(Undefined, arg); */
    return x;
  }

void**
HashArray::remove(void *arg)
{
    if (element_len < sizeof(void*))
	abort();
    void** p = (*f->lookup)(this, arg);
    if (HashNone(*p)) return NULL;
    cur_size--;
    *p = HashDeleted;
    return p;
}

#if 0
void ForEachTabElement(register HashTable *tab, MapHashFunc f, void *extra)
  { register i; register RootPtr *element;
    i = 1<<tab->tab_len_log; element = tab->data;
    for (; i > 0; i--, element = (Object*)((char*)element + tab->element_len))
        if (!HashNone(*element))
	    (*f)(extra, element);
  }

Func
NewOpHashTab(n, nOps, defaultVal)
    union Object defaultVal;
  { return 0; }
#endif

void HashArray::rehash()
{
    RootPtr *old_data = data;
    register int i; register char *element;
    register size;
#ifndef LINKER
    if (LogVerbose > 0)
	fprintf(LogFile, "[Rehash #%x => %d slots]\n", this, 2<<tab_len_log);
#endif
    i = 1<<tab_len_log; element = (char*)old_data;
    tab_len_log++;
    size = 1<<(element_len_log + tab_len_log);
#ifdef DO_GC
    data =	(RootPtr*)GC_malloc(size);
#else
    data =	(RootPtr*)malloc(size);
    memset(data, 0, size);
#endif
    cur_size = 0;
    if (old_data) {
	size = element_len;
	for (; i > 0; i--, element += size)
            if (!HashNone(*(Object*)element))
		insert((void**)element);
	if ((flags & HashDontFreeBuffer) == 0)
	  {
#ifdef DO_GC
	    GC_free (old_data);
#else
	    free(old_data);
#endif
	  }
    }
    flags &= ~HashDontFreeBuffer;
}

void HashArray::initialize(int init_size, int el_size)
{
    register int i;

    for (i = 3; (1 << i) < init_size; i++) ;
    tab_len_log = i;

    for (i = 0; (1 << i) < el_size; i++) ;
    element_len = 1 << i;
    element_len_log = i;
    int size = 1 << (element_len_log + tab_len_log);
#ifdef DO_GC
    data = GC_malloc (size);
#else
    data = malloc(size);
    bzero(data, size);
#endif
    flags = 0;
    f = &StdHashFuncs;
    cur_size = 0;
}

/* 8 == sizeof(struct{Symbol *; void*}) */
HashTable::HashTable(int init_size, void * defaultVal)
: HashArray(init_size, 8)
{
    if (defaultVal == NULL)
	f = &StdHashFuncs;
    else {
#ifdef DO_GC
	f = (struct HashFuncs*)GC_malloc(sizeof(struct HashFuncs));
#else
	f = (struct HashFuncs*)malloc(sizeof(struct HashFuncs));
#endif
	*f = StdHashFuncs;
	f->defaultVal = defaultVal;
    }
}

HashTable *HashTable::New(int n, RootPtr defaultVal)
{
  return GC_NEW HashTable(n, defaultVal);
}

void HashArray::rehash_if_needed()
{
    int limit = 1 << tab_len_log;
    limit -= limit >> 3; /* limit is ceiling(max_space*7/8) */
    cur_size++;
    if (cur_size >= limit)
	rehash();
}

void HashArray::insert(void** element)
{
    register void **src = element;
    register len = element_len >> 2;
    register void **dst = (*f->lookup)(this, element[0]);
    if (HashNone(*dst))
      {
	while (--len >= 0)
	    *dst++ = *src++;
	rehash_if_needed();
      }
    else
	while (--len >= 0)
	    *dst++ = *src++;
  }

void Insert(HashTable *tab, const RootPtr arg, const void * val)
{
    Object args[2];
    args[0] = arg;
    args[1] = (Object)val;
    tab->insert(args);
}

typedef void (*TabIterator)(void * extra, void * arg, void * val);
#define CALL_f(name, value) (*f)(extra, name, value)
void ForEachInTab(register HashTable *tab, TabIterator f, void *extra)
  { DoForEachInTab(tab, CALL_f) }

#if 0
int TableSize(HashTable *tab)
  {
#if 1
    return tab->cur_size;
#else
    register i; register MapPair *pair; register n = 0;
    for (i = tab->tabLen, pair = tab->tab; i > 0; i--, pair++)
        if (pair->arg.O != NULLARG) n++;
    return n;
#endif
  }
#endif
