/* ARISA - Validity Cache
 * Copyright (C) 2004 Carl Ritson
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

/***
 * The validity cache makes use of the fact that pointers such always be
 * atomic data types on modern hardware.  If they aren't then... we have
 * a problem.
 ***/

#include "arisa.h"

// Size is determined at compile time
#define CACHE_SIZE 	256
#define PAIRING_SIZE 	32

static void **cache = NULL;
static void **pairing = NULL;

// these are for debugging
//static int no_tests	= 0;
//static int no_hits	= 0;
//static int no_inserts	= 0;
//static int no_deletes	= 0;

static uint32_t hash_ptr(void *ptr) {
	uint8_t *p = (uint8_t *)&ptr;
	uint32_t hash;

	if(sizeof(void *) == 4) {
		hash = (p[0] * 37) + p[1];
		hash = (hash * 37) + p[2];
		hash = (hash * 37) + p[3];
	} else if(sizeof(void *) == 8) {
		hash = (p[0] * 37) + p[1];
		hash = (hash * 37) + p[2];
		hash = (hash * 37) + p[3];
		hash = (hash * 37) + p[4];
		hash = (hash * 37) + p[5];
		hash = (hash * 37) + p[6];
		hash = (hash * 37) + p[7];
	}
	
	return hash;
}

void validity_cache_init(void) {
	int i;
	
	cache	= xalloc(sizeof(void *)*CACHE_SIZE);
	pairing = xalloc(sizeof(void *)*PAIRING_SIZE);
	
	for(i = 0; i < CACHE_SIZE; ++i)
		cache[i] = NULL;
	for(i = 0; i < PAIRING_SIZE; ++i)
		pairing[i] = NULL;
}

void validity_cache_free(void) {
	if(cache != NULL) {
		xfree(cache);
		cache = NULL;
	}
	if(pairing != NULL) {
		xfree(pairing);
		pairing = NULL;
	}
}

void validity_cache_manage(void) {
//	D("validity-cache; tests: %d, hits: %d, inserts: %d, deletes: %d",
//			no_tests,no_hits,no_inserts,no_deletes);
}

int validity_test(void *ptr) {
//	no_tests++;
	if(ptr != NULL && cache[hash_ptr(ptr) % CACHE_SIZE] == ptr) {
//		no_hits++;
		return 1;
	} else {
		return 0;
	}
}

void validity_insert(void *ptr) {
//	no_inserts++;
	if(ptr != NULL)
		cache[hash_ptr(ptr) % CACHE_SIZE] = ptr;
}

void validity_delete(void *ptr) {
//	no_deletes++;
	if(ptr != NULL)
		cache[hash_ptr(ptr) % CACHE_SIZE] = NULL;
}

int validity_pair_test(void *ptr1, void *ptr2) {
	if(ptr1 != NULL && ptr2 != NULL) {
		uint32_t h1 = hash_ptr(ptr1);
		uint32_t h2 = hash_ptr(ptr2);
		if(cache[h1 % CACHE_SIZE] == ptr1
				&& cache[h2 % CACHE_SIZE] == ptr2
				&& pairing[(h1+h2) % PAIRING_SIZE] == ptr2)
			return 1;
	}
	return 0;
}

void validity_pair_insert(void *ptr1, void *ptr2) {
	if(ptr1 != NULL && ptr2 != NULL) {
		uint32_t h1 = hash_ptr(ptr1);
		uint32_t h2 = hash_ptr(ptr2);
		
		cache[h1 % CACHE_SIZE] = ptr1;
		cache[h2 % CACHE_SIZE] = ptr2;
		pairing[(h1+h2) % PAIRING_SIZE] = ptr2;
	}
}

void validity_pair_delete(void *ptr1, void *ptr2) {
	if(ptr1 != NULL && ptr2 != NULL) {
		uint32_t h1 = hash_ptr(ptr1);
		uint32_t h2 = hash_ptr(ptr2);

		cache[h1 % CACHE_SIZE] = NULL;
		cache[h2 % CACHE_SIZE] = NULL;
		pairing[(h1+h2) % PAIRING_SIZE] = NULL;
	}
}
