]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
7c673cae FG |
2 | // vim: ts=8 sw=2 smarttab |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
11fdf7f2 | 10 | * License version 2.1, as published by the Free Software |
7c673cae | 11 | * Foundation. See file COPYING. |
11fdf7f2 | 12 | * |
7c673cae FG |
13 | */ |
14 | #ifndef CEPH_BUFFER_H | |
15 | #define CEPH_BUFFER_H | |
16 | ||
17 | #if defined(__linux__) || defined(__FreeBSD__) | |
18 | #include <stdlib.h> | |
19 | #endif | |
20 | #include <limits.h> | |
21 | ||
22 | #ifndef _XOPEN_SOURCE | |
23 | # define _XOPEN_SOURCE 600 | |
24 | #endif | |
25 | ||
26 | #include <stdio.h> | |
27 | #include <sys/uio.h> | |
28 | ||
29 | #if defined(__linux__) // For malloc(2). | |
30 | #include <malloc.h> | |
31 | #endif | |
32 | ||
33 | #include <inttypes.h> | |
34 | #include <stdint.h> | |
35 | #include <string.h> | |
36 | ||
f67539c2 | 37 | #if !defined(__CYGWIN__) && !defined(_WIN32) |
7c673cae FG |
38 | # include <sys/mman.h> |
39 | #endif | |
40 | ||
41 | #include <iosfwd> | |
42 | #include <iomanip> | |
43 | #include <list> | |
2a845540 | 44 | #include <memory> |
7c673cae FG |
45 | #include <vector> |
46 | #include <string> | |
11fdf7f2 TL |
47 | #if __cplusplus >= 201703L |
48 | #include <string_view> | |
49 | #endif // __cplusplus >= 201703L | |
50 | ||
7c673cae FG |
51 | #include <exception> |
52 | #include <type_traits> | |
53 | ||
54 | #include "page.h" | |
55 | #include "crc32c.h" | |
56 | #include "buffer_fwd.h" | |
57 | ||
f67539c2 | 58 | |
7c673cae | 59 | #ifdef __CEPH__ |
11fdf7f2 | 60 | # include "include/ceph_assert.h" |
7c673cae FG |
61 | #else |
62 | # include <assert.h> | |
63 | #endif | |
64 | ||
65 | #include "inline_memory.h" | |
66 | ||
11fdf7f2 | 67 | #define CEPH_BUFFER_API |
7c673cae | 68 | |
11fdf7f2 TL |
69 | #ifdef HAVE_SEASTAR |
70 | namespace seastar { | |
71 | template <typename T> class temporary_buffer; | |
72 | namespace net { | |
73 | class packet; | |
74 | } | |
75 | } | |
76 | #endif // HAVE_SEASTAR | |
7c673cae | 77 | class deleter; |
9f95a23c TL |
78 | |
79 | template<typename T> class DencDumper; | |
7c673cae FG |
80 | |
81 | namespace ceph { | |
82 | ||
11fdf7f2 TL |
83 | template <class T> |
84 | struct nop_delete { | |
85 | void operator()(T*) {} | |
86 | }; | |
87 | ||
88 | // This is not unique_ptr-like smart pointer! It just signalizes ownership | |
89 | // but DOES NOT manage the resource. It WILL LEAK if not manually deleted. | |
90 | // It's rather a replacement for raw pointer than any other smart one. | |
91 | // | |
92 | // Considered options: | |
93 | // * unique_ptr with custom deleter implemented in .cc (would provide | |
94 | // the non-zero-cost resource management), | |
95 | // * GSL's owner<T*> (pretty neat but would impose an extra depedency), | |
96 | // * unique_ptr with nop deleter, | |
97 | // * raw pointer (doesn't embed ownership enforcement - std::move). | |
98 | template <class T> | |
99 | struct unique_leakable_ptr : public std::unique_ptr<T, ceph::nop_delete<T>> { | |
100 | using std::unique_ptr<T, ceph::nop_delete<T>>::unique_ptr; | |
101 | }; | |
102 | ||
7c673cae | 103 | namespace buffer CEPH_BUFFER_API { |
9f95a23c | 104 | inline namespace v15_2_0 { |
494da23a | 105 | |
f67539c2 TL |
106 | /// Actual definitions in common/error_code.h |
107 | struct error; | |
108 | struct bad_alloc; | |
109 | struct end_of_buffer; | |
110 | struct malformed_input; | |
111 | struct error_code; | |
7c673cae | 112 | |
7c673cae FG |
113 | /// count of cached crc hits (matching input) |
114 | int get_cached_crc(); | |
115 | /// count of cached crc hits (mismatching input, required adjustment) | |
116 | int get_cached_crc_adjusted(); | |
117 | /// count of crc cache misses | |
118 | int get_missed_crc(); | |
119 | /// enable/disable tracking of cached crcs | |
120 | void track_cached_crc(bool b); | |
121 | ||
7c673cae FG |
122 | /* |
123 | * an abstract raw buffer. with a reference count. | |
124 | */ | |
125 | class raw; | |
126 | class raw_malloc; | |
127 | class raw_static; | |
7c673cae FG |
128 | class raw_posix_aligned; |
129 | class raw_hack_aligned; | |
31f18b77 | 130 | class raw_claimed_char; |
7c673cae FG |
131 | class raw_unshareable; // diagnostic, unshareable char buffer |
132 | class raw_combined; | |
133 | class raw_claim_buffer; | |
134 | ||
135 | ||
7c673cae | 136 | /* |
11fdf7f2 | 137 | * named constructors |
7c673cae | 138 | */ |
11fdf7f2 TL |
139 | ceph::unique_leakable_ptr<raw> copy(const char *c, unsigned len); |
140 | ceph::unique_leakable_ptr<raw> create(unsigned len); | |
f67539c2 | 141 | ceph::unique_leakable_ptr<raw> create(unsigned len, char c); |
11fdf7f2 | 142 | ceph::unique_leakable_ptr<raw> create_in_mempool(unsigned len, int mempool); |
9f95a23c TL |
143 | ceph::unique_leakable_ptr<raw> claim_char(unsigned len, char *buf); |
144 | ceph::unique_leakable_ptr<raw> create_malloc(unsigned len); | |
145 | ceph::unique_leakable_ptr<raw> claim_malloc(unsigned len, char *buf); | |
146 | ceph::unique_leakable_ptr<raw> create_static(unsigned len, char *buf); | |
11fdf7f2 TL |
147 | ceph::unique_leakable_ptr<raw> create_aligned(unsigned len, unsigned align); |
148 | ceph::unique_leakable_ptr<raw> create_aligned_in_mempool(unsigned len, unsigned align, int mempool); | |
149 | ceph::unique_leakable_ptr<raw> create_page_aligned(unsigned len); | |
150 | ceph::unique_leakable_ptr<raw> create_small_page_aligned(unsigned len); | |
9f95a23c | 151 | ceph::unique_leakable_ptr<raw> claim_buffer(unsigned len, char *buf, deleter del); |
7c673cae | 152 | |
11fdf7f2 TL |
153 | #ifdef HAVE_SEASTAR |
154 | /// create a raw buffer to wrap seastar cpu-local memory, using foreign_ptr to | |
155 | /// make it safe to share between cpus | |
1e59de90 | 156 | ceph::unique_leakable_ptr<buffer::raw> create(seastar::temporary_buffer<char>&& buf); |
11fdf7f2 TL |
157 | /// create a raw buffer to wrap seastar cpu-local memory, without the safety |
158 | /// of foreign_ptr. the caller must otherwise guarantee that the buffer ptr is | |
159 | /// destructed on this cpu | |
1e59de90 | 160 | ceph::unique_leakable_ptr<buffer::raw> create_local(seastar::temporary_buffer<char>&& buf); |
7c673cae FG |
161 | #endif |
162 | ||
163 | /* | |
164 | * a buffer pointer. references (a subsequence of) a raw buffer. | |
165 | */ | |
166 | class CEPH_BUFFER_API ptr { | |
9f95a23c TL |
167 | friend class list; |
168 | protected: | |
7c673cae FG |
169 | raw *_raw; |
170 | unsigned _off, _len; | |
11fdf7f2 | 171 | private: |
7c673cae FG |
172 | |
173 | void release(); | |
174 | ||
11fdf7f2 TL |
175 | template<bool is_const> |
176 | class iterator_impl { | |
7c673cae FG |
177 | const ptr *bp; ///< parent ptr |
178 | const char *start; ///< starting pointer into bp->c_str() | |
179 | const char *pos; ///< pointer into bp->c_str() | |
180 | const char *end_ptr; ///< pointer to bp->end_c_str() | |
11fdf7f2 | 181 | const bool deep; ///< if true, do not allow shallow ptr copies |
7c673cae | 182 | |
11fdf7f2 TL |
183 | iterator_impl(typename std::conditional<is_const, const ptr*, ptr*>::type p, |
184 | size_t offset, bool d) | |
7c673cae FG |
185 | : bp(p), |
186 | start(p->c_str() + offset), | |
187 | pos(start), | |
188 | end_ptr(p->end_c_str()), | |
11fdf7f2 TL |
189 | deep(d) |
190 | {} | |
7c673cae FG |
191 | |
192 | friend class ptr; | |
193 | ||
194 | public: | |
11fdf7f2 TL |
195 | using pointer = typename std::conditional<is_const, const char*, char *>::type; |
196 | pointer get_pos_add(size_t n) { | |
197 | auto r = pos; | |
9f95a23c | 198 | *this += n; |
7c673cae FG |
199 | return r; |
200 | } | |
7c673cae FG |
201 | ptr get_ptr(size_t len) { |
202 | if (deep) { | |
203 | return buffer::copy(get_pos_add(len), len); | |
204 | } else { | |
205 | size_t off = pos - bp->c_str(); | |
9f95a23c | 206 | *this += len; |
7c673cae FG |
207 | return ptr(*bp, off, len); |
208 | } | |
209 | } | |
7c673cae | 210 | |
f67539c2 | 211 | iterator_impl& operator+=(size_t len); |
7c673cae FG |
212 | |
213 | const char *get_pos() { | |
214 | return pos; | |
215 | } | |
216 | const char *get_end() { | |
217 | return end_ptr; | |
218 | } | |
219 | ||
220 | size_t get_offset() { | |
221 | return pos - start; | |
222 | } | |
223 | ||
224 | bool end() const { | |
225 | return pos == end_ptr; | |
226 | } | |
227 | }; | |
228 | ||
11fdf7f2 TL |
229 | public: |
230 | using const_iterator = iterator_impl<true>; | |
231 | using iterator = iterator_impl<false>; | |
232 | ||
233 | ptr() : _raw(nullptr), _off(0), _len(0) {} | |
11fdf7f2 | 234 | ptr(ceph::unique_leakable_ptr<raw> r); |
7c673cae FG |
235 | // cppcheck-suppress noExplicitConstructor |
236 | ptr(unsigned l); | |
237 | ptr(const char *d, unsigned l); | |
238 | ptr(const ptr& p); | |
239 | ptr(ptr&& p) noexcept; | |
240 | ptr(const ptr& p, unsigned o, unsigned l); | |
11fdf7f2 | 241 | ptr(const ptr& p, ceph::unique_leakable_ptr<raw> r); |
7c673cae FG |
242 | ptr& operator= (const ptr& p); |
243 | ptr& operator= (ptr&& p) noexcept; | |
244 | ~ptr() { | |
11fdf7f2 TL |
245 | // BE CAREFUL: this destructor is called also for hypercombined ptr_node. |
246 | // After freeing underlying raw, `*this` can become inaccessible as well! | |
7c673cae FG |
247 | release(); |
248 | } | |
249 | ||
250 | bool have_raw() const { return _raw ? true:false; } | |
251 | ||
11fdf7f2 | 252 | void swap(ptr& other) noexcept; |
7c673cae | 253 | |
11fdf7f2 | 254 | iterator begin(size_t offset=0) { |
7c673cae FG |
255 | return iterator(this, offset, false); |
256 | } | |
11fdf7f2 TL |
257 | const_iterator begin(size_t offset=0) const { |
258 | return const_iterator(this, offset, false); | |
259 | } | |
260 | const_iterator cbegin() const { | |
261 | return begin(); | |
262 | } | |
263 | const_iterator begin_deep(size_t offset=0) const { | |
264 | return const_iterator(this, offset, true); | |
7c673cae FG |
265 | } |
266 | ||
267 | // misc | |
7c673cae | 268 | bool is_aligned(unsigned align) const { |
f67539c2 | 269 | return ((uintptr_t)c_str() & (align-1)) == 0; |
7c673cae FG |
270 | } |
271 | bool is_page_aligned() const { return is_aligned(CEPH_PAGE_SIZE); } | |
272 | bool is_n_align_sized(unsigned align) const | |
273 | { | |
274 | return (length() % align) == 0; | |
275 | } | |
276 | bool is_n_page_sized() const { return is_n_align_sized(CEPH_PAGE_SIZE); } | |
277 | bool is_partial() const { | |
278 | return have_raw() && (start() > 0 || end() < raw_length()); | |
279 | } | |
280 | ||
3efd9988 FG |
281 | int get_mempool() const; |
282 | void reassign_to_mempool(int pool); | |
283 | void try_assign_to_mempool(int pool); | |
284 | ||
7c673cae | 285 | // accessors |
7c673cae FG |
286 | const char *c_str() const; |
287 | char *c_str(); | |
288 | const char *end_c_str() const; | |
289 | char *end_c_str(); | |
290 | unsigned length() const { return _len; } | |
291 | unsigned offset() const { return _off; } | |
292 | unsigned start() const { return _off; } | |
293 | unsigned end() const { return _off + _len; } | |
294 | unsigned unused_tail_length() const; | |
295 | const char& operator[](unsigned n) const; | |
296 | char& operator[](unsigned n); | |
297 | ||
298 | const char *raw_c_str() const; | |
299 | unsigned raw_length() const; | |
300 | int raw_nref() const; | |
301 | ||
302 | void copy_out(unsigned o, unsigned l, char *dest) const; | |
303 | ||
31f18b77 | 304 | unsigned wasted() const; |
7c673cae FG |
305 | |
306 | int cmp(const ptr& o) const; | |
307 | bool is_zero() const; | |
308 | ||
309 | // modifiers | |
310 | void set_offset(unsigned o) { | |
11fdf7f2 TL |
311 | #ifdef __CEPH__ |
312 | ceph_assert(raw_length() >= o); | |
313 | #else | |
7c673cae | 314 | assert(raw_length() >= o); |
11fdf7f2 | 315 | #endif |
7c673cae FG |
316 | _off = o; |
317 | } | |
318 | void set_length(unsigned l) { | |
11fdf7f2 TL |
319 | #ifdef __CEPH__ |
320 | ceph_assert(raw_length() >= l); | |
321 | #else | |
7c673cae | 322 | assert(raw_length() >= l); |
11fdf7f2 | 323 | #endif |
7c673cae FG |
324 | _len = l; |
325 | } | |
326 | ||
327 | unsigned append(char c); | |
328 | unsigned append(const char *p, unsigned l); | |
11fdf7f2 TL |
329 | #if __cplusplus >= 201703L |
330 | inline unsigned append(std::string_view s) { | |
331 | return append(s.data(), s.length()); | |
332 | } | |
333 | #endif // __cplusplus >= 201703L | |
334 | void copy_in(unsigned o, unsigned l, const char *src, bool crc_reset = true); | |
335 | void zero(bool crc_reset = true); | |
336 | void zero(unsigned o, unsigned l, bool crc_reset = true); | |
337 | unsigned append_zeros(unsigned l); | |
338 | ||
339 | #ifdef HAVE_SEASTAR | |
340 | /// create a temporary_buffer, copying the ptr as its deleter | |
341 | operator seastar::temporary_buffer<char>() &; | |
342 | /// convert to temporary_buffer, stealing the ptr as its deleter | |
343 | operator seastar::temporary_buffer<char>() &&; | |
344 | #endif // HAVE_SEASTAR | |
345 | ||
346 | }; | |
347 | ||
7c673cae | 348 | |
11fdf7f2 TL |
349 | struct ptr_hook { |
350 | mutable ptr_hook* next; | |
351 | ||
352 | ptr_hook() = default; | |
353 | ptr_hook(ptr_hook* const next) | |
354 | : next(next) { | |
355 | } | |
7c673cae FG |
356 | }; |
357 | ||
11fdf7f2 TL |
358 | class ptr_node : public ptr_hook, public ptr { |
359 | public: | |
360 | struct cloner { | |
361 | ptr_node* operator()(const ptr_node& clone_this); | |
362 | }; | |
363 | struct disposer { | |
364 | void operator()(ptr_node* const delete_this) { | |
f67539c2 | 365 | if (!__builtin_expect(dispose_if_hypercombined(delete_this), 0)) { |
11fdf7f2 TL |
366 | delete delete_this; |
367 | } | |
368 | } | |
369 | }; | |
370 | ||
371 | ~ptr_node() = default; | |
372 | ||
373 | static std::unique_ptr<ptr_node, disposer> | |
374 | create(ceph::unique_leakable_ptr<raw> r) { | |
375 | return create_hypercombined(std::move(r)); | |
376 | } | |
9f95a23c TL |
377 | static std::unique_ptr<ptr_node, disposer> |
378 | create(const unsigned l) { | |
11fdf7f2 TL |
379 | return create_hypercombined(buffer::create(l)); |
380 | } | |
381 | template <class... Args> | |
9f95a23c TL |
382 | static std::unique_ptr<ptr_node, disposer> |
383 | create(Args&&... args) { | |
11fdf7f2 TL |
384 | return std::unique_ptr<ptr_node, disposer>( |
385 | new ptr_node(std::forward<Args>(args)...)); | |
386 | } | |
387 | ||
388 | static ptr_node* copy_hypercombined(const ptr_node& copy_this); | |
7c673cae | 389 | |
11fdf7f2 | 390 | private: |
f67539c2 TL |
391 | friend list; |
392 | ||
11fdf7f2 TL |
393 | template <class... Args> |
394 | ptr_node(Args&&... args) : ptr(std::forward<Args>(args)...) { | |
395 | } | |
396 | ptr_node(const ptr_node&) = default; | |
397 | ||
398 | ptr& operator= (const ptr& p) = delete; | |
399 | ptr& operator= (ptr&& p) noexcept = delete; | |
400 | ptr_node& operator= (const ptr_node& p) = delete; | |
401 | ptr_node& operator= (ptr_node&& p) noexcept = delete; | |
402 | void swap(ptr& other) noexcept = delete; | |
403 | void swap(ptr_node& other) noexcept = delete; | |
404 | ||
405 | static bool dispose_if_hypercombined(ptr_node* delete_this); | |
11fdf7f2 TL |
406 | static std::unique_ptr<ptr_node, disposer> create_hypercombined( |
407 | ceph::unique_leakable_ptr<raw> r); | |
408 | }; | |
7c673cae FG |
409 | /* |
410 | * list - the useful bit! | |
411 | */ | |
412 | ||
413 | class CEPH_BUFFER_API list { | |
7c673cae | 414 | public: |
11fdf7f2 TL |
415 | // this the very low-level implementation of singly linked list |
416 | // ceph::buffer::list is built on. We don't use intrusive slist | |
417 | // of Boost (or any other 3rd party) to save extra dependencies | |
418 | // in our public headers. | |
419 | class buffers_t { | |
420 | // _root.next can be thought as _head | |
421 | ptr_hook _root; | |
422 | ptr_hook* _tail; | |
11fdf7f2 TL |
423 | |
424 | public: | |
425 | template <class T> | |
426 | class buffers_iterator { | |
427 | typename std::conditional< | |
428 | std::is_const<T>::value, const ptr_hook*, ptr_hook*>::type cur; | |
429 | template <class U> friend class buffers_iterator; | |
430 | public: | |
431 | using value_type = T; | |
432 | using reference = typename std::add_lvalue_reference<T>::type; | |
433 | using pointer = typename std::add_pointer<T>::type; | |
434 | using difference_type = std::ptrdiff_t; | |
435 | using iterator_category = std::forward_iterator_tag; | |
436 | ||
437 | template <class U> | |
438 | buffers_iterator(U* const p) | |
439 | : cur(p) { | |
440 | } | |
1e59de90 TL |
441 | // copy constructor |
442 | buffers_iterator(const buffers_iterator<T>& other) | |
443 | : cur(other.cur) { | |
444 | } | |
445 | // converting constructor, from iterator -> const_iterator only | |
446 | template <class U, typename std::enable_if< | |
447 | std::is_const<T>::value && !std::is_const<U>::value, int>::type = 0> | |
11fdf7f2 TL |
448 | buffers_iterator(const buffers_iterator<U>& other) |
449 | : cur(other.cur) { | |
450 | } | |
451 | buffers_iterator() = default; | |
452 | ||
453 | T& operator*() const { | |
454 | return *reinterpret_cast<T*>(cur); | |
455 | } | |
456 | T* operator->() const { | |
457 | return reinterpret_cast<T*>(cur); | |
458 | } | |
459 | ||
460 | buffers_iterator& operator++() { | |
461 | cur = cur->next; | |
462 | return *this; | |
463 | } | |
464 | buffers_iterator operator++(int) { | |
465 | const auto temp(*this); | |
466 | ++*this; | |
467 | return temp; | |
468 | } | |
469 | ||
470 | template <class U> | |
471 | buffers_iterator& operator=(buffers_iterator<U>& other) { | |
472 | cur = other.cur; | |
473 | return *this; | |
474 | } | |
475 | ||
476 | bool operator==(const buffers_iterator& rhs) const { | |
477 | return cur == rhs.cur; | |
478 | } | |
479 | bool operator!=(const buffers_iterator& rhs) const { | |
480 | return !(*this==rhs); | |
481 | } | |
11fdf7f2 TL |
482 | }; |
483 | ||
484 | typedef buffers_iterator<const ptr_node> const_iterator; | |
485 | typedef buffers_iterator<ptr_node> iterator; | |
486 | ||
487 | typedef const ptr_node& const_reference; | |
488 | typedef ptr_node& reference; | |
489 | ||
490 | buffers_t() | |
491 | : _root(&_root), | |
9f95a23c | 492 | _tail(&_root) { |
11fdf7f2 TL |
493 | } |
494 | buffers_t(const buffers_t&) = delete; | |
495 | buffers_t(buffers_t&& other) | |
496 | : _root(other._root.next == &other._root ? &_root : other._root.next), | |
9f95a23c | 497 | _tail(other._tail == &other._root ? &_root : other._tail) { |
11fdf7f2 TL |
498 | other._root.next = &other._root; |
499 | other._tail = &other._root; | |
11fdf7f2 TL |
500 | |
501 | _tail->next = &_root; | |
502 | } | |
503 | buffers_t& operator=(buffers_t&& other) { | |
504 | if (&other != this) { | |
505 | clear_and_dispose(); | |
506 | swap(other); | |
507 | } | |
508 | return *this; | |
509 | } | |
510 | ||
511 | void push_back(reference item) { | |
512 | item.next = &_root; | |
513 | // this updates _root.next when called on empty | |
514 | _tail->next = &item; | |
515 | _tail = &item; | |
11fdf7f2 TL |
516 | } |
517 | ||
518 | void push_front(reference item) { | |
519 | item.next = _root.next; | |
520 | _root.next = &item; | |
521 | _tail = _tail == &_root ? &item : _tail; | |
11fdf7f2 TL |
522 | } |
523 | ||
524 | // *_after | |
525 | iterator erase_after(const_iterator it) { | |
526 | const auto* to_erase = it->next; | |
527 | ||
528 | it->next = to_erase->next; | |
529 | _root.next = _root.next == to_erase ? to_erase->next : _root.next; | |
530 | _tail = _tail == to_erase ? (ptr_hook*)&*it : _tail; | |
11fdf7f2 TL |
531 | return it->next; |
532 | } | |
533 | ||
534 | void insert_after(const_iterator it, reference item) { | |
535 | item.next = it->next; | |
536 | it->next = &item; | |
537 | _root.next = it == end() ? &item : _root.next; | |
538 | _tail = const_iterator(_tail) == it ? &item : _tail; | |
11fdf7f2 TL |
539 | } |
540 | ||
541 | void splice_back(buffers_t& other) { | |
9f95a23c | 542 | if (other.empty()) { |
11fdf7f2 TL |
543 | return; |
544 | } | |
545 | ||
546 | other._tail->next = &_root; | |
547 | // will update root.next if empty() == true | |
548 | _tail->next = other._root.next; | |
549 | _tail = other._tail; | |
11fdf7f2 TL |
550 | |
551 | other._root.next = &other._root; | |
552 | other._tail = &other._root; | |
11fdf7f2 TL |
553 | } |
554 | ||
11fdf7f2 TL |
555 | bool empty() const { return _tail == &_root; } |
556 | ||
557 | const_iterator begin() const { | |
558 | return _root.next; | |
559 | } | |
560 | const_iterator before_begin() const { | |
561 | return &_root; | |
562 | } | |
563 | const_iterator end() const { | |
564 | return &_root; | |
565 | } | |
566 | iterator begin() { | |
567 | return _root.next; | |
568 | } | |
569 | iterator before_begin() { | |
570 | return &_root; | |
571 | } | |
572 | iterator end() { | |
573 | return &_root; | |
574 | } | |
575 | ||
576 | reference front() { | |
577 | return reinterpret_cast<reference>(*_root.next); | |
578 | } | |
579 | reference back() { | |
580 | return reinterpret_cast<reference>(*_tail); | |
581 | } | |
582 | const_reference front() const { | |
583 | return reinterpret_cast<const_reference>(*_root.next); | |
584 | } | |
585 | const_reference back() const { | |
586 | return reinterpret_cast<const_reference>(*_tail); | |
587 | } | |
588 | ||
589 | void clone_from(const buffers_t& other) { | |
590 | clear_and_dispose(); | |
591 | for (auto& node : other) { | |
592 | ptr_node* clone = ptr_node::cloner()(node); | |
593 | push_back(*clone); | |
594 | } | |
595 | } | |
596 | void clear_and_dispose() { | |
1e59de90 TL |
597 | ptr_node::disposer dispose; |
598 | for (auto it = begin(), e = end(); it != e; /* nop */) { | |
599 | auto& node = *it++; | |
600 | dispose(&node); | |
601 | } | |
602 | _tail = &_root; | |
603 | _root.next = _tail; | |
11fdf7f2 TL |
604 | } |
605 | iterator erase_after_and_dispose(iterator it) { | |
606 | auto* to_dispose = &*std::next(it); | |
607 | auto ret = erase_after(it); | |
608 | ptr_node::disposer()(to_dispose); | |
609 | return ret; | |
610 | } | |
611 | ||
612 | void swap(buffers_t& other) { | |
613 | const auto copy_root = _root; | |
614 | _root.next = \ | |
615 | other._root.next == &other._root ? &this->_root : other._root.next; | |
616 | other._root.next = \ | |
617 | copy_root.next == &_root ? &other._root : copy_root.next; | |
618 | ||
619 | const auto copy_tail = _tail; | |
620 | _tail = other._tail == &other._root ? &this->_root : other._tail; | |
621 | other._tail = copy_tail == &_root ? &other._root : copy_tail; | |
622 | ||
623 | _tail->next = &_root; | |
624 | other._tail->next = &other._root; | |
11fdf7f2 TL |
625 | } |
626 | }; | |
627 | ||
7c673cae FG |
628 | class iterator; |
629 | ||
630 | private: | |
11fdf7f2 TL |
631 | // my private bits |
632 | buffers_t _buffers; | |
633 | ||
634 | // track bufferptr we can modify (especially ::append() to). Not all bptrs | |
635 | // bufferlist holds have this trait -- if somebody ::push_back(const ptr&), | |
636 | // he expects it won't change. | |
f67539c2 | 637 | ptr_node* _carriage; |
9f95a23c | 638 | unsigned _len, _num; |
11fdf7f2 | 639 | |
7c673cae | 640 | template <bool is_const> |
11fdf7f2 | 641 | class CEPH_BUFFER_API iterator_impl { |
7c673cae FG |
642 | protected: |
643 | typedef typename std::conditional<is_const, | |
644 | const list, | |
645 | list>::type bl_t; | |
646 | typedef typename std::conditional<is_const, | |
11fdf7f2 TL |
647 | const buffers_t, |
648 | buffers_t >::type list_t; | |
7c673cae | 649 | typedef typename std::conditional<is_const, |
11fdf7f2 TL |
650 | typename buffers_t::const_iterator, |
651 | typename buffers_t::iterator>::type list_iter_t; | |
7c673cae FG |
652 | bl_t* bl; |
653 | list_t* ls; // meh.. just here to avoid an extra pointer dereference.. | |
7c673cae | 654 | list_iter_t p; |
11fdf7f2 | 655 | unsigned off; // in bl |
7c673cae FG |
656 | unsigned p_off; // in *p |
657 | friend class iterator_impl<true>; | |
658 | ||
659 | public: | |
11fdf7f2 TL |
660 | using iterator_category = std::forward_iterator_tag; |
661 | using value_type = typename std::conditional<is_const, const char, char>::type; | |
662 | using difference_type = std::ptrdiff_t; | |
663 | using pointer = typename std::add_pointer<value_type>::type; | |
664 | using reference = typename std::add_lvalue_reference<value_type>::type; | |
665 | ||
7c673cae FG |
666 | // constructor. position. |
667 | iterator_impl() | |
668 | : bl(0), ls(0), off(0), p_off(0) {} | |
669 | iterator_impl(bl_t *l, unsigned o=0); | |
670 | iterator_impl(bl_t *l, unsigned o, list_iter_t ip, unsigned po) | |
11fdf7f2 | 671 | : bl(l), ls(&bl->_buffers), p(ip), off(o), p_off(po) {} |
7c673cae FG |
672 | iterator_impl(const list::iterator& i); |
673 | ||
674 | /// get current iterator offset in buffer::list | |
675 | unsigned get_off() const { return off; } | |
11fdf7f2 | 676 | |
7c673cae FG |
677 | /// get number of bytes remaining from iterator position to the end of the buffer::list |
678 | unsigned get_remaining() const { return bl->length() - off; } | |
679 | ||
680 | /// true if iterator is at the end of the buffer::list | |
681 | bool end() const { | |
682 | return p == ls->end(); | |
683 | //return off == bl->length(); | |
684 | } | |
7c673cae FG |
685 | void seek(unsigned o); |
686 | char operator*() const; | |
9f95a23c | 687 | iterator_impl& operator+=(unsigned o); |
7c673cae FG |
688 | iterator_impl& operator++(); |
689 | ptr get_current_ptr() const; | |
11fdf7f2 | 690 | bool is_pointing_same_raw(const ptr& other) const; |
7c673cae FG |
691 | |
692 | bl_t& get_bl() const { return *bl; } | |
693 | ||
694 | // copy data out. | |
695 | // note that these all _append_ to dest! | |
696 | void copy(unsigned len, char *dest); | |
697 | // deprecated, use copy_deep() | |
698 | void copy(unsigned len, ptr &dest) __attribute__((deprecated)); | |
699 | void copy_deep(unsigned len, ptr &dest); | |
700 | void copy_shallow(unsigned len, ptr &dest); | |
701 | void copy(unsigned len, list &dest); | |
702 | void copy(unsigned len, std::string &dest); | |
703 | void copy_all(list &dest); | |
704 | ||
705 | // get a pointer to the currenet iterator position, return the | |
706 | // number of bytes we can read from that position (up to want), | |
707 | // and advance the iterator by that amount. | |
708 | size_t get_ptr_and_advance(size_t want, const char **p); | |
709 | ||
710 | /// calculate crc from iterator position | |
711 | uint32_t crc32c(size_t length, uint32_t crc); | |
712 | ||
713 | friend bool operator==(const iterator_impl& lhs, | |
714 | const iterator_impl& rhs) { | |
715 | return &lhs.get_bl() == &rhs.get_bl() && lhs.get_off() == rhs.get_off(); | |
716 | } | |
717 | friend bool operator!=(const iterator_impl& lhs, | |
718 | const iterator_impl& rhs) { | |
719 | return &lhs.get_bl() != &rhs.get_bl() || lhs.get_off() != rhs.get_off(); | |
720 | } | |
721 | }; | |
722 | ||
723 | public: | |
724 | typedef iterator_impl<true> const_iterator; | |
725 | ||
726 | class CEPH_BUFFER_API iterator : public iterator_impl<false> { | |
727 | public: | |
728 | iterator() = default; | |
729 | iterator(bl_t *l, unsigned o=0); | |
730 | iterator(bl_t *l, unsigned o, list_iter_t ip, unsigned po); | |
7c673cae | 731 | // copy data in |
11fdf7f2 | 732 | void copy_in(unsigned len, const char *src, bool crc_reset = true); |
7c673cae | 733 | void copy_in(unsigned len, const list& otherl); |
11fdf7f2 | 734 | }; |
7c673cae | 735 | |
11fdf7f2 TL |
736 | struct reserve_t { |
737 | char* bp_data; | |
738 | unsigned* bp_len; | |
739 | unsigned* bl_len; | |
7c673cae FG |
740 | }; |
741 | ||
742 | class contiguous_appender { | |
11fdf7f2 TL |
743 | ceph::bufferlist& bl; |
744 | ceph::bufferlist::reserve_t space; | |
745 | char* pos; | |
7c673cae FG |
746 | bool deep; |
747 | ||
748 | /// running count of bytes appended that are not reflected by @pos | |
749 | size_t out_of_band_offset = 0; | |
750 | ||
11fdf7f2 TL |
751 | contiguous_appender(bufferlist& bl, size_t len, bool d) |
752 | : bl(bl), | |
753 | space(bl.obtain_contiguous_space(len)), | |
754 | pos(space.bp_data), | |
7c673cae | 755 | deep(d) { |
7c673cae FG |
756 | } |
757 | ||
758 | void flush_and_continue() { | |
11fdf7f2 TL |
759 | const size_t l = pos - space.bp_data; |
760 | *space.bp_len += l; | |
761 | *space.bl_len += l; | |
762 | space.bp_data = pos; | |
7c673cae FG |
763 | } |
764 | ||
765 | friend class list; | |
9f95a23c | 766 | template<typename Type> friend class ::DencDumper; |
7c673cae FG |
767 | |
768 | public: | |
769 | ~contiguous_appender() { | |
11fdf7f2 | 770 | flush_and_continue(); |
7c673cae FG |
771 | } |
772 | ||
773 | size_t get_out_of_band_offset() const { | |
774 | return out_of_band_offset; | |
775 | } | |
11fdf7f2 | 776 | void append(const char* __restrict__ p, size_t l) { |
7c673cae FG |
777 | maybe_inline_memcpy(pos, p, l, 16); |
778 | pos += l; | |
779 | } | |
780 | char *get_pos_add(size_t len) { | |
781 | char *r = pos; | |
782 | pos += len; | |
783 | return r; | |
784 | } | |
9f95a23c | 785 | char *get_pos() const { |
7c673cae FG |
786 | return pos; |
787 | } | |
788 | ||
789 | void append(const bufferptr& p) { | |
11fdf7f2 TL |
790 | const auto plen = p.length(); |
791 | if (!plen) { | |
7c673cae FG |
792 | return; |
793 | } | |
794 | if (deep) { | |
11fdf7f2 | 795 | append(p.c_str(), plen); |
7c673cae FG |
796 | } else { |
797 | flush_and_continue(); | |
11fdf7f2 TL |
798 | bl.append(p); |
799 | space = bl.obtain_contiguous_space(0); | |
800 | out_of_band_offset += plen; | |
7c673cae FG |
801 | } |
802 | } | |
803 | void append(const bufferlist& l) { | |
7c673cae FG |
804 | if (deep) { |
805 | for (const auto &p : l._buffers) { | |
806 | append(p.c_str(), p.length()); | |
807 | } | |
808 | } else { | |
809 | flush_and_continue(); | |
11fdf7f2 TL |
810 | bl.append(l); |
811 | space = bl.obtain_contiguous_space(0); | |
7c673cae FG |
812 | out_of_band_offset += l.length(); |
813 | } | |
814 | } | |
815 | ||
9f95a23c | 816 | size_t get_logical_offset() const { |
11fdf7f2 | 817 | return out_of_band_offset + (pos - space.bp_data); |
7c673cae FG |
818 | } |
819 | }; | |
820 | ||
821 | contiguous_appender get_contiguous_appender(size_t len, bool deep=false) { | |
11fdf7f2 | 822 | return contiguous_appender(*this, len, deep); |
7c673cae FG |
823 | } |
824 | ||
11fdf7f2 TL |
825 | class contiguous_filler { |
826 | friend buffer::list; | |
827 | char* pos; | |
828 | ||
829 | contiguous_filler(char* const pos) : pos(pos) {} | |
830 | ||
831 | public: | |
832 | void advance(const unsigned len) { | |
833 | pos += len; | |
834 | } | |
835 | void copy_in(const unsigned len, const char* const src) { | |
836 | memcpy(pos, src, len); | |
837 | advance(len); | |
838 | } | |
839 | char* c_str() { | |
840 | return pos; | |
841 | } | |
842 | }; | |
843 | // The contiguous_filler is supposed to be not costlier than a single | |
844 | // pointer. Keep it dumb, please. | |
845 | static_assert(sizeof(contiguous_filler) == sizeof(char*), | |
846 | "contiguous_filler should be no costlier than pointer"); | |
847 | ||
7c673cae | 848 | class page_aligned_appender { |
f67539c2 | 849 | bufferlist& bl; |
7c673cae | 850 | unsigned min_alloc; |
7c673cae FG |
851 | |
852 | page_aligned_appender(list *l, unsigned min_pages) | |
f67539c2 TL |
853 | : bl(*l), |
854 | min_alloc(min_pages * CEPH_PAGE_SIZE) { | |
855 | } | |
856 | ||
857 | void _refill(size_t len); | |
858 | ||
859 | template <class Func> | |
860 | void _append_common(size_t len, Func&& impl_f) { | |
861 | const auto free_in_last = bl.get_append_buffer_unused_tail_length(); | |
862 | const auto first_round = std::min(len, free_in_last); | |
863 | if (first_round) { | |
864 | impl_f(first_round); | |
865 | } | |
2a845540 TL |
866 | // no C++17 for the sake of the C++11 guarantees of librados, sorry. |
867 | const auto second_round = len - first_round; | |
868 | if (second_round) { | |
f67539c2 TL |
869 | _refill(second_round); |
870 | impl_f(second_round); | |
871 | } | |
872 | } | |
7c673cae FG |
873 | |
874 | friend class list; | |
875 | ||
876 | public: | |
f67539c2 TL |
877 | void append(const bufferlist& l) { |
878 | bl.append(l); | |
879 | bl.obtain_contiguous_space(0); | |
7c673cae FG |
880 | } |
881 | ||
f67539c2 TL |
882 | void append(const char* buf, size_t entire_len) { |
883 | _append_common(entire_len, | |
884 | [buf, this] (const size_t chunk_len) mutable { | |
885 | bl.append(buf, chunk_len); | |
886 | buf += chunk_len; | |
887 | }); | |
7c673cae FG |
888 | } |
889 | ||
f67539c2 TL |
890 | void append_zero(size_t entire_len) { |
891 | _append_common(entire_len, [this] (const size_t chunk_len) { | |
892 | bl.append_zero(chunk_len); | |
893 | }); | |
894 | } | |
895 | ||
896 | void substr_of(const list& bl, unsigned off, unsigned len) { | |
897 | for (const auto& bptr : bl.buffers()) { | |
898 | if (off >= bptr.length()) { | |
899 | off -= bptr.length(); | |
900 | continue; | |
7c673cae | 901 | } |
f67539c2 TL |
902 | const auto round_size = std::min(bptr.length() - off, len); |
903 | append(bptr.c_str() + off, round_size); | |
904 | len -= round_size; | |
905 | off = 0; | |
7c673cae FG |
906 | } |
907 | } | |
908 | }; | |
909 | ||
910 | page_aligned_appender get_page_aligned_appender(unsigned min_pages=1) { | |
911 | return page_aligned_appender(this, min_pages); | |
912 | } | |
913 | ||
914 | private: | |
11fdf7f2 TL |
915 | // always_empty_bptr has no underlying raw but its _len is always 0. |
916 | // This is useful for e.g. get_append_buffer_unused_tail_length() as | |
917 | // it allows to avoid conditionals on hot paths. | |
f67539c2 | 918 | static ptr_node always_empty_bptr; |
11fdf7f2 | 919 | ptr_node& refill_append_space(const unsigned len); |
7c673cae | 920 | |
f67539c2 TL |
921 | // for page_aligned_appender; never ever expose this publicly! |
922 | // carriage / append_buffer is just an implementation's detail. | |
923 | ptr& get_append_buffer() { | |
924 | return *_carriage; | |
925 | } | |
926 | ||
7c673cae FG |
927 | public: |
928 | // cons/des | |
11fdf7f2 TL |
929 | list() |
930 | : _carriage(&always_empty_bptr), | |
931 | _len(0), | |
9f95a23c | 932 | _num(0) { |
11fdf7f2 TL |
933 | } |
934 | // cppcheck-suppress noExplicitConstructor | |
7c673cae | 935 | // cppcheck-suppress noExplicitConstructor |
11fdf7f2 TL |
936 | list(unsigned prealloc) |
937 | : _carriage(&always_empty_bptr), | |
938 | _len(0), | |
9f95a23c | 939 | _num(0) { |
7c673cae FG |
940 | reserve(prealloc); |
941 | } | |
942 | ||
11fdf7f2 TL |
943 | list(const list& other) |
944 | : _carriage(&always_empty_bptr), | |
945 | _len(other._len), | |
9f95a23c | 946 | _num(other._num) { |
11fdf7f2 | 947 | _buffers.clone_from(other._buffers); |
7c673cae | 948 | } |
9f95a23c TL |
949 | |
950 | list(list&& other) noexcept | |
951 | : _buffers(std::move(other._buffers)), | |
952 | _carriage(other._carriage), | |
953 | _len(other._len), | |
954 | _num(other._num) { | |
955 | other.clear(); | |
956 | } | |
11fdf7f2 TL |
957 | |
958 | ~list() { | |
959 | _buffers.clear_and_dispose(); | |
960 | } | |
961 | ||
7c673cae FG |
962 | list& operator= (const list& other) { |
963 | if (this != &other) { | |
11fdf7f2 TL |
964 | _carriage = &always_empty_bptr; |
965 | _buffers.clone_from(other._buffers); | |
7c673cae | 966 | _len = other._len; |
9f95a23c | 967 | _num = other._num; |
7c673cae FG |
968 | } |
969 | return *this; | |
970 | } | |
11fdf7f2 | 971 | list& operator= (list&& other) noexcept { |
7c673cae | 972 | _buffers = std::move(other._buffers); |
11fdf7f2 | 973 | _carriage = other._carriage; |
7c673cae | 974 | _len = other._len; |
9f95a23c | 975 | _num = other._num; |
7c673cae FG |
976 | other.clear(); |
977 | return *this; | |
978 | } | |
979 | ||
f64942e4 | 980 | uint64_t get_wasted_space() const; |
9f95a23c | 981 | unsigned get_num_buffers() const { return _num; } |
11fdf7f2 TL |
982 | const ptr_node& front() const { return _buffers.front(); } |
983 | const ptr_node& back() const { return _buffers.back(); } | |
7c673cae | 984 | |
3efd9988 | 985 | int get_mempool() const; |
31f18b77 FG |
986 | void reassign_to_mempool(int pool); |
987 | void try_assign_to_mempool(int pool); | |
988 | ||
989 | size_t get_append_buffer_unused_tail_length() const { | |
11fdf7f2 | 990 | return _carriage->unused_tail_length(); |
31f18b77 FG |
991 | } |
992 | ||
11fdf7f2 | 993 | const buffers_t& buffers() const { return _buffers; } |
f67539c2 | 994 | buffers_t& mut_buffers() { return _buffers; } |
11fdf7f2 | 995 | void swap(list& other) noexcept; |
7c673cae FG |
996 | unsigned length() const { |
997 | #if 0 | |
998 | // DEBUG: verify _len | |
999 | unsigned len = 0; | |
1000 | for (std::list<ptr>::const_iterator it = _buffers.begin(); | |
1001 | it != _buffers.end(); | |
1002 | it++) { | |
1003 | len += (*it).length(); | |
1004 | } | |
11fdf7f2 TL |
1005 | #ifdef __CEPH__ |
1006 | ceph_assert(len == _len); | |
1007 | #else | |
7c673cae | 1008 | assert(len == _len); |
11fdf7f2 | 1009 | #endif // __CEPH__ |
7c673cae FG |
1010 | #endif |
1011 | return _len; | |
1012 | } | |
1013 | ||
7c673cae | 1014 | bool contents_equal(const buffer::list& other) const; |
9f95a23c | 1015 | bool contents_equal(const void* other, size_t length) const; |
7c673cae | 1016 | |
7c673cae FG |
1017 | bool is_provided_buffer(const char *dst) const; |
1018 | bool is_aligned(unsigned align) const; | |
1019 | bool is_page_aligned() const; | |
1020 | bool is_n_align_sized(unsigned align) const; | |
1021 | bool is_n_page_sized() const; | |
1022 | bool is_aligned_size_and_memory(unsigned align_size, | |
1023 | unsigned align_memory) const; | |
1024 | ||
1025 | bool is_zero() const; | |
1026 | ||
1027 | // modifiers | |
11fdf7f2 TL |
1028 | void clear() noexcept { |
1029 | _carriage = &always_empty_bptr; | |
1030 | _buffers.clear_and_dispose(); | |
7c673cae | 1031 | _len = 0; |
9f95a23c | 1032 | _num = 0; |
7c673cae | 1033 | } |
11fdf7f2 | 1034 | void push_back(const ptr& bp) { |
7c673cae FG |
1035 | if (bp.length() == 0) |
1036 | return; | |
11fdf7f2 | 1037 | _buffers.push_back(*ptr_node::create(bp).release()); |
7c673cae | 1038 | _len += bp.length(); |
9f95a23c | 1039 | _num += 1; |
7c673cae | 1040 | } |
11fdf7f2 | 1041 | void push_back(ptr&& bp) { |
7c673cae FG |
1042 | if (bp.length() == 0) |
1043 | return; | |
1044 | _len += bp.length(); | |
9f95a23c | 1045 | _num += 1; |
11fdf7f2 TL |
1046 | _buffers.push_back(*ptr_node::create(std::move(bp)).release()); |
1047 | _carriage = &always_empty_bptr; | |
7c673cae | 1048 | } |
11fdf7f2 TL |
1049 | void push_back(const ptr_node&) = delete; |
1050 | void push_back(ptr_node&) = delete; | |
1051 | void push_back(ptr_node&&) = delete; | |
1052 | void push_back(std::unique_ptr<ptr_node, ptr_node::disposer> bp) { | |
11fdf7f2 TL |
1053 | _carriage = bp.get(); |
1054 | _len += bp->length(); | |
9f95a23c | 1055 | _num += 1; |
11fdf7f2 | 1056 | _buffers.push_back(*bp.release()); |
7c673cae | 1057 | } |
9f95a23c TL |
1058 | void push_back(raw* const r) = delete; |
1059 | void push_back(ceph::unique_leakable_ptr<raw> r) { | |
1060 | _buffers.push_back(*ptr_node::create(std::move(r)).release()); | |
11fdf7f2 TL |
1061 | _carriage = &_buffers.back(); |
1062 | _len += _buffers.back().length(); | |
9f95a23c | 1063 | _num += 1; |
7c673cae FG |
1064 | } |
1065 | ||
1066 | void zero(); | |
1067 | void zero(unsigned o, unsigned l); | |
1068 | ||
1069 | bool is_contiguous() const; | |
1070 | void rebuild(); | |
11fdf7f2 | 1071 | void rebuild(std::unique_ptr<ptr_node, ptr_node::disposer> nb); |
7c673cae | 1072 | bool rebuild_aligned(unsigned align); |
b32b8144 FG |
1073 | // max_buffers = 0 mean don't care _buffers.size(), other |
1074 | // must make _buffers.size() <= max_buffers after rebuilding. | |
7c673cae | 1075 | bool rebuild_aligned_size_and_memory(unsigned align_size, |
b32b8144 FG |
1076 | unsigned align_memory, |
1077 | unsigned max_buffers = 0); | |
7c673cae FG |
1078 | bool rebuild_page_aligned(); |
1079 | ||
31f18b77 | 1080 | void reserve(size_t prealloc); |
7c673cae | 1081 | |
f67539c2 TL |
1082 | [[deprecated("in favor of operator=(list&&)")]] void claim(list& bl) { |
1083 | *this = std::move(bl); | |
1084 | } | |
9f95a23c | 1085 | void claim_append(list& bl); |
f67539c2 TL |
1086 | void claim_append(list&& bl) { |
1087 | claim_append(bl); | |
1088 | } | |
7c673cae | 1089 | |
7c673cae FG |
1090 | // copy with explicit volatile-sharing semantics |
1091 | void share(const list& bl) | |
1092 | { | |
1093 | if (this != &bl) { | |
1094 | clear(); | |
11fdf7f2 TL |
1095 | for (const auto& bp : bl._buffers) { |
1096 | _buffers.push_back(*ptr_node::create(bp).release()); | |
7c673cae | 1097 | } |
11fdf7f2 | 1098 | _len = bl._len; |
9f95a23c | 1099 | _num = bl._num; |
7c673cae FG |
1100 | } |
1101 | } | |
1102 | ||
11fdf7f2 TL |
1103 | #ifdef HAVE_SEASTAR |
1104 | /// convert the bufferlist into a network packet | |
1105 | operator seastar::net::packet() &&; | |
1106 | #endif | |
1107 | ||
9f95a23c TL |
1108 | iterator begin(size_t offset=0) { |
1109 | return iterator(this, offset); | |
7c673cae FG |
1110 | } |
1111 | iterator end() { | |
1112 | return iterator(this, _len, _buffers.end(), 0); | |
1113 | } | |
1114 | ||
9f95a23c TL |
1115 | const_iterator begin(size_t offset=0) const { |
1116 | return const_iterator(this, offset); | |
7c673cae | 1117 | } |
9f95a23c TL |
1118 | const_iterator cbegin(size_t offset=0) const { |
1119 | return begin(offset); | |
11fdf7f2 | 1120 | } |
7c673cae FG |
1121 | const_iterator end() const { |
1122 | return const_iterator(this, _len, _buffers.end(), 0); | |
1123 | } | |
1124 | ||
7c673cae FG |
1125 | void append(char c); |
1126 | void append(const char *data, unsigned len); | |
11fdf7f2 | 1127 | void append(std::string s) { |
7c673cae FG |
1128 | append(s.data(), s.length()); |
1129 | } | |
11fdf7f2 TL |
1130 | #if __cplusplus >= 201703L |
1131 | // To forcibly disambiguate between string and string_view in the | |
1132 | // case of arrays | |
1133 | template<std::size_t N> | |
1134 | void append(const char (&s)[N]) { | |
1135 | append(s, N); | |
1136 | } | |
1137 | void append(const char* s) { | |
1138 | append(s, strlen(s)); | |
1139 | } | |
1140 | void append(std::string_view s) { | |
1141 | append(s.data(), s.length()); | |
1142 | } | |
1143 | #endif // __cplusplus >= 201703L | |
7c673cae FG |
1144 | void append(const ptr& bp); |
1145 | void append(ptr&& bp); | |
1146 | void append(const ptr& bp, unsigned off, unsigned len); | |
1147 | void append(const list& bl); | |
f67539c2 TL |
1148 | /// append each non-empty line from the stream and add '\n', |
1149 | /// so a '\n' will be added even the stream does not end with EOL. | |
1150 | /// | |
1151 | /// For example, if the stream contains "ABC\n\nDEF", "ABC\nDEF\n" is | |
1152 | /// actually appended. | |
7c673cae | 1153 | void append(std::istream& in); |
11fdf7f2 | 1154 | contiguous_filler append_hole(unsigned len); |
7c673cae FG |
1155 | void append_zero(unsigned len); |
1156 | void prepend_zero(unsigned len); | |
11fdf7f2 | 1157 | |
f67539c2 | 1158 | reserve_t obtain_contiguous_space(const unsigned len); |
11fdf7f2 | 1159 | |
7c673cae FG |
1160 | /* |
1161 | * get a char | |
1162 | */ | |
1163 | const char& operator[](unsigned n) const; | |
1164 | char *c_str(); | |
1165 | std::string to_str() const; | |
1166 | ||
1167 | void substr_of(const list& other, unsigned off, unsigned len); | |
1168 | ||
7c673cae FG |
1169 | // funky modifer |
1170 | void splice(unsigned off, unsigned len, list *claim_by=0 /*, bufferlist& replace_with */); | |
1171 | void write(int off, int len, std::ostream& out) const; | |
1172 | ||
1173 | void encode_base64(list& o); | |
1174 | void decode_base64(list& o); | |
1175 | ||
1176 | void write_stream(std::ostream &out) const; | |
1177 | void hexdump(std::ostream &out, bool trailing_newline = true) const; | |
9f95a23c | 1178 | ssize_t pread_file(const char *fn, uint64_t off, uint64_t len, std::string *error); |
7c673cae FG |
1179 | int read_file(const char *fn, std::string *error); |
1180 | ssize_t read_fd(int fd, size_t len); | |
f67539c2 | 1181 | ssize_t recv_fd(int fd, size_t len); |
7c673cae FG |
1182 | int write_file(const char *fn, int mode=0644); |
1183 | int write_fd(int fd) const; | |
1184 | int write_fd(int fd, uint64_t offset) const; | |
f67539c2 | 1185 | int send_fd(int fd) const; |
7c673cae FG |
1186 | template<typename VectorT> |
1187 | void prepare_iov(VectorT *piov) const { | |
11fdf7f2 | 1188 | #ifdef __CEPH__ |
9f95a23c | 1189 | ceph_assert(_num <= IOV_MAX); |
11fdf7f2 | 1190 | #else |
9f95a23c | 1191 | assert(_num <= IOV_MAX); |
11fdf7f2 | 1192 | #endif |
9f95a23c | 1193 | piov->resize(_num); |
7c673cae FG |
1194 | unsigned n = 0; |
1195 | for (auto& p : _buffers) { | |
1196 | (*piov)[n].iov_base = (void *)p.c_str(); | |
1197 | (*piov)[n].iov_len = p.length(); | |
1198 | ++n; | |
1199 | } | |
1200 | } | |
20effc67 TL |
1201 | |
1202 | struct iovec_t { | |
1203 | uint64_t offset; | |
1204 | uint64_t length; | |
1205 | std::vector<iovec> iov; | |
1206 | }; | |
1207 | using iov_vec_t = std::vector<iovec_t>; | |
1208 | iov_vec_t prepare_iovs() const; | |
1209 | ||
7c673cae FG |
1210 | uint32_t crc32c(uint32_t crc) const; |
1211 | void invalidate_crc(); | |
31f18b77 FG |
1212 | |
1213 | // These functions return a bufferlist with a pointer to a single | |
1214 | // static buffer. They /must/ not outlive the memory they | |
1215 | // reference. | |
1216 | static list static_from_mem(char* c, size_t l); | |
1217 | static list static_from_cstring(char* c); | |
1218 | static list static_from_string(std::string& s); | |
7c673cae FG |
1219 | }; |
1220 | ||
9f95a23c | 1221 | } // inline namespace v15_2_0 |
11fdf7f2 | 1222 | |
7c673cae FG |
1223 | /* |
1224 | * efficient hash of one or more bufferlists | |
1225 | */ | |
1226 | ||
1227 | class hash { | |
1228 | uint32_t crc; | |
1229 | ||
1230 | public: | |
1231 | hash() : crc(0) { } | |
1232 | // cppcheck-suppress noExplicitConstructor | |
1233 | hash(uint32_t init) : crc(init) { } | |
1234 | ||
31f18b77 | 1235 | void update(const buffer::list& bl) { |
7c673cae FG |
1236 | crc = bl.crc32c(crc); |
1237 | } | |
1238 | ||
1239 | uint32_t digest() { | |
1240 | return crc; | |
1241 | } | |
1242 | }; | |
1243 | ||
f67539c2 TL |
1244 | inline bool operator==(const bufferlist &lhs, const bufferlist &rhs) { |
1245 | if (lhs.length() != rhs.length()) | |
1246 | return false; | |
1247 | return std::equal(lhs.begin(), lhs.end(), rhs.begin()); | |
7c673cae | 1248 | } |
f67539c2 TL |
1249 | |
1250 | inline bool operator<(const bufferlist& lhs, const bufferlist& rhs) { | |
1251 | auto l = lhs.begin(), r = rhs.begin(); | |
1252 | for (; l != lhs.end() && r != rhs.end(); ++l, ++r) { | |
1253 | if (*l < *r) return true; | |
1254 | if (*l > *r) return false; | |
7c673cae | 1255 | } |
f67539c2 | 1256 | return (l == lhs.end()) && (r != rhs.end()); // lhs.length() < rhs.length() |
7c673cae FG |
1257 | } |
1258 | ||
f67539c2 TL |
1259 | inline bool operator<=(const bufferlist& lhs, const bufferlist& rhs) { |
1260 | auto l = lhs.begin(), r = rhs.begin(); | |
1261 | for (; l != lhs.end() && r != rhs.end(); ++l, ++r) { | |
1262 | if (*l < *r) return true; | |
1263 | if (*l > *r) return false; | |
7c673cae | 1264 | } |
f67539c2 | 1265 | return l == lhs.end(); // lhs.length() <= rhs.length() |
7c673cae | 1266 | } |
f67539c2 TL |
1267 | |
1268 | inline bool operator!=(const bufferlist &l, const bufferlist &r) { | |
1269 | return !(l == r); | |
7c673cae | 1270 | } |
f67539c2 TL |
1271 | inline bool operator>(const bufferlist& lhs, const bufferlist& rhs) { |
1272 | return rhs < lhs; | |
1273 | } | |
1274 | inline bool operator>=(const bufferlist& lhs, const bufferlist& rhs) { | |
1275 | return rhs <= lhs; | |
7c673cae | 1276 | } |
7c673cae FG |
1277 | |
1278 | std::ostream& operator<<(std::ostream& out, const buffer::ptr& bp); | |
1279 | ||
494da23a | 1280 | std::ostream& operator<<(std::ostream& out, const buffer::raw &r); |
7c673cae FG |
1281 | |
1282 | std::ostream& operator<<(std::ostream& out, const buffer::list& bl); | |
1283 | ||
31f18b77 | 1284 | inline bufferhash& operator<<(bufferhash& l, const bufferlist &r) { |
7c673cae FG |
1285 | l.update(r); |
1286 | return l; | |
1287 | } | |
1288 | ||
11fdf7f2 | 1289 | } // namespace buffer |
7c673cae | 1290 | |
11fdf7f2 | 1291 | } // namespace ceph |
7c673cae | 1292 | |
f67539c2 | 1293 | |
7c673cae | 1294 | #endif |