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