]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/circular_buffer.hh
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / include / seastar / core / circular_buffer.hh
CommitLineData
11fdf7f2
TL
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
20 */
21
22#pragma once
23
24#include <seastar/core/transfer.hh>
25#include <seastar/core/bitops.hh>
f67539c2 26#include <seastar/util/concepts.hh>
11fdf7f2
TL
27#include <memory>
28#include <algorithm>
29
30namespace seastar {
31
32/// A growable double-ended queue container that can be efficiently
33/// extended (and shrunk) from both ends. Implementation is a single
34/// storage vector.
35///
36/// Similar to libstdc++'s std::deque, except that it uses a single
37/// level store, and so is more efficient for simple stored items.
38/// Similar to boost::circular_buffer_space_optimized, except it uses
39/// uninitialized storage for unoccupied elements (and thus move/copy
40/// constructors instead of move/copy assignments, which are less
41/// efficient).
42///
43/// The storage of the circular_buffer is expanded automatically in
44/// exponential increments.
45/// When adding new elements:
46/// * if size + 1 > capacity: all iterators and references are
47/// invalidated,
48/// * otherwise only the begin() or end() iterator is invalidated:
49/// * push_front() and emplace_front() will invalidate begin() and
50/// * push_back() and emplace_back() will invalidate end().
51///
52/// Removing elements never invalidates any references and only
53/// invalidates begin() or end() iterators:
54/// * pop_front() will invalidate begin() and
55/// * pop_back() will invalidate end().
56///
57/// reserve() may also invalidate all iterators and references.
58template <typename T, typename Alloc = std::allocator<T>>
59class circular_buffer {
60 struct impl : Alloc {
61 T* storage = nullptr;
62 // begin, end interpreted (mod capacity)
63 size_t begin = 0;
64 size_t end = 0;
65 size_t capacity = 0;
f67539c2
TL
66
67 impl(Alloc a) noexcept : Alloc(std::move(a)) { }
68 void reset() {
69 storage = {};
70 begin = 0;
71 end = 0;
72 capacity = 0;
73 }
11fdf7f2 74 };
f67539c2
TL
75 static_assert(!std::is_default_constructible_v<Alloc>
76 || std::is_nothrow_default_constructible_v<Alloc>);
77 static_assert(std::is_nothrow_move_constructible_v<Alloc>);
11fdf7f2
TL
78 impl _impl;
79public:
80 using value_type = T;
81 using size_type = size_t;
82 using reference = T&;
83 using pointer = T*;
84 using const_reference = const T&;
85 using const_pointer = const T*;
86public:
f67539c2
TL
87 circular_buffer() noexcept SEASTAR_CONCEPT(requires std::default_initializable<Alloc>) : circular_buffer(Alloc()) {}
88 circular_buffer(Alloc alloc) noexcept;
11fdf7f2
TL
89 circular_buffer(circular_buffer&& X) noexcept;
90 circular_buffer(const circular_buffer& X) = delete;
91 ~circular_buffer();
92 circular_buffer& operator=(const circular_buffer&) = delete;
93 circular_buffer& operator=(circular_buffer&& b) noexcept;
94 void push_front(const T& data);
95 void push_front(T&& data);
96 template <typename... A>
97 void emplace_front(A&&... args);
98 void push_back(const T& data);
99 void push_back(T&& data);
100 template <typename... A>
101 void emplace_back(A&&... args);
f67539c2
TL
102 T& front() noexcept;
103 const T& front() const noexcept;
104 T& back() noexcept;
105 const T& back() const noexcept;
106 void pop_front() noexcept;
107 void pop_back() noexcept;
11fdf7f2
TL
108 bool empty() const;
109 size_t size() const;
110 size_t capacity() const;
111 void reserve(size_t);
112 void clear();
f67539c2
TL
113 T& operator[](size_t idx) noexcept;
114 const T& operator[](size_t idx) const noexcept;
11fdf7f2
TL
115 template <typename Func>
116 void for_each(Func func);
117 // access an element, may return wrong or destroyed element
118 // only useful if you do not rely on data accuracy (e.g. prefetch)
f67539c2 119 T& access_element_unsafe(size_t idx) noexcept;
11fdf7f2
TL
120private:
121 void expand();
122 void expand(size_t);
123 void maybe_expand(size_t nr = 1);
124 size_t mask(size_t idx) const;
125
126 template<typename CB, typename ValueType>
127 struct cbiterator : std::iterator<std::random_access_iterator_tag, ValueType> {
128 typedef std::iterator<std::random_access_iterator_tag, ValueType> super_t;
129
f67539c2
TL
130 ValueType& operator*() const noexcept { return cb->_impl.storage[cb->mask(idx)]; }
131 ValueType* operator->() const noexcept { return &cb->_impl.storage[cb->mask(idx)]; }
11fdf7f2 132 // prefix
f67539c2 133 cbiterator<CB, ValueType>& operator++() noexcept {
11fdf7f2
TL
134 idx++;
135 return *this;
136 }
137 // postfix
f67539c2 138 cbiterator<CB, ValueType> operator++(int unused) noexcept {
11fdf7f2
TL
139 auto v = *this;
140 idx++;
141 return v;
142 }
143 // prefix
f67539c2 144 cbiterator<CB, ValueType>& operator--() noexcept {
11fdf7f2
TL
145 idx--;
146 return *this;
147 }
148 // postfix
f67539c2 149 cbiterator<CB, ValueType> operator--(int unused) noexcept {
11fdf7f2
TL
150 auto v = *this;
151 idx--;
152 return v;
153 }
f67539c2 154 cbiterator<CB, ValueType> operator+(typename super_t::difference_type n) const noexcept {
11fdf7f2
TL
155 return cbiterator<CB, ValueType>(cb, idx + n);
156 }
f67539c2 157 cbiterator<CB, ValueType> operator-(typename super_t::difference_type n) const noexcept {
11fdf7f2
TL
158 return cbiterator<CB, ValueType>(cb, idx - n);
159 }
f67539c2 160 cbiterator<CB, ValueType>& operator+=(typename super_t::difference_type n) noexcept {
11fdf7f2
TL
161 idx += n;
162 return *this;
163 }
f67539c2 164 cbiterator<CB, ValueType>& operator-=(typename super_t::difference_type n) noexcept {
11fdf7f2
TL
165 idx -= n;
166 return *this;
167 }
f67539c2 168 bool operator==(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
169 return idx == rhs.idx;
170 }
f67539c2 171 bool operator!=(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
172 return idx != rhs.idx;
173 }
f67539c2 174 bool operator<(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
175 return idx < rhs.idx;
176 }
f67539c2 177 bool operator>(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
178 return idx > rhs.idx;
179 }
f67539c2 180 bool operator>=(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
181 return idx >= rhs.idx;
182 }
f67539c2 183 bool operator<=(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
184 return idx <= rhs.idx;
185 }
f67539c2 186 typename super_t::difference_type operator-(const cbiterator<CB, ValueType>& rhs) const noexcept {
11fdf7f2
TL
187 return idx - rhs.idx;
188 }
189 private:
190 CB* cb;
191 size_t idx;
f67539c2 192 cbiterator(CB* b, size_t i) noexcept : cb(b), idx(i) {}
11fdf7f2
TL
193 friend class circular_buffer;
194 };
195 friend class iterator;
196
197public:
198 typedef cbiterator<circular_buffer, T> iterator;
199 typedef cbiterator<const circular_buffer, const T> const_iterator;
200
f67539c2 201 iterator begin() noexcept {
11fdf7f2
TL
202 return iterator(this, _impl.begin);
203 }
f67539c2 204 const_iterator begin() const noexcept {
11fdf7f2
TL
205 return const_iterator(this, _impl.begin);
206 }
f67539c2 207 iterator end() noexcept {
11fdf7f2
TL
208 return iterator(this, _impl.end);
209 }
f67539c2 210 const_iterator end() const noexcept {
11fdf7f2
TL
211 return const_iterator(this, _impl.end);
212 }
f67539c2 213 const_iterator cbegin() const noexcept {
11fdf7f2
TL
214 return const_iterator(this, _impl.begin);
215 }
f67539c2 216 const_iterator cend() const noexcept {
11fdf7f2
TL
217 return const_iterator(this, _impl.end);
218 }
f67539c2 219 iterator erase(iterator first, iterator last) noexcept;
11fdf7f2
TL
220};
221
222template <typename T, typename Alloc>
223inline
224size_t
225circular_buffer<T, Alloc>::mask(size_t idx) const {
226 return idx & (_impl.capacity - 1);
227}
228
229template <typename T, typename Alloc>
230inline
231bool
232circular_buffer<T, Alloc>::empty() const {
233 return _impl.begin == _impl.end;
234}
235
236template <typename T, typename Alloc>
237inline
238size_t
239circular_buffer<T, Alloc>::size() const {
240 return _impl.end - _impl.begin;
241}
242
243template <typename T, typename Alloc>
244inline
245size_t
246circular_buffer<T, Alloc>::capacity() const {
247 return _impl.capacity;
248}
249
250template <typename T, typename Alloc>
251inline
252void
253circular_buffer<T, Alloc>::reserve(size_t size) {
254 if (capacity() < size) {
255 // Make sure that the new capacity is a power of two.
256 expand(size_t(1) << log2ceil(size));
257 }
258}
259
260template <typename T, typename Alloc>
261inline
262void
263circular_buffer<T, Alloc>::clear() {
264 erase(begin(), end());
265}
266
f67539c2
TL
267template <typename T, typename Alloc>
268inline
269circular_buffer<T, Alloc>::circular_buffer(Alloc alloc) noexcept
270 : _impl(std::move(alloc)) {
271}
272
11fdf7f2
TL
273template <typename T, typename Alloc>
274inline
275circular_buffer<T, Alloc>::circular_buffer(circular_buffer&& x) noexcept
276 : _impl(std::move(x._impl)) {
f67539c2 277 x._impl.reset();
11fdf7f2
TL
278}
279
280template <typename T, typename Alloc>
281inline
282circular_buffer<T, Alloc>& circular_buffer<T, Alloc>::operator=(circular_buffer&& x) noexcept {
283 if (this != &x) {
284 this->~circular_buffer();
285 new (this) circular_buffer(std::move(x));
286 }
287 return *this;
288}
289
290template <typename T, typename Alloc>
291template <typename Func>
292inline
293void
294circular_buffer<T, Alloc>::for_each(Func func) {
295 auto s = _impl.storage;
296 auto m = _impl.capacity - 1;
297 for (auto i = _impl.begin; i != _impl.end; ++i) {
298 func(s[i & m]);
299 }
300}
301
302template <typename T, typename Alloc>
303inline
304circular_buffer<T, Alloc>::~circular_buffer() {
305 for_each([this] (T& obj) {
f67539c2 306 std::allocator_traits<Alloc>::destroy(_impl, &obj);
11fdf7f2
TL
307 });
308 _impl.deallocate(_impl.storage, _impl.capacity);
309}
310
311template <typename T, typename Alloc>
312void
313circular_buffer<T, Alloc>::expand() {
314 expand(std::max<size_t>(_impl.capacity * 2, 1));
315}
316
317template <typename T, typename Alloc>
318void
319circular_buffer<T, Alloc>::expand(size_t new_cap) {
320 auto new_storage = _impl.allocate(new_cap);
321 auto p = new_storage;
322 try {
323 for_each([this, &p] (T& obj) {
324 transfer_pass1(_impl, &obj, p);
325 p++;
326 });
327 } catch (...) {
328 while (p != new_storage) {
f67539c2 329 std::allocator_traits<Alloc>::destroy(_impl, --p);
11fdf7f2
TL
330 }
331 _impl.deallocate(new_storage, new_cap);
332 throw;
333 }
334 p = new_storage;
335 for_each([this, &p] (T& obj) {
336 transfer_pass2(_impl, &obj, p++);
337 });
338 std::swap(_impl.storage, new_storage);
339 std::swap(_impl.capacity, new_cap);
340 _impl.begin = 0;
341 _impl.end = p - _impl.storage;
342 _impl.deallocate(new_storage, new_cap);
343}
344
345template <typename T, typename Alloc>
346inline
347void
348circular_buffer<T, Alloc>::maybe_expand(size_t nr) {
349 if (_impl.end - _impl.begin + nr > _impl.capacity) {
350 expand();
351 }
352}
353
354template <typename T, typename Alloc>
355inline
356void
357circular_buffer<T, Alloc>::push_front(const T& data) {
358 maybe_expand();
359 auto p = &_impl.storage[mask(_impl.begin - 1)];
f67539c2 360 std::allocator_traits<Alloc>::construct(_impl, p, data);
11fdf7f2
TL
361 --_impl.begin;
362}
363
364template <typename T, typename Alloc>
365inline
366void
367circular_buffer<T, Alloc>::push_front(T&& data) {
368 maybe_expand();
369 auto p = &_impl.storage[mask(_impl.begin - 1)];
f67539c2 370 std::allocator_traits<Alloc>::construct(_impl, p, std::move(data));
11fdf7f2
TL
371 --_impl.begin;
372}
373
374template <typename T, typename Alloc>
375template <typename... Args>
376inline
377void
378circular_buffer<T, Alloc>::emplace_front(Args&&... args) {
379 maybe_expand();
380 auto p = &_impl.storage[mask(_impl.begin - 1)];
f67539c2 381 std::allocator_traits<Alloc>::construct(_impl, p, std::forward<Args>(args)...);
11fdf7f2
TL
382 --_impl.begin;
383}
384
385template <typename T, typename Alloc>
386inline
387void
388circular_buffer<T, Alloc>::push_back(const T& data) {
389 maybe_expand();
390 auto p = &_impl.storage[mask(_impl.end)];
f67539c2 391 std::allocator_traits<Alloc>::construct(_impl, p, data);
11fdf7f2
TL
392 ++_impl.end;
393}
394
395template <typename T, typename Alloc>
396inline
397void
398circular_buffer<T, Alloc>::push_back(T&& data) {
399 maybe_expand();
400 auto p = &_impl.storage[mask(_impl.end)];
f67539c2 401 std::allocator_traits<Alloc>::construct(_impl, p, std::move(data));
11fdf7f2
TL
402 ++_impl.end;
403}
404
405template <typename T, typename Alloc>
406template <typename... Args>
407inline
408void
409circular_buffer<T, Alloc>::emplace_back(Args&&... args) {
410 maybe_expand();
411 auto p = &_impl.storage[mask(_impl.end)];
f67539c2 412 std::allocator_traits<Alloc>::construct(_impl, p, std::forward<Args>(args)...);
11fdf7f2
TL
413 ++_impl.end;
414}
415
416template <typename T, typename Alloc>
417inline
418T&
f67539c2 419circular_buffer<T, Alloc>::front() noexcept {
11fdf7f2
TL
420 return _impl.storage[mask(_impl.begin)];
421}
422
423template <typename T, typename Alloc>
424inline
425const T&
f67539c2 426circular_buffer<T, Alloc>::front() const noexcept {
11fdf7f2
TL
427 return _impl.storage[mask(_impl.begin)];
428}
429
430template <typename T, typename Alloc>
431inline
432T&
f67539c2 433circular_buffer<T, Alloc>::back() noexcept {
11fdf7f2
TL
434 return _impl.storage[mask(_impl.end - 1)];
435}
436
437template <typename T, typename Alloc>
438inline
439const T&
f67539c2 440circular_buffer<T, Alloc>::back() const noexcept {
11fdf7f2
TL
441 return _impl.storage[mask(_impl.end - 1)];
442}
443
444template <typename T, typename Alloc>
445inline
446void
f67539c2
TL
447circular_buffer<T, Alloc>::pop_front() noexcept {
448 std::allocator_traits<Alloc>::destroy(_impl, &front());
11fdf7f2
TL
449 ++_impl.begin;
450}
451
452template <typename T, typename Alloc>
453inline
454void
f67539c2
TL
455circular_buffer<T, Alloc>::pop_back() noexcept {
456 std::allocator_traits<Alloc>::destroy(_impl, &back());
11fdf7f2
TL
457 --_impl.end;
458}
459
460template <typename T, typename Alloc>
461inline
462T&
f67539c2 463circular_buffer<T, Alloc>::operator[](size_t idx) noexcept {
11fdf7f2
TL
464 return _impl.storage[mask(_impl.begin + idx)];
465}
466
467template <typename T, typename Alloc>
468inline
469const T&
f67539c2 470circular_buffer<T, Alloc>::operator[](size_t idx) const noexcept {
11fdf7f2
TL
471 return _impl.storage[mask(_impl.begin + idx)];
472}
473
474template <typename T, typename Alloc>
475inline
476T&
f67539c2 477circular_buffer<T, Alloc>::access_element_unsafe(size_t idx) noexcept {
11fdf7f2
TL
478 return _impl.storage[mask(_impl.begin + idx)];
479}
480
481template <typename T, typename Alloc>
482inline
483typename circular_buffer<T, Alloc>::iterator
f67539c2 484circular_buffer<T, Alloc>::erase(iterator first, iterator last) noexcept {
11fdf7f2
TL
485 static_assert(std::is_nothrow_move_assignable<T>::value, "erase() assumes move assignment does not throw");
486 if (first == last) {
487 return last;
488 }
489 // Move to the left or right depending on which would result in least amount of moves.
490 // This also guarantees that iterators will be stable when removing from either front or back.
491 if (std::distance(begin(), first) < std::distance(last, end())) {
492 auto new_start = std::move_backward(begin(), first, last);
493 auto i = begin();
494 while (i < new_start) {
f67539c2 495 std::allocator_traits<Alloc>::destroy(_impl, &*i++);
11fdf7f2
TL
496 }
497 _impl.begin = new_start.idx;
498 return last;
499 } else {
500 auto new_end = std::move(last, end(), first);
501 auto i = new_end;
502 auto e = end();
503 while (i < e) {
f67539c2 504 std::allocator_traits<Alloc>::destroy(_impl, &*i++);
11fdf7f2
TL
505 }
506 _impl.end = new_end.idx;
507 return first;
508 }
509}
510
511}