612 lines
14 KiB
C
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
|