/src/immer/immer/detail/arrays/with_capacity.hpp
Line | Count | Source |
1 | | // |
2 | | // immer: immutable data structures for C++ |
3 | | // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente |
4 | | // |
5 | | // This software is distributed under the Boost Software License, Version 1.0. |
6 | | // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt |
7 | | // |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <immer/config.hpp> |
12 | | #include <immer/detail/arrays/no_capacity.hpp> |
13 | | |
14 | | #include <cassert> |
15 | | #include <cstddef> |
16 | | #include <stdexcept> |
17 | | |
18 | | namespace immer { |
19 | | namespace detail { |
20 | | namespace arrays { |
21 | | |
22 | | template <typename T, typename MemoryPolicy> |
23 | | struct with_capacity |
24 | | { |
25 | | using no_capacity_t = no_capacity<T, MemoryPolicy>; |
26 | | |
27 | | using node_t = node<T, MemoryPolicy>; |
28 | | using edit_t = typename MemoryPolicy::transience_t::edit; |
29 | | using size_t = std::size_t; |
30 | | |
31 | | node_t* ptr; |
32 | | size_t size; |
33 | | size_t capacity; |
34 | | |
35 | | static const with_capacity& empty() |
36 | 44.4k | { |
37 | 44.4k | static const with_capacity empty_{node_t::make_n(1), 0, 1}; |
38 | 44.4k | return empty_; |
39 | 44.4k | } |
40 | | |
41 | | with_capacity(node_t* p, size_t s, size_t c) |
42 | 100k | : ptr{p} |
43 | 100k | , size{s} |
44 | 100k | , capacity{c} |
45 | 100k | { |
46 | 100k | } |
47 | | |
48 | | with_capacity(const with_capacity& other) |
49 | 44.4k | : with_capacity{other.ptr, other.size, other.capacity} |
50 | 44.4k | { |
51 | 44.4k | inc(); |
52 | 44.4k | } |
53 | | |
54 | | with_capacity(const no_capacity_t& other) |
55 | 40.5k | : with_capacity{other.ptr, other.size, other.size} |
56 | 40.5k | { |
57 | 40.5k | inc(); |
58 | 40.5k | } |
59 | | |
60 | | with_capacity(with_capacity&& other) |
61 | 40.5k | : with_capacity{empty()} |
62 | 40.5k | { |
63 | 40.5k | swap(*this, other); |
64 | 40.5k | } |
65 | | |
66 | | with_capacity& operator=(const with_capacity& other) |
67 | | { |
68 | | auto next = other; |
69 | | swap(*this, next); |
70 | | return *this; |
71 | | } |
72 | | |
73 | | with_capacity& operator=(with_capacity&& other) |
74 | 56.1k | { |
75 | 56.1k | swap(*this, other); |
76 | 56.1k | return *this; |
77 | 56.1k | } |
78 | | |
79 | | friend void swap(with_capacity& x, with_capacity& y) |
80 | 96.7k | { |
81 | 96.7k | using std::swap; |
82 | 96.7k | swap(x.ptr, y.ptr); |
83 | 96.7k | swap(x.size, y.size); |
84 | 96.7k | swap(x.capacity, y.capacity); |
85 | 96.7k | } |
86 | | |
87 | 100k | ~with_capacity() { dec(); } |
88 | | |
89 | | void inc() |
90 | 84.9k | { |
91 | 84.9k | using immer::detail::get; |
92 | 84.9k | ptr->refs().inc(); |
93 | 84.9k | } |
94 | | |
95 | | void dec() |
96 | 100k | { |
97 | 100k | using immer::detail::get; |
98 | 100k | if (ptr->refs().dec()) |
99 | 0 | node_t::delete_n(ptr, size, capacity); |
100 | 100k | } |
101 | | |
102 | | const T* data() const { return ptr->data(); } |
103 | 1.42M | T* data() { return ptr->data(); } |
104 | | |
105 | | T* data_mut(edit_t e) |
106 | | { |
107 | | if (!ptr->can_mutate(e)) { |
108 | | auto p = node_t::copy_e(e, capacity, ptr, size); |
109 | | dec(); |
110 | | ptr = p; |
111 | | } |
112 | | return data(); |
113 | | } |
114 | | |
115 | | operator no_capacity_t() const |
116 | 4.51k | { |
117 | 4.51k | if (size == capacity) { |
118 | 2.34k | ptr->refs().inc(); |
119 | 2.34k | return {ptr, size}; |
120 | 2.34k | } else { |
121 | 2.17k | return {node_t::copy_n(size, ptr, size), size}; |
122 | 2.17k | } |
123 | 4.51k | } |
124 | | |
125 | | template <typename Iter, |
126 | | typename Sent, |
127 | | std::enable_if_t<is_forward_iterator_v<Iter> && |
128 | | compatible_sentinel_v<Iter, Sent>, |
129 | | bool> = true> |
130 | | static with_capacity from_range(Iter first, Sent last) |
131 | | { |
132 | | auto count = static_cast<size_t>(detail::distance(first, last)); |
133 | | if (count == 0) |
134 | | return empty(); |
135 | | else |
136 | | return {node_t::copy_n(count, first, last), count, count}; |
137 | | } |
138 | | |
139 | | template <typename U> |
140 | | static with_capacity from_initializer_list(std::initializer_list<U> values) |
141 | | { |
142 | | using namespace std; |
143 | | return from_range(begin(values), end(values)); |
144 | | } |
145 | | |
146 | | static with_capacity from_fill(size_t n, T v) |
147 | | { |
148 | | return {node_t::fill_n(n, v), n, n}; |
149 | | } |
150 | | |
151 | | template <typename Fn> |
152 | | void for_each_chunk(Fn&& fn) const |
153 | | { |
154 | | std::forward<Fn>(fn)(data(), data() + size); |
155 | | } |
156 | | |
157 | | template <typename Fn> |
158 | | bool for_each_chunk_p(Fn&& fn) const |
159 | | { |
160 | | return std::forward<Fn>(fn)(data(), data() + size); |
161 | | } |
162 | | |
163 | | const T& get(std::size_t index) const { return data()[index]; } |
164 | | |
165 | | const T& get_check(std::size_t index) const |
166 | | { |
167 | | if (index >= size) |
168 | | IMMER_THROW(std::out_of_range{"out of range"}); |
169 | | return data()[index]; |
170 | | } |
171 | | |
172 | | bool equals(const with_capacity& other) const |
173 | | { |
174 | | return ptr == other.ptr || |
175 | | (size == other.size && |
176 | | std::equal(data(), data() + size, other.data())); |
177 | | } |
178 | | |
179 | | static size_t recommend_up(size_t sz, size_t cap) |
180 | 13.3k | { |
181 | 13.3k | auto max = std::numeric_limits<size_t>::max(); |
182 | 13.3k | return sz <= cap ? cap |
183 | 13.3k | : cap >= max / 2 ? max |
184 | | /* otherwise */ |
185 | 12.2k | : std::max(2 * cap, sz); |
186 | 13.3k | } |
187 | | |
188 | | static size_t recommend_down(size_t sz, size_t cap) |
189 | 1.63k | { |
190 | 1.63k | return sz == 0 ? 1 |
191 | 1.63k | : sz < cap / 2 ? sz * 2 |
192 | 867 | : |
193 | 867 | /* otherwise */ cap; |
194 | 1.63k | } |
195 | | |
196 | | with_capacity push_back(T value) const |
197 | | { |
198 | | auto cap = recommend_up(size + 1, capacity); |
199 | | auto p = node_t::copy_n(cap, ptr, size); |
200 | | IMMER_TRY { |
201 | | new (p->data() + size) T(std::move(value)); |
202 | | return {p, size + 1, cap}; |
203 | | } |
204 | | IMMER_CATCH (...) { |
205 | | node_t::delete_n(p, size, cap); |
206 | | IMMER_RETHROW; |
207 | | } |
208 | | } |
209 | | |
210 | | void push_back_mut(edit_t e, T value) |
211 | 1.43M | { |
212 | 1.43M | if (ptr->can_mutate(e) && capacity > size) { |
213 | 1.42M | new (data() + size) T(std::move(value)); |
214 | 1.42M | ++size; |
215 | 1.42M | } else { |
216 | 13.3k | auto cap = recommend_up(size + 1, capacity); |
217 | 13.3k | auto p = node_t::copy_e(e, cap, ptr, size); |
218 | 13.3k | IMMER_TRY { |
219 | 13.3k | new (p->data() + size) T(std::move(value)); |
220 | 13.3k | *this = {p, size + 1, cap}; |
221 | 13.3k | } |
222 | 13.3k | IMMER_CATCH (...) { |
223 | 0 | node_t::delete_n(p, size, cap); |
224 | 0 | IMMER_RETHROW; |
225 | 0 | } |
226 | 13.3k | } |
227 | 1.43M | } |
228 | | |
229 | | with_capacity assoc(std::size_t idx, T value) const |
230 | | { |
231 | | auto p = node_t::copy_n(capacity, ptr, size); |
232 | | IMMER_TRY { |
233 | | p->data()[idx] = std::move(value); |
234 | | return {p, size, capacity}; |
235 | | } |
236 | | IMMER_CATCH (...) { |
237 | | node_t::delete_n(p, size, capacity); |
238 | | IMMER_RETHROW; |
239 | | } |
240 | | } |
241 | | |
242 | | void assoc_mut(edit_t e, std::size_t idx, T value) |
243 | | { |
244 | | if (ptr->can_mutate(e)) { |
245 | | data()[idx] = std::move(value); |
246 | | } else { |
247 | | auto p = node_t::copy_n(capacity, ptr, size); |
248 | | IMMER_TRY { |
249 | | p->data()[idx] = std::move(value); |
250 | | *this = {p, size, capacity}; |
251 | | } |
252 | | IMMER_CATCH (...) { |
253 | | node_t::delete_n(p, size, capacity); |
254 | | IMMER_RETHROW; |
255 | | } |
256 | | } |
257 | | } |
258 | | |
259 | | template <typename Fn> |
260 | | with_capacity update(std::size_t idx, Fn&& op) const |
261 | | { |
262 | | auto p = node_t::copy_n(capacity, ptr, size); |
263 | | IMMER_TRY { |
264 | | auto& elem = p->data()[idx]; |
265 | | elem = std::forward<Fn>(op)(std::move(elem)); |
266 | | return {p, size, capacity}; |
267 | | } |
268 | | IMMER_CATCH (...) { |
269 | | node_t::delete_n(p, size, capacity); |
270 | | IMMER_RETHROW; |
271 | | } |
272 | | } |
273 | | |
274 | | template <typename Fn> |
275 | | void update_mut(edit_t e, std::size_t idx, Fn&& op) |
276 | 1.56k | { |
277 | 1.56k | if (ptr->can_mutate(e)) { |
278 | 905 | auto& elem = data()[idx]; |
279 | 905 | elem = std::forward<Fn>(op)(std::move(elem)); |
280 | 905 | } else { |
281 | 662 | auto p = node_t::copy_e(e, capacity, ptr, size); |
282 | 662 | IMMER_TRY { |
283 | 662 | auto& elem = p->data()[idx]; |
284 | 662 | elem = std::forward<Fn>(op)(std::move(elem)); |
285 | 662 | *this = {p, size, capacity}; |
286 | 662 | } |
287 | 662 | IMMER_CATCH (...) { |
288 | 0 | node_t::delete_n(p, size, capacity); |
289 | 0 | IMMER_RETHROW; |
290 | 0 | } |
291 | 662 | } |
292 | 1.56k | } |
293 | | |
294 | | with_capacity take(std::size_t sz) const |
295 | | { |
296 | | assert(sz <= size); |
297 | | auto cap = recommend_down(sz, capacity); |
298 | | auto p = node_t::copy_n(cap, ptr, sz); |
299 | | return {p, sz, cap}; |
300 | | } |
301 | | |
302 | | void take_mut(edit_t e, std::size_t sz) |
303 | 5.10k | { |
304 | 5.10k | assert(sz <= size); |
305 | 5.10k | if (ptr->can_mutate(e)) { |
306 | 3.46k | detail::destroy_n(data() + size, size - sz); |
307 | 3.46k | size = sz; |
308 | 3.46k | } else { |
309 | 1.63k | auto cap = recommend_down(sz, capacity); |
310 | 1.63k | auto p = node_t::copy_e(e, cap, ptr, sz); |
311 | 1.63k | *this = {p, sz, cap}; |
312 | 1.63k | } |
313 | 5.10k | } |
314 | | }; |
315 | | |
316 | | } // namespace arrays |
317 | | } // namespace detail |
318 | | } // namespace immer |