js8call/qorderedmap.h

612 lines
14 KiB
C++

#ifndef ORDEREDMAP_H
#define ORDEREDMAP_H
#include <QtGlobal>
#include <QHash>
#include <QLinkedList>
#include <QList>
#include <QPair>
template <typename Key> inline bool oMHashEqualToKey(const Key &key1, const Key &key2)
{
// Key type must provide '==' operator
return key1 == key2;
}
template <typename Ptr> inline bool oMHashEqualToKey(Ptr *key1, Ptr *key2)
{
Q_ASSERT(sizeof(quintptr) == sizeof(Ptr *));
return quintptr(key1) == quintptr(key2);
}
template <typename Ptr> inline bool oMHashEqualToKey(const Ptr *key1, const Ptr *key2)
{
Q_ASSERT(sizeof(quintptr) == sizeof(const Ptr *));
return quintptr(key1) == quintptr(key2);
}
template <typename Key, typename Value>
class OrderedMap
{
class OMHash;
typedef typename QLinkedList<Key>::iterator QllIterator;
typedef typename QLinkedList<Key>::const_iterator QllConstIterator;
typedef QPair<Value, QllIterator> OMHashValue;
typedef typename OMHash::iterator OMHashIterator;
typedef typename OMHash::const_iterator OMHashConstIterator;
public:
class iterator;
class const_iterator;
typedef typename OrderedMap<Key, Value>::iterator Iterator;
typedef typename OrderedMap<Key, Value>::const_iterator ConstIterator;
explicit OrderedMap();
OrderedMap(const OrderedMap<Key, Value>& other);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
OrderedMap(OrderedMap<Key, Value>&& other);
#endif
void clear();
bool contains(const Key &key) const;
int count() const;
bool empty() const;
iterator insert(const Key &key, const Value &value);
bool isEmpty() const;
QList<Key> keys() const;
int remove(const Key &key);
int size() const;
Value take(const Key &key);
Value value(const Key &key) const;
Value value(const Key &key, const Value &defaultValue) const;
QList<Value> values() const;
OrderedMap<Key, Value> & operator=(const OrderedMap<Key, Value>& other);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
OrderedMap<Key, Value> & operator=(OrderedMap<Key, Value>&& other);
#endif
bool operator==(const OrderedMap<Key, Value> &other) const;
bool operator!=(const OrderedMap<Key, Value> &other) const;
Value& operator[](const Key &key);
const Value operator[](const Key &key) const;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
iterator erase(iterator pos);
iterator find(const Key& key);
const_iterator find(const Key& key) const;
class const_iterator;
class iterator
{
QllIterator qllIter;
OMHash *data;
friend class const_iterator;
friend class OrderedMap;
public:
iterator() : data(NULL) {}
iterator(const QllIterator &qllIter, OMHash *data) :
qllIter(qllIter), data(data) {}
const Key & key() const
{
return *qllIter;
}
Value & value() const
{
OMHashIterator hit = data->find(*qllIter);
OMHashValue &pair = hit.value();
return pair.first;
}
Value & operator*() const
{
return value();
}
iterator operator+(int i) const
{
QllIterator q = qllIter;
q += i;
return iterator(q, data);
}
iterator operator-(int i) const
{
return operator +(- i);
}
iterator& operator+=(int i)
{
qllIter += i;
return *this;
}
iterator& operator-=(int i)
{
qllIter -= i;
return *this;
}
iterator& operator++()
{
++qllIter;
return *this;
}
iterator operator++(int)
{
iterator it = *this;
qllIter++;
return it;
}
iterator operator--()
{
--qllIter;
return *this;
}
iterator operator--(int)
{
iterator it = *this;
qllIter--;
return it;
}
bool operator ==(const iterator &other) const
{
return (qllIter == other.qllIter);
}
bool operator !=(const iterator &other) const
{
return (qllIter != other.qllIter);
}
};
class const_iterator
{
QllConstIterator qllConstIter;
const OMHash *data;
public:
const_iterator() : data(NULL) {}
const_iterator(const iterator &i) :
qllConstIter(i.qllIter), data(i.data) {}
const_iterator(const QllConstIterator &qllConstIter, const OMHash* data) :
qllConstIter(qllConstIter), data(data) {}
const Key & key() const
{
return *qllConstIter;
}
const Value & value() const
{
OMHashConstIterator hit = data->find(*qllConstIter);
const OMHashValue &pair = hit.value();
return pair.first;
}
const Value & operator*() const
{
return value();
}
const_iterator operator+(int i) const
{
QllConstIterator q = qllConstIter;
q += i;
return const_iterator(q, data);
}
const_iterator operator-(int i) const
{
return operator +(- i);
}
const_iterator& operator+=(int i)
{
qllConstIter += i;
return *this;
}
const_iterator& operator-=(int i)
{
qllConstIter -= i;
return *this;
}
const_iterator& operator++()
{
++qllConstIter;
return *this;
}
const_iterator operator++(int)
{
const_iterator it = *this;
qllConstIter++;
return it;
}
const_iterator operator--()
{
--qllConstIter;
return *this;
}
const_iterator operator--(int)
{
const_iterator it = *this;
qllConstIter--;
return it;
}
bool operator ==(const const_iterator &other) const
{
return (qllConstIter == other.qllConstIter);
}
bool operator !=(const const_iterator &other) const
{
return (qllConstIter != other.qllConstIter);
}
};
private:
class OMHash : public QHash<Key, OMHashValue >
{
public:
bool operator == (const OMHash &other) const
{
if (size() != other.size()) {
return false;
}
if (QHash<Key, OMHashValue >::operator ==(other)) {
return true;
}
typename QHash<Key, OMHashValue >::const_iterator it1 = this->constBegin();
typename QHash<Key, OMHashValue >::const_iterator it2 = other.constBegin();
while(it1 != this->end()) {
OMHashValue v1 = it1.value();
OMHashValue v2 = it2.value();
if ((v1.first != v2.first) || !oMHashEqualToKey<Key>(it1.key(), it2.key())) {
return false;
}
++it1;
++it2;
}
return true;
}
};
private:
void copy(const OrderedMap<Key, Value> &other);
OMHash data;
QLinkedList<Key> insertOrder;
};
template <typename Key, typename Value>
OrderedMap<Key, Value>::OrderedMap() {}
template <typename Key, typename Value>
OrderedMap<Key, Value>::OrderedMap(const OrderedMap<Key, Value>& other)
{
copy(other);
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
template <typename Key, typename Value>
OrderedMap<Key, Value>::OrderedMap(OrderedMap<Key, Value>&& other)
{
data = std::move(other.data);
insertOrder = std::move(other.insertOrder);
}
#endif
template <typename Key, typename Value>
void OrderedMap<Key, Value>::clear()
{
data.clear();
insertOrder.clear();
}
template <typename Key, typename Value>
bool OrderedMap<Key, Value>::contains(const Key &key) const
{
return data.contains(key);
}
template <typename Key, typename Value>
int OrderedMap<Key, Value>::count() const
{
return data.count();
}
template <typename Key, typename Value>
bool OrderedMap<Key, Value>::empty() const
{
return data.empty();
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::iterator OrderedMap<Key, Value>::insert(const Key &key, const Value &value)
{
OMHashIterator it = data.find(key);
if (it == data.end()) {
// New key
QllIterator ioIter = insertOrder.insert(insertOrder.end(), key);
OMHashValue pair(value, ioIter);
data.insert(key, pair);
return iterator(ioIter, &data);
}
OMHashValue pair = it.value();
// remove old reference
insertOrder.erase(pair.second);
// Add new reference
QllIterator ioIter = insertOrder.insert(insertOrder.end(), key);
pair.first = value;
pair.second = ioIter;
return iterator(ioIter, &data);
}
template <typename Key, typename Value>
bool OrderedMap<Key, Value>::isEmpty() const
{
return data.isEmpty();
}
template<typename Key, typename Value>
QList<Key> OrderedMap<Key, Value>::keys() const
{
return QList<Key>::fromStdList(insertOrder.toStdList());
}
template<typename Key, typename Value>
int OrderedMap<Key, Value>::remove(const Key &key)
{
OMHashIterator it = data.find(key);
if (it == data.end()) {
return 0;
}
OMHashValue pair = it.value();
insertOrder.erase(pair.second);
data.erase(it);
return 1;
}
template<typename Key, typename Value>
int OrderedMap<Key, Value>::size() const
{
return data.size();
}
template<typename Key, typename Value>
void OrderedMap<Key, Value>::copy(const OrderedMap<Key, Value> &other)
{
/* Since I'm storing iterators of QLinkedList, I simply cannot make
* a trivial copy of the linked list. This is a limitation due to implicit
* sharing used in Qt containers, due to which iterator active on one
* QLL can change the data of another QLL even after creating a copy.
*
* Because of this, the old iterators have to be invalidated and new ones
* have to be generated.
*/
insertOrder.clear();
// Copy hash
data = other.data;
QllConstIterator cit = other.insertOrder.begin();
for (; cit != other.insertOrder.end(); ++cit) {
Key key = *cit;
QllIterator ioIter = insertOrder.insert(insertOrder.end(), key);
OMHashIterator it = data.find(key);
(*it).second = ioIter;
}
}
template<typename Key, typename Value>
Value OrderedMap<Key, Value>::take(const Key &key)
{
OMHashIterator it = data.find(key);
if (it == data.end()) {
return Value();
}
OMHashValue pair = it.value();
insertOrder.erase(pair.second);
data.erase(it);
return pair.first;
}
template <typename Key, typename Value>
Value OrderedMap<Key, Value>::value(const Key &key) const
{
return data.value(key).first;
}
template <typename Key, typename Value>
Value OrderedMap<Key, Value>::value(const Key &key, const Value &defaultValue) const
{
OMHashConstIterator it = data.constFind(key);
if (it == data.end()) {
return defaultValue;
}
OMHashValue pair = it.value();
return pair.first;
}
template <typename Key, typename Value>
QList<Value> OrderedMap<Key, Value>::values() const
{
QList<Value> values;
foreach (const Key &key, insertOrder.toStdList()) {
OMHashValue v = data.value(key);
values.append(v.first);
}
return values;
}
template <typename Key, typename Value>
OrderedMap<Key, Value> & OrderedMap<Key, Value>::operator=(const OrderedMap<Key, Value>& other)
{
if (this != &other) {
copy(other);
}
return *this;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
template <typename Key, typename Value>
OrderedMap<Key, Value> & OrderedMap<Key, Value>::operator=(OrderedMap<Key, Value>&& other)
{
if (this != &other) {
data = other.data;
insertOrder = other.insertOrder;
}
return *this;
}
#endif
template <typename Key, typename Value>
bool OrderedMap<Key, Value>::operator==(const OrderedMap<Key, Value> &other) const
{
// 2 Ordered maps are equal if they have the same contents in the same order
return ((data == other.data) && (insertOrder == other.insertOrder));
}
template <typename Key, typename Value>
bool OrderedMap<Key, Value>::operator!=(const OrderedMap<Key, Value> &other) const
{
return ((data != other.data) || (insertOrder != other.insertOrder));
}
template <typename Key, typename Value>
Value& OrderedMap<Key, Value>::operator[](const Key &key)
{
OMHashIterator it = data.find(key);
if (it == data.end()) {
insert(key, Value());
it = data.find(key);
}
OMHashValue &pair = it.value();
return pair.first;
}
template <typename Key, typename Value>
const Value OrderedMap<Key, Value>::operator[](const Key &key) const
{
return value(key);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::iterator OrderedMap<Key, Value>::begin()
{
return iterator(insertOrder.begin(), &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::const_iterator OrderedMap<Key, Value>::begin() const
{
return const_iterator(insertOrder.begin(), &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::iterator OrderedMap<Key, Value>::end()
{
return iterator(insertOrder.end(), &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::const_iterator OrderedMap<Key, Value>::end() const
{
return const_iterator(insertOrder.end(), &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::iterator OrderedMap<Key, Value>::erase(iterator pos)
{
OMHashIterator hit = data.find(*(pos.qllIter));
if (hit == data.end()) {
return pos;
}
data.erase(hit);
QllIterator ioIter = insertOrder.erase(pos.qllIter);
return iterator(ioIter, &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::iterator OrderedMap<Key, Value>::find(const Key& key)
{
OMHashIterator hit = data.find(key);
if (hit == data.end()) {
return end();
}
return iterator(hit.value().second, &data);
}
template <typename Key, typename Value>
typename OrderedMap<Key, Value>::const_iterator OrderedMap<Key, Value>::find(const Key& key) const
{
OMHashConstIterator hit = data.find(key);
if (hit == data.end()) {
return end();
}
return const_iterator(hit.value().second, &data);
}
#endif // ORDEREDMAP_H