1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2015 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #ifndef REALM_INDEX_SET_HPP
20 #define REALM_INDEX_SET_HPP
23 #include <initializer_list>
25 #include <type_traits>
31 template<typename OuterIterator>
32 class MutableChunkedRangeVectorIterator;
34 // An iterator for ChunkedRangeVector, templated on the vector iterator/const_iterator
35 template<typename OuterIterator>
36 class ChunkedRangeVectorIterator {
38 using iterator_category = std::bidirectional_iterator_tag;
39 using value_type = typename std::remove_reference<decltype(*OuterIterator()->data.begin())>::type;
40 using difference_type = ptrdiff_t;
41 using pointer = const value_type*;
42 using reference = const value_type&;
44 ChunkedRangeVectorIterator(OuterIterator outer, OuterIterator end, value_type* inner)
45 : m_outer(outer), m_end(end), m_inner(inner) { }
47 reference operator*() const noexcept { return *m_inner; }
48 pointer operator->() const noexcept { return m_inner; }
50 template<typename Other> bool operator==(Other const& it) const noexcept;
51 template<typename Other> bool operator!=(Other const& it) const noexcept;
53 ChunkedRangeVectorIterator& operator++() noexcept;
54 ChunkedRangeVectorIterator operator++(int) noexcept;
56 ChunkedRangeVectorIterator& operator--() noexcept;
57 ChunkedRangeVectorIterator operator--(int) noexcept;
59 // Advance directly to the next outer block
60 void next_chunk() noexcept;
62 OuterIterator outer() const noexcept { return m_outer; }
63 size_t offset() const noexcept { return m_inner - &m_outer->data[0]; }
66 OuterIterator m_outer;
69 friend struct ChunkedRangeVector;
70 friend class MutableChunkedRangeVectorIterator<OuterIterator>;
73 // A mutable iterator that adds some invariant-preserving mutation methods
74 template<typename OuterIterator>
75 class MutableChunkedRangeVectorIterator : public ChunkedRangeVectorIterator<OuterIterator> {
77 using ChunkedRangeVectorIterator<OuterIterator>::ChunkedRangeVectorIterator;
79 // Set this iterator to the given range and update the parent if needed
80 void set(size_t begin, size_t end);
81 // Adjust the begin and end of this iterator by the given amounts and
82 // update the parent if needed
83 void adjust(ptrdiff_t front, ptrdiff_t back);
84 // Shift this iterator by the given amount and update the parent if needed
85 void shift(ptrdiff_t distance);
88 // A vector which stores ranges in chunks with a maximum size
89 struct ChunkedRangeVector {
91 std::vector<std::pair<size_t, size_t>> data;
96 std::vector<Chunk> m_data;
98 using value_type = std::pair<size_t, size_t>;
99 using iterator = MutableChunkedRangeVectorIterator<typename decltype(m_data)::iterator>;
100 using const_iterator = ChunkedRangeVectorIterator<typename decltype(m_data)::const_iterator>;
103 static const size_t max_size = 4;
105 static const size_t max_size = 4096 / sizeof(std::pair<size_t, size_t>);
108 iterator begin() noexcept { return empty() ? end() : iterator(m_data.begin(), m_data.end(), &m_data[0].data[0]); }
109 iterator end() noexcept { return iterator(m_data.end(), m_data.end(), nullptr); }
110 const_iterator begin() const noexcept { return cbegin(); }
111 const_iterator end() const noexcept { return cend(); }
112 const_iterator cbegin() const noexcept { return empty() ? cend() : const_iterator(m_data.cbegin(), m_data.end(), &m_data[0].data[0]); }
113 const_iterator cend() const noexcept { return const_iterator(m_data.end(), m_data.end(), nullptr); }
115 bool empty() const noexcept { return m_data.empty(); }
117 iterator insert(iterator pos, value_type value);
118 iterator erase(iterator pos) noexcept;
119 void push_back(value_type value);
120 iterator ensure_space(iterator pos);
122 void verify() const noexcept;
126 class IndexSet : private _impl::ChunkedRangeVector {
128 static const size_t npos = -1;
130 using ChunkedRangeVector::value_type;
131 using ChunkedRangeVector::iterator;
132 using ChunkedRangeVector::const_iterator;
133 using ChunkedRangeVector::begin;
134 using ChunkedRangeVector::end;
135 using ChunkedRangeVector::empty;
136 using ChunkedRangeVector::verify;
138 IndexSet() = default;
139 IndexSet(std::initializer_list<size_t>);
141 // Check if the index set contains the given index
142 bool contains(size_t index) const noexcept;
144 // Counts the number of indices in the set in the given range
145 size_t count(size_t start_index=0, size_t end_index=-1) const noexcept;
147 // Add an index to the set, doing nothing if it's already present
148 void add(size_t index);
149 void add(IndexSet const& is);
151 // Add an index which has had all of the ranges in the set before it removed
152 // Returns the unshifted index
153 size_t add_shifted(size_t index);
154 // Add indexes which have had the ranges in `shifted_by` added and the ranges
155 // in the current set removed
156 void add_shifted_by(IndexSet const& shifted_by, IndexSet const& values);
158 // Remove all indexes from the set and then add a single range starting from
159 // zero with the given length
160 void set(size_t len);
162 // Insert an index at the given position, shifting existing indexes at or
163 // after that point back by one
164 void insert_at(size_t index, size_t count=1);
165 void insert_at(IndexSet const&);
167 // Shift indexes at or after the given point back by one
168 void shift_for_insert_at(size_t index, size_t count=1);
169 void shift_for_insert_at(IndexSet const&);
171 // Delete an index at the given position, shifting indexes after that point
173 void erase_at(size_t index);
174 void erase_at(IndexSet const&);
176 // If the given index is in the set remove it and return npos; otherwise unshift() it
177 size_t erase_or_unshift(size_t index);
179 // Remove the indexes at the given index from the set, without shifting
180 void remove(size_t index, size_t count=1);
181 void remove(IndexSet const&);
183 // Shift an index by inserting each of the indexes in this set
184 size_t shift(size_t index) const noexcept;
185 // Shift an index by deleting each of the indexes in this set
186 size_t unshift(size_t index) const noexcept;
188 // Remove all indexes from the set
189 void clear() noexcept;
191 // An iterator over the individual indices in the set rather than the ranges
192 class IndexIterator : public std::iterator<std::forward_iterator_tag, size_t> {
194 IndexIterator(IndexSet::const_iterator it) : m_iterator(it) { }
195 size_t operator*() const noexcept { return m_iterator->first + m_offset; }
196 bool operator==(IndexIterator const& it) const noexcept { return m_iterator == it.m_iterator; }
197 bool operator!=(IndexIterator const& it) const noexcept { return m_iterator != it.m_iterator; }
199 IndexIterator& operator++() noexcept
202 if (m_iterator->first + m_offset == m_iterator->second) {
209 IndexIterator operator++(int) noexcept
217 IndexSet::const_iterator m_iterator;
221 class IndexIteratableAdaptor {
223 using value_type = size_t;
224 using iterator = IndexIterator;
225 using const_iterator = iterator;
227 const_iterator begin() const noexcept { return m_index_set.begin(); }
228 const_iterator end() const noexcept { return m_index_set.end(); }
230 IndexIteratableAdaptor(IndexSet const& is) : m_index_set(is) { }
232 IndexSet const& m_index_set;
235 IndexIteratableAdaptor as_indexes() const noexcept { return *this; }
238 // Find the range which contains the index, or the first one after it if
240 iterator find(size_t index) noexcept;
241 iterator find(size_t index, iterator it) noexcept;
242 // Insert the index before the given position, combining existing ranges as
244 // returns inserted position
245 iterator do_add(iterator pos, size_t index);
246 void do_erase(iterator it, size_t index);
247 iterator do_remove(iterator it, size_t index, size_t count);
249 void shift_until_end_by(iterator begin, ptrdiff_t shift);
253 // This was added in C++14 but is missing from libstdc++ 4.9
254 template<typename Iterator>
255 std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) noexcept
257 return std::reverse_iterator<Iterator>(it);
264 template<typename OtherIterator>
265 inline bool ChunkedRangeVectorIterator<T>::operator==(OtherIterator const& it) const noexcept
267 return m_outer == it.outer() && m_inner == it.operator->();
271 template<typename OtherIterator>
272 inline bool ChunkedRangeVectorIterator<T>::operator!=(OtherIterator const& it) const noexcept
274 return !(*this == it);
278 inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator++() noexcept
281 if (offset() == m_outer->data.size())
287 inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator++(int) noexcept
295 inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator--() noexcept
297 if (!m_inner || m_inner == &m_outer->data.front()) {
299 m_inner = &m_outer->data.back();
308 inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator--(int) noexcept
316 inline void ChunkedRangeVectorIterator<T>::next_chunk() noexcept
319 m_inner = m_outer != m_end ? &m_outer->data[0] : nullptr;
325 #endif // REALM_INDEX_SET_HPP