/*
* This file is part of SkunkDB
* Copyright (c) 2001 Gianni Tedesco
*
*    SkunkDB 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.
*
*    SkunkDB 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 SkunkDB; if not, write to the Free Software
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


#include <skunk_query.h>
#include <skunk_hash.h>

#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int sdbq_init(p_sdbq q, int fd)
{
	struct stat st;

	sdbq_close(q);
	
	/* Stat and mmap() the file */
	if ( (fstat(fd, &st)) < 0 )
		return 0;

	if ( st.st_size < SKUNK_DIRECTORY_MOD*sizeof(skunk_table_t) )
		return 0;
	
	if ( (q->map=mmap(NULL, st.st_size,
		PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
		sdbq_close(q);
		return 0;
	}

	/* Fill in the struct */
	q->maplen=st.st_size;
	q->fd=fd;
	return 1;
}

void sdbq_close(p_sdbq q)
{
	if ( q->map ) munmap(q->map, q->maplen);
	q->maplen=0;
	q->map=NULL;
}

int sdbq_query(p_sdbq q, u_int8_t *key, skunk_ofs_t klen)
{
	p_skunk_table t;
	p_skunk_bucket b;
	int i, dist=0;
	
	q->item.hash=skunk_hash(key, klen);

	/* Open checks the DB is big enough for this */
	t=(p_skunk_table)q->map;
	t = (p_skunk_table)((long)t + (MOD(q->item.hash)*sizeof(skunk_table_t)));
	
	if (!t->ofs || !t->len || t->ofs > q->maplen ) {
		return -1;
	}
	
	i=(q->item.hash^klen)%t->len;
	for(;;) {
		skunk_ofs_t nofs=t->ofs + (i*sizeof(skunk_bucket_t));

		if ( nofs > q->maplen ) return -1;
		b=(p_skunk_bucket)((long)q->map + nofs);
		
		if ( b->hash == q->item.hash && klen==b->klen ) {
			char *record=(char *)(((long)q->map)+b->ofs);

			/* Check the validity */
			if ( (b->ofs+b->klen+b->vlen) > q->maplen )
				goto not_this;

			/* Check the key */
			if ( memcmp(record, key, klen) != 0 ) 
				goto not_this;

			/* Fill in the structure */
			q->item.key_len=b->klen;
			q->item.val_len=b->vlen;
			q->item.key=(u_int8_t *)record;
			record+=q->item.key_len;
			q->item.val=(u_int8_t *)record;

			return 1;
		}

not_this:
		if ( ++dist>t->len ) break;
		i=(i+1)%t->len;
	}
	
	return 0;
}
