1 // Copyright (C) 2020 Codership Oy <info@codership.com>
2
3 /**
4 * @file A wrapper over std::deque to emulate continuous integer sequence map.
5 *
6 * Holes are tolerated, but take the same amount of memory as elements.
7 * For that purpose target class must have a default value which is treated as
8 * null. As a result, removing N elements does not always reduce the size by N.
9 *
10 * Insert at iterator behavior also had to be changed from that of std::deque:
11 * to be consistent insert happens exactly TO the element pointed by iterator
12 * so it mostly works as "update" and only adds new elements at the end()
13 *
14 * The implementation is optimized towards elements mostly added at the back and
15 * removed from the front.
16 */
17
18 #ifndef GU_DEQMAP_HPP
19 #define GU_DEQMAP_HPP
20
21 #include "gu_exception.hpp" // NotFound
22
23 #include <deque>
24 #include <utility> // std::pair<>
25 #include <iterator> // bidirectional_iterator_tag
26 #include <stdexcept> // std::invalid_argument
27 #include <sstream>
28 #if __cplusplus >= 201103L
29 #include <type_traits>
30 #endif
31
32 #ifdef GU_DEQMAP_CONSISTENCY_CHECKS
33 #include <iostream>
34 #include <cstdlib>
35 #define GU_DEQMAP_ASSERT_CONSISTENCY assert_consistency(__func__, __LINE__)
36 #else
37 #define GU_DEQMAP_ASSERT_CONSISTENCY
38 #endif /* GU_DEQMAP_CONSISTENCY_CHECKS */
39
40 namespace gu
41 {
42
43 template <typename Key, typename Val, class Alloc = std::allocator<Val> >
44 class DeqMap
45 {
46 typedef std::deque<Val, Alloc> base_type;
47
48 public:
49
50 typedef Key index_type;
51 #if __cplusplus >= 201103L
52 typedef typename std::make_signed<index_type>::type difference_type;
53 #else
54 typedef long long difference_type;
55 #endif
56
57 typedef typename base_type::size_type size_type;
58 typedef typename base_type::value_type value_type;
59 typedef typename base_type::pointer pointer;
60 typedef typename base_type::const_pointer const_pointer;
61 typedef typename base_type::reference reference;
62 typedef typename base_type::const_reference const_reference;
63 typedef typename base_type::allocator_type allocator_type;
64
65 typedef typename base_type::iterator iterator;
66 typedef typename base_type::const_iterator const_iterator;
67 typedef typename base_type::reverse_iterator reverse_iterator;
68 typedef typename base_type::const_reverse_iterator const_reverse_iterator;
69
null_value()70 static value_type null_value() { return value_type(); }
71
72 /** A test for an unset element (hole) */
not_set(const_reference val)73 static bool not_set(const_reference val) { return val == null_value(); }
74
75 /**
76 * @param begin initial index value for the map. It is required for
77 * push_back(value_type&) and push_front(value_type&)
78 * to be meaningful operations.
79 */
80 explicit
DeqMap(index_type begin,const allocator_type & allocator=allocator_type ())81 DeqMap(index_type begin,
82 const allocator_type& allocator = allocator_type())
83 :
84 base_ (allocator),
85 begin_ (begin),
86 end_ (begin_)
87 {
88 GU_DEQMAP_ASSERT_CONSISTENCY;
89 }
90
~DeqMap()91 ~DeqMap()
92 {
93 GU_DEQMAP_ASSERT_CONSISTENCY;
94 };
95
96 /** total number of elements allocated (not all of them set) */
size() const97 size_type size() const { return base_.size(); }
empty() const98 bool empty() const { return base_.empty(); }
99
100 /**
101 * @param begin initial index value for the map. See constructor.
102 */
103 void
clear(index_type begin)104 clear(index_type begin)
105 {
106 GU_DEQMAP_ASSERT_CONSISTENCY;
107 base_.clear();
108 begin_ = begin;
109 end_ = begin_;
110 GU_DEQMAP_ASSERT_CONSISTENCY;
111 }
112
index_begin() const113 index_type index_begin() const { return begin_; }
index_end() const114 index_type index_end() const { return end_; }
115
index_front() const116 index_type index_front() const { return index_begin(); }
index_back() const117 index_type index_back() const { return index_end() - 1; }
118
begin()119 iterator begin() { return base_.begin(); }
end()120 iterator end() { return base_.end(); }
121
begin() const122 const_iterator begin() const { return base_.begin(); }
end() const123 const_iterator end() const { return base_.end(); }
124
rbegin()125 reverse_iterator rbegin() { return base_.rbegin(); }
rend()126 reverse_iterator rend() { return base_.rend(); }
127
rbegin() const128 const_reverse_iterator rbegin() const { return base_.rbegin(); }
rend() const129 const_reverse_iterator rend() const { return base_.rend(); }
130
front() const131 const_reference front() const { return base_.front(); }
132
back() const133 const_reference back() const { return base_.back(); }
134
operator [](index_type i) const135 const_reference operator[] (index_type i) const { return base_[i - begin_]; }
136
137 const_reference
at(index_type i) const138 at(index_type i) const
139 {
140 if (begin_ <= i && i < end_)
141 {
142 const_reference v(operator[](i));
143 if (!not_set(v)) return v;
144 }
145
146 throw NotFound();
147 }
148
149 iterator
find(index_type i)150 find(index_type i) { return find_tmpl<iterator>(*this, i); }
151
152 const_iterator
find(index_type i) const153 find(index_type i) const { return find_tmpl<const_iterator>(*this, i); }
154
155 /* pop_front() and pop_back() are the fastest element removal operations
156 * - so set them as base for the rest. */
157 void
pop_front()158 pop_front()
159 {
160 do
161 {
162 base_.pop_front();
163 ++begin_;
164 }
165 while (!empty() && not_set(front())); // trim front
166 GU_DEQMAP_ASSERT_CONSISTENCY;
167 }
168
169 void
pop_back()170 pop_back()
171 {
172 do
173 {
174 base_.pop_back();
175 --end_;
176 }
177 while (!empty() && not_set(back())); // trim back
178 GU_DEQMAP_ASSERT_CONSISTENCY;
179 }
180
181 iterator
erase(iterator position)182 erase(iterator position)
183 {
184 /* Invalid ranges for std::deque::erase() produce undefined behavior
185 * so we are not checking for empty container here. */
186 GU_DEQMAP_ASSERT_CONSISTENCY;
187
188 if (begin() == position)
189 {
190 pop_front();
191 return begin();
192 }
193 else if (--end() == position)
194 {
195 pop_back();
196 return end();
197 }
198 else /* don't remove elements from the middle, just unset them */
199 {
200 return unset(position);
201 }
202 }
203
204 iterator
erase(iterator first,iterator last)205 erase(iterator first, iterator last)
206 {
207 GU_DEQMAP_ASSERT_CONSISTENCY;
208
209 if (begin() == first)
210 {
211 base_.erase(first, last);
212 begin_ += last - first;
213
214 if (!empty() && not_set(front())) // trim front
215 {
216 pop_front();
217 }
218
219 GU_DEQMAP_ASSERT_CONSISTENCY;
220 return begin();
221 }
222 else if (base_.end() == last)
223 {
224 base_.erase(first, last);
225 end_ -= last - first;
226
227 if (!empty() && not_set(back())) // trim back
228 {
229 pop_back();
230 }
231
232 GU_DEQMAP_ASSERT_CONSISTENCY;
233 return end();
234 }
235 else /* don't remove elements from the middle, just unset them */
236 {
237 while (first < last) first = unset(first);
238
239 GU_DEQMAP_ASSERT_CONSISTENCY;
240 return first;
241 }
242 }
243
244 void
erase(index_type const idx)245 erase(index_type const idx)
246 {
247 if (idx == begin_)
248 {
249 pop_front();
250 }
251 else if (idx == end_ - 1)
252 {
253 pop_back();
254 }
255 else
256 {
257 base_[idx - begin_] = null_value();
258 }
259 }
260
261 /* push_front() and push_back() are the fastest element insertion operations
262 * - so set them as base for the rest. */
263 void
push_front(const value_type & val)264 push_front(const value_type& val)
265 {
266 if (!(null_value() == val))
267 {
268 push_front_unchecked(val);
269 }
270 else
271 {
272 throw_null_value_exception(__func__, val, index_begin() - 1);
273 }
274 }
275
276 void
push_back(const value_type & val)277 push_back (const value_type& val)
278 {
279 if (!(null_value() == val))
280 {
281 push_back_unchecked(val);
282 }
283 else
284 {
285 throw_null_value_exception(__func__, val, index_end());
286 }
287 }
288
289 iterator
insert(iterator position,const value_type & val)290 insert(iterator position, const value_type& val)
291 {
292 GU_DEQMAP_ASSERT_CONSISTENCY;
293
294 if (null_value() == val)
295 {
296 throw_null_value_exception(__func__, val, index(position));
297 }
298
299 if (end() == position)
300 {
301 push_back_unchecked(val);
302 }
303 else /* don't insert elements in the middle, just assign them */
304 {
305 *position = val;
306 }
307
308 GU_DEQMAP_ASSERT_CONSISTENCY;
309
310 return position;
311 }
312
313 void
insert(iterator position,size_type n,const value_type & val)314 insert(iterator position, size_type n, const value_type& val)
315 {
316 GU_DEQMAP_ASSERT_CONSISTENCY;
317
318 if (null_value() == val)
319 {
320 throw_null_value_exception(__func__, val, index(position));
321 }
322
323 while (position != end() && n)
324 {
325 position = set(position, val);
326 --n;
327 }
328
329 if (n)
330 {
331 end_ += n;
332 base_.insert(position, n, val);
333 }
334
335 GU_DEQMAP_ASSERT_CONSISTENCY;
336 }
337
338 void
insert(index_type const i,const value_type & val)339 insert(index_type const i, const value_type& val)
340 {
341 GU_DEQMAP_ASSERT_CONSISTENCY;
342
343 if (null_value() == val)
344 {
345 throw_null_value_exception(__func__, val, i);
346 }
347
348 if (begin_ != end_)
349 {
350 if (i >= end_)
351 {
352 if (i == end_)
353 {
354 push_back_unchecked(val);
355 }
356 else
357 {
358 size_type const off(i - end_ + 1);
359 base_.insert(end(), off, null_value());
360 end_ += off;
361 base_.back() = val;
362 }
363 }
364 else if (i < begin_)
365 {
366 if (i + 1 == begin_)
367 {
368 push_front_unchecked(val);
369 }
370 else
371 {
372 size_type const off(begin_ - i);
373 base_.insert(begin(), off, null_value());
374 begin_ = i;
375 base_.front() = val;
376 }
377 }
378 else
379 {
380 base_[i - begin_] = val;
381 }
382 }
383 else
384 {
385 begin_ = end_ = i;
386 push_back_unchecked(val);
387 }
388
389 GU_DEQMAP_ASSERT_CONSISTENCY;
390 }
391
392 index_type
index(const_iterator it) const393 index(const_iterator it) const
394 {
395 GU_DEQMAP_ASSERT_CONSISTENCY;
396
397 return (it - base_.begin()) + begin_;
398 }
399
400 index_type
index(const_reverse_iterator it) const401 index(const_reverse_iterator it) const
402 {
403 GU_DEQMAP_ASSERT_CONSISTENCY;
404
405 return end_ - (it - base_.rbegin()) - 1;
406 }
407
408 /**
409 * This is a port of std::map::upper_bound(): it returns the index of the
410 * first *set* element in container that is supposed to go after i. Unset
411 * elements are treated as absent.
412 */
413 index_type
upper_bound(index_type i) const414 upper_bound(index_type i) const
415 {
416 GU_DEQMAP_ASSERT_CONSISTENCY;
417
418 if (i >= end_)
419 {
420 return end_;
421 }
422
423 if (i >= begin_)
424 {
425 do
426 {
427 ++i;
428 }
429 while (i < end_ && not_set(operator[](i)));
430
431 return i;
432 }
433
434 return begin_;
435 }
436
437 void
print(std::ostream & os) const438 print(std::ostream& os) const
439 {
440 os << "gu::DeqMap(size: " << size() << ", begin: " << +index_begin()
441 << ", end: " << +index_end();
442 os << ", front: ";
443 size() ? os << +front() : os << "n/a";
444 os << ", back: ";
445 size() ? os << +back() : os << "n/a";
446 os << ')';
447 }
448
449 private:
450
451 base_type base_;
452 index_type begin_;
453 index_type end_;
454
455 iterator&
unset(iterator & it)456 unset(iterator& it)
457 {
458 *it = null_value();
459 return ++it;
460 }
461
462 iterator&
set(iterator & it,const value_type & val)463 set(iterator& it, const value_type& val)
464 {
465 *it = val;
466 return ++it;
467 }
468
469 void
push_front_unchecked(const value_type & val)470 push_front_unchecked(const value_type& val)
471 {
472 base_.push_front(val);
473 --begin_;
474 }
475
476 void
push_back_unchecked(const value_type & val)477 push_back_unchecked(const value_type& val)
478 {
479 base_.push_back(val);
480 ++end_;
481 }
482
483 void
throw_null_value_exception(const char * const func_name,const value_type & val,const index_type & pos)484 throw_null_value_exception(const char* const func_name,
485 const value_type& val,
486 const index_type& pos)
487 {
488 std::ostringstream what;
489
490 what << "Null value '" << val << "' with index " << pos
491 << " was passed to " << func_name;
492
493 throw std::invalid_argument(what.str());
494 }
495
496 /* Template to avoid code duplication in find() methods */
497 template <typename Iter, typename This>
498 static Iter
find_tmpl(This & t,index_type i)499 find_tmpl (This& t, index_type i)
500 {
501 #ifdef GU_DEQMAP_CONSISTENCY_CHECKS
502 t.GU_DEQMAP_ASSERT_CONSISTENCY;
503 #endif
504 if (i >= t.begin_ && i < t.end_)
505 return t.begin() += (i - t.begin_);
506 else
507 return t.end();
508 }
509
510 #ifdef GU_DEQMAP_CONSISTENCY_CHECKS
511 void
assert_consistency(const char * const func_name,int const line) const512 assert_consistency(const char* const func_name, int const line) const
513 {
514 bool ok(true);
515 int check(1);
516
517 ok = ok && (begin() + size() == end()); check += ok;
518 ok = ok && (index_begin() + index_type(size()) == index_end());
519 check += ok;
520
521 if (!empty())
522 {
523 ok = ok && (!(front() == null_value())); check += ok;
524 ok = ok && (!(back() == null_value())); check += ok;
525
526 ok = ok && (operator[](index_begin()) == front()); check += ok;
527 ok = ok && (operator[](index_end() - 1) == back()); check += ok;
528
529 ok = ok && (*begin() == front()); check += ok;
530 ok = ok && (*(--end()) == back()); check += ok;
531
532 if (size() == 1)
533 {
534 ok = ok && (front() == back()); check += ok;
535 ok = ok && (*begin() == *rbegin()); check += ok;
536 }
537 }
538 else
539 {
540 ok = ok && (begin() == end()); check += ok;
541 }
542
543 if (!ok)
544 {
545 std::cerr << "gu::DeqMap consistency check " << check
546 << " failed at " << func_name
547 << "():" << line << " map: ";
548 print(std::cerr);
549 std::cerr << std::endl;
550
551 abort();
552 }
553 }
554 #endif /* GU_DEQMAP_CONSISTENCY_CHECKS */
555
556 }; /* class DeqMap */
557
558 template <typename K, typename V, typename A>
559 static inline std::ostream&
operator <<(std::ostream & os,const DeqMap<K,V,A> & m)560 operator<<(std::ostream& os, const DeqMap<K, V, A>& m)
561 {
562 m.print(os);
563 return os;
564 }
565
566 } /* namespace gu */
567
568 #endif /* GU_DEQMAP_HPP */
569