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