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