/*
* MXBT.C
*
* Written by Paul Edwards and released to the public domain.
*
* BTree functions that operate on an index file compatible with that
* used by Mix Database Toolchest (or something like that).
*
* The btree index file is comprised of three different record types,
*
* stored as a fixed length (e.g. 512 bytes), even though the record
*
* types may not require that full length.
*
* 1. A control record. This is the first record in the file, and
* contains important information such as the fixed length that all
* the other records are.
*
* 2. Leaf nodes. These are identified by having "-1" in the first
* field (a "long"). Every search key will be found in one of these
* records. The leaf nodes appear after the control record.
*
* 3. Index nodes. These are identified by not having "-1" in the
* first field. Some, but not all, of the keys will be found in
* one of these fields. The index nodes appear after the last leaf
* node.
*
* Note that if you wish to update records in this index file, you
* should update the leaf nodes before the index nodes.
*
* The objective of all this is to find a matching key, which will
* then have a corresponding "long" value. This long value can
* then be used as an offset into an appropriate data file.
*
* The basic structure of the index records goes like this. Each
* index record has several keys stored in it. If your key is
* lower than the first key, then the recType field doubles up as
* the *record number* of the left branch. Multiply that by the
* record size and you have your offset. Otherwise, go through
* the index entries until you find the an entry with a key greater
* than yours, or you reach the end. The previous entry to that
* one will have the left node for you to follow, in the "lower"
* variable. Again, this is a record number. The idea is that
* you keep following the appropriate left branch until you hit
* a leaf node, then you do a sequential search. Crikey, if
* you're a real loser you could even do a binary search of the
* leaf node.
*
*/
/*
* The algorithm we employ is:
*
* 1. fetch the control record
*
* 2. Search the index nodes for our key until we reach a leaf
* node.
*
* 3. Search the leaf node for our key.
*/
#include <stdio.h>
#include "mxbt.h"
static void mxbtInit(MXBT * mxbt);
static void mxbtOpen(MXBT * mxbt, char *indexFile);
static void mxbtClose(MXBT * mxbt);
static void mxbtFetchControl(MXBT * mxbt);
static void mxbtSetKey(MXBT * mxbt, void *searchKey);
static void mxbtSetCompare(MXBT * mxbt, int (*compare) (void *testKey, void *searchKey, int len));
static void mxbtReadRec(MXBT * mxbt);
static void mxbtFindLeaf(MXBT * mxbt);
static void mxbtSearchLeaf(MXBT * mxbt);
long mxbtOneSearch(MXBT * mxbt, char *indexFile, void *searchKey, int (*compare) (void *testKey, void *searchKey, int len))
{
mxbt->error = 0;
mxbtInit(mxbt);
if (!mxbt->error)
{
mxbtOpen(mxbt, indexFile);
if (!mxbt->error)
{
mxbtFetchControl(mxbt);
if (!mxbt->error)
{
mxbtSetKey(mxbt, searchKey);
mxbtSetCompare(mxbt, compare);
mxbtFindLeaf(mxbt);
if (!mxbt->error)
{
mxbtSearchLeaf(mxbt);
}
}
mxbtClose(mxbt);
}
}
if (mxbt->error)
{
return -1L;
}
else
{
return mxbt->value;
}
}
static void mxbtInit(MXBT * mxbt)
{
mxbt->buf = mxbt->myunion.intbuf;
mxbt->index = (struct mxbt_indexrec *)mxbt->buf;
mxbt->leaf = (struct mxbt_leafrec *)mxbt->buf;
}
static void mxbtOpen(MXBT * mxbt, char *indexFile)
{
mxbt->fp = fopen(indexFile, "rb");
if (mxbt->fp == NULL)
{
mxbt->error = 1;
}
}
static void mxbtClose(MXBT * mxbt)
{
if (fclose(mxbt->fp) != 0)
{
mxbt->error = 1;
}
}
static void mxbtFetchControl(MXBT * mxbt)
{
if (fread(&mxbt->recSize, sizeof(unsigned short), 1, mxbt->fp) != 1)
{
mxbt->error = 1;
}
else if (fread(&mxbt->control, sizeof mxbt->control, 1, mxbt->fp) != 1)
{
mxbt->error = 1;
}
}
static void mxbtSetKey(MXBT * mxbt, void *searchKey)
{
mxbt->searchK = searchKey;
}
static void mxbtSetCompare(MXBT * mxbt, int (*compare) (void *testKey, void *searchKey, int len))
{
mxbt->compareF = compare;
}
static void mxbtReadRec(MXBT * mxbt)
{
size_t x;
int y;
y = fseek(mxbt->fp, mxbt->recordNum * mxbt->recSize, SEEK_SET);
if (y != 0)
{
mxbt->error = 1;
}
else
{
x = fread(mxbt->buf, mxbt->recSize, 1, mxbt->fp);
if (x != 1)
{
mxbt->error = 1;
}
}
}
static void mxbtFindLeaf(MXBT * mxbt)
{
int cnt, x;
mxbt->recordNum = mxbt->control.indexStart;
mxbtReadRec(mxbt);
while (!mxbt->error && (mxbt->index->recType != -1))
{
cnt = mxbt->index->keyCount;
if (cnt < 0)
{
mxbt->error = 1;
}
else
{
for (x = 0; x < cnt; x++)
{
if (mxbt->compareF((char *)mxbt->index +
mxbt->index->keys[x].offset, mxbt->searchK,
mxbt->index->keys[x].len) > 0)
{
break;
}
}
if (x == 0)
{
mxbt->recordNum = mxbt->index->recType;
}
else
{
mxbt->recordNum = mxbt->index->keys[x - 1].lower;
}
mxbtReadRec(mxbt);
}
}
}
static void mxbtSearchLeaf(MXBT * mxbt)
{
int cnt, x, ret;
cnt = mxbt->leaf->keyCount;
if (cnt <= 0)
{
mxbt->error = 1;
}
else
{
for (x = 0; x < cnt; x++)
{
ret = mxbt->compareF((char *)mxbt->leaf +
mxbt->leaf->keys[x].offset, mxbt->searchK,
mxbt->leaf->keys[x].len);
if (ret > 0)
{
mxbt->error = 1;
break;
}
else if (ret == 0)
{
mxbt->value = mxbt->leaf->keys[x].value;
break;
}
}
if (x == cnt)
{
mxbt->error = 1;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1