/* Copyright (c) 1993 by Sanjay Ghemawat */
#include <string.h>
#include "HashMap.h"
#include "Symbol.h"

// Comparison routine for strings
static inline int str_equal(char const* a, char const* b) {
    return (strcmp(a, b) == 0);
}

// Hash routine for strings
static unsigned int str_hash(char const* str) {
    unsigned long result = 0;

    while (*str != '\0') {
	// Shift result one nibble and add character
	result = (result << 4) + *str;

	// Extract high nibble from result
	unsigned long high = (result & 0xf0000000);

	if (high != 0) {
	    // Merge high nibble into low end of result
	    result = result ^ (high >> 24);

	    // Clear high nibble from result
	    result = result ^ high;
	}

	str++;
    }

    return ((unsigned int) result);
}

declareHashMap(_SymbolStore,char const*,SymbolRep*,str_equal,str_hash)
implementHashMap(_SymbolStore,char const*,SymbolRep*,str_equal,str_hash)

Symbol::Symbol(char const* str, int len) {
    /* make sure string is null-terminated */
    if (str[len] != '\0') {
	/* Make null-terminated copy */
	char* copy = new char[len+1];
	strncpy(copy, str, len);
	copy[len] = '\0';

	rep = SymbolRep::find_rep(copy, len);
	delete [] copy;
    }
    else {
	rep = SymbolRep::find_rep(str, len);
    }
}

Symbol Symbol::operator + (Symbol const& s) const {
    int len = rep->length + s.rep->length;

    char* copy = new char[len+1];
    strcpy(copy, rep->contents);
    strcat(copy, s.rep->contents);
    SymbolRep* result = SymbolRep::find_rep(copy, len);
    delete [] copy;

    return Symbol(result);
}

SymbolRep* SymbolRep::find_rep(char const* str, int len) {
    static _SymbolStore* map = 0;

    /* Initialize map if necessary */
    if (map == 0) {
	map = new _SymbolStore;
    }

    /* Lookup string in map */
    SymbolRep* rep;
    if (! map->find(str, rep)) {
	/* Add entry to map */
	if (len < 0) len = strlen(str);
	char* copy = new char[len+1];
	strcpy(copy, str);

	rep = new SymbolRep;
	rep->contents = copy;
	rep->length = len;

	map->insert(copy, rep);
    }
    return rep;
}

unsigned int Symbol::hash(Symbol const& s) {
    unsigned long result = ((unsigned long) s.rep) * 2654435767;
    return ((unsigned int) result);
}
