// -*- Mode: C++ -*-
// GiST.cpp
//
// Copyright (c) 1996, Regents of the University of California
// $Header: /cvsroot/Tree-M/GiST/GiST.cpp,v 1.1 2001/05/06 00:45:51 root Exp $
#include <stdio.h>
#include <string.h>
#include "GiST.h"
#include "GiSTpath.h" // for GiSTRootPage
GiST::GiST()
{
isOpen=0;
debug=0;
store=0;
}
void
GiST::Create(const char *filename)
{
GiSTpage page;
if(IsOpen()) return;
store=CreateStore();
store->Create(filename);
if(!store->IsOpen()) return;
page=store->Allocate();
GiSTnode *node=NewNode(this);
node->Path().MakeRoot();
WriteNode(node);
delete node;
isOpen=1;
}
void
GiST::Open(const char *filename)
{
if(IsOpen()) return;
store=CreateStore();
store->Open(filename);
if(!store->IsOpen()) return;
isOpen=1;
}
void
GiST::Close()
{
if(IsOpen()) {
store->Close();
isOpen=0;
}
}
void
GiST::Insert(const GiSTentry &entry)
{
InsertHelper(entry, 0);
}
void
GiST::InsertHelper(const GiSTentry &entry,
int level, // level of tree at which to insert
int *splitvec) // a vector to trigger Split instead of forced reinsert
{
GiSTnode *leaf;
int overflow=0;
leaf=ChooseSubtree(GiSTRootPage, entry, level);
leaf->Insert(entry);
if (leaf->IsOverFull(*store)) {
if(ForcedReinsert()&&!leaf->Path().IsRoot()&&(!splitvec||!splitvec[level])) {
int split[GIST_MAX_LEVELS];
// R*-tree-style forced reinsert
for(int i=0; i<GIST_MAX_LEVELS; i++) split[i]=0;
OverflowTreatment(leaf, entry, split);
overflow=1;
}
else Split(&leaf, entry);
if(leaf->IsOverFull(*store)) {
// we only should get here if we reinserted, and the node re-filled
assert(overflow);
leaf->DeleteEntry(entry.Position());
Split(&leaf, entry);
}
}
else WriteNode(leaf);
if(!overflow) AdjustKeys(leaf, NULL);
delete leaf;
}
void
GiST::OverflowTreatment(GiSTnode *node, const GiSTentry& entry, int *splitvec)
{
GiSTlist<GiSTentry*> deleted;
// remove the "top" p entries from the node
deleted=RemoveTop(node);
WriteNode(node);
AdjustKeys(node, NULL);
// note that we've seen this level already
splitvec[node->Level()]=1;
// for each of the deleted entries, call InsertHelper at this level
while (!deleted.IsEmpty()) {
GiSTentry *tmpentry=deleted.RemoveFront();
InsertHelper(*tmpentry, node->Level(), splitvec);
delete tmpentry;
}
}
GiSTlist<GiSTentry*>
GiST::RemoveTop(GiSTnode *node)
{
GiSTlist<GiSTentry*> deleted;
int count=node->NumEntries();
// default: remove the first ones on the page
int num_rem=(int)((count+1)*RemoveRatio()+0.5);
for(int i=num_rem-1; i>=0; i--) {
deleted.Append((GiSTentry *)(*node)[i].Ptr()->Copy());
node->DeleteEntry(i);
}
return(deleted);
}
void
GiST::AdjustKeys(GiSTnode *node, GiSTnode **parent)
{
GiSTnode *P;
if(node->Path().IsRoot()) return;
// Read in node's parent
if(parent==NULL) {
GiSTpath parent_path=node->Path();
parent_path.MakeParent();
P=ReadNode(parent_path);
parent=&P;
}
else P=*parent;
// Get the old entry pointing to node
GiSTentry *entry=P->SearchPtr(node->Path().Page());
assert(entry!=NULL);
// Get union of node
GiSTentry *actual=node->Union();
actual->SetPtr(node->Path().Page());
if(!entry->IsEqual(*actual)) {
int pos=entry->Position();
P->DeleteEntry(pos);
P->InsertBefore(*actual, pos);
// A split may be necessary.
// XXX: should we do Forced Reinsert here too?
if(P->IsOverFull(*store)) {
Split(parent, *actual);
GiSTpage page=node->Path().Page();
node->Path()=P->Path();
node->Path().MakeChild(page);
}
else {
WriteNode(P);
AdjustKeys(P, NULL);
}
}
if(parent==&P) delete P;
delete actual;
delete entry;
}
void
GiST::Sync()
{
store->Sync();
}
void
GiST::Delete(const GiSTpredicate& pred)
{
GiSTcursor *c=Search(pred);
int condensed;
GiSTentry *e;
do {
if(c==NULL) return;
e=c->Next();
GiSTpath path=c->Path();
delete c;
if(e==NULL) return;
// Read in the node that this belongs to
GiSTnode *node=ReadNode(path);
node->DeleteEntry(e->Position());
WriteNode(node);
condensed=CondenseTree(node);
delete node;
if(condensed) {
ShortenTree();
// because the tree changed, we need to search all over again!
// XXX - this is inefficient! users may want to avoid condensing.
c=Search(pred);
}
} while(e!=NULL);
}
void
GiST::ShortenTree()
{
GiSTpath path;
// Shorten the tree if necessary (This should only be done if root actually changed!)
path.MakeRoot();
GiSTnode *root=ReadNode(path);
if(!root->IsLeaf()&&root->NumEntries()==1) {
path.MakeChild((*root)[0]->Ptr());
GiSTnode *child=ReadNode(path);
store->Deallocate(path.Page());
child->SetSibling(0);
child->Path().MakeRoot();
WriteNode(child);
delete child;
}
delete root;
}
// handle underfull leaf nodes
int
GiST::CondenseTree(GiSTnode *node)
{
GiSTlist<GiSTentry*> Q;
int deleted=0;
// Must be condensing a leaf
assert(node->IsLeaf());
while(!node->Path().IsRoot()) {
GiSTpath parent_path=node->Path();
parent_path.MakeParent();
GiSTnode *P=ReadNode(parent_path);
GiSTentry *En=P->SearchPtr(node->Path().Page());
assert(En!=NULL);
// Handle under-full node
if(node->IsUnderFull(*store)) {
if(!IsOrdered()) {
TruePredicate truePredicate;
GiSTlist<GiSTentry*> list=node->Search(truePredicate);
while(!list.IsEmpty()) {
GiSTentry *e=list.RemoveFront();
Q.Append(e);
}
P->DeleteEntry(En->Position());
WriteNode(P);
deleted=1;
AdjustKeys(P, NULL);
}
else {
// Try to borrow entries, else coalesce with a neighbor
// Have to look at left sibling???
GiSTpage neighbor_page=P->SearchNeighbors(node->Path().Page());
GiSTpath neighbor_path=node->Path();
neighbor_path.MakeSibling(neighbor_page);
if(neighbor_page!=0) {
GiSTnode *neighbor;
// If neighbor is RIGHT sibling...
if(node->Sibling()==neighbor_page) neighbor=ReadNode(neighbor_path);
else {
neighbor=node;
node=ReadNode(neighbor_path);
}
GiSTentry *e=P->SearchPtr(node->Path().Page());
node->Coalesce(*neighbor, *e);
delete e;
// If not overfull, coalesce, kill right node
if(!node->IsOverFull(*store)) {
node->SetSibling(neighbor->Sibling());
WriteNode(node);
// Delete the neighbor from parent
GiSTentry *e=P->SearchPtr(neighbor->Path().Page());
P->DeleteEntry(e->Position());
WriteNode(P);
delete e;
store->Deallocate(neighbor->Path().Page());
deleted=1;
}
// If overfull, split (same as borrowing)
else {
GiSTnode *node2=node->PickSplit();
node2->Path()=neighbor->Path();
node2->SetSibling(neighbor->Sibling());
WriteNode(node);
WriteNode(node2);
AdjustKeys(node2, &P);
delete node2;
deleted=1;
}
delete neighbor;
}
}
}
// Adjust covering predicate
if(!deleted) AdjustKeys(node, &P);
parent_path=node->Path();
parent_path.MakeParent();
delete node;
// Propagate deletes
if(!deleted) break;
node=P;
}
// Re-insert orphaned entries
while(!Q.IsEmpty()) {
GiSTentry *e=Q.RemoveFront();
InsertHelper(*e, e->Level());
delete e;
}
return(deleted);
}
GiSTnode*
GiST::ChooseSubtree(GiSTpage page, const GiSTentry &entry, int level)
{
GiSTnode *node;
GiSTpath path;
for(;;) {
path.MakeChild(page);
node=ReadNode(path);
if(level==node->Level()||node->IsLeaf()) break;
page=node->SearchMinPenalty(entry);
delete node;
}
return node;
}
void
GiST::Split(GiSTnode **node, const GiSTentry& entry)
{
int went_left=0, new_root=0;
if((*node)->Path().IsRoot()) {
new_root=1;
(*node)->Path().MakeChild(store->Allocate());
}
GiSTnode *node2=(*node)->PickSplit();
node2->Path().MakeSibling(store->Allocate());
GiSTentry *e=(*node)->SearchPtr(entry.Ptr());
if(e!=NULL) {
went_left=1;
delete e;
}
node2->SetSibling((*node)->Sibling());
(*node)->SetSibling(node2->Path().Page());
WriteNode(*node);
WriteNode(node2);
GiSTentry *e1=(*node)->Union();
GiSTentry *e2=node2->Union();
e1->SetPtr((*node)->Path().Page());
e2->SetPtr(node2->Path().Page());
// Create new root if root is being split
if (new_root) {
GiSTnode *root=NewNode(this);
root->SetLevel((*node)->Level()+1);
root->InsertBefore(*e1, 0);
root->InsertBefore(*e2, 1);
root->Path().MakeRoot();
WriteNode(root);
delete root;
}
else {
// Insert entry for N' in parent
GiSTpath parent_path=(*node)->Path();
parent_path.MakeParent();
GiSTnode *parent=ReadNode(parent_path);
// Find the entry for N in parent
GiSTentry *e=parent->SearchPtr((*node)->Path().Page());
assert(e!=NULL);
// Insert the new entry right after it
int pos=e->Position();
parent->DeleteEntry(pos);
parent->InsertBefore(*e1, pos);
parent->InsertBefore(*e2, pos+1);
delete e;
if(!parent->IsOverFull(*store)) WriteNode(parent);
else {
Split(&parent, went_left? *e1: *e2);
GiSTpage page=(*node)->Path().Page();
(*node)->Path()=parent->Path();
(*node)->Path().MakeChild(page);
page=node2->Path().Page();
node2->Path()=(*node)->Path();
node2->Path().MakeSibling(page);
}
delete parent;
}
if(!went_left) {
delete *node;
*node=node2;
}
else delete node2;
delete e1;
delete e2;
}
GiSTnode*
GiST::ReadNode(const GiSTpath& path) const
{
char *buf=new char[store->PageSize()];
GiSTnode *node=NewNode((GiST *)this);
store->Read(path.Page(), buf);
node->Unpack(buf);
#ifdef PRINTING_OBJECTS
if(debug) {
cout << "READ PAGE " << path.Page() << ":\n";
node->Print(cout);
}
#endif
node->Path()=path;
delete []buf;
return node;
}
void
GiST::WriteNode(GiSTnode *node)
{
char *buf=new char[store->PageSize()];
// make Purify happy
memset(buf, 0, store->PageSize());
#ifdef PRINTING_OBJECTS
if(debug) {
cout << "WRITE PAGE " << node->Path().Page() << ":\n";
node->Print(cout);
}
#endif
node->Pack(buf);
store->Write(node->Path().Page(), buf);
delete buf;
}
#ifdef PRINTING_OBJECTS
void
GiST::DumpNode(ostream& os, GiSTpath path) const
{
GiSTnode *node=ReadNode(path);
node->Print(os);
if(!node->IsLeaf()) {
TruePredicate truePredicate;
GiSTlist<GiSTentry*> list=node->Search(truePredicate);
while (!list.IsEmpty()) {
GiSTentry *e=list.RemoveFront();
path.MakeChild(e->Ptr());
DumpNode(os, path);
path.MakeParent();
delete e;
}
}
delete node;
}
void
GiST::Print(ostream& os) const
{
GiSTpath path;
path.MakeRoot();
DumpNode(os, path);
}
#endif
GiSTcursor*
GiST::Search(const GiSTpredicate &query) const
{
return new GiSTcursor(*this, query);
}
GiST::~GiST()
{
Close();
delete store;
}