2002-12-14 22:43:18 +01:00
|
|
|
/*
|
|
|
|
* PROGRAM: Client/Server Common Code
|
|
|
|
* MODULE: tree.h
|
|
|
|
* DESCRIPTION: Generic In-memory B+ Tree
|
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
* You may obtain a copy of the Licence at
|
|
|
|
* http://www.gnu.org/licences/lgpl.html
|
|
|
|
*
|
|
|
|
* As a special exception this file can also be included in modules
|
|
|
|
* with other source code as long as that source code has been
|
|
|
|
* released under an Open Source Initiative certificed licence.
|
|
|
|
* More information about OSI certification can be found at:
|
|
|
|
* http://www.opensource.org
|
|
|
|
*
|
|
|
|
* This module is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public Licence for more details.
|
|
|
|
*
|
|
|
|
* This module was created by members of the firebird development
|
|
|
|
* team. All individual contributions remain the Copyright (C) of
|
|
|
|
* those individuals and all rights are reserved. Contributors to
|
|
|
|
* this file are either listed below or can be obtained from a CVS
|
|
|
|
* history command.
|
2002-12-14 22:43:18 +01:00
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* Created by: Nickolay Samofatov <skidder@bssys.com>
|
2002-12-14 22:43:18 +01:00
|
|
|
*
|
2003-09-08 22:23:46 +02:00
|
|
|
* Contributor(s):
|
|
|
|
*
|
2002-12-14 22:43:18 +01:00
|
|
|
*
|
2004-04-11 09:12:09 +02:00
|
|
|
* $Id: tree.h,v 1.26 2004-04-11 07:12:09 robocop Exp $
|
2002-12-14 22:43:18 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef TREE_H
|
|
|
|
#define TREE_H
|
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
#include <exception>
|
2003-11-04 00:59:24 +01:00
|
|
|
#include "../jrd/gdsassert.h"
|
2002-12-14 22:43:18 +01:00
|
|
|
#include <string.h>
|
2003-02-20 07:36:40 +01:00
|
|
|
#ifdef HAVE_STDLIB_H
|
|
|
|
#include <stdlib.h> /* XPG: prototypes for malloc/free have to be in
|
|
|
|
stdlib.h (EKU) */
|
2003-02-13 23:49:20 +01:00
|
|
|
#endif
|
2002-12-14 22:43:18 +01:00
|
|
|
#include "vector.h"
|
2003-01-03 17:03:30 +01:00
|
|
|
#include <new>
|
2004-03-26 00:12:50 +01:00
|
|
|
|
|
|
|
namespace Firebird {
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
// This macro controls merging of nodes of all B+ trees
|
|
|
|
// Now it merges pages only when resulting page will be 3/4 filled or less
|
|
|
|
// Be careful while changing this expression. N=2 must always cause merge
|
2004-03-26 00:12:50 +01:00
|
|
|
static inline bool NEED_MERGE(int current_count, int page_count) {
|
2004-03-28 11:10:30 +02:00
|
|
|
return current_count * 4 / 3 <= page_count;
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
// Note: small values will cause wasting of memory because overhead for
|
|
|
|
// each page is 28-32 bytes (on 32-bit platforms)
|
|
|
|
// 100 is an optimal value for range 10^5 - 10^7 items and it generates
|
|
|
|
// total tree overhead of ~10%
|
2004-03-26 00:12:50 +01:00
|
|
|
const int LEAF_PAGE_SIZE = 100;
|
|
|
|
const int NODE_PAGE_SIZE = 100;
|
2003-01-07 17:35:10 +01:00
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
// This is maximum level of tree nesting. 10^9 elements for binary tree case
|
|
|
|
// should be more than enough. No checks are performed in code against overflow of this value
|
|
|
|
const int MAX_TREE_LEVEL = 30;
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
class MallocAllocator {
|
|
|
|
public:
|
2003-01-16 18:47:10 +01:00
|
|
|
void *allocate(size_t size) {
|
2002-12-14 22:43:18 +01:00
|
|
|
return malloc(size);
|
|
|
|
}
|
2003-01-16 18:47:10 +01:00
|
|
|
void deallocate(void *p) {
|
|
|
|
free(p);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
enum LocType { locEqual, locLess, locGreat, locGreatEqual, locLessEqual };
|
|
|
|
|
2003-08-06 18:30:49 +02:00
|
|
|
// Fast and simple B+ tree of simple types.
|
|
|
|
// Tree is always accessed via accessor classes. There is default accessor
|
|
|
|
// built into the class to simplify programming in single-threaded
|
|
|
|
// non-reenterant access model.
|
2002-12-14 22:43:18 +01:00
|
|
|
//
|
|
|
|
// Notes:
|
|
|
|
//
|
|
|
|
// 1) Items in the tree MUST be unique (this is performance optimization),
|
|
|
|
// you can always convert set of non-unique items to a set of unique items with count
|
|
|
|
// like this:
|
|
|
|
// struct TreeItem {
|
|
|
|
// Value value;
|
|
|
|
// int count;
|
|
|
|
// static const Key& generate(void *sender, const TreeItem& item) {
|
|
|
|
// return KeyOfValue::generate(sender, value);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// Small and simple (just a few lines) template derived from BePlusTree can be created
|
|
|
|
// for this when real need arises. It will still be much faster than allowing duplicates
|
|
|
|
// in BePlusTree itself
|
|
|
|
//
|
|
|
|
// 2) We could store ultimate item count for each node and make tree accessable like
|
|
|
|
// an indexed dynamic array without increase of algorithm calculation costs (this is one
|
|
|
|
// more classical B+ tree feature). This is also not done to improve tree performance a little
|
|
|
|
//
|
|
|
|
template <typename Value, typename Key = Value, typename Allocator = MallocAllocator,
|
|
|
|
typename KeyOfValue = DefaultKeyValue<Value>,
|
|
|
|
typename Cmp = DefaultComparator<Key>,
|
|
|
|
int LeafCount = LEAF_PAGE_SIZE,
|
|
|
|
int NodeCount = NODE_PAGE_SIZE >
|
|
|
|
class BePlusTree {
|
|
|
|
public:
|
2004-03-26 00:12:50 +01:00
|
|
|
BePlusTree(Allocator *_pool) : pool(_pool), level(0), root(NULL), defaultAccessor(this) { };
|
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
~BePlusTree() {
|
2004-03-26 00:12:50 +01:00
|
|
|
// We delete tree which was not fully created
|
|
|
|
if (!root) return;
|
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
// Find first items page
|
|
|
|
void *temp = root;
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int i = level; i > 0; i--)
|
2002-12-14 22:43:18 +01:00
|
|
|
temp = (*(NodeList *)temp)[0];
|
|
|
|
ItemList *items = (ItemList *)temp;
|
|
|
|
|
|
|
|
// Delete all items pages
|
|
|
|
NodeList *lists = items->parent;
|
|
|
|
while ( items ) {
|
|
|
|
ItemList *t = items->next;
|
|
|
|
items->~ItemList();
|
2003-01-16 18:47:10 +01:00
|
|
|
pool->deallocate(items);
|
2002-12-14 22:43:18 +01:00
|
|
|
items = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete all upper layers of tree
|
|
|
|
while (lists) {
|
|
|
|
NodeList *list = lists;
|
|
|
|
lists = lists->parent;
|
|
|
|
while ( list ) {
|
|
|
|
NodeList *t = list->next;
|
|
|
|
list->~NodeList();
|
2003-01-16 18:47:10 +01:00
|
|
|
pool->deallocate(list);
|
2002-12-14 22:43:18 +01:00
|
|
|
list = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
bool add(const Value& item);
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
/* Remove item as quickly as possible. Current position is undefined after this call */
|
2003-08-08 01:20:25 +02:00
|
|
|
void fastRemove() { defaultAccessor.fastRemove(); }
|
2002-12-14 22:43:18 +01:00
|
|
|
|
2003-08-06 18:30:49 +02:00
|
|
|
bool locate(Key& key) { return defaultAccessor.locate(locEqual, key); }
|
|
|
|
|
|
|
|
bool locate(LocType lt, Key& key) { return defaultAccessor.locate(lt, key); }
|
|
|
|
|
|
|
|
bool getFirst() { return defaultAccessor.getFirst(); }
|
|
|
|
|
|
|
|
bool getLast() { return defaultAccessor.getLast(); }
|
|
|
|
|
|
|
|
bool getNext() { return defaultAccessor.getNext(); }
|
|
|
|
|
|
|
|
bool getPrev() { return defaultAccessor.getPrev(); }
|
|
|
|
|
|
|
|
Value& current() const { return defaultAccessor.current(); }
|
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
private:
|
|
|
|
BePlusTree(Allocator *_pool, void *rootPage) : pool(_pool), level(0),
|
2003-08-06 18:30:49 +02:00
|
|
|
root(new(rootPage) ItemList()), defaultAccessor(this) {}
|
2002-12-14 22:43:18 +01:00
|
|
|
|
2003-07-04 14:19:55 +02:00
|
|
|
class NodeList;
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
class ItemList : public SortedVector<Value,LeafCount,Key,KeyOfValue,Cmp> {
|
|
|
|
public:
|
|
|
|
NodeList *parent;
|
|
|
|
ItemList *next, *prev;
|
|
|
|
// Adds newly created item to doubly-linked list
|
|
|
|
ItemList(ItemList *items) : parent(NULL) {
|
|
|
|
if ( (next = items->next) )
|
|
|
|
next->prev = this;
|
|
|
|
prev = items;
|
|
|
|
items->next = this;
|
|
|
|
}
|
|
|
|
// Create first item in the linked list
|
2003-06-20 19:55:31 +02:00
|
|
|
ItemList() : parent(NULL), next(NULL), prev(NULL) {};
|
2003-07-04 14:19:55 +02:00
|
|
|
|
|
|
|
friend class BePlusTree;
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
friend class BePlusTree::NodeList;
|
|
|
|
#endif
|
2002-12-14 22:43:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class NodeList : public SortedVector<void*,NodeCount,Key,NodeList,Cmp> {
|
|
|
|
public:
|
|
|
|
// Adds newly created item to the doubly-linked list
|
|
|
|
NodeList(NodeList *items) : parent(NULL) {
|
|
|
|
if ( (next = items->next) )
|
|
|
|
next->prev = this;
|
|
|
|
prev = items;
|
|
|
|
items->next = this;
|
|
|
|
}
|
|
|
|
// Create first item in the linked list
|
|
|
|
NodeList() : parent(NULL), next(NULL), prev(NULL) {}
|
|
|
|
|
|
|
|
int level;
|
|
|
|
NodeList *parent;
|
|
|
|
NodeList *next, *prev;
|
|
|
|
static const Key& generate(void *sender, void *item) {
|
|
|
|
for (int lev = ((NodeList *)sender)->level; lev > 0; lev--)
|
|
|
|
item = *((NodeList *)item)->begin();
|
2003-06-20 19:55:31 +02:00
|
|
|
return KeyOfValue::generate(item,*((BePlusTree::ItemList *)item)->begin());
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
static void setNodeParentAndLevel(void *node, int level, NodeList *parent) {
|
|
|
|
if (level) {
|
|
|
|
((NodeList *)node)->parent = parent;
|
|
|
|
((NodeList *)node)->level = level-1;
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
|
|
|
else
|
2002-12-14 22:43:18 +01:00
|
|
|
((ItemList *)node)->parent = parent;
|
|
|
|
}
|
|
|
|
static void setNodeParent(void *node, int level, NodeList *parent) {
|
|
|
|
if (level)
|
|
|
|
((NodeList *)node)->parent = parent;
|
|
|
|
else
|
|
|
|
((ItemList *)node)->parent = parent;
|
|
|
|
}
|
|
|
|
};
|
2003-08-06 20:06:22 +02:00
|
|
|
|
|
|
|
public:
|
2003-08-06 18:30:49 +02:00
|
|
|
class Accessor {
|
|
|
|
public:
|
2004-03-26 00:12:50 +01:00
|
|
|
Accessor(BePlusTree* _tree) : tree(_tree), curr(NULL), curPos(0) {}
|
2003-08-06 18:30:49 +02:00
|
|
|
|
|
|
|
/* Remove item as quickly as possible. Current position is undefined after this call */
|
2003-08-06 18:55:17 +02:00
|
|
|
void fastRemove() {
|
|
|
|
if ( !tree->level ) {
|
|
|
|
curr->remove(curPos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( curr->getCount() == 1 ) {
|
|
|
|
// Only one node left in the current page. We cannot remove it directly
|
|
|
|
// because is would invalidate our tree structure
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(curPos == 0);
|
2003-08-06 18:55:17 +02:00
|
|
|
ItemList *temp;
|
|
|
|
if ( (temp = curr->prev) && NEED_MERGE(temp->getCount(), LeafCount) ) {
|
|
|
|
tree->_removePage(0, curr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = curr->next) && NEED_MERGE(temp->getCount(), LeafCount) ) {
|
|
|
|
tree->_removePage(0, curr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = curr->prev) ) {
|
2004-04-11 09:12:09 +02:00
|
|
|
(*curr)[0] = (*temp)[temp->getCount() - 1];
|
|
|
|
temp->shrink(temp->getCount() - 1);
|
2003-08-06 18:55:17 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = curr->next) ) {
|
|
|
|
(*curr)[0] = (*temp)[0];
|
|
|
|
temp->remove(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// It means the tree is broken
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2003-08-06 18:55:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
curr->remove(curPos);
|
|
|
|
ItemList *temp;
|
2004-04-11 09:12:09 +02:00
|
|
|
if ( (temp = curr->prev) && NEED_MERGE(temp->getCount() + curr->getCount(), LeafCount) ) {
|
2003-08-06 18:55:17 +02:00
|
|
|
// After join upper levels of the tree remain stable because join doesn't change
|
|
|
|
// key of the page. The same applies to lower case too.
|
|
|
|
temp->join(*curr);
|
|
|
|
tree->_removePage(0, curr);
|
|
|
|
}
|
|
|
|
else
|
2004-04-11 09:12:09 +02:00
|
|
|
if ( (temp = curr->next) && NEED_MERGE(temp->getCount() + curr->getCount(), LeafCount) ) {
|
2003-08-06 18:55:17 +02:00
|
|
|
curr->join(*temp);
|
|
|
|
tree->_removePage(0, temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-08-06 18:30:49 +02:00
|
|
|
|
|
|
|
bool locate(Key& key) {
|
|
|
|
return locate(locEqual, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inlining is efficient here because LocType will be known in most cases
|
|
|
|
// and compiler will be able to eliminate most of code
|
|
|
|
bool locate(LocType lt, Key& key) {
|
|
|
|
void *list = tree->root;
|
2004-03-26 00:12:50 +01:00
|
|
|
if (!list) return false; // Uninitalized tree
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int lev = tree->level; lev; lev--) {
|
2003-08-06 18:30:49 +02:00
|
|
|
int pos;
|
|
|
|
if (!((NodeList *)list)->find(key, pos))
|
|
|
|
if ( --pos < 0 ) pos = 0;
|
|
|
|
list = (*(NodeList *)list)[pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
curr = (ItemList *)list;
|
|
|
|
bool found = curr->find(key, curPos);
|
|
|
|
switch (lt) {
|
|
|
|
case locEqual: return found;
|
|
|
|
case locGreatEqual:
|
|
|
|
if (curPos == curr->getCount()) {
|
|
|
|
curr = curr->next;
|
|
|
|
curPos = 0;
|
|
|
|
}
|
|
|
|
return found || curr;
|
|
|
|
case locLessEqual:
|
|
|
|
if (found) return true;
|
|
|
|
case locLess:
|
|
|
|
curPos--;
|
|
|
|
if (curPos < 0) {
|
|
|
|
curr = curr->prev;
|
|
|
|
if (!curr) return false;
|
2004-04-11 09:12:09 +02:00
|
|
|
curPos = curr->getCount() - 1;
|
2003-08-06 18:30:49 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case locGreat:
|
|
|
|
if (found) curPos++;
|
|
|
|
if (curPos == curr->getCount()) {
|
|
|
|
curr = curr->next;
|
|
|
|
curPos = 0;
|
|
|
|
}
|
|
|
|
return curr != 0;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool getFirst() {
|
|
|
|
void *items = tree->root;
|
2004-03-26 00:12:50 +01:00
|
|
|
if (!items) return false; // Uninitalized tree
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int i = tree->level; i > 0; i--)
|
2003-08-06 18:30:49 +02:00
|
|
|
items = (*(NodeList *)items)[0];
|
|
|
|
curr = (ItemList *)items;
|
|
|
|
curPos = 0;
|
|
|
|
return ((ItemList *)items)->getCount();
|
|
|
|
}
|
|
|
|
bool getLast() {
|
|
|
|
void *items = tree->root;
|
2004-03-26 00:12:50 +01:00
|
|
|
if (!items) return false; // Uninitalized tree
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int i = tree->level; i > 0; i--)
|
|
|
|
items = (*(NodeList *)items)[((NodeList *)items)->getCount() - 1];
|
2003-08-06 18:30:49 +02:00
|
|
|
curr = (ItemList *)items;
|
2004-04-11 09:12:09 +02:00
|
|
|
curPos = ((ItemList *)items)->getCount() - 1;
|
2003-08-06 18:30:49 +02:00
|
|
|
return curPos >= 0;
|
|
|
|
}
|
|
|
|
bool getNext() {
|
|
|
|
curPos++;
|
|
|
|
if (curPos >= curr->getCount()) {
|
|
|
|
curr = curr->next;
|
|
|
|
curPos = 0;
|
|
|
|
}
|
|
|
|
return curr != 0;
|
|
|
|
}
|
|
|
|
bool getPrev() {
|
|
|
|
curPos--;
|
|
|
|
if (curPos < 0) {
|
|
|
|
curr = curr->prev;
|
|
|
|
if (!curr) return false;
|
|
|
|
curPos = curr->getCount()-1;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Value& current() const { return (*curr)[curPos]; }
|
|
|
|
private:
|
|
|
|
BePlusTree* tree;
|
|
|
|
ItemList *curr;
|
|
|
|
int curPos;
|
|
|
|
};
|
2003-08-06 20:06:22 +02:00
|
|
|
|
|
|
|
private:
|
2002-12-14 22:43:18 +01:00
|
|
|
Allocator *pool;
|
|
|
|
int level;
|
|
|
|
void *root;
|
2003-08-06 18:30:49 +02:00
|
|
|
Accessor defaultAccessor;
|
2002-12-14 22:43:18 +01:00
|
|
|
|
|
|
|
void _removePage(int level, void *node);
|
|
|
|
|
|
|
|
friend class MemoryPool;
|
2003-07-04 14:19:55 +02:00
|
|
|
friend class NodeList;
|
2003-08-08 02:38:25 +02:00
|
|
|
friend class Accessor;
|
2002-12-14 22:43:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/************************ BePlusTree implementation ******************/
|
|
|
|
|
2004-03-26 00:12:50 +01:00
|
|
|
template <typename Value, typename Key, typename Allocator, typename KeyOfValue, typename Cmp, int LeafCount, int NodeCount>
|
|
|
|
bool BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::add(const Value& item)
|
|
|
|
{
|
|
|
|
// Finish initialization of the tree if necessary
|
|
|
|
if (!root) root = new (pool->allocate(sizeof(ItemList))) ItemList();
|
|
|
|
|
|
|
|
// Find leaf page for our item
|
|
|
|
void *vList = this->root;
|
|
|
|
const Key& key = KeyOfValue::generate(NULL, item);
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int lev = this->level; lev > 0 ; lev--) {
|
2004-03-26 00:12:50 +01:00
|
|
|
int pos;
|
|
|
|
if (!((NodeList *)vList)->find(key, pos))
|
|
|
|
if ( --pos < 0 ) pos = 0;
|
|
|
|
vList = (*(NodeList *)vList)[pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
ItemList *leaf = (ItemList *)vList;
|
|
|
|
|
|
|
|
int pos;
|
|
|
|
if (leaf->find(key, pos)) return false;
|
|
|
|
|
|
|
|
if (leaf->getCount() < LeafCount) {
|
|
|
|
leaf->insert(pos, item);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Page is full. Look up nearby pages for space if possible
|
|
|
|
ItemList *temp;
|
|
|
|
// Adding items to the next page is cheaper in most cases that
|
|
|
|
// is why it is checked first
|
|
|
|
if ((temp = leaf->next) && temp->getCount() < LeafCount) {
|
|
|
|
// Found space on the next page
|
|
|
|
if (pos == LeafCount) {
|
|
|
|
// This would be ok if items were unique: temp->insert(0, item);
|
|
|
|
// The same applies to all simular cases below
|
|
|
|
temp->insert(0,item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Maybe splitting array by half would make things faster ?
|
|
|
|
// It should do it in case of random size items.
|
|
|
|
// It would make things slower in case of sequental items addition.
|
|
|
|
// Let's leave it as is now.
|
2004-04-11 09:12:09 +02:00
|
|
|
temp->insert(0, (*leaf)[LeafCount - 1]);
|
|
|
|
leaf->shrink(LeafCount - 1);
|
2004-03-26 00:12:50 +01:00
|
|
|
leaf->insert(pos, item);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((temp = leaf->prev) && temp->getCount() < LeafCount) {
|
|
|
|
// Found space on the previous page
|
|
|
|
if (pos == 0) {
|
|
|
|
temp->insert(temp->getCount(), item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
temp->insert(temp->getCount(), (*leaf)[0]);
|
|
|
|
leaf->remove(0);
|
2004-04-11 09:12:09 +02:00
|
|
|
leaf->insert(pos - 1, item);
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nearby pages are also full. We need to add one more leaf page to the list
|
|
|
|
// This shouldn't happen very often. Traverse tree up trying to add node
|
|
|
|
|
|
|
|
// No re-enterance allowed !!!
|
|
|
|
// Since we haven't done anything with tree yet, thus we don't need to recover
|
|
|
|
// anything in case of error thrown at this allocation here
|
|
|
|
ItemList *newLeaf = new(this->pool->allocate(sizeof(ItemList))) ItemList(leaf);
|
|
|
|
|
|
|
|
// Start building recovery map.
|
|
|
|
// This array contains index of the element we try to add on page of each level
|
|
|
|
// -1 means that element is on new page
|
|
|
|
// In case of low memory condition we use this data to recover to innocent state
|
|
|
|
int recovery_map[MAX_TREE_LEVEL];
|
|
|
|
|
|
|
|
if (pos == LeafCount) {
|
|
|
|
newLeaf->insert(0,item);
|
|
|
|
recovery_map[0] = -1;
|
|
|
|
}
|
|
|
|
else {
|
2004-04-11 09:12:09 +02:00
|
|
|
newLeaf->insert(0, (*leaf)[LeafCount - 1]);
|
|
|
|
leaf->shrink(leaf->getCount() - 1);
|
2004-03-26 00:12:50 +01:00
|
|
|
leaf->insert(pos, item);
|
|
|
|
recovery_map[0] = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *newNode = newLeaf;
|
|
|
|
NodeList *nodeList = leaf->parent;
|
|
|
|
int curLevel = 0;
|
|
|
|
try {
|
|
|
|
while (nodeList) {
|
|
|
|
// Easy case. We've got some space on the node page
|
|
|
|
if (nodeList->getCount() < NodeCount) {
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, nodeList);
|
|
|
|
nodeList->add(newNode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Page is full. Look up nearby pages for space if possible
|
|
|
|
nodeList->find(NodeList::generate(nodeList, newNode), pos);
|
|
|
|
NodeList *list;
|
|
|
|
|
|
|
|
if ((list = nodeList->next) && list->getCount() < NodeCount) {
|
|
|
|
// Found space on the next page
|
|
|
|
if (pos == NodeCount) {
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, list);
|
|
|
|
list->insert(0, newNode);
|
|
|
|
}
|
|
|
|
else {
|
2004-04-11 09:12:09 +02:00
|
|
|
void *t = (*nodeList)[NodeCount - 1];
|
2004-03-26 00:12:50 +01:00
|
|
|
NodeList::setNodeParent(t, curLevel, list);
|
|
|
|
list->insert(0, t);
|
2004-04-11 09:12:09 +02:00
|
|
|
nodeList->shrink(NodeCount - 1);
|
2004-03-26 00:12:50 +01:00
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, nodeList);
|
|
|
|
nodeList->insert(pos, newNode);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((list = nodeList->prev) && list->getCount() < NodeCount) {
|
|
|
|
// Found space on the previous page
|
|
|
|
if (pos == 0) {
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, list);
|
|
|
|
list->insert(list->getCount(), newNode);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
void *t = (*nodeList)[0];
|
|
|
|
NodeList::setNodeParent(t, curLevel, list);
|
|
|
|
list->insert(list->getCount(), t);
|
|
|
|
nodeList->remove(0);
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, nodeList);
|
2004-04-11 09:12:09 +02:00
|
|
|
nodeList->insert(pos - 1, newNode);
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No space found. Allocate NodeList page and climb up the tree
|
|
|
|
|
|
|
|
// No re-enterance allowed !!!
|
|
|
|
// Exceptions from this point
|
|
|
|
// are cleaned up lower
|
|
|
|
NodeList *newList = new(this->pool->allocate(sizeof(NodeList))) NodeList(nodeList);
|
|
|
|
|
|
|
|
if (pos == NodeCount) {
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, newList);
|
|
|
|
newList->insert(0, newNode);
|
2004-04-11 09:12:09 +02:00
|
|
|
recovery_map[curLevel + 1] = -1;
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
|
|
|
else {
|
2004-04-11 09:12:09 +02:00
|
|
|
void *t = (*nodeList)[NodeCount - 1];
|
2004-03-26 00:12:50 +01:00
|
|
|
NodeList::setNodeParent(t, curLevel, newList);
|
|
|
|
newList->insert(0, t);
|
2004-04-11 09:12:09 +02:00
|
|
|
nodeList->shrink(NodeCount - 1);
|
2004-03-26 00:12:50 +01:00
|
|
|
NodeList::setNodeParentAndLevel(newNode, curLevel, nodeList);
|
|
|
|
nodeList->insert(pos, newNode);
|
2004-04-11 09:12:09 +02:00
|
|
|
recovery_map[curLevel + 1] = pos;
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
|
|
|
newNode = newList;
|
|
|
|
nodeList = nodeList->parent;
|
|
|
|
curLevel++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the worst case. We reached the top of tree but were not able to insert node
|
|
|
|
// Allocate new root page and increase level of our tree
|
|
|
|
nodeList = new(this->pool->allocate(sizeof(NodeList))) NodeList();
|
|
|
|
nodeList->level = this->level;
|
|
|
|
nodeList->insert(0, this->root);
|
|
|
|
NodeList::setNodeParentAndLevel(newNode, this->level, nodeList);
|
|
|
|
NodeList::setNodeParent(this->root, this->level, nodeList);
|
|
|
|
nodeList->add(newNode);
|
|
|
|
this->root = nodeList;
|
|
|
|
this->level++;
|
|
|
|
} catch(const std::exception&) {
|
|
|
|
// Recover tree to innocent state
|
|
|
|
while (curLevel) {
|
|
|
|
NodeList *item = reinterpret_cast<NodeList*>(newNode);
|
|
|
|
void *lower;
|
|
|
|
if (recovery_map[curLevel] < 0) {
|
|
|
|
lower = (*item)[0];
|
|
|
|
} else {
|
|
|
|
lower = (*item->prev)[recovery_map[curLevel]];
|
|
|
|
item->prev->remove(recovery_map[curLevel]);
|
|
|
|
item->prev->insert(item->prev->getCount(), (*item)[0]);
|
2004-04-11 09:12:09 +02:00
|
|
|
NodeList::setNodeParent((*item)[0], curLevel - 1, item->prev);
|
2004-03-26 00:12:50 +01:00
|
|
|
}
|
|
|
|
item->~NodeList();
|
|
|
|
this->pool->deallocate(newNode);
|
|
|
|
newNode = lower;
|
|
|
|
curLevel--;
|
|
|
|
}
|
|
|
|
ItemList *item = reinterpret_cast<ItemList*>(newNode);
|
|
|
|
if (recovery_map[0] >= 0) {
|
|
|
|
item->prev->remove(recovery_map[0]);
|
|
|
|
item->prev->insert(item->prev->getCount(), (*item)[0]);
|
|
|
|
}
|
|
|
|
item->~ItemList();
|
|
|
|
this->pool->deallocate(newNode);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2002-12-14 22:43:18 +01:00
|
|
|
template <typename Value, typename Key, typename Allocator, typename KeyOfValue, typename Cmp, int LeafCount, int NodeCount>
|
|
|
|
void BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::_removePage(int nodeLevel, void *node)
|
|
|
|
{
|
|
|
|
NodeList *list;
|
|
|
|
// Get parent and adjust the links
|
|
|
|
if (nodeLevel) {
|
|
|
|
NodeList *temp = (NodeList *)node;
|
|
|
|
if (temp->prev)
|
|
|
|
temp->prev->next = temp->next;
|
|
|
|
if (temp->next)
|
|
|
|
temp->next->prev = temp->prev;
|
|
|
|
list = temp->parent;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ItemList *temp = (ItemList *)node;
|
|
|
|
if (temp->prev)
|
|
|
|
temp->prev->next = temp->next;
|
|
|
|
if (temp->next)
|
|
|
|
temp->next->prev = temp->prev;
|
|
|
|
list = temp->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( list->getCount() == 1 ) {
|
|
|
|
// Only one node left in the list. We cannot remove it directly
|
|
|
|
// because is would invalidate our tree structure
|
|
|
|
NodeList *temp;
|
|
|
|
if ( (temp = list->prev) && NEED_MERGE(temp->getCount(), NodeCount) ) {
|
2004-04-11 09:12:09 +02:00
|
|
|
_removePage(nodeLevel + 1, list);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = list->next) && NEED_MERGE(temp->getCount(), NodeCount) ) {
|
2004-04-11 09:12:09 +02:00
|
|
|
_removePage(nodeLevel + 1, list);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = list->prev) ) {
|
|
|
|
NodeList::setNodeParent(
|
2004-04-11 09:12:09 +02:00
|
|
|
((*list)[0] = (*temp)[temp->getCount() - 1]), nodeLevel, list);
|
|
|
|
temp->shrink(temp->getCount() - 1);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = list->next) ) {
|
|
|
|
NodeList::setNodeParent(
|
|
|
|
((*list)[0] = (*temp)[0]), nodeLevel, list);
|
|
|
|
temp->remove(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// It means the tree is broken
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(false);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int pos;
|
2004-03-26 00:12:50 +01:00
|
|
|
#ifndef DEV_BUILD
|
2004-04-11 09:12:09 +02:00
|
|
|
list->find(NodeList::generate(list, node), pos);
|
2002-12-16 19:33:54 +01:00
|
|
|
#else
|
2004-04-11 09:12:09 +02:00
|
|
|
bool found = list->find(NodeList::generate(list, node), pos);
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(found);
|
2002-12-16 19:33:54 +01:00
|
|
|
#endif
|
2002-12-14 22:43:18 +01:00
|
|
|
list->remove(pos);
|
|
|
|
|
2004-04-11 09:12:09 +02:00
|
|
|
if (list == root && list->getCount() == 1) {
|
2002-12-14 22:43:18 +01:00
|
|
|
// We reached the top of the tree and were asked to modify root
|
|
|
|
// page so only one node will be left in this case.
|
|
|
|
// Reduce the level of the tree
|
|
|
|
root = (*list)[0];
|
|
|
|
level--;
|
|
|
|
NodeList::setNodeParent(root, level, NULL);
|
|
|
|
list->~NodeList();
|
2003-01-16 18:47:10 +01:00
|
|
|
pool->deallocate(list);
|
2004-01-28 08:50:41 +01:00
|
|
|
}
|
|
|
|
else {
|
2002-12-14 22:43:18 +01:00
|
|
|
NodeList *temp;
|
|
|
|
if ( (temp = list->prev) &&
|
2004-04-11 09:12:09 +02:00
|
|
|
NEED_MERGE(temp->getCount() + list->getCount(), NodeCount) )
|
2002-12-14 22:43:18 +01:00
|
|
|
{
|
|
|
|
// After join upper levels of the tree remain stable because join doesn't change
|
|
|
|
// key of the page. The same applies to lower case too.
|
|
|
|
temp->join(*list);
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int i = 0; i < list->getCount(); i++)
|
2002-12-14 22:43:18 +01:00
|
|
|
NodeList::setNodeParent((*list)[i], nodeLevel, temp);
|
2004-04-11 09:12:09 +02:00
|
|
|
_removePage(nodeLevel + 1, list);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
if ( (temp = list->next) &&
|
2004-04-11 09:12:09 +02:00
|
|
|
NEED_MERGE(temp->getCount() + list->getCount(), NodeCount) )
|
2002-12-14 22:43:18 +01:00
|
|
|
{
|
|
|
|
list->join(*temp);
|
2004-04-11 09:12:09 +02:00
|
|
|
for (int i = 0; i < temp->getCount(); i++)
|
2002-12-14 22:43:18 +01:00
|
|
|
NodeList::setNodeParent((*temp)[i], nodeLevel, list);
|
2004-04-11 09:12:09 +02:00
|
|
|
_removePage(nodeLevel + 1, temp);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nodeLevel)
|
|
|
|
((NodeList *)node)->~NodeList();
|
|
|
|
else
|
|
|
|
((ItemList *)node)->~ItemList();
|
2003-01-16 18:47:10 +01:00
|
|
|
pool->deallocate(node);
|
2002-12-14 22:43:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}; /* namespace Firebird */
|
|
|
|
|
|
|
|
#endif
|