aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/common/bihash.h
blob: cde93d154098bd186cd2ac3c122ae75874f0259e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#ifndef BIHASH_H
#define BIHASH_H

#include <QHash>

/**
 * @brief Bi-directional QHash
 *
 * Bi-directional hash treats both inserted values as keys to each other.
 * Bi-directional hash built on the <tt>left</tt> and <tt>right</tt> values concept.
 *
 * It's not multi-value hash, so when you try to insert existing value to any
 * of sides (left or right), it will replace the whole conflicting entry.
 *
 * It doesn't provide operator[], because returning reference to an object,
 * which then can be changed outside would desynchronize internals of this hash
 * (internal inverted map could not be synchronized properly according to changes
 * to the external reference).
 */
template <class L, class R>
class BiHash
{
    public:
        /**
         * @brief Creates empty hash.
         */
        BiHash() {}

        /**
         * @brief Creates hash initialized with given values.
         * @param list C++11 style initializer list, like: <tt>{{x=y}, {a=b}}</tt>
         */
        BiHash(std::initializer_list<std::pair<L, R>> list)
        {
            hash = QHash<L,R>(list);
            initInverted();
        }

        /**
         * @brief Creates bi-hash basing on QHash.
         * @param other QHash to copy data from.
         *
         * If multiple keys in the QHash refer to the same value,
         * this is not defined which one of those values will remain,
         * but there will definitely be copied only one of them.
         */
        BiHash(const QHash<L, R> & other)
        {
            unite(other);
        }

        /**
         * @brief Creates copy of BiHash.
         * @param other BiHash to copy.
         */
        BiHash(const BiHash<L, R> & other) : hash(other.hash), inverted(other.inverted) {}

        /**
         * @brief Inserts new values to the hash.
         * @param left Left value to be inserted.
         * @param right Right value to be inserted.
         *
         * Note that if the \p left is already in left values, then the current entry
         * for the left will be replaced with this new one.
         * The same applies for the \p right.
         *
         * If it happens, that both \p left and \p right have already entries in the
         * hash, but those are 2 different entries, then the insert operation will
         * remove both conflicting records and insert the new one.
         */
        void insert(const L& left, const R& right)
        {
            if (hash.contains(left))
                removeLeft(left);

            if (inverted.contains(right))
                removeRight(right);

            inverted.insert(right, left);
            hash.insert(left, right);
        }

        /**
         * @brief Tests if left values contain given value.
         * @param left Value to test.
         * @return true if left values containe the \p left value.
         */
        bool containsLeft(const L& left) const
        {
            return hash.contains(left);
        }

        /**
         * @brief Tests if right values contain given value.
         * @param right Value to test.
         * @return true if right values containe the \p right value.
         */
        bool containsRight(const R& right) const
        {
            return inverted.contains(right);
        }

        /**
         * @brief Removes entry with left value equal to given value.
         * @param left Value to remove by.
         * @return Number of removed entries.
         */
        int	removeLeft(const L& left)
        {
            if (!hash.contains(left))
                return 0;

            inverted.remove(hash.value(left));
            hash.remove(left);

            return 1;
        }

        /**
         * @brief Removes entry with right value equal to given value.
         * @param right Value to remove by.
         * @return Number of removed entries.
         */
        int	removeRight(const R& right)
        {
            if (!inverted.contains(right))
                return 0;

            hash.remove(inverted.value(right));
            inverted.remove(right);

            return 1;
        }

        /**
         * @brief Pops entry with left value equal to given value.
         * @param left Value to pop by.
         * @return Right value assigned to given left value.
         */
        R takeLeft(const L& left)
        {
            R right = hash.take(left);
            inverted.remove(right);
            return right;
        }

        /**
         * @brief Pops entry with right value equal to given value.
         * @param right Value to pop by.
         * @return Left value assigned to given right value.
         */
        L takeRight(const R& right)
        {
            R left = inverted.take(right);
            hash.remove(left);
            return left;
        }

        /**
         * @brief Copies all entries from other BiHash to this hash.
         * @param other BiHash to copy from.
         * @return Reference to this hash after values are copied.
         *
         * Any entries from the \p other hash will overwrite current
         * entries in case of conflict (by either left or right value).
         */
        BiHash<L,R>& unite(const BiHash<L,R>& other)
        {
            unite(other.hash);
            return *this;
        }

        /**
         * @overload
         */
        BiHash<L,R>& unite(const QHash<L,R>& other)
        {
            QHashIterator<L, R> it(other);
            while (it.hasNext())
                insert(it.next().key(), it.value());

            return *this;
        }

        /**
         * @brief Finds right value associated with given left value.
         * @param left Left value to match.
         * @return Right value if found, or default constructed value of right type.
         */
        R valueByLeft(const L& left) const
        {
            return hash.value(left);
        }

        /**
         * @brief Finds left value associated with given right value.
         * @param right Right value to match.
         * @return Left value if found, or default constructed value of left type.
         */
        L valueByRight(const R& right) const
        {
            return inverted.value(right);
        }

        /**
         * @brief Provides all left values.
         * @return List of values from left side.
         */
        QList<L> leftValues() const
        {
            return hash.keys();
        }

        /**
         * @brief Provides all right values.
         * @return List of values from right side.
         */
        QList<R> rightValues() const
        {
            return inverted.keys();
        }

        /**
         * @brief Provides java-like iterator for the hash.
         * @return Iterator object for this hash.
         */
        QHashIterator<L,R> iterator() const
        {
            return QHashIterator<L,R>(hash);
        }

        /**
         * @brief Removes all entries from the hash.
         */
        void clear()
        {
            hash.clear();
            inverted.clear();
        }

        /**
         * @brief Counts all entries in the hash.
         * @return Number of entries.
         */
        int count() const
        {
            return hash.count();
        }

        /**
         * @brief Tests whether the hash is empty or not.
         * @return true if the hash is empty, false otherwise.
         */
        bool isEmpty() const
        {
            return hash.isEmpty();
        }

        /**
         * @brief Provides QHash from this BiHash.
         * @return QHash with left values as keys.
         */
        const QHash<L,R>& toQHash() const
        {
            return hash;
        }

        /**
         * @brief Provides QHash with inverted values (right-to-left)
         * @return QHash with right values as keys.
         */
        const QHash<R,L>& toInvertedQHash() const
        {
            return inverted;
        }

    private:
        /**
         * @brief Fills inverted internal hash basing on values from hash class member.
         */
        void initInverted()
        {
            QHashIterator<L,R> it(hash);
            while (it.hasNext())
            {
                it.next();
                inverted[it.value()] = it.key();
            }
        }

        /**
         * @brief Hash containing left-to-right mapping.
         */
        QHash<L,R> hash;

        /**
         * @brief Hash containing right-to-left mapping.
         */
        QHash<R,L> inverted;
};

#endif // BIHASH_H