/* File: HashFile.m - db(3) to HashTable bridge
 *
 * By: Christopher Lane
 * Symbolic Systems Resources Group
 * Knowledge Systems Laboratory
 * Stanford University
 *
 * Date: 6 August 1990
 *
 * Copyright: 1990 by The Leland Stanford Junior University.  This program
 * may be distributed without restriction for non-commercial use.
 */

#import <c.h>
#import <libc.h>
#import <string.h>
#import <assert.h>

#import "HashFile.h"

#define NXRewind(stream) NXSeek(stream, 0L, NX_FROMSTART)

@implementation HashFile

+ newFromFile:(const char *) name
{
	return [HashFile newFromFile:name keyDesc:"@" valueDesc:"@"];
}

+ newFromFile:(const char *) name keyDesc:(const char *) aKeyDesc
{
	return [HashFile newFromFile:name keyDesc:aKeyDesc valueDesc:"@"];
}

+ newFromFile:(const char *) name keyDesc:(const char *) aKeyDesc valueDesc:(const char *) aValueDesc
{
	NXTypedStream *typedStream;

	self = [super newKeyDesc:aKeyDesc valueDesc:aValueDesc];

	if((db = dbOpen(filename = (char *) name)) == NULL) {
		[self free];
		return nil;
		}

	readOnly = (BOOL) ((db->flag & (dbFlagReadOnly | dbFlagCompressed)) != 0);

	assert(d.k.s = (char *) malloc((size_t) LEAFSIZE));
	assert(d.c.s = (char *) malloc((size_t) LEAFSIZE));
	
	d.k.n = malloc_size(d.k.s);
	d.c.n = malloc_size(d.c.s);

	assert(stream = NXOpenMemory(NULL, 0, NX_READWRITE));
	assert(typedStream = NXOpenTypedStream(stream, NX_WRITEONLY));
	offset = NXTell(stream);
	NXCloseTypedStream(typedStream);

	return self;
}

+ (BOOL) isHashFile:(const char *) name
{
	return dbExists((char *) name);
}

- free
{	
	free(d.k.s);
	free(d.c.s);
	NXCloseMemory(stream, NX_FREEBUFFER);
	assert(dbClose(db));
	
	return [super free];
}

- empty
{	
	if(!readOnly && dbClose(db) && dbCreate(filename) && (db = dbInit(filename)) != NULL) return [super empty];
	
	return nil;
}

- (unsigned) count
{
	unsigned i = 0;
	
	if(dbFirst(db, &d)) do { ++i; } while(dbNext(db, &d));
	
	assert(i >= count);
	
	return i;
}

- (BOOL) isKey:(const void *) aKey
{
	if([super isKey:aKey]) return YES;

	[self _keyCvtIn:aKey];
	
	return((BOOL) dbFind(db, &d));
}

- (void *) valueForKey:(const void *) aKey
{
	void *value = (void *) nil;
	
	if([super isKey:aKey]) return [super valueForKey:aKey];
	
	[self _keyCvtIn:aKey];
	
	if(dbFetch(db, &d)) [super insertKey:aKey value:(value = [self _valueCvtOut])];
		
	return value;
}

- (void *) insertKey:(const void *) aKey value:(void *) aValue
{
	if(readOnly) return (void *) nil;
	
	[self _keyCvtIn:aKey];
	[self _valueCvtIn:aValue];

	if(!dbStore(db, &d)) return (void *) nil;
	
	return [super insertKey:aKey value:aValue];
}

- (void *) removeKey:(const void *) aKey;
{
	if(readOnly) return (void *) nil;

	[self _keyCvtIn:aKey];
	if(!dbDelete(db, &d)) return (void *) nil;
	
	return([super removeKey:aKey]);
}

- (NXHashState) initState
{
	NXHashState state = [super initState];

	if(state.i = dbFirst(db, &d)) {
		state.i = d.k.n;
		state.j = d.c.n;
		}
	
	return state;
}

- (BOOL) nextState:(NXHashState *) aState key:(const void **) aKey value:(void **) aValue
{
	if(aState->i == FALSE) return NO;
		
	d.k.n = aState->i;
	d.c.n = aState->j;

	if(dbGet(db, &d)) {
		*aKey = [self _keyCvtOut];
		*aValue = [self _valueCvtOut];
		}
	else return NO;

	if(aState->i = dbNext(db, &d)) {
		aState->i = d.k.n;
		aState->j = d.c.n;
		}

	return YES;
}

- (void) _keyCvtIn:(const void *) aKey
{
	[self _datumCvtIn:&d.k from:aKey using:keyDesc];
}

- (void) _valueCvtIn:(const void *) aValue
{
	[self _datumCvtIn:&d.c from:aValue using:valueDesc];
}

- (void) _datumCvtIn:(Datum *) aDatum from:(const void *) aBuffer using:(const char *) aDesc
{
	NXTypedStream *typedStream;
	
	NXRewind(stream);
	
	assert(typedStream = NXOpenTypedStream(stream, NX_WRITEONLY));
	
	offset = NXTell(stream);

	NXWriteType(typedStream, aDesc, &aBuffer);
	
	aDatum->n = NXTell(stream) - offset;

	NXCloseTypedStream(typedStream);
	
	NXSeek(stream, offset, NX_FROMSTART);
	
	NXRead(stream, (void *) aDatum->s, aDatum->n);
}

- (void *) _keyCvtOut
{
	return [self _datumCvtOut:&d.k using:keyDesc];
}

- (void *) _valueCvtOut
{
	return [self _datumCvtOut:&d.c using:valueDesc];
}

- (void *) _datumCvtOut:(Datum *) aDatum using:(const char *) aDesc
{
	NXTypedStream *typedStream;
	void *buffer;
	
	assert(buffer = (void *) malloc((size_t) aDatum->n));
	
	NXSeek(stream, offset, NX_FROMSTART);
	
	NXWrite(stream, (void *) aDatum->s, aDatum->n);
	
	NXRewind(stream);

	assert(typedStream = NXOpenTypedStream(stream, NX_READONLY));

	NXReadType(typedStream, aDesc, &buffer);
	
	NXCloseTypedStream(typedStream);
	
	return buffer;
}

@end
