/* Routines for string interning - we handle this ourselves because of
 * initialization headaches. Currently, we don't do anything
 * sophisticated with the garbage collector to try to GC
 * interned strings. */

/* We use a power-of-two sized hash table to hold interned strings, 
 * and the low bits of the hash code of the string to pick a hash bin
 */

#include "toba.h"
#include "java_lang_Object.h"
#include "java_lang_String.h"
#include "sthreads.h"
#include "runtime.h"

#define LOG_TABLE_SIZE     12
#define INTERN_TABLE_SIZE  ( 1<< LOG_TABLE_SIZE )
#define HASH_MASK          0xfff

struct hash_bucket {
    struct in_java_lang_String *str;
    struct hash_bucket *next;
};

static struct sthread_mutex htlock;
static struct hash_bucket *hashtable[INTERN_TABLE_SIZE];

/* May hold the lock upon entry */
static struct in_java_lang_String *
lookup_string(struct in_java_lang_String *str, Int bucket)
{
    struct hash_bucket *h = hashtable[bucket];
    while (h != 0) {
	if (str->class->M.equals_O_lnqT0.f(str,h->str) )
	    return h->str;
	h = h->next;
	
    }
    return 0;
}


/* Must NOT hold the lock upon entry */
static struct in_java_lang_String *
insert_string(struct in_java_lang_String *str, Int bucket)
{
    struct mythread *thr = mythread();
    struct in_java_lang_String *interned;

    sthread_dontkill_start(thr);
    sthread_mutex_lock(&htlock);
    
    /* Check again under lock */
    interned = lookup_string(str, bucket);

    if (interned == 0) {
	struct hash_bucket *b; 
	b = allocate(sizeof(struct hash_bucket));
	b->next = hashtable[bucket];
	b->str  = str;
	hashtable[bucket] = b;
	interned = str;
    }

    sthread_mutex_unlock(&htlock);
    sthread_dontkill_end(thr);

    return interned;
}

struct in_java_lang_String *
intern_string(struct in_java_lang_String *str)
{
    Int hashcode = str->class->M.hashCode__mK6Su.f(str);
    Int bucket = hashcode & HASH_MASK;
    struct in_java_lang_String *interned;
    
    interned = lookup_string(str, bucket);
    if (interned == 0) {
	interned = insert_string(str, bucket);
    }
    
    return interned;
}

void 
intern_string_init()
{
    /* Install our native version of string interning 
     * into java_lang_String */
    sthread_mutex_init(&htlock);
    cl_java_lang_String.M.intern__cF4y7.f = (Object(*)(Object))intern_string;
}
