COMMON: Add std::map and std::multimap replacements

moved from hpl1/std
This commit is contained in:
grisenti 2023-05-30 19:14:04 +02:00
parent 996ae31916
commit 2c2cc1720b
3 changed files with 857 additions and 0 deletions

167
common/multimap.h Normal file
View File

@ -0,0 +1,167 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COMMON_MULTIMAP_H
#define COMMON_MULTIMAP_H
#include "common/util.h"
#include "common/rb_tree.h"
namespace Common {
/**
* multimap implementation meant as a drop-in replacement for the C++ standard library's std::multimap
*/
template<class Key, class Val, class CompFunc = Common::Less<Key> >
class MultiMap {
using TreeT = RBTree<Pair<Key, Val>, Key, PairFirst<Key, Val>, CompFunc>;
public:
using value_type = Pair<Key, Val>;
using iterator = typename TreeT::BasicIterator;
using const_iterator = typename TreeT::ConstIterator;
/**
* Clears the map
*/
void clear() {
_items.clear();
}
/**
* Gets the iterator start
*/
iterator begin() {
return _items.begin();
}
/**
* Get the iterator end
*/
iterator end() {
return _items.end();
}
/**
* Get the const iterator start
*/
const_iterator begin() const {
return _items.begin();
}
/**
* Get the const iterator end
*/
const_iterator end() const {
return _items.end();
}
/**
* Returns an iterator for the first element of the map that is
* not less than the given key
*/
const_iterator lower_bound(const Key &key) const {
return _items.lowerBound(key);
}
iterator lower_bound(const Key &key) {
return _items.lowerBound(key);
}
iterator upper_bound(const Key &key) {
return _items.upperBound(key);
}
/**
* Find the entry with the given key
*/
iterator find(const Key &theKey) {
iterator it = _items.lowerBound(theKey);
if (it != this->end() && compareEqual(it->first, theKey))
return it;
return this->end();
}
const_iterator find(const Key &theKey) const {
const_iterator it = _items.lowerBound(theKey);
if (it != this->end() && compareEqual(it->first, theKey))
return it;
return this->end();
}
/**
* Erases an entry in the map
*/
iterator erase(iterator it) {
return _items.erase(it);
}
iterator erase(iterator first, iterator last) {
return _items.erase(first, last);
}
iterator erase(const Key &theKey) {
const iterator first = find(theKey);
iterator last = first;
while (last != this->end() && compareEqual(last->first, theKey))
++last;
return _items.erase(first, last);
}
iterator insert(const value_type &val) {
return _items.insert(val);
}
/**
* Returns the size of the map
*/
size_t size() const {
return _items.size();
}
bool empty() const {
return _items.isEmpty();
}
/**
* Returns the number of elements with a matching key
*/
size_t count(const Key &theKey) {
int count_ = 0;
for (iterator it = this->begin(); it != this->end(); ++it) {
if (compareEqual(it->first, theKey))
++count_;
}
return count_;
}
private:
bool compareEqual(const Key &a, const Key &b) {
return !_comp(a, b) && !_comp(b, a);
}
TreeT _items;
CompFunc _comp;
};
} // End of namespace Common
#endif

511
common/rb_tree.h Normal file
View File

@ -0,0 +1,511 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COMMON_RB_TREE_H
#define COMMON_RB_TREE_H
#include "common/func.h"
#include "common/util.h"
#include "common/scummsys.h"
namespace Common {
template<typename Key, typename Val>
struct PairFirst {
const Key &operator()(const Pair<Key, Val> &p) {
return p.first;
}
};
template<typename T>
struct Identity {
const T &operator()(const T &t) {
return t;
}
};
/**
* Red-black tree implementation with insertion and deletion algorithms based on the ones
* found in Introduction to Algorithms by Cormen, Leiserson, Rivest and Stein.
* Used in the implementation of Common::StableMap and Common::MultiMap
*/
template<class ValueType, class Key, class KeyProj, class KeyComp = Common::Less<Key> >
class RBTree {
public:
enum class Color {
kRed,
kBlack,
};
struct Node {
Node *parent;
Node *left;
Node *right;
Color color;
ValueType value;
};
template<typename Ref, typename Ptr>
class Iterator {
public:
friend RBTree;
Iterator() = default;
Iterator(const Iterator &) = default;
Iterator &operator=(const Iterator &) = default;
Ref operator*() const { return _current->value; }
Ptr operator->() const { return &_current->value; }
Iterator &operator++() {
if (_current->right) {
_current = leftmost(_current->right);
} else {
auto p = _current->parent;
while (p && p->right == _current) {
_current = p;
p = p->parent;
}
_current = p;
}
return *this;
}
Iterator operator++(int) {
auto temp = *this;
++(*this);
return temp;
}
bool operator==(const Iterator &rhs) const {
return _current == rhs._current;
}
bool operator!=(const Iterator &rhs) const {
return _current != rhs._current;
}
private:
explicit Iterator(Node *n) : _current(n) {}
Node *_current = nullptr;
};
using BasicIterator = Iterator<ValueType &, ValueType *>;
using ConstIterator = Iterator<const ValueType &, const ValueType *>;
RBTree(KeyComp comp = {}) : _comp(Common::move(comp)) {
}
RBTree(const RBTree &other) : _comp(other._comp) {
for (const auto &val : other)
insert(val);
}
RBTree(RBTree &&other) : _root(other._root), _leftmost(other._leftmost), _size(other._size), _comp(Common::move(other._comp)) {
}
RBTree &operator=(const RBTree &rhs) {
*this = RBTree(rhs);
return *this;
}
RBTree &operator=(RBTree &&rhs) {
clear();
_root = rhs._root;
_leftmost = rhs._leftmost;
_size = rhs._size;
_comp = Common::move(rhs._comp);
rhs._root = nullptr;
rhs._leftmost = nullptr;
rhs._size = 0;
return *this;
}
void clear() {
erase(begin(), end());
_size = 0;
_root = nullptr;
_leftmost = nullptr;
}
BasicIterator begin() { return BasicIterator{_leftmost}; }
ConstIterator begin() const { return ConstIterator{_leftmost}; }
BasicIterator end() { return BasicIterator{nullptr}; }
ConstIterator end() const { return ConstIterator{nullptr}; }
BasicIterator lowerBound(const Key &key) {
Node *it = _root;
Node *res = nullptr;
while (it) {
if (!_comp(KeyProj()(it->value), key)) {
res = it;
it = it->left;
} else {
it = it->right;
}
}
return BasicIterator{res};
}
ConstIterator lowerBound(const Key &key) const {
Node *it = _root;
Node *res = nullptr;
while (it) {
if (!_comp(KeyProj()(it->value), key)) {
res = it;
it = it->left;
} else {
it = it->right;
}
}
return ConstIterator{res};
}
BasicIterator upperBound(const Key &key) {
Node *it = _root;
Node *res = nullptr;
while (it) {
if (!_comp(key, KeyProj()(it->value))) {
it = it->right;
} else {
res = it;
it = it->left;
}
}
return BasicIterator{res};
}
ConstIterator upperBound(const Key &key) const {
Node *it = _root;
Node *res = nullptr;
while (it) {
if (!_comp(key, KeyProj()(it->value))) {
it = it->right;
} else {
res = it;
it = it->left;
}
}
return ConstIterator{res};
}
BasicIterator erase(BasicIterator it) {
Node *const z = it._current;
Node *y = z;
assert(y);
const auto ret = it++;
Color y_prev_color = y->color;
Node *x;
Node *xp = nullptr;
if (!y->left) {
x = y->right;
xp = y->parent; // since x is put in y's place by the next call
transplant(y, y->right);
} else if (!y->right) {
x = y->left;
xp = y->parent;
transplant(y, y->left);
} else {
y = leftmost(z->right);
y_prev_color = y->color;
x = y->right;
if (y != z->right) {
xp = y->parent;
transplant(y, y->right);
y->right = z->right;
y->right->parent = y;
} else {
xp = y;
}
transplant(z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
if (y_prev_color == Color::kBlack)
fixDelete(x, xp);
delete z;
--_size;
return ret;
}
BasicIterator erase(BasicIterator first, BasicIterator last) {
while (first != last)
erase(first++);
return last;
}
BasicIterator insert(const ValueType &val) {
return internalInsert(&_root, val);
}
// requires that either start is lowerbound of val or start is end()
// otherwise the resulting tree could be unsorted
BasicIterator insert(BasicIterator start, const ValueType &val) {
if (start == end())
return insert(val);
return internalInsert(&start._current, val);
}
size_t size() const { return _size; }
bool isEmpty() const { return _size == 0; }
~RBTree() { clear(); }
private:
KeyComp _comp;
Node *_root = nullptr;
Node *_leftmost = nullptr;
size_t _size = 0;
Node *merge(Node *left, Node *right) {
if (!right)
return left;
else if (!left)
return right;
else {
auto lm = leftmost(right);
lm->left = left;
left->parent = lm;
return right;
}
}
BasicIterator internalInsert(Node **starting_point, const ValueType &val) {
auto it = starting_point;
Node *parent = nullptr;
while (*it) {
parent = *it;
if (_comp(KeyProj()((*it)->value), KeyProj()(val))) {
it = &(*it)->right;
} else {
it = &(*it)->left;
}
}
*it = new Node{
parent,
nullptr,
nullptr,
Color::kRed,
val,
};
if (!_leftmost || (parent == _leftmost && _leftmost->left == *it))
_leftmost = *it;
++_size;
auto ret = BasicIterator{*it};
fixInsert(*it);
return ret;
}
static Node *leftmost(Node *n) {
while (n->left)
n = n->left;
return n;
}
// neither rotate changes _leftmost
void rotateLeft(Node *t) {
assert(t);
assert(t->right);
Node *r = t->right;
Node *p = t->parent;
// set r->left as t->right
t->right = r->left;
if (r->left)
r->left->parent = t;
// set the parent of r
r->parent = p;
if (!p)
_root = r;
else if (p->right == t)
p->right = r;
else
p->left = r;
// set the parent of t
t->parent = r;
r->left = t;
}
void rotateRight(Node *t) {
assert(t);
assert(t->left);
Node *l = t->left;
Node *p = t->parent;
assert(p != l);
// set l->right as t->left
t->left = l->right;
if (l->right)
l->right->parent = t;
// set the parent of l
l->parent = p;
if (!p)
_root = l;
else if (p->right == t)
p->right = l;
else
p->left = l;
// set the parent of t
l->right = t;
t->parent = l;
}
void transplant(Node *t, Node *u) {
if (!t->parent) {
_root = u;
} else if (t == t->parent->left) {
t->parent->left = u;
} else {
t->parent->right = u;
}
if (u) {
u->parent = t->parent;
}
if (t == _leftmost)
_leftmost = u ? leftmost(u) : t->parent;
}
void fixInsert(Node *t) {
while (t->parent && t->parent->color == Color::kRed) {
Node *p = t->parent;
Node *g = p->parent;
// cannot be null since p is not _root
assert(g);
if (p == g->left) {
Node *const u = g->right;
if (u && u->color == Color::kRed) {
p->color = u->color = Color::kBlack;
g->color = Color::kRed;
t = g;
} else {
if (t == p->right) {
rotateLeft(p);
t = p;
p = t->parent;
}
p->color = Color::kBlack;
// g is not changed by the previous rotation
g->color = Color::kRed;
rotateRight(g);
}
} else {
Node *const u = g->left;
if (u && u->color == Color::kRed) {
p->color = u->color = Color::kBlack;
g->color = Color::kRed;
t = g;
} else {
if (t == p->left) {
rotateRight(p);
t = p;
p = t->parent;
}
p->color = Color::kBlack;
g->color = Color::kRed;
rotateLeft(g);
}
}
}
_root->color = Color::kBlack;
}
void fixDelete(Node *t, Node *p) {
while (t != _root && (!t || t->color == Color::kBlack)) {
if (t == p->left) {
// since the deleted node was black and t is in its
// place, it has to have a sibling
Node *b = p->right;
assert(b);
if (b->color == Color::kRed) {
b->color = Color::kBlack;
p->color = Color::kRed;
rotateLeft(p);
p = b->left;
// since b was red, it had two black children,
// the right one now being p->right
b = p->right;
}
if ((!b->left || b->left->color == Color::kBlack) &&
(!b->right || b->right->color == Color::kBlack)) {
b->color = Color::kRed;
t = p;
p = p->parent;
} else {
if (!b->right || b->right->color == Color::kBlack) {
// b->left exists, since b->right is black, and it is red
// because of one of the above checks
b->left->color = Color::kBlack;
b->color = Color::kRed;
rotateRight(b);
b = p->right; // p is not changed by the rotation
}
b->color = p->color;
p->color = Color::kBlack;
if (b->right)
b->right->color = Color::kBlack;
rotateLeft(p);
break;
}
} else { // same as above case but left and right swapped
Node *b = p->left;
assert(b);
if (b->color == Color::kRed) {
b->color = Color::kBlack;
p->color = Color::kRed;
rotateRight(p);
p = b->right;
b = p->left;
}
if ((!b->left || b->left->color == Color::kBlack) &&
(!b->right || b->right->color == Color::kBlack)) {
b->color = Color::kRed;
t = p;
p = p->parent;
} else {
if (!b->left || b->left->color == Color::kBlack) {
b->right->color = Color::kBlack;
b->color = Color::kRed;
rotateLeft(b);
b = p->left;
}
b->color = p->color;
p->color = Color::kBlack;
if (b->left)
b->left->color = Color::kBlack;
rotateRight(p);
break;
}
}
}
if (t)
t->color = Color::kBlack;
}
};
} // End of namespace Common
#endif

179
common/stablemap.h Normal file
View File

@ -0,0 +1,179 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef COMMON_STABLEMAP_H
#define COMMON_STABLEMAP_H
#include "common/rb_tree.h"
namespace Common {
/**
* associative container meant as a drop-in replacement the C++ standard library's std::map
*/
template<class Key, class Val, class CompFunc = Common::Less<Key> >
class StableMap {
using TreeT = RBTree<Pair<Key, Val>, Key, PairFirst<Key, Val>, CompFunc>;
public:
using value_type = Pair<Key, Val>;
using iterator = typename TreeT::BasicIterator;
using const_iterator = typename TreeT::ConstIterator;
/**
* Clears the map
*/
void clear() {
_items.clear();
}
/**
* Gets the iterator start
*/
iterator begin() {
return _items.begin();
}
/**
* Get the iterator end
*/
iterator end() {
return _items.end();
}
/**
* Get the const iterator start
*/
const_iterator begin() const {
return _items.begin();
}
/**
* Get the const iterator end
*/
const_iterator end() const {
return _items.end();
}
/**
* Returns an iterator for the first element of the map that is
* not less than the given key
*/
const_iterator lower_bound(const Key &key) const {
return _items.lowerBound(key);
}
iterator lower_bound(const Key &key) {
return _items.lowerBound(key);
}
iterator upper_bound(const Key &key) {
return _items.upperBound(key);
}
/**
* Find the entry with the given key
*/
iterator find(const Key &theKey) {
iterator it = _items.lowerBound(theKey);
if (it != this->end() && compareEqual(it->first, theKey))
return it;
return this->end();
}
const_iterator find(const Key &theKey) const {
const_iterator it = _items.lowerBound(theKey);
if (it != this->end() && compareEqual(it->first, theKey))
return it;
return this->end();
}
/**
* Square brackets operator accesses items by key, creating if necessary
*/
Val &operator[](const Key &theKey) {
iterator it = _items.lowerBound(theKey);
if (it == this->end() || !compareEqual(it->first, theKey)) {
return _items.insert(theKey).second;
}
return *it->second;
}
/**
* Erases an entry in the map
*/
iterator erase(iterator it) {
return _items.erase(it);
}
iterator erase(iterator first, iterator last) {
return _items.erase(first, last);
}
iterator erase(const Key &theKey) {
iterator it = find(theKey);
if (it != this->end())
return erase(it);
return it;
}
Pair<iterator, bool> insert(const value_type &val) {
iterator it = _items.lowerBound(val.first);
if (it == this->end() || !compareEqual(it->first, val.first))
return {_items.insert(val), true};
return {it, false};
}
/**
* Returns the size of the map
*/
size_t size() const {
return _items.size();
}
bool empty() const {
return _items.isEmpty();
}
/**
* Returns the number of elements with a matching key
*/
size_t count(const Key &key) {
int count_ = 0;
for (iterator it = this->begin(); it != this->end(); ++it) {
if (compareEqual(it->first, key))
++count_;
}
return count_;
}
private:
bool compareEqual(const Key &a, const Key &b) {
return !_comp(a, b) && !_comp(b, a);
}
TreeT _items;
CompFunc _comp;
};
} // End of namespace Common
#endif