]> git.proxmox.com Git - ceph.git/blob - ceph/src/msg/async/dpdk/circular_buffer.h
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / msg / async / dpdk / circular_buffer.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 /*
3 * This file is open source software, licensed to you under the terms
4 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
5 * distributed with this work for additional information regarding copyright
6 * ownership. You may not use this file except in compliance with the License.
7 *
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 /*
20 * Copyright (C) 2014 Cloudius Systems, Ltd.
21 */
22
23 #ifndef CEPH_CIRCULAR_BUFFER_HH_
24 #define CEPH_CIRCULAR_BUFFER_HH_
25
26 // A growable double-ended queue container that can be efficiently
27 // extended (and shrunk) from both ends. Implementation is a single
28 // storage vector.
29 //
30 // Similar to libstdc++'s std::deque, except that it uses a single level
31 // store, and so is more efficient for simple stored items.
32 // Similar to boost::circular_buffer_space_optimized, except it uses
33 // uninitialized storage for unoccupied elements (and thus move/copy
34 // constructors instead of move/copy assignments, which are less efficient).
35
36 #include <memory>
37 #include <algorithm>
38
39 #include "transfer.h"
40
41 template <typename T, typename Alloc = std::allocator<T>>
42 class circular_buffer {
43 struct impl : Alloc {
44 T* storage = nullptr;
45 // begin, end interpreted (mod capacity)
46 size_t begin = 0;
47 size_t end = 0;
48 size_t capacity = 0;
49 };
50 impl _impl;
51 public:
52 using value_type = T;
53 using size_type = size_t;
54 using reference = T&;
55 using pointer = T*;
56 using const_reference = const T&;
57 using const_pointer = const T*;
58 public:
59 circular_buffer() = default;
60 circular_buffer(circular_buffer&& X);
61 circular_buffer(const circular_buffer& X) = delete;
62 ~circular_buffer();
63 circular_buffer& operator=(const circular_buffer&) = delete;
64 circular_buffer& operator=(circular_buffer&&) = delete;
65 void push_front(const T& data);
66 void push_front(T&& data);
67 template <typename... A>
68 void emplace_front(A&&... args);
69 void push_back(const T& data);
70 void push_back(T&& data);
71 template <typename... A>
72 void emplace_back(A&&... args);
73 T& front();
74 T& back();
75 void pop_front();
76 void pop_back();
77 bool empty() const;
78 size_t size() const;
79 size_t capacity() const;
80 T& operator[](size_t idx);
81 template <typename Func>
82 void for_each(Func func);
83 // access an element, may return wrong or destroyed element
84 // only useful if you do not rely on data accuracy (e.g. prefetch)
85 T& access_element_unsafe(size_t idx);
86 private:
87 void expand();
88 void maybe_expand(size_t nr = 1);
89 size_t mask(size_t idx) const;
90
91 template<typename CB, typename ValueType>
92 struct cbiterator : std::iterator<std::random_access_iterator_tag, ValueType> {
93 typedef std::iterator<std::random_access_iterator_tag, ValueType> super_t;
94
95 ValueType& operator*() const { return cb->_impl.storage[cb->mask(idx)]; }
96 ValueType* operator->() const { return &cb->_impl.storage[cb->mask(idx)]; }
97 // prefix
98 cbiterator<CB, ValueType>& operator++() {
99 idx++;
100 return *this;
101 }
102 // postfix
103 cbiterator<CB, ValueType> operator++(int unused) {
104 auto v = *this;
105 idx++;
106 return v;
107 }
108 // prefix
109 cbiterator<CB, ValueType>& operator--() {
110 idx--;
111 return *this;
112 }
113 // postfix
114 cbiterator<CB, ValueType> operator--(int unused) {
115 auto v = *this;
116 idx--;
117 return v;
118 }
119 cbiterator<CB, ValueType> operator+(typename super_t::difference_type n) const {
120 return cbiterator<CB, ValueType>(cb, idx + n);
121 }
122 cbiterator<CB, ValueType> operator-(typename super_t::difference_type n) const {
123 return cbiterator<CB, ValueType>(cb, idx - n);
124 }
125 cbiterator<CB, ValueType>& operator+=(typename super_t::difference_type n) {
126 idx += n;
127 return *this;
128 }
129 cbiterator<CB, ValueType>& operator-=(typename super_t::difference_type n) {
130 idx -= n;
131 return *this;
132 }
133 bool operator==(const cbiterator<CB, ValueType>& rhs) const {
134 return idx == rhs.idx;
135 }
136 bool operator!=(const cbiterator<CB, ValueType>& rhs) const {
137 return idx != rhs.idx;
138 }
139 bool operator<(const cbiterator<CB, ValueType>& rhs) const {
140 return idx < rhs.idx;
141 }
142 bool operator>(const cbiterator<CB, ValueType>& rhs) const {
143 return idx > rhs.idx;
144 }
145 bool operator>=(const cbiterator<CB, ValueType>& rhs) const {
146 return idx >= rhs.idx;
147 }
148 bool operator<=(const cbiterator<CB, ValueType>& rhs) const {
149 return idx <= rhs.idx;
150 }
151 typename super_t::difference_type operator-(const cbiterator<CB, ValueType>& rhs) const {
152 return idx - rhs.idx;
153 }
154 private:
155 CB* cb;
156 size_t idx;
157 cbiterator<CB, ValueType>(CB* b, size_t i) : cb(b), idx(i) {}
158 friend class circular_buffer;
159 };
160 friend class iterator;
161
162 public:
163 typedef cbiterator<circular_buffer, T> iterator;
164 typedef cbiterator<const circular_buffer, const T> const_iterator;
165
166 iterator begin() {
167 return iterator(this, _impl.begin);
168 }
169 const_iterator begin() const {
170 return const_iterator(this, _impl.begin);
171 }
172 iterator end() {
173 return iterator(this, _impl.end);
174 }
175 const_iterator end() const {
176 return const_iterator(this, _impl.end);
177 }
178 const_iterator cbegin() const {
179 return const_iterator(this, _impl.begin);
180 }
181 const_iterator cend() const {
182 return const_iterator(this, _impl.end);
183 }
184 };
185
186 template <typename T, typename Alloc>
187 inline size_t circular_buffer<T, Alloc>::mask(size_t idx) const {
188 return idx & (_impl.capacity - 1);
189 }
190
191 template <typename T, typename Alloc>
192 inline bool circular_buffer<T, Alloc>::empty() const {
193 return _impl.begin == _impl.end;
194 }
195
196 template <typename T, typename Alloc>
197 inline size_t circular_buffer<T, Alloc>::size() const {
198 return _impl.end - _impl.begin;
199 }
200
201 template <typename T, typename Alloc>
202 inline size_t circular_buffer<T, Alloc>::capacity() const {
203 return _impl.capacity;
204 }
205
206 template <typename T, typename Alloc>
207 inline circular_buffer<T, Alloc>::circular_buffer(circular_buffer&& x)
208 : _impl(std::move(x._impl)) {
209 x._impl = {};
210 }
211
212 template <typename T, typename Alloc>
213 template <typename Func>
214 inline void circular_buffer<T, Alloc>::for_each(Func func) {
215 auto s = _impl.storage;
216 auto m = _impl.capacity - 1;
217 for (auto i = _impl.begin; i != _impl.end; ++i) {
218 func(s[i & m]);
219 }
220 }
221
222 template <typename T, typename Alloc>
223 inline circular_buffer<T, Alloc>::~circular_buffer() {
224 for_each([this] (T& obj) {
225 _impl.destroy(&obj);
226 });
227 _impl.deallocate(_impl.storage, _impl.capacity);
228 }
229
230 template <typename T, typename Alloc>
231 void circular_buffer<T, Alloc>::expand() {
232 auto new_cap = std::max<size_t>(_impl.capacity * 2, 1);
233 auto new_storage = _impl.allocate(new_cap);
234 auto p = new_storage;
235 try {
236 for_each([this, &p] (T& obj) {
237 transfer_pass1(_impl, &obj, p);
238 p++;
239 });
240 } catch (...) {
241 while (p != new_storage) {
242 _impl.destroy(--p);
243 }
244 _impl.deallocate(new_storage, new_cap);
245 throw;
246 }
247 p = new_storage;
248 for_each([this, &p] (T& obj) {
249 transfer_pass2(_impl, &obj, p++);
250 });
251 std::swap(_impl.storage, new_storage);
252 std::swap(_impl.capacity, new_cap);
253 _impl.begin = 0;
254 _impl.end = p - _impl.storage;
255 _impl.deallocate(new_storage, new_cap);
256 }
257
258 template <typename T, typename Alloc>
259 inline void circular_buffer<T, Alloc>::maybe_expand(size_t nr) {
260 if (_impl.end - _impl.begin + nr > _impl.capacity) {
261 expand();
262 }
263 }
264
265 template <typename T, typename Alloc>
266 inline void circular_buffer<T, Alloc>::push_front(const T& data) {
267 maybe_expand();
268 auto p = &_impl.storage[mask(_impl.begin - 1)];
269 _impl.construct(p, data);
270 --_impl.begin;
271 }
272
273 template <typename T, typename Alloc>
274 inline void circular_buffer<T, Alloc>::push_front(T&& data) {
275 maybe_expand();
276 auto p = &_impl.storage[mask(_impl.begin - 1)];
277 _impl.construct(p, std::move(data));
278 --_impl.begin;
279 }
280
281 template <typename T, typename Alloc>
282 template <typename... Args>
283 inline void circular_buffer<T, Alloc>::emplace_front(Args&&... args) {
284 maybe_expand();
285 auto p = &_impl.storage[mask(_impl.begin - 1)];
286 _impl.construct(p, std::forward<Args>(args)...);
287 --_impl.begin;
288 }
289
290 template <typename T, typename Alloc>
291 inline void circular_buffer<T, Alloc>::push_back(const T& data) {
292 maybe_expand();
293 auto p = &_impl.storage[mask(_impl.end)];
294 _impl.construct(p, data);
295 ++_impl.end;
296 }
297
298 template <typename T, typename Alloc>
299 inline void circular_buffer<T, Alloc>::push_back(T&& data) {
300 maybe_expand();
301 auto p = &_impl.storage[mask(_impl.end)];
302 _impl.construct(p, std::move(data));
303 ++_impl.end;
304 }
305
306 template <typename T, typename Alloc>
307 template <typename... Args>
308 inline void circular_buffer<T, Alloc>::emplace_back(Args&&... args) {
309 maybe_expand();
310 auto p = &_impl.storage[mask(_impl.end)];
311 _impl.construct(p, std::forward<Args>(args)...);
312 ++_impl.end;
313 }
314
315 template <typename T, typename Alloc>
316 inline T& circular_buffer<T, Alloc>::front() {
317 return _impl.storage[mask(_impl.begin)];
318 }
319
320 template <typename T, typename Alloc>
321 inline T& circular_buffer<T, Alloc>::back() {
322 return _impl.storage[mask(_impl.end - 1)];
323 }
324
325 template <typename T, typename Alloc>
326 inline void circular_buffer<T, Alloc>::pop_front() {
327 _impl.destroy(&front());
328 ++_impl.begin;
329 }
330
331 template <typename T, typename Alloc>
332 inline void circular_buffer<T, Alloc>::pop_back() {
333 _impl.destroy(&back());
334 --_impl.end;
335 }
336
337 template <typename T, typename Alloc>
338 inline T& circular_buffer<T, Alloc>::operator[](size_t idx) {
339 return _impl.storage[mask(_impl.begin + idx)];
340 }
341
342 template <typename T, typename Alloc>
343 inline T& circular_buffer<T, Alloc>::access_element_unsafe(size_t idx) {
344 return _impl.storage[mask(_impl.begin + idx)];
345 }
346
347 #endif /* CEPH_CIRCULAR_BUFFER_HH_ */