Seastar
High performance C++ framework for concurrent servers
circular_buffer_fixed_capacity.hh
Go to the documentation of this file.
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) 2017 ScyllaDB
20 */
21
22#pragma once
23
24// A fixed capacity double-ended queue container that can be efficiently
25// extended (and shrunk) from both ends. Implementation is a single
26// storage vector.
27//
28// Similar to libstdc++'s std::deque, except that it uses a single level
29// store, and so is more efficient for simple stored items.
30
31#ifndef SEASTAR_MODULE
32#include <type_traits>
33#include <cstddef>
34#include <iterator>
35#include <utility>
36#include <memory>
37#include <seastar/util/modules.hh>
38#endif
39
41
42namespace seastar {
43
51SEASTAR_MODULE_EXPORT
52template <typename T, size_t Capacity>
54 size_t _begin = 0;
55 size_t _end = 0;
56 union maybe_storage {
57 T data;
58 maybe_storage() noexcept {}
59 ~maybe_storage() {}
60 };
61 maybe_storage _storage[Capacity];
62private:
63 static size_t mask(size_t idx) { return idx % Capacity; }
64 T* obj(size_t idx) { return &_storage[mask(idx)].data; }
65 const T* obj(size_t idx) const { return &_storage[mask(idx)].data; }
66public:
67 static_assert((Capacity & (Capacity - 1)) == 0, "capacity must be a power of two");
68 static_assert(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>,
69 "circular_buffer_fixed_capacity only supports nothrow-move value types");
70 using value_type = T;
71 using size_type = size_t;
72 using reference = T&;
73 using pointer = T*;
74 using const_reference = const T&;
75 using const_pointer = const T*;
76 using difference_type = ssize_t;
77public:
78 template <typename ValueType>
79 class cbiterator {
80 using holder = std::conditional_t<std::is_const_v<ValueType>, const maybe_storage, maybe_storage>;
81 holder* _start;
82 size_t _idx;
83 private:
84 cbiterator(holder* start, size_t idx) noexcept : _start(start), _idx(idx) {}
85 public:
86 using iterator_category = std::random_access_iterator_tag;
87 using value_type = ValueType;
88 using difference_type = ssize_t;
89 using pointer = ValueType*;
90 using reference = ValueType&;
91 public:
92 cbiterator();
93 ValueType& operator*() const { return _start[mask(_idx)].data; }
94 ValueType* operator->() const { return &operator*(); }
95 // prefix
96 cbiterator& operator++() {
97 ++_idx;
98 return *this;
99 }
100 // postfix
101 cbiterator operator++(int) {
102 auto v = *this;
103 ++_idx;
104 return v;
105 }
106 // prefix
107 cbiterator& operator--() {
108 --_idx;
109 return *this;
110 }
111 // postfix
112 cbiterator operator--(int) {
113 auto v = *this;
114 --_idx;
115 return v;
116 }
117 cbiterator operator+(difference_type n) const {
118 return cbiterator{_start, _idx + n};
119 }
120 friend cbiterator operator+(difference_type n, cbiterator i) {
121 return i + n;
122 }
123 cbiterator operator-(difference_type n) const {
124 return cbiterator{_start, _idx - n};
125 }
126 cbiterator& operator+=(difference_type n) {
127 _idx += n;
128 return *this;
129 }
130 cbiterator& operator-=(difference_type n) {
131 _idx -= n;
132 return *this;
133 }
134 bool operator==(const cbiterator& rhs) const {
135 return _idx == rhs._idx;
136 }
137 bool operator!=(const cbiterator& rhs) const {
138 return _idx != rhs._idx;
139 }
140 bool operator<(const cbiterator& rhs) const {
141 return ssize_t(_idx - rhs._idx) < 0;
142 }
143 bool operator>(const cbiterator& rhs) const {
144 return ssize_t(_idx - rhs._idx) > 0;
145 }
146 bool operator<=(const cbiterator& rhs) const {
147 return ssize_t(_idx - rhs._idx) <= 0;
148 }
149 bool operator>=(const cbiterator& rhs) const {
150 return ssize_t(_idx - rhs._idx) >= 0;
151 }
152 difference_type operator-(const cbiterator& rhs) const {
153 return _idx - rhs._idx;
154 }
156 };
157public:
158 using iterator = cbiterator<T>;
160public:
165 void push_front(const T& data);
166 void push_front(T&& data);
167 template <typename... A>
168 T& emplace_front(A&&... args);
169 void push_back(const T& data);
170 void push_back(T&& data);
171 template <typename... A>
172 T& emplace_back(A&&... args);
173 T& front();
174 T& back();
175 void pop_front();
176 void pop_back();
177 bool empty() const;
178 size_t size() const;
179 size_t capacity() const;
180 T& operator[](size_t idx);
181 void clear();
182 iterator begin() {
183 return iterator(_storage, _begin);
184 }
185 const_iterator begin() const {
186 return const_iterator(_storage, _begin);
187 }
188 iterator end() {
189 return iterator(_storage, _end);
190 }
191 const_iterator end() const {
192 return const_iterator(_storage, _end);
193 }
194 const_iterator cbegin() const {
195 return const_iterator(_storage, _begin);
196 }
197 const_iterator cend() const {
198 return const_iterator(_storage, _end);
199 }
200 iterator erase(iterator first, iterator last);
201};
202
203template <typename T, size_t Capacity>
204inline
205bool
206circular_buffer_fixed_capacity<T, Capacity>::empty() const {
207 return _begin == _end;
208}
209
210template <typename T, size_t Capacity>
211inline
212size_t
213circular_buffer_fixed_capacity<T, Capacity>::size() const {
214 return _end - _begin;
215}
216
217template <typename T, size_t Capacity>
218inline
219size_t
220circular_buffer_fixed_capacity<T, Capacity>::capacity() const {
221 return Capacity;
222}
223
224template <typename T, size_t Capacity>
225inline
226circular_buffer_fixed_capacity<T, Capacity>::circular_buffer_fixed_capacity(circular_buffer_fixed_capacity&& x) noexcept
227 : _begin(x._begin), _end(x._end) {
228 std::uninitialized_move(x.begin(), x.end(), begin());
229}
230
231template <typename T, size_t Capacity>
232inline
233circular_buffer_fixed_capacity<T, Capacity>&
234circular_buffer_fixed_capacity<T, Capacity>::operator=(circular_buffer_fixed_capacity&& x) noexcept {
235 if (this != &x) {
236 this->~circular_buffer_fixed_capacity();
237 new (this) circular_buffer_fixed_capacity(std::move(x));
238 }
239 return *this;
240}
241
242template <typename T, size_t Capacity>
243inline
244circular_buffer_fixed_capacity<T, Capacity>::~circular_buffer_fixed_capacity() {
245 clear();
246}
247
248template <typename T, size_t Capacity>
249inline
250void
251circular_buffer_fixed_capacity<T, Capacity>::push_front(const T& data) {
252 new (obj(_begin - 1)) T(data);
253 --_begin;
254}
255
256template <typename T, size_t Capacity>
257inline
258void
259circular_buffer_fixed_capacity<T, Capacity>::push_front(T&& data) {
260 new (obj(_begin - 1)) T(std::move(data));
261 --_begin;
262}
263
264template <typename T, size_t Capacity>
265template <typename... Args>
266inline
267T&
268circular_buffer_fixed_capacity<T, Capacity>::emplace_front(Args&&... args) {
269 auto p = new (obj(_begin - 1)) T(std::forward<Args>(args)...);
270 --_begin;
271 return *p;
272}
273
274template <typename T, size_t Capacity>
275inline
276void
277circular_buffer_fixed_capacity<T, Capacity>::push_back(const T& data) {
278 new (obj(_end)) T(data);
279 ++_end;
280}
281
282template <typename T, size_t Capacity>
283inline
284void
285circular_buffer_fixed_capacity<T, Capacity>::push_back(T&& data) {
286 new (obj(_end)) T(std::move(data));
287 ++_end;
288}
289
290template <typename T, size_t Capacity>
291template <typename... Args>
292inline
293T&
294circular_buffer_fixed_capacity<T, Capacity>::emplace_back(Args&&... args) {
295 auto p = new (obj(_end)) T(std::forward<Args>(args)...);
296 ++_end;
297 return *p;
298}
299
300template <typename T, size_t Capacity>
301inline
302T&
303circular_buffer_fixed_capacity<T, Capacity>::front() {
304 return *obj(_begin);
305}
306
307template <typename T, size_t Capacity>
308inline
309T&
310circular_buffer_fixed_capacity<T, Capacity>::back() {
311 return *obj(_end - 1);
312}
313
314template <typename T, size_t Capacity>
315inline
316void
317circular_buffer_fixed_capacity<T, Capacity>::pop_front() {
318 obj(_begin)->~T();
319 ++_begin;
320}
321
322template <typename T, size_t Capacity>
323inline
324void
325circular_buffer_fixed_capacity<T, Capacity>::pop_back() {
326 obj(_end - 1)->~T();
327 --_end;
328}
329
330template <typename T, size_t Capacity>
331inline
332T&
333circular_buffer_fixed_capacity<T, Capacity>::operator[](size_t idx) {
334 return *obj(_begin + idx);
335}
336
337template <typename T, size_t Capacity>
338inline
339typename circular_buffer_fixed_capacity<T, Capacity>::iterator
340circular_buffer_fixed_capacity<T, Capacity>::erase(iterator first, iterator last) {
341 static_assert(std::is_nothrow_move_assignable_v<T>, "erase() assumes move assignment does not throw");
342 if (first == last) {
343 return last;
344 }
345 // Move to the left or right depending on which would result in least amount of moves.
346 // This also guarantees that iterators will be stable when removing from either front or back.
347 if (std::distance(begin(), first) < std::distance(last, end())) {
348 auto new_start = std::move_backward(begin(), first, last);
349 auto i = begin();
350 while (i < new_start) {
351 i++->~T();
352 }
353 _begin = new_start.idx;
354 return last;
355 } else {
356 auto new_end = std::move(last, end(), first);
357 auto i = new_end;
358 auto e = end();
359 while (i < e) {
360 i++->~T();
361 }
362 _end = new_end.idx;
363 return first;
364 }
365}
366
367template <typename T, size_t Capacity>
368inline
369void
370circular_buffer_fixed_capacity<T, Capacity>::clear() {
371 for (auto& obj : *this) {
372 obj.~T();
373 }
374 _begin = _end = 0;
375}
376
377}
378
Definition: circular_buffer_fixed_capacity.hh:79
Definition: circular_buffer_fixed_capacity.hh:53
Seastar API namespace.
Definition: abort_on_ebadf.hh:26