1 #pragma once
2
3 #include "dsx-ns.h"
4 #ifdef dsx
5 #include <iterator>
6 #include <type_traits>
7 #include "dxxsconf.h"
8 #include "object.h"
9 #include "segment.h"
10
11 #ifndef DXX_SEGITER_DEBUG_OBJECT_LINKAGE
12 # ifdef NDEBUG
13 # define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 0
14 # else
15 # define DXX_SEGITER_DEBUG_OBJECT_LINKAGE 1
16 # endif
17 #endif
18
19 #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
20 #include <cassert>
21 #endif
22
23 namespace detail
24 {
25
26 template <typename>
27 struct unspecified_pointer_t;
28
29 };
30
31 namespace dsx {
32
33 template <typename T>
34 class segment_object_range_t
35 {
36 class iterator;
37 const iterator b;
38 public:
segment_object_range_t(iterator && o)39 segment_object_range_t(iterator &&o) :
40 b(std::move(o))
41 {
42 }
begin()43 const iterator &begin() const { return b; }
end()44 static iterator end() { return T(object_none, static_cast<typename T::allow_none_construction *>(nullptr)); }
45 template <typename OF, typename SF>
construct(const unique_segment & s,OF & of,SF & sf)46 static segment_object_range_t construct(const unique_segment &s, OF &of, SF &sf)
47 {
48 if (s.objects == object_none)
49 return end();
50 auto &&opi = of(s.objects);
51 const object_base &o = opi;
52 #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
53 /* Assert that the first object in the segment claims to be
54 * in the segment that claims to have that object.
55 */
56 assert(&*sf(o.segnum) == &s);
57 /* Assert that the first object in the segment agrees that there
58 * are no objects before it in the segment.
59 */
60 assert(o.prev == object_none);
61 #endif
62 /* Check that the supplied SF can produce `const unique_segment &`.
63 * In !NDEBUG builds, this would be checked as a side effect of the
64 * assert conditions. In NDEBUG builds, it would not be checked.
65 *
66 * Wrap the expression in a sizeof to prevent the compiler from
67 * emitting code to implement the test.
68 */
69 static_cast<void>(sizeof(&*sf(o.segnum) == &s));
70 return iterator(std::move(opi));
71 }
72 };
73
74 template <typename T>
75 class segment_object_range_t<T>::iterator :
76 T
77 {
78 using T::m_ptr;
79 using T::m_idx;
80 public:
81 using iterator_category = std::forward_iterator_tag;
82 using value_type = T;
83 using difference_type = std::ptrdiff_t;
84 using pointer = void;
85 using reference = T;
iterator(T && o)86 iterator(T &&o) :
87 T(std::move(o))
88 {
89 }
90 T operator *() const { return *this; }
91 iterator &operator++()
92 {
93 const auto oi = m_idx;
94 const auto op = m_ptr;
95 const auto ni = op->next;
96 m_idx = ni;
97 /* If ni == object_none, then the iterator is at end() and there should
98 * be no further reads of m_ptr. Optimizing compilers will typically
99 * recognize that setting m_ptr=nullptr in this case is unnecessary and
100 * omit the store. Omit the nullptr assignment so that less optimizing
101 * compilers do not generate a useless branch.
102 */
103 m_ptr += static_cast<std::size_t>(ni) - static_cast<std::size_t>(oi);
104 if (ni != object_none)
105 {
106 /* If ni == object_none, then `m_ptr` is now invalid and these
107 * tests would be undefined.
108 */
109 #if DXX_SEGITER_DEBUG_OBJECT_LINKAGE
110 /* Assert that the next object in the segment agrees that
111 * the preceding object is the previous object.
112 */
113 assert(oi == m_ptr->prev);
114 /* Assert that the old object and new object are in the same
115 * segment.
116 */
117 assert(op->segnum == m_ptr->segnum);
118 #endif
119 }
120 return *this;
121 }
122 bool operator==(const iterator &rhs) const
123 {
124 return m_idx == rhs.m_idx;
125 }
126 bool operator!=(const iterator &rhs) const
127 {
128 return !(*this == rhs);
129 }
130 };
131
132 template <typename OF, typename SF, typename R = segment_object_range_t<decltype(std::declval<OF &>()(object_first))>>
133 [[nodiscard]]
objects_in(const unique_segment & s,OF & of,SF & sf)134 static inline R objects_in(const unique_segment &s, OF &of, SF &sf)
135 {
136 return R::construct(s, of, sf);
137 }
138
139 }
140 #endif
141