[BACK]Return to BTree.h CVS log [TXT][DIR] Up to [Development] / fam / include

File: [Development] / fam / include / BTree.h (download)

Revision 1.1, Thu Apr 24 19:08:27 2003 UTC (14 years, 5 months ago) by trev
Branch point for: MAIN

Initial revision

//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
//  
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of version 2.1 of the GNU Lesser General Public License
//  as published by the Free Software Foundation.
//
//  This program is distributed in the hope that it would be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
//  license provided herein, whether implied or otherwise, is limited to
//  this program in accordance with the express provisions of the GNU Lesser
//  General Public License.  Patent licenses, if any, provided herein do not
//  apply to combinations of this program with other product or programs, or
//  any other product whatsoever. This program is distributed without any
//  warranty that the program is delivered free of the rightful claim of any
//  third person by way of infringement or the like.  See the GNU Lesser
//  General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write the Free Software Foundation, Inc., 59
//  Temple Place - Suite 330, Boston MA 02111-1307, USA.

#ifndef BTree_included
#define BTree_included

#include "Boolean.h"

//  This is an in-core B-Tree implementation.
//
//  The public interface is fairly sparse: find, insert and remove
//  are the provided functions.  insert() and remove() return true
//  on success, false on failure.  find() returns 0 on failure.
//
//  size() returns the number of key/value pairs in the tree.
//
//  sizeofnode() returns the size of a BTree node, in bytes.
//
//  BTree is instantiated by
//
//      libfam/Client.h:
//          BTree<int, void *> *userData;
//          BTree<int, bool>   *endExist;
//      fam/Listener.c++:
//          BTree<int, NegotiatingClient *> negotiating_clients;
//      fam/RequestMap.h:
//          typedef BTree<Request, ClientInterest *> RequestMap;
//      fam/Set.h:
//          template <class T> class Set : public BTree<T, bool>
//          typedef BTree<T, bool> inherited;
//
//  Set (derived from BTree) is instantiated by
//
//      fam/TCP_Client.h:
//          Set<Interest *> to_be_scanned;
//      fam/Pollster.h:
//          static Set<Interest *> polled_interests;
//          static Set<ServerHost *> polled_hosts;
//      fam/FileSystem.h:
//          typedef Set<ClientInterest *> Interests;
//
template <class Key, class Value> class BTree {

public:

    BTree();
    virtual ~BTree();

    Value find(const Key& k) const;
    bool insert(const Key& k, const Value& v);
    bool remove(const Key& k);

    Key first() const;
    Key next(const Key& k) const;

    unsigned size() const		{ return npairs; }

    static unsigned sizeofnode()	{ return sizeof (Node); }

private:

    enum { fanout = 32 };
    enum Status { OK, NO, OVER, UNDER };

    struct Node;
    struct Closure {

	Closure(Status s)		    : status(s), key(0),
                                              value(0), link(0) { }
	Closure(Key k, Value v, Node *l)    : status(OVER), key(k), value(v),
					      link(l) { }
	Closure(Status s, const Closure& i) : status(s), key(i.key),
					      value(i.value), link(i.link) { }
	Closure(Status s, const Key& k)	    : status(s), key(k),
                                              value(0), link(0) { }
	operator Status ()		    { return status; }

	Status status;
	Key    key;
	Value  value;
	Node  *link;

    };

    struct Node {

	Node(Node *, const Closure&);
	Node(Node *, unsigned index);
	~Node();

	unsigned find(const Key&) const;
	Closure next(const Key&) const;
	bool insert(unsigned, const Closure&);
	Closure remove(unsigned);
	void join(const Closure&, Node *);

	unsigned n;
	Key   key  [fanout];
	Node *link [fanout + 1];
	Value value[fanout];

    private:

	Node(const Node&);		// Do not copy
	Node& operator = (const Node&);	// or assign.

    };

    // Instance Variables

    Node *root;
    unsigned npairs;

    Closure insert(Node *, const Key&, const Value&);
    Status remove(Node *, const Key&);
    Status underflow(Node *, unsigned);
    Closure remove_rightmost(Node *);

    BTree(const BTree&);		// Do not copy
    BTree& operator = (const BTree&);	//  or assign.

};

//  Construct a Node from a Node and a Closure.  New node
//  has a single key-value pair, its left child
//  is the other node, and its right child is the link
//  field of the closure.

template <class K, class V>
BTree<K, V>::Node::Node(Node *p, const Closure& closure)
{
    n = 1;
    link[0] = p;
    key[0] = closure.key;
    value[0] = closure.value;
    link[1] = closure.link;
}

//  Construct a Node from a Node and an index.  Steals
//  the elements index..n of other Node for itself.
//  When this constructor is done, both this->link[0] and
//  that->link[that->n] point to the same place.  The caller
//  must resolve that.

template <class K, class V>
BTree<K, V>::Node::Node(Node *that, unsigned index)
{
    n = that->n - index;
    for (int i = 0; i < n; i++)
    {   key[i] = that->key[index + i];
	value[i] = that->value[index + i];
	link[i] = that->link[index + i];
    }
    link[n] = that->link[index + n];
    that->n = index;
}

//  Node destructor.  Recursively deletes subnodes.

template <class K, class V>
BTree<K, V>::Node::~Node()
{
    for (int i = 0; i <= n; i++)
	delete link[i];
}

//  Node::find() uses binary search to find the nearest key.
//  return index of key that matches or of next key to left.
//
//  E.g., if find() returns 3 then either key[3] matches or
//  key passed in is between key[3] and key[4].

template <class Key, class Value>
unsigned
BTree<Key, Value>::Node::find(const Key& k) const
{
    unsigned l = 0, r = n;
    while (l < r)
    {   int m = (l + r) / 2;
	if (k == key[m])
	    return m;
	else if (k < key[m])
	    r = m;
	else // (k > key[m])
	    l = m + 1;
    }
    assert(l == n || k < key[l]);
    return l;
}

//  Node::insert() inserts key/value into j'th position in node.
//  Inserts closure's link to the right.  Returns true if successful,
//  false if node is already full.

template <class K, class V>
bool
BTree<K, V>::Node::insert(unsigned j, const Closure& closure)
{
    if (n < fanout)
    {   for (int i = n; i > j; --i)
	{   key[i] = key[i - 1];
	    value[i] = value[i - 1];
	    link[i + 1] = link[i];
	}
	key[j] = closure.key; value[j] = closure.value;
	link[j + 1] = closure.link;
	n++;
	assert(j == 0     || key[j - 1] < key[j]);
	assert(j == n - 1 || key[j] < key[j + 1]);
	return true;
    }
    else
	return false;
}

//  Node::remove extracts the j'th key/value and the link
//  to the right and returns them.

template <class Key, class Value>
BTree<Key, Value>::Closure
BTree<Key, Value>::Node::remove(unsigned j)
{
    Key k = key[j];
    Value v = value[j];
    Node *l = link[j + 1];
    for (unsigned i = j + 1; i < n; i++)
    {   key[i - 1] = key[i];
	value[i - 1] = value[i];
	link[i] = link[i + 1];
    }
    --n;
    return Closure(k, v, l);
}

// Node::join() copies key/value from closure and moves all
// key/value/links from other Node into this node.
// Other Node is modified to contain no keys, no values, no links.

template <class K, class V>
void
BTree<K, V>::Node::join(const Closure& it, Node *that)
{
    assert(that);
    assert(n + that->n <= fanout - 1);
    key[n] = it.key;
    value[n] = it.value;
    for (int i = 0, j = n + 1; i < that->n; i++, j++)
    {   key[j] = that->key[i];
	value[j] = that->value[i];
	link[j] = that->link[i];
    }
    n += that->n + 1;
    link[n] = that->link[that->n];
    that->n = 0;
    that->link[0] = NULL;
}

///////////////////////////////////////////////////////////////////////////////

//  BTB constructor -- check that fanout is even.

template <class K, class V>
BTree<K, V>::BTree()
    : root(NULL), npairs(0)
{
    assert(!(fanout % 2));
}

//  BTB destructor -- delete all Nodes.

template <class K, class V>
BTree<K, V>::~BTree()
{
    delete root;
}

//  BTB::find() -- search BTB for Key, return matching value.

template <class Key, class Value>
Value
BTree<Key, Value>::find(const Key& key) const
{
    for (Node *p = root; p; )
    {   int i = p->find(key);
	if (i < p->n && key == p->key[i])
	    return p->value[i];
	else
	    p = p->link[i];
    }
    return Value(0);
}

template <class Key, class Value>
Key
BTree<Key, Value>::first() const
{
    if (!root)
	return 0;
    assert(root->n);

    Node *p, *q;
    for (p = root; q = p->link[0]; p = q)
	continue;
    return p->key[0];
}

template <class Key, class Value>
Key
BTree<Key, Value>::next(const Key& pred) const
{
    if (!root)
	return 0;

    assert(root->n);

    Closure it = root->next(pred);
    switch (Status(it))
    {
    case OK:
	return it.key;

    case OVER:
	return 0;			// All done.

    default:
	assert(0);
	return 0;
    }
}

template <class Key, class Value>
BTree<Key, Value>::Closure
BTree<Key, Value>::Node::next(const Key& pred) const
{
    if (!this)
	return OVER;

    unsigned i = find(pred);
    assert(i <= n);
    if (i < n && key[i] == pred)
	i++;
    Closure it = link[i]->next(pred);
    if (Status(it) != OVER)
	return it;
    if (i == n)
	return OVER;
    else
	return Closure(OK, key[i]);
}

//  BTB::insert() -- insert a new key/value pair
//  into the tree.  Fails and returns false if key is already
//  in tree.
//
//  This code is the only place that makes the tree taller.


template <class Key, class Value>
bool
BTree<Key, Value>::insert(const Key& key, const Value& value)
{
    Closure it = insert(root, key, value);
    switch (Status(it))
    {
    case OK:
	npairs++;
	return true;

    case NO:
	return false;

    case OVER:
	root = new Node(root, it);
	npairs++;
	return true;

    default:
	assert(0);
	return false;
    }
}

//  BTB::insert(Node *, ...) -- helper function for
//  BTB::insert(Key&, ...)  Recurses to the appropriate leaf, splits
//  nodes as necessary on the way back.

template <class Key, class Value>
BTree<Key, Value>::Closure
BTree<Key, Value>::insert(Node *p, const Key& key, const Value& value)
{
    if (!p) return Closure(key, value, NULL);
    //  If you're running Purify on a client linking with libfam, and it says
    //  that line is causing a 3-byte UMR for BTree<int, bool>::insert() in
    //  FAMNextEvent() ("Reading 8 bytes from 0x... on the stack (3 bytes at
    //  0x... uninit)"), and sizeof(bool) == 1, it's actually OK.  Those 3
    //  bytes are the padding between the bool Value and the Node *link in
    //  BTree<int, bool>::Closure.

    int i = p->find(key);
    if (i < p->n && key == p->key[i])
	return NO;			// key already exists.

    Closure it = insert(p->link[i], key, value);

    if (Status(it) == OVER)
    {   if (p->insert(i, it))
	    return Closure(OK);
	else
	{   if (i > fanout / 2)
	    {   Node *np = new Node(p, fanout / 2 + 1);
		np->insert(i - fanout / 2 - 1, it);
		assert(p->n > fanout / 2);
		it = p->remove(fanout / 2);
		return Closure(it.key, it.value, np);
	    }
	    else if (i < fanout / 2)
	    {
		Node *np = new Node(p, fanout / 2);
		p->insert(i, it);
		assert(p->n > fanout / 2);
		it = p->remove(fanout / 2);
		return Closure(it.key, it.value, np);
	    }
	    else // (i == fanout / 2)
	    {
		Node *np = new Node(p, fanout / 2);
		np->link[0] = it.link;
		return Closure(it.key, it.value, np);
	    }
	}
    }
    
    return it;
}

//  BTB::remove() -- remove a key/value from the tree.
//  This function is the only one that makes the tree shorter.

template <class Key, class Value>
bool
BTree<Key, Value>::remove(const Key& key)
{
    Status s = remove(root, key);
    switch (s)
    {
    case OK:
	assert(npairs);
	--npairs;
	assert(!root || root->n);
	return true;

    case NO:
	assert(!root || root->n);
	return false;

    case UNDER:
	if (root->n == 0)
	{   Node *nr = root->link[0];
	    root->link[0] = NULL;	// don't delete subtree
	    delete root;
	    root = nr;
	}
	assert(npairs);
	--npairs;
	assert(!root || root->n);
	return true;

    default:
	assert(0);
	return false;
    }
}

//  BTB::underflow -- helper function to BTB::remove() When a node
//  has too few elements (less than fanout / 2), this function is
//  invoked.  It tries to join i'th child of node p with one of its
//  adjacent siblings, then tries to move entries from an adjacent
//  sibling to child node.
//
//  Returns UNDER if node p is too small afterward, OK otherwise.

template <class Key, class Value>
BTree<Key, Value>::Status
BTree<Key, Value>::underflow(Node *p, unsigned i)
{
    assert(p);
    assert(i <= p->n);
    Node *cp = p->link[i];
    assert(cp);
    
    Node *rp = i < p->n ? p->link[i + 1] : NULL;
    Node *lp = i > 0    ? p->link[i - 1] : NULL;
    assert(!rp || rp->n >= fanout / 2);
    assert(!lp || lp->n >= fanout / 2);

    if (rp && rp->n == fanout / 2)
    {
	// join cp w/ rp;
	cp->join(p->remove(i), rp);
	delete rp;
    }
    else if (lp && lp->n == fanout / 2)
    {
	// join lp w/ cp;
	lp->join(p->remove(i - 1), cp);
	delete cp;
    }
    else if (lp)
    {
	// shuffle from lp to cp;
	Closure li = lp->remove(lp->n - 1);
	cp->insert(0, Closure(p->key[i - 1], p->value[i - 1], cp->link[0]));
	cp->link[0] = li.link;
	p->key[i - 1] = li.key;
	p->value[i - 1] = li.value;
	return OK;
    }
    else if (rp)
    {
	// shuffle from rp to cp;
	Closure ri = rp->remove(0);
	cp->insert(cp->n, Closure(p->key[i], p->value[i], rp->link[0]));
	p->key[i] = ri.key;
	p->value[i] = ri.value;
	rp->link[0] = ri.link;
	return OK;
    }
    
    return p->n >= fanout / 2 ? OK : UNDER;
}

//  BTB::remove_rightmost() -- helper to BTB::remove().
//
//  Removes rightmost leaf key/value from subtree and returns them.
//  If Nodes become too small in the process, invokes Node::underflow()
//  to fix them up.  Return status is either OK or UNDER, indicating
//  root of subtree is too small.


template <class Key, class Value>
BTree<Key, Value>::Closure
BTree<Key, Value>::remove_rightmost(Node *p)
{
    int i = p->n;
    Node *cp = p->link[i];
    if (cp)
    {   Closure it = remove_rightmost(cp);
	if (Status(it) == UNDER)
	    return Closure(underflow(p, i), it);
	else
	    return it;
    }
    else
    {   Closure it = p->remove(p->n - 1);
	if (p->n >= fanout / 2)
	    return Closure(OK, it);
	else
	    return Closure(UNDER, it);
    }	    
}

//  BTB::remove(Node *, ...) -- helper to BTB::remove(const Key&).
//
//  Recurses into tree to find key/value to delete.  When it finds
//  them, it pulls the rightmost leaf key/value off the branch to
//  the left and replaces the removed k/v with them.  Watches for
//  Node underflows from BTB::remove_rightmost() and propagates them
//  back up.

template <class Key, class Value>
BTree<Key, Value>::Status
BTree<Key, Value>::remove(Node *p, const Key& key)
{
    if (!p)
	return NO;			// key not found
    int i = p->find(key);
    if (i < p->n && key == p->key[i])
    {
	// Delete this one.

	Closure it = p->remove(i);
	if (p->link[i])
	{   Closure rm = remove_rightmost(p->link[i]);
	    assert(!rm.link);
	    p->insert(i, Closure(rm.key, rm.value, it.link));
	    if (Status(rm) == UNDER)
		return underflow(p, i);
	}
	return p->n >= fanout / 2 ? OK : UNDER;
    }
    else
    {   Node *cp = p->link[i];
	Status s = remove(cp, key);
	if (s == UNDER)
	    return underflow(p, i);
	else
	    return s;
    }
}



//#ifndef NDEBUG
//
//template <class K, class V>
//BTree<K, V>::BTree()
//{
//    //assert(sizeof K <= sizeof BTB::Key);
//    //assert(sizeof V <= sizeof BTB::Value);
//}
//
//#endif /* !NDEBUG */


#endif /* !BTree_included */