/* * 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 #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); SplayNode(const Value &); Value data; mutable SplayNode *left; mutable SplayNode *right; mutable SplayNode *visitThreadUp; 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; }; 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; static void DefaultFree(Value &v) { delete v; } Splay():head(nullptr), elements (0) {} template Value const *find (FindValue const &, int( * compare)(FindValue const &a, Value const &b)) const; /// If the given value matches a stored one, returns that matching value. /// Otherwise, stores the given unique value and returns nil. const Value *insert(const Value &, SPLAYCMP *); void remove(Value const &, SPLAYCMP *compare); void destroy(SPLAYFREE * = 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; /// left-to-right visit of all stored Values template void visit(ValueVisitor &) const; private: /// left-to-right walk through all nodes template void visitEach(NodeVisitor &) const; mutable SplayNode * head; size_t elements; }; extern int splayLastResult; template SplayNode::SplayNode(const Value &someData): data(someData), left(nullptr), right(nullptr), visitThreadUp(nullptr) {} template SplayNode const * SplayNode::start() const { auto cur = this; while (cur->left) cur = cur->left; return cur; } template SplayNode const * SplayNode::finish() const { auto cur = this; while (cur->right) cur = cur->right; return cur; } 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 Splay::visitEach(Visitor &visitor) const { // In-order walk through tree using modified Morris Traversal: To avoid a // leftover thread up (and, therefore, a fatal loop in the tree) due to a // visitor exception, we use an extra pointer visitThreadUp instead of // manipulating the right child link and interfering with other methods // that use that link. // This also helps to distinguish between up and down movements, eliminating // the need to descent into left subtree a second time after traversing the // thread to find the loop and remove the temporary thread. if (!head) return; auto cur = head; auto movedUp = false; cur->visitThreadUp = nullptr; while (cur) { if (!cur->left || movedUp) { // no (unvisited) left subtree, so handle current node ... const auto old = cur; if (cur->right) { // ... and descent into right subtree cur = cur->right; movedUp = false; } else if (cur->visitThreadUp) { // ... or back up the thread cur = cur->visitThreadUp; movedUp = true; } else { // end of traversal cur = nullptr; } visitor(old); // old may be destroyed here } else { // first descent into left subtree // find right-most child in left tree auto rmc = cur->left; while (rmc->right) { rmc->visitThreadUp = nullptr; // cleanup old threads on the way rmc = rmc->right; } // create thread up back to cur rmc->visitThreadUp = cur; // finally descent into left subtree cur = cur->left; movedUp = false; } } } template template void Splay::visit(Visitor &visitor) const { const auto internalVisitor = [&visitor](const SplayNode *node) { visitor(node->data); }; visitEach(internalVisitor); } 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 typename Splay::Value const * Splay::insert(Value const &value, SPLAYCMP *compare) { if (const auto similarValue = find(value, compare)) return similarValue; // do not insert duplicates if (head == nullptr) head = new SplayNode(value); else head = head->insert(value, compare); ++elements; return nullptr; // no duplicates found } 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) { const auto destroyer = [free_func](SplayNode *node) { free_func(node->data); delete node; }; visitEach(destroyer); 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 */