// -*- Mode: C++ -*-

//          GiSTfile.cpp
//
// Copyright (c) 1996, Regents of the University of California
// $Header: /cvsroot/Tree-M/GiST/GiSTfile.cpp,v 1.1 2001/05/06 00:45:51 root Exp $

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#ifdef UNIX
#include <unistd.h>
#else
#include <io.h>
#endif

#ifdef UNIX
#define O_BINARY 0
#endif

#include "GiSTfile.h"

// The first page in the file has these "magic words"
// and the head of the deleted page list.
static char magic[]="GiST data file";

void 
GiSTfile::Create(const char *filename)
{
	if(IsOpen()) return;
	fileHandle=open(filename, O_RDWR|O_BINARY);
	if(fileHandle>=0) {
		close(fileHandle);
		return;
	}
	fileHandle=open(filename, O_BINARY|O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE);
	if(fileHandle<0) return;
	SetOpen(1);

	/* Reserve page 0 */
	char *page=new char[PageSize()];

	memset(page, 0, PageSize());
	memcpy(page, magic, sizeof magic);
	write(fileHandle, page, PageSize());
	delete page;
}

void 
GiSTfile::Open(const char *filename)
{
	char *page;

	if(IsOpen()) return;
	fileHandle=open(filename, O_RDWR|O_BINARY);
	if(fileHandle<0) return;
	// Verify that the magic words are there
	page=new char[PageSize()];
	read(fileHandle, page, PageSize());
	if(memcmp(page, magic, sizeof(magic))) {
		close(fileHandle);
		delete page;
		return;
	}
	delete page;
	SetOpen(1);
}

void 
GiSTfile::Close()
{
	if(!IsOpen()) return;
	close(fileHandle);
	SetOpen(0);
}

void
GiSTfile::Read(GiSTpage page, char *buf)
{
	if(IsOpen()) {
		lseek(fileHandle, page*PageSize(), SEEK_SET);
		read(fileHandle, buf, PageSize());
	}
}

void
GiSTfile::Write(GiSTpage page, const char *buf)
{
	if(IsOpen()) {
		lseek(fileHandle, page*PageSize(), SEEK_SET);
		write(fileHandle, buf, PageSize());
	}
}

GiSTpage 
GiSTfile::Allocate()
{
	GiSTpage page;
	char *buf;

	if(!IsOpen()) return (0);
	// See if there's a deleted page
	buf=new char[PageSize()];
	Read(0, buf);
	memcpy(&page, buf+sizeof(magic), sizeof(GiSTpage));
	if(page) {
		// Reclaim this page
		Read(page, buf);
		Write(0, buf);
	}
	else {
		page=lseek(fileHandle, 0, SEEK_END)/PageSize();
		memset(buf, 0, PageSize());
		write(fileHandle, buf, PageSize());
	}
	delete buf;
	return page;
}

void 
GiSTfile::Deallocate(GiSTpage page)
{
	char *buf;
	GiSTpage temp;

	if(!IsOpen()) return;
	// Get the old head of the list
	buf=new char[PageSize()];
	Read(0, buf);
	memcpy(&temp, buf+sizeof(magic), sizeof(GiSTpage));
	// Write the new head of the list
	memcpy(buf+sizeof(magic), &page, sizeof(GiSTpage));
	Write(0, buf);
	// In our new head, put link to old head
	memcpy(buf+sizeof(magic), &temp, sizeof(GiSTpage));
	Write(page, buf);
	delete buf;
}
