/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #ifndef SQUID_INCLUDE_SPLAY_H #define SQUID_INCLUDE_SPLAY_H #include "fatal.h" #include // private class of Splay. Do not use directly template class SplayNode { public: typedef V Value; typedef int SPLAYCMP(Value const &a, Value const &b); typedef void SPLAYFREE(Value &); typedef void SPLAYWALKEE(Value const & nodedata, void *state); static void DefaultFree (Value &aValue) {delete aValue;} SplayNode(const Value &); Value data; mutable SplayNode *left; mutable SplayNode *right; void destroy(SPLAYFREE * = DefaultFree); void walk(SPLAYWALKEE *, void *callerState); SplayNode const * start() const; SplayNode const * finish() const; SplayNode * remove(const Value data, SPLAYCMP * compare); SplayNode * insert(Value data, SPLAYCMP * compare); /// look in the splay for data for where compare(data,candidate) == true. /// return NULL if not found, a pointer to the sought data if found. template SplayNode * splay(const FindValue &data, int( * compare)(FindValue const &a, Value const &b)) const; /// recursively visit left nodes, this node, and then right nodes template void visit(Visitor &v) const; }; typedef SplayNode splayNode; template class SplayConstIterator; template class SplayIterator; template class Splay { public: typedef V Value; typedef int SPLAYCMP(Value const &a, Value const &b); typedef void SPLAYFREE(Value &); typedef SplayIterator iterator; typedef const SplayConstIterator const_iterator; Splay():head(nullptr), elements (0) {} template Value const *find (FindValue const &, int( * compare)(FindValue const &a, Value const &b)) const; void insert(Value const &, SPLAYCMP *compare); void remove(Value const &, SPLAYCMP *compare); void destroy(SPLAYFREE * = SplayNode::DefaultFree); SplayNode const * start() const; SplayNode const * finish() const; size_t size() const; bool empty() const { return size() == 0; } const_iterator begin() const; const_iterator end() const; /// recursively visit all nodes, in left-to-right order template void visit(Visitor &v) const; private: mutable SplayNode * head; size_t elements; }; SQUIDCEXTERN int splayLastResult; template SplayNode::SplayNode (Value const &someData) : data(someData), left(nullptr), right (nullptr) {} template void SplayNode::walk(SPLAYWALKEE * walkee, void *state) { if (left) left->walk(walkee, state); walkee(data, state); if (right) right->walk(walkee, state); } template SplayNode const * SplayNode::start() const { if (left) return left->start(); return this; } template SplayNode const * SplayNode::finish() const { if (right) return right->finish(); return this; } template void SplayNode::destroy(SPLAYFREE * free_func) { if (left) left->destroy(free_func); if (right) right->destroy(free_func); free_func(data); delete this; } template SplayNode * SplayNode::remove(Value const dataToRemove, SPLAYCMP * compare) { SplayNode *result = splay(dataToRemove, compare); if (splayLastResult == 0) { /* found it */ SplayNode *newTop; if (result->left == nullptr) { newTop = result->right; } else { newTop = result->left->splay(dataToRemove, compare); /* temporary */ newTop->right = result->right; result->right = nullptr; } delete result; return newTop; } return result; /* It wasn't there */ } template SplayNode * SplayNode::insert(Value dataToInsert, SPLAYCMP * compare) { /* create node to insert */ SplayNode *newNode = new SplayNode(dataToInsert); SplayNode *newTop = splay(dataToInsert, compare); if (splayLastResult < 0) { newNode->left = newTop->left; newNode->right = newTop; newTop->left = nullptr; return newNode; } else if (splayLastResult > 0) { newNode->right = newTop->right; newNode->left = newTop; newTop->right = nullptr; return newNode; } else { /* duplicate entry */ delete newNode; return newTop; } } template template SplayNode * SplayNode::splay(FindValue const &dataToFind, int( * compare)(FindValue const &a, Value const &b)) const { Value temp = Value(); SplayNode N(temp); SplayNode *l; SplayNode *r; SplayNode *y; N.left = N.right = nullptr; l = r = &N; SplayNode *top = const_cast *>(this); for (;;) { splayLastResult = compare(dataToFind, top->data); if (splayLastResult < 0) { if (top->left == nullptr) break; if ((splayLastResult = compare(dataToFind, top->left->data)) < 0) { y = top->left; /* rotate right */ top->left = y->right; y->right = top; top = y; if (top->left == nullptr) break; } r->left = top; /* link right */ r = top; top = top->left; } else if (splayLastResult > 0) { if (top->right == nullptr) break; if ((splayLastResult = compare(dataToFind, top->right->data)) > 0) { y = top->right; /* rotate left */ top->right = y->left; y->left = top; top = y; if (top->right == nullptr) break; } l->right = top; /* link left */ l = top; top = top->right; } else { break; } } l->right = top->left; /* assemble */ r->left = top->right; top->left = N.right; top->right = N.left; return top; } template template void SplayNode::visit(Visitor &visitor) const { if (left) left->visit(visitor); visitor(data); if (right) right->visit(visitor); } template template void Splay::visit(Visitor &visitor) const { if (head) head->visit(visitor); } template template typename Splay::Value const * Splay::find (FindValue const &value, int( * compare)(FindValue const &a, Value const &b)) const { if (head == nullptr) return nullptr; head = head->splay(value, compare); if (splayLastResult != 0) return nullptr; return &head->data; } template void Splay::insert(Value const &value, SPLAYCMP *compare) { if (find(value, compare) != nullptr) // ignore duplicates return; if (head == nullptr) head = new SplayNode(value); else head = head->insert(value, compare); ++elements; } template void Splay::remove(Value const &value, SPLAYCMP *compare) { // also catches the head==NULL case if (find(value, compare) == nullptr) return; head = head->remove(value, compare); --elements; } template SplayNode const * Splay:: start() const { if (head) return head->start(); return nullptr; } template SplayNode const * Splay:: finish() const { if (head) return head->finish(); return nullptr; } template void Splay:: destroy(SPLAYFREE *free_func) { if (head) head->destroy(free_func); head = nullptr; elements = 0; } template size_t Splay::size() const { return elements; } template const SplayConstIterator Splay::begin() const { return const_iterator(head); } template const SplayConstIterator Splay::end() const { return const_iterator(nullptr); } // XXX: This does not seem to iterate the whole thing in some cases. template class SplayConstIterator { public: typedef const V value_type; SplayConstIterator (SplayNode *aNode); bool operator == (SplayConstIterator const &right) const; SplayConstIterator operator ++ (int dummy); SplayConstIterator &operator ++ (); V const & operator * () const; private: void advance(); void addLeftPath(SplayNode *aNode); void init(SplayNode *); std::stack *> toVisit; }; template SplayConstIterator::SplayConstIterator (SplayNode *aNode) { init(aNode); } template bool SplayConstIterator::operator == (SplayConstIterator const &right) const { if (toVisit.empty() && right.toVisit.empty()) return true; if (!toVisit.empty() && !right.toVisit.empty()) return toVisit.top() == right.toVisit.top(); // only one of the two is empty return false; } template SplayConstIterator & SplayConstIterator::operator ++ () { advance(); return *this; } template SplayConstIterator SplayConstIterator::operator ++ (int) { SplayConstIterator result = *this; advance(); return result; } /* advance is simple enough: * if the stack is empty, we're done. * otherwise, pop the last visited node * then, pop the next node to visit * if that has a right child, add it and it's * left-to-end path. * then add the node back. */ template void SplayConstIterator::advance() { if (toVisit.empty()) return; toVisit.pop(); if (toVisit.empty()) return; // not empty SplayNode *currentNode = toVisit.top(); toVisit.pop(); addLeftPath(currentNode->right); toVisit.push(currentNode); } template void SplayConstIterator::addLeftPath(SplayNode *aNode) { if (aNode == nullptr) return; do { toVisit.push(aNode); aNode = aNode->left; } while (aNode != nullptr); } template void SplayConstIterator::init(SplayNode *head) { addLeftPath(head); } template V const & SplayConstIterator::operator * () const { /* can't dereference when past the end */ if (toVisit.size() == 0) fatal ("Attempt to dereference SplayConstIterator past-the-end\n"); return toVisit.top()->data; } #endif /* SQUID_INCLUDE_SPLAY_H */