1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
17 #if defined(__linux__) || defined(__FreeBSD__)
23 # define _XOPEN_SOURCE 600
29 #if defined(__linux__) // For malloc(2).
37 #if !defined(__CYGWIN__) && !defined(_WIN32)
38 # include <sys/mman.h>
47 #if __cplusplus >= 201703L
48 #include <string_view>
49 #endif // __cplusplus >= 201703L
52 #include <type_traits>
56 #include "buffer_fwd.h"
60 # include "include/ceph_assert.h"
65 #include "inline_memory.h"
67 #define CEPH_BUFFER_API
71 template <typename T
> class temporary_buffer
;
76 #endif // HAVE_SEASTAR
79 template<typename T
> class DencDumper
;
85 void operator()(T
*) {}
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.
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).
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
;
103 namespace buffer CEPH_BUFFER_API
{
104 inline namespace v15_2_0
{
106 /// Actual definitions in common/error_code.h
109 struct end_of_buffer
;
110 struct malformed_input
;
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
);
123 * an abstract raw buffer. with a reference count.
128 class raw_posix_aligned
;
129 class raw_hack_aligned
;
130 class raw_claimed_char
;
131 class raw_unshareable
; // diagnostic, unshareable char buffer
133 class raw_claim_buffer
;
139 ceph::unique_leakable_ptr
<raw
> copy(const char *c
, unsigned len
);
140 ceph::unique_leakable_ptr
<raw
> create(unsigned len
);
141 ceph::unique_leakable_ptr
<raw
> create(unsigned len
, char c
);
142 ceph::unique_leakable_ptr
<raw
> create_in_mempool(unsigned len
, int mempool
);
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
);
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
);
151 ceph::unique_leakable_ptr
<raw
> claim_buffer(unsigned len
, char *buf
, deleter del
);
154 /// create a raw buffer to wrap seastar cpu-local memory, using foreign_ptr to
155 /// make it safe to share between cpus
156 ceph::unique_leakable_ptr
<buffer::raw
> create(seastar::temporary_buffer
<char>&& buf
);
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
160 ceph::unique_leakable_ptr
<buffer::raw
> create_local(seastar::temporary_buffer
<char>&& buf
);
164 * a buffer pointer. references (a subsequence of) a raw buffer.
166 class CEPH_BUFFER_API ptr
{
175 template<bool is_const
>
176 class iterator_impl
{
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()
181 const bool deep
; ///< if true, do not allow shallow ptr copies
183 iterator_impl(typename
std::conditional
<is_const
, const ptr
*, ptr
*>::type p
,
184 size_t offset
, bool d
)
186 start(p
->c_str() + offset
),
188 end_ptr(p
->end_c_str()),
195 using pointer
= typename
std::conditional
<is_const
, const char*, char *>::type
;
196 pointer
get_pos_add(size_t n
) {
201 ptr
get_ptr(size_t len
) {
203 return buffer::copy(get_pos_add(len
), len
);
205 size_t off
= pos
- bp
->c_str();
207 return ptr(*bp
, off
, len
);
211 iterator_impl
& operator+=(size_t len
);
213 const char *get_pos() {
216 const char *get_end() {
220 size_t get_offset() {
225 return pos
== end_ptr
;
230 using const_iterator
= iterator_impl
<true>;
231 using iterator
= iterator_impl
<false>;
233 ptr() : _raw(nullptr), _off(0), _len(0) {}
234 ptr(ceph::unique_leakable_ptr
<raw
> r
);
235 // cppcheck-suppress noExplicitConstructor
237 ptr(const char *d
, unsigned l
);
239 ptr(ptr
&& p
) noexcept
;
240 ptr(const ptr
& p
, unsigned o
, unsigned l
);
241 ptr(const ptr
& p
, ceph::unique_leakable_ptr
<raw
> r
);
242 ptr
& operator= (const ptr
& p
);
243 ptr
& operator= (ptr
&& p
) noexcept
;
245 // BE CAREFUL: this destructor is called also for hypercombined ptr_node.
246 // After freeing underlying raw, `*this` can become inaccessible as well!
250 bool have_raw() const { return _raw
? true:false; }
252 void swap(ptr
& other
) noexcept
;
254 iterator
begin(size_t offset
=0) {
255 return iterator(this, offset
, false);
257 const_iterator
begin(size_t offset
=0) const {
258 return const_iterator(this, offset
, false);
260 const_iterator
cbegin() const {
263 const_iterator
begin_deep(size_t offset
=0) const {
264 return const_iterator(this, offset
, true);
268 bool is_aligned(unsigned align
) const {
269 return ((uintptr_t)c_str() & (align
-1)) == 0;
271 bool is_page_aligned() const { return is_aligned(CEPH_PAGE_SIZE
); }
272 bool is_n_align_sized(unsigned align
) const
274 return (length() % align
) == 0;
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());
281 int get_mempool() const;
282 void reassign_to_mempool(int pool
);
283 void try_assign_to_mempool(int pool
);
286 const char *c_str() const;
288 const char *end_c_str() const;
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
);
298 const char *raw_c_str() const;
299 unsigned raw_length() const;
300 int raw_nref() const;
302 void copy_out(unsigned o
, unsigned l
, char *dest
) const;
304 unsigned wasted() const;
306 int cmp(const ptr
& o
) const;
307 bool is_zero() const;
310 void set_offset(unsigned o
) {
312 ceph_assert(raw_length() >= o
);
314 assert(raw_length() >= o
);
318 void set_length(unsigned l
) {
320 ceph_assert(raw_length() >= l
);
322 assert(raw_length() >= l
);
327 unsigned append(char c
);
328 unsigned append(const char *p
, unsigned l
);
329 #if __cplusplus >= 201703L
330 inline unsigned append(std::string_view s
) {
331 return append(s
.data(), s
.length());
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
);
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
350 mutable ptr_hook
* next
;
352 ptr_hook() = default;
353 ptr_hook(ptr_hook
* const next
)
358 class ptr_node
: public ptr_hook
, public ptr
{
361 ptr_node
* operator()(const ptr_node
& clone_this
);
364 void operator()(ptr_node
* const delete_this
) {
365 if (!__builtin_expect(dispose_if_hypercombined(delete_this
), 0)) {
371 ~ptr_node() = default;
373 static std::unique_ptr
<ptr_node
, disposer
>
374 create(ceph::unique_leakable_ptr
<raw
> r
) {
375 return create_hypercombined(std::move(r
));
377 static std::unique_ptr
<ptr_node
, disposer
>
378 create(const unsigned l
) {
379 return create_hypercombined(buffer::create(l
));
381 template <class... Args
>
382 static std::unique_ptr
<ptr_node
, disposer
>
383 create(Args
&&... args
) {
384 return std::unique_ptr
<ptr_node
, disposer
>(
385 new ptr_node(std::forward
<Args
>(args
)...));
388 static ptr_node
* copy_hypercombined(const ptr_node
& copy_this
);
393 template <class... Args
>
394 ptr_node(Args
&&... args
) : ptr(std::forward
<Args
>(args
)...) {
396 ptr_node(const ptr_node
&) = default;
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;
405 static bool dispose_if_hypercombined(ptr_node
* delete_this
);
406 static std::unique_ptr
<ptr_node
, disposer
> create_hypercombined(
407 ceph::unique_leakable_ptr
<raw
> r
);
410 * list - the useful bit!
413 class CEPH_BUFFER_API list
{
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.
420 // _root.next can be thought as _head
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
;
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
;
438 buffers_iterator(U
* const p
)
442 buffers_iterator(const buffers_iterator
<T
>& other
)
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>
448 buffers_iterator(const buffers_iterator
<U
>& other
)
451 buffers_iterator() = default;
453 T
& operator*() const {
454 return *reinterpret_cast<T
*>(cur
);
456 T
* operator->() const {
457 return reinterpret_cast<T
*>(cur
);
460 buffers_iterator
& operator++() {
464 buffers_iterator
operator++(int) {
465 const auto temp(*this);
471 buffers_iterator
& operator=(buffers_iterator
<U
>& other
) {
476 bool operator==(const buffers_iterator
& rhs
) const {
477 return cur
== rhs
.cur
;
479 bool operator!=(const buffers_iterator
& rhs
) const {
480 return !(*this==rhs
);
484 typedef buffers_iterator
<const ptr_node
> const_iterator
;
485 typedef buffers_iterator
<ptr_node
> iterator
;
487 typedef const ptr_node
& const_reference
;
488 typedef ptr_node
& reference
;
494 buffers_t(const buffers_t
&) = delete;
495 buffers_t(buffers_t
&& other
)
496 : _root(other
._root
.next
== &other
._root
? &_root
: other
._root
.next
),
497 _tail(other
._tail
== &other
._root
? &_root
: other
._tail
) {
498 other
._root
.next
= &other
._root
;
499 other
._tail
= &other
._root
;
501 _tail
->next
= &_root
;
503 buffers_t
& operator=(buffers_t
&& other
) {
504 if (&other
!= this) {
511 void push_back(reference item
) {
513 // this updates _root.next when called on empty
518 void push_front(reference item
) {
519 item
.next
= _root
.next
;
521 _tail
= _tail
== &_root
? &item
: _tail
;
525 iterator
erase_after(const_iterator it
) {
526 const auto* to_erase
= it
->next
;
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
;
534 void insert_after(const_iterator it
, reference item
) {
535 item
.next
= it
->next
;
537 _root
.next
= it
== end() ? &item
: _root
.next
;
538 _tail
= const_iterator(_tail
) == it
? &item
: _tail
;
541 void splice_back(buffers_t
& other
) {
546 other
._tail
->next
= &_root
;
547 // will update root.next if empty() == true
548 _tail
->next
= other
._root
.next
;
551 other
._root
.next
= &other
._root
;
552 other
._tail
= &other
._root
;
555 bool empty() const { return _tail
== &_root
; }
557 const_iterator
begin() const {
560 const_iterator
before_begin() const {
563 const_iterator
end() const {
569 iterator
before_begin() {
577 return reinterpret_cast<reference
>(*_root
.next
);
580 return reinterpret_cast<reference
>(*_tail
);
582 const_reference
front() const {
583 return reinterpret_cast<const_reference
>(*_root
.next
);
585 const_reference
back() const {
586 return reinterpret_cast<const_reference
>(*_tail
);
589 void clone_from(const buffers_t
& other
) {
591 for (auto& node
: other
) {
592 ptr_node
* clone
= ptr_node::cloner()(node
);
596 void clear_and_dispose() {
597 ptr_node::disposer dispose
;
598 for (auto it
= begin(), e
= end(); it
!= e
; /* nop */) {
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
);
612 void swap(buffers_t
& other
) {
613 const auto copy_root
= _root
;
615 other
._root
.next
== &other
._root
? &this->_root
: other
._root
.next
;
617 copy_root
.next
== &_root
? &other
._root
: copy_root
.next
;
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
;
623 _tail
->next
= &_root
;
624 other
._tail
->next
= &other
._root
;
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.
640 template <bool is_const
>
641 class CEPH_BUFFER_API iterator_impl
{
643 typedef typename
std::conditional
<is_const
,
646 typedef typename
std::conditional
<is_const
,
648 buffers_t
>::type list_t
;
649 typedef typename
std::conditional
<is_const
,
650 typename
buffers_t::const_iterator
,
651 typename
buffers_t::iterator
>::type list_iter_t
;
653 list_t
* ls
; // meh.. just here to avoid an extra pointer dereference..
655 unsigned off
; // in bl
656 unsigned p_off
; // in *p
657 friend class iterator_impl
<true>;
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
;
666 // constructor. position.
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
)
671 : bl(l
), ls(&bl
->_buffers
), p(ip
), off(o
), p_off(po
) {}
672 iterator_impl(const list::iterator
& i
);
674 /// get current iterator offset in buffer::list
675 unsigned get_off() const { return off
; }
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
; }
680 /// true if iterator is at the end of the buffer::list
682 return p
== ls
->end();
683 //return off == bl->length();
685 void seek(unsigned o
);
686 char operator*() const;
687 iterator_impl
& operator+=(unsigned o
);
688 iterator_impl
& operator++();
689 ptr
get_current_ptr() const;
690 bool is_pointing_same_raw(const ptr
& other
) const;
692 bl_t
& get_bl() const { return *bl
; }
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
);
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
);
710 /// calculate crc from iterator position
711 uint32_t crc32c(size_t length
, uint32_t crc
);
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();
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();
724 typedef iterator_impl
<true> const_iterator
;
726 class CEPH_BUFFER_API iterator
: public iterator_impl
<false> {
728 iterator() = default;
729 iterator(bl_t
*l
, unsigned o
=0);
730 iterator(bl_t
*l
, unsigned o
, list_iter_t ip
, unsigned po
);
732 void copy_in(unsigned len
, const char *src
, bool crc_reset
= true);
733 void copy_in(unsigned len
, const list
& otherl
);
742 class contiguous_appender
{
743 ceph::bufferlist
& bl
;
744 ceph::bufferlist::reserve_t space
;
748 /// running count of bytes appended that are not reflected by @pos
749 size_t out_of_band_offset
= 0;
751 contiguous_appender(bufferlist
& bl
, size_t len
, bool d
)
753 space(bl
.obtain_contiguous_space(len
)),
758 void flush_and_continue() {
759 const size_t l
= pos
- space
.bp_data
;
766 template<typename Type
> friend class ::DencDumper
;
769 ~contiguous_appender() {
770 flush_and_continue();
773 size_t get_out_of_band_offset() const {
774 return out_of_band_offset
;
776 void append(const char* __restrict__ p
, size_t l
) {
777 maybe_inline_memcpy(pos
, p
, l
, 16);
780 char *get_pos_add(size_t len
) {
785 char *get_pos() const {
789 void append(const bufferptr
& p
) {
790 const auto plen
= p
.length();
795 append(p
.c_str(), plen
);
797 flush_and_continue();
799 space
= bl
.obtain_contiguous_space(0);
800 out_of_band_offset
+= plen
;
803 void append(const bufferlist
& l
) {
805 for (const auto &p
: l
._buffers
) {
806 append(p
.c_str(), p
.length());
809 flush_and_continue();
811 space
= bl
.obtain_contiguous_space(0);
812 out_of_band_offset
+= l
.length();
816 size_t get_logical_offset() const {
817 return out_of_band_offset
+ (pos
- space
.bp_data
);
821 contiguous_appender
get_contiguous_appender(size_t len
, bool deep
=false) {
822 return contiguous_appender(*this, len
, deep
);
825 class contiguous_filler
{
829 contiguous_filler(char* const pos
) : pos(pos
) {}
832 void advance(const unsigned len
) {
835 void copy_in(const unsigned len
, const char* const src
) {
836 memcpy(pos
, src
, len
);
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");
848 class page_aligned_appender
{
852 page_aligned_appender(list
*l
, unsigned min_pages
)
854 min_alloc(min_pages
* CEPH_PAGE_SIZE
) {
857 void _refill(size_t len
);
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
);
866 // no C++17 for the sake of the C++11 guarantees of librados, sorry.
867 const auto second_round
= len
- first_round
;
869 _refill(second_round
);
870 impl_f(second_round
);
877 void append(const bufferlist
& l
) {
879 bl
.obtain_contiguous_space(0);
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
);
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
);
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();
902 const auto round_size
= std::min(bptr
.length() - off
, len
);
903 append(bptr
.c_str() + off
, round_size
);
910 page_aligned_appender
get_page_aligned_appender(unsigned min_pages
=1) {
911 return page_aligned_appender(this, min_pages
);
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.
918 static ptr_node always_empty_bptr
;
919 ptr_node
& refill_append_space(const unsigned len
);
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() {
930 : _carriage(&always_empty_bptr
),
934 // cppcheck-suppress noExplicitConstructor
935 // cppcheck-suppress noExplicitConstructor
936 list(unsigned prealloc
)
937 : _carriage(&always_empty_bptr
),
943 list(const list
& other
)
944 : _carriage(&always_empty_bptr
),
947 _buffers
.clone_from(other
._buffers
);
950 list(list
&& other
) noexcept
951 : _buffers(std::move(other
._buffers
)),
952 _carriage(other
._carriage
),
959 _buffers
.clear_and_dispose();
962 list
& operator= (const list
& other
) {
963 if (this != &other
) {
964 _carriage
= &always_empty_bptr
;
965 _buffers
.clone_from(other
._buffers
);
971 list
& operator= (list
&& other
) noexcept
{
972 _buffers
= std::move(other
._buffers
);
973 _carriage
= other
._carriage
;
980 uint64_t get_wasted_space() const;
981 unsigned get_num_buffers() const { return _num
; }
982 const ptr_node
& front() const { return _buffers
.front(); }
983 const ptr_node
& back() const { return _buffers
.back(); }
985 int get_mempool() const;
986 void reassign_to_mempool(int pool
);
987 void try_assign_to_mempool(int pool
);
989 size_t get_append_buffer_unused_tail_length() const {
990 return _carriage
->unused_tail_length();
993 const buffers_t
& buffers() const { return _buffers
; }
994 buffers_t
& mut_buffers() { return _buffers
; }
995 void swap(list
& other
) noexcept
;
996 unsigned length() const {
998 // DEBUG: verify _len
1000 for (std::list
<ptr
>::const_iterator it
= _buffers
.begin();
1001 it
!= _buffers
.end();
1003 len
+= (*it
).length();
1006 ceph_assert(len
== _len
);
1008 assert(len
== _len
);
1014 bool contents_equal(const buffer::list
& other
) const;
1015 bool contents_equal(const void* other
, size_t length
) const;
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;
1025 bool is_zero() const;
1028 void clear() noexcept
{
1029 _carriage
= &always_empty_bptr
;
1030 _buffers
.clear_and_dispose();
1034 void push_back(const ptr
& bp
) {
1035 if (bp
.length() == 0)
1037 _buffers
.push_back(*ptr_node::create(bp
).release());
1038 _len
+= bp
.length();
1041 void push_back(ptr
&& bp
) {
1042 if (bp
.length() == 0)
1044 _len
+= bp
.length();
1046 _buffers
.push_back(*ptr_node::create(std::move(bp
)).release());
1047 _carriage
= &always_empty_bptr
;
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
) {
1053 _carriage
= bp
.get();
1054 _len
+= bp
->length();
1056 _buffers
.push_back(*bp
.release());
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());
1061 _carriage
= &_buffers
.back();
1062 _len
+= _buffers
.back().length();
1067 void zero(unsigned o
, unsigned l
);
1069 bool is_contiguous() const;
1071 void rebuild(std::unique_ptr
<ptr_node
, ptr_node::disposer
> nb
);
1072 bool rebuild_aligned(unsigned align
);
1073 // max_buffers = 0 mean don't care _buffers.size(), other
1074 // must make _buffers.size() <= max_buffers after rebuilding.
1075 bool rebuild_aligned_size_and_memory(unsigned align_size
,
1076 unsigned align_memory
,
1077 unsigned max_buffers
= 0);
1078 bool rebuild_page_aligned();
1080 void reserve(size_t prealloc
);
1082 [[deprecated("in favor of operator=(list&&)")]] void claim(list
& bl
) {
1083 *this = std::move(bl
);
1085 void claim_append(list
& bl
);
1086 void claim_append(list
&& bl
) {
1090 // copy with explicit volatile-sharing semantics
1091 void share(const list
& bl
)
1095 for (const auto& bp
: bl
._buffers
) {
1096 _buffers
.push_back(*ptr_node::create(bp
).release());
1104 /// convert the bufferlist into a network packet
1105 operator seastar::net::packet() &&;
1108 iterator
begin(size_t offset
=0) {
1109 return iterator(this, offset
);
1112 return iterator(this, _len
, _buffers
.end(), 0);
1115 const_iterator
begin(size_t offset
=0) const {
1116 return const_iterator(this, offset
);
1118 const_iterator
cbegin(size_t offset
=0) const {
1119 return begin(offset
);
1121 const_iterator
end() const {
1122 return const_iterator(this, _len
, _buffers
.end(), 0);
1125 void append(char c
);
1126 void append(const char *data
, unsigned len
);
1127 void append(std::string s
) {
1128 append(s
.data(), s
.length());
1130 #if __cplusplus >= 201703L
1131 // To forcibly disambiguate between string and string_view in the
1133 template<std::size_t N
>
1134 void append(const char (&s
)[N
]) {
1137 void append(const char* s
) {
1138 append(s
, strlen(s
));
1140 void append(std::string_view s
) {
1141 append(s
.data(), s
.length());
1143 #endif // __cplusplus >= 201703L
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
);
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.
1151 /// For example, if the stream contains "ABC\n\nDEF", "ABC\nDEF\n" is
1152 /// actually appended.
1153 void append(std::istream
& in
);
1154 contiguous_filler
append_hole(unsigned len
);
1155 void append_zero(unsigned len
);
1156 void prepend_zero(unsigned len
);
1158 reserve_t
obtain_contiguous_space(const unsigned len
);
1163 const char& operator[](unsigned n
) const;
1165 std::string
to_str() const;
1167 void substr_of(const list
& other
, unsigned off
, unsigned len
);
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;
1173 void encode_base64(list
& o
);
1174 void decode_base64(list
& o
);
1176 void write_stream(std::ostream
&out
) const;
1177 void hexdump(std::ostream
&out
, bool trailing_newline
= true) const;
1178 ssize_t
pread_file(const char *fn
, uint64_t off
, uint64_t len
, std::string
*error
);
1179 int read_file(const char *fn
, std::string
*error
);
1180 ssize_t
read_fd(int fd
, size_t len
);
1181 ssize_t
recv_fd(int fd
, size_t len
);
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;
1185 int send_fd(int fd
) const;
1186 template<typename VectorT
>
1187 void prepare_iov(VectorT
*piov
) const {
1189 ceph_assert(_num
<= IOV_MAX
);
1191 assert(_num
<= IOV_MAX
);
1195 for (auto& p
: _buffers
) {
1196 (*piov
)[n
].iov_base
= (void *)p
.c_str();
1197 (*piov
)[n
].iov_len
= p
.length();
1205 std::vector
<iovec
> iov
;
1207 using iov_vec_t
= std::vector
<iovec_t
>;
1208 iov_vec_t
prepare_iovs() const;
1210 uint32_t crc32c(uint32_t crc
) const;
1211 void invalidate_crc();
1213 // These functions return a bufferlist with a pointer to a single
1214 // static buffer. They /must/ not outlive the memory they
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
);
1221 } // inline namespace v15_2_0
1224 * efficient hash of one or more bufferlists
1232 // cppcheck-suppress noExplicitConstructor
1233 hash(uint32_t init
) : crc(init
) { }
1235 void update(const buffer::list
& bl
) {
1236 crc
= bl
.crc32c(crc
);
1244 inline bool operator==(const bufferlist
&lhs
, const bufferlist
&rhs
) {
1245 if (lhs
.length() != rhs
.length())
1247 return std::equal(lhs
.begin(), lhs
.end(), rhs
.begin());
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;
1256 return (l
== lhs
.end()) && (r
!= rhs
.end()); // lhs.length() < rhs.length()
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;
1265 return l
== lhs
.end(); // lhs.length() <= rhs.length()
1268 inline bool operator!=(const bufferlist
&l
, const bufferlist
&r
) {
1271 inline bool operator>(const bufferlist
& lhs
, const bufferlist
& rhs
) {
1274 inline bool operator>=(const bufferlist
& lhs
, const bufferlist
& rhs
) {
1278 std::ostream
& operator<<(std::ostream
& out
, const buffer::ptr
& bp
);
1280 std::ostream
& operator<<(std::ostream
& out
, const buffer::raw
&r
);
1282 std::ostream
& operator<<(std::ostream
& out
, const buffer::list
& bl
);
1284 inline bufferhash
& operator<<(bufferhash
& l
, const bufferlist
&r
) {
1289 } // namespace buffer