// -*- Mode: C++ -*- // GiST.cpp // // Copyright (c) 1996, Regents of the University of California // $Header: /schmorpforge/Tree-M/GiST/GiST.cpp,v 1.1 2001/05/06 00:45:51 root Exp $ #include #include #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; iIsOverFull(*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 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 GiST::RemoveTop(GiSTnode *node) { GiSTlist 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 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 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 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; }