                            /* hashtab.c, 23 May 11 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#if HAVE_INTTYPE_H
#include <inttypes.h>     /* defines uint32_t etc */
#else
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#endif

#include "global.h"
#include "errors.h"
#include "utils.h"
#include "fltval.h"
#include "hashfunc.h"
#include "../hash/hashtable.h"
#include "../hash/hashtable_itr.h"
#include "hashtab.h"

// The characters in the literal and the mantissa of
// the matched interesting number (because of fuzzy matching)
//  are used as the key.
struct num_seen_key	
	{
	short len_mantissa;
	short len_spec_mantissa;
        DIGIT_CHAR *mantissa;
        DIGIT_CHAR *exponent;
        DIGIT_CHAR *spec_mantissa;
	};

struct hashtable *saved_htable; // Hash table of numbers we have seen
struct num_seen_rec *matched_num_list = NULL;

static unsigned int hashfromkey(void *ky)
{
// Generate a hash value from a key

struct num_seen_key *key = (struct num_seen_key *)ky;
unsigned int hash_val = hashlittle(key->mantissa, key->len_mantissa, 0);
size_t exp_len = strlen(key->exponent);

if (exp_len != 0)
   hash_val = hashlittle(key->exponent, exp_len, hash_val);

hash_val = hashlittle(key->spec_mantissa, key->len_spec_mantissa, hash_val);

return hash_val;
}

static int equalkeys(void *ky_1, void *ky_2)
{
// Return true if two keys that have the same hash value are identical

struct num_seen_key *key_1 = (struct num_seen_key *)ky_1;
struct num_seen_key *key_2 = (struct num_seen_key *)ky_2;

if (key_1->len_mantissa != key_2->len_mantissa)
   return 0;

if (memcmp(key_1->mantissa, key_2->mantissa, key_1->len_mantissa) == 0)
    return (strcmp(key_1->exponent, key_2->exponent) == 0);

return 0;
}


struct num_seen_rec *
mk_seen_val(enum match_enum match_kind,
                struct num_db_rec *spec_num_ptr,
                fp_t seen_val,
                DIGIT_CHAR *mantissa,
                int len_mantissa,
                DIGIT_CHAR *exponent)
{
// Create a record containing information about the number we have seen

size_t len_exponent;
struct num_seen_rec *seen_num_ptr;

seen_num_ptr=get_heap(sizeof(struct num_seen_rec));
if (seen_num_ptr == NULL)
   {
   print_warning("Failed to allocate storage: mk_seen_val 1\n");
   return NULL;
   }
seen_num_ptr->matched_num=spec_num_ptr;
seen_num_ptr->match_kind=match_kind;
seen_num_ptr->total_occurrences=0;
seen_num_ptr->file_occurrences=0;
if (enum_eq(match_kind, VALUE_MATCH))
   {
   fp_init(&seen_num_ptr->seen_valu);
   fp_assign(&seen_num_ptr->seen_valu, seen_val);
   }
if (enum_eq(match_kind, NEED_MATCH))
   {
   seen_num_ptr->need_id = spec_num_ptr->need_info->need_id;
   }
if (!enum_eq(match_kind, NO_MATCH)) // Link matched entries
   {
   seen_num_ptr->next=matched_num_list;
   matched_num_list=seen_num_ptr;
   }
seen_num_ptr->len_mantissa=len_mantissa;
seen_num_ptr->mantissa=get_heap(len_mantissa+1);
len_exponent=strlen(exponent)+1;
seen_num_ptr->exponent=get_heap(len_exponent);
if ((seen_num_ptr->mantissa == NULL) || (seen_num_ptr->exponent == NULL))
   {
   print_warning("Failed to allocate storage: mk_seen_val 2\n");
   return NULL;
   }
if (len_mantissa == MAX_NUM_DIGITS)
   {
   memcpy(seen_num_ptr->mantissa, mantissa, len_mantissa);
   seen_num_ptr->mantissa[MAX_NUM_DIGITS-1]=NULL_CHAR;
   }
else
   memcpy(seen_num_ptr->mantissa, mantissa, len_mantissa+1);
memcpy(seen_num_ptr->exponent, exponent, len_exponent);

return seen_num_ptr;
}


struct num_seen_rec *
     get_saved_info(struct num_db_rec *spec_num_ptr,
                  DIGIT_CHAR *mantissa,
                  int len_mantissa,
                  DIGIT_CHAR *exponent,
		  void **key)
{
struct num_seen_key *new_key = get_heap(sizeof(struct num_seen_key));

if (key != NULL)
   *key=(struct new_seen_key *)new_key;

// printf("len %d  %s  %s\n", len_mantissa, mantissa, exponent);

if (new_key == NULL)
   {
   print_error("Unable to allocate storage: get_saved_info\n");
   return NULL;
   }
new_key->len_mantissa = len_mantissa;
new_key->mantissa=mantissa;
new_key->exponent=exponent;
if (spec_num_ptr)
   {
   new_key->spec_mantissa=spec_num_ptr->mantissa;
   new_key->len_spec_mantissa=spec_num_ptr->len_mantissa;
   }
else
   {
   new_key->spec_mantissa=NULL;
   new_key->len_spec_mantissa=0;
   }

return (struct num_seen_rec *)hashtable_search(saved_htable, new_key);
}


BOOL cnt_seen_num(enum match_enum match_kind,
                  struct num_db_rec *spec_num_ptr,
                  fp_t seen_val,
                  DIGIT_CHAR *mantissa,
                  int len_mantissa,
                  DIGIT_CHAR *exponent)
{
// Count how many times a number has been seen.
// Return FALSE the first time called with a given value,
// otherwise return TRUE.

struct num_seen_key *key;
struct num_seen_rec *val_ptr;

// printf("len %d  %s  %s\n", len_mantissa, mantissa, exponent);

// Strip any leading zero for consistency with case where none given.
if (*mantissa == '0')
   {
   mantissa++;
   len_mantissa--;
   }

val_ptr = get_saved_info(spec_num_ptr, mantissa, len_mantissa, exponent,
							(void **)&key);

if (key == NULL)
   return FALSE;

if (val_ptr == NULL)  // Have we seen this number before?
   {
   val_ptr=mk_seen_val(match_kind, spec_num_ptr, seen_val,
					mantissa, len_mantissa, exponent);
   // Set key to point at the duplicated strings that won't go away
   key->mantissa=val_ptr->mantissa;
   key->exponent=val_ptr->exponent;
   if (!hashtable_insert(saved_htable, key, val_ptr))
      return FALSE;
// printf("hashtable contains %u items.\n", hashtable_count(saved_htable));
   val_ptr->total_occurrences=1;
   val_ptr->file_occurrences=1;

   return FALSE;
   }
else
   free(key);

val_ptr->match_kind |= match_kind;
val_ptr->total_occurrences++;
val_ptr->file_occurrences++;

return TRUE;
}


void print_saved(void)
{
// Iterate over a hash table printing out number information

struct num_seen_keys *key;
struct num_seen_rec *val_ptr;
struct hashtable_itr *itr;

// Iterator only returns a valid iterator if the hashtable is not empty
itr = hashtable_iterator(saved_htable);
if (hashtable_count(saved_htable) > 0)
   {
   do {
       key = hashtable_iterator_key(itr);
       val_ptr = hashtable_iterator_value(itr);
      } while (hashtable_iterator_advance(itr));
   }
}


BOOL init_hash_table(void)
{
saved_htable = create_hashtable(1000, hashfromkey, equalkeys);
if (saved_htable == NULL)
   return FALSE;

return TRUE;
}

