]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | #include <exception> | |
47 | #include <type_traits> | |
48 | ||
49 | #include "page.h" | |
50 | #include "crc32c.h" | |
51 | #include "buffer_fwd.h" | |
52 | ||
53 | #ifdef __CEPH__ | |
54 | # include "include/assert.h" | |
55 | #else | |
56 | # include <assert.h> | |
57 | #endif | |
58 | ||
59 | #include "inline_memory.h" | |
60 | ||
61 | #if __GNUC__ >= 4 | |
62 | #define CEPH_BUFFER_API __attribute__ ((visibility ("default"))) | |
63 | #else | |
64 | #define CEPH_BUFFER_API | |
65 | #endif | |
66 | ||
67 | #if defined(HAVE_XIO) | |
68 | struct xio_reg_mem; | |
69 | class XioDispatchHook; | |
70 | #endif | |
71 | class deleter; | |
72 | ||
73 | namespace ceph { | |
74 | ||
75 | namespace buffer CEPH_BUFFER_API { | |
76 | /* | |
77 | * exceptions | |
78 | */ | |
79 | ||
80 | struct error : public std::exception{ | |
81 | const char *what() const throw () override; | |
82 | }; | |
83 | struct bad_alloc : public error { | |
84 | const char *what() const throw () override; | |
85 | }; | |
86 | struct end_of_buffer : public error { | |
87 | const char *what() const throw () override; | |
88 | }; | |
89 | struct malformed_input : public error { | |
90 | explicit malformed_input(const std::string& w) { | |
91 | snprintf(buf, sizeof(buf), "buffer::malformed_input: %s", w.c_str()); | |
92 | } | |
93 | const char *what() const throw () override; | |
94 | private: | |
95 | char buf[256]; | |
96 | }; | |
97 | struct error_code : public malformed_input { | |
98 | explicit error_code(int error); | |
99 | int code; | |
100 | }; | |
101 | ||
102 | ||
103 | /// total bytes allocated | |
104 | int get_total_alloc(); | |
105 | ||
106 | /// history total bytes allocated | |
107 | uint64_t get_history_alloc_bytes(); | |
108 | ||
109 | /// total num allocated | |
110 | uint64_t get_history_alloc_num(); | |
111 | ||
112 | /// enable/disable alloc tracking | |
113 | void track_alloc(bool b); | |
114 | ||
115 | /// count of cached crc hits (matching input) | |
116 | int get_cached_crc(); | |
117 | /// count of cached crc hits (mismatching input, required adjustment) | |
118 | int get_cached_crc_adjusted(); | |
119 | /// count of crc cache misses | |
120 | int get_missed_crc(); | |
121 | /// enable/disable tracking of cached crcs | |
122 | void track_cached_crc(bool b); | |
123 | ||
124 | /// count of calls to buffer::ptr::c_str() | |
125 | int get_c_str_accesses(); | |
126 | /// enable/disable tracking of buffer::ptr::c_str() calls | |
127 | void track_c_str(bool b); | |
128 | ||
129 | /* | |
130 | * an abstract raw buffer. with a reference count. | |
131 | */ | |
132 | class raw; | |
133 | class raw_malloc; | |
134 | class raw_static; | |
135 | class raw_mmap_pages; | |
136 | class raw_posix_aligned; | |
137 | class raw_hack_aligned; | |
138 | class raw_char; | |
31f18b77 | 139 | class raw_claimed_char; |
7c673cae FG |
140 | class raw_pipe; |
141 | class raw_unshareable; // diagnostic, unshareable char buffer | |
142 | class raw_combined; | |
143 | class raw_claim_buffer; | |
144 | ||
145 | ||
146 | class xio_mempool; | |
147 | class xio_msg_buffer; | |
148 | ||
149 | /* | |
150 | * named constructors | |
151 | */ | |
152 | raw* copy(const char *c, unsigned len); | |
153 | raw* create(unsigned len); | |
154 | raw* claim_char(unsigned len, char *buf); | |
155 | raw* create_malloc(unsigned len); | |
156 | raw* claim_malloc(unsigned len, char *buf); | |
157 | raw* create_static(unsigned len, char *buf); | |
158 | raw* create_aligned(unsigned len, unsigned align); | |
159 | raw* create_page_aligned(unsigned len); | |
160 | raw* create_zero_copy(unsigned len, int fd, int64_t *offset); | |
161 | raw* create_unshareable(unsigned len); | |
162 | raw* create_static(unsigned len, char *buf); | |
163 | raw* claim_buffer(unsigned len, char *buf, deleter del); | |
164 | ||
165 | #if defined(HAVE_XIO) | |
166 | raw* create_msg(unsigned len, char *buf, XioDispatchHook *m_hook); | |
167 | #endif | |
168 | ||
169 | /* | |
170 | * a buffer pointer. references (a subsequence of) a raw buffer. | |
171 | */ | |
172 | class CEPH_BUFFER_API ptr { | |
173 | raw *_raw; | |
174 | unsigned _off, _len; | |
175 | ||
176 | void release(); | |
177 | ||
178 | public: | |
179 | class iterator { | |
180 | const ptr *bp; ///< parent ptr | |
181 | const char *start; ///< starting pointer into bp->c_str() | |
182 | const char *pos; ///< pointer into bp->c_str() | |
183 | const char *end_ptr; ///< pointer to bp->end_c_str() | |
184 | bool deep; ///< if true, no not allow shallow ptr copies | |
185 | ||
186 | iterator(const ptr *p, size_t offset, bool d) | |
187 | : bp(p), | |
188 | start(p->c_str() + offset), | |
189 | pos(start), | |
190 | end_ptr(p->end_c_str()), | |
191 | deep(d) {} | |
192 | ||
193 | friend class ptr; | |
194 | ||
195 | public: | |
196 | const char *get_pos_add(size_t n) { | |
197 | const char *r = pos; | |
198 | pos += n; | |
199 | if (pos > end_ptr) | |
200 | throw end_of_buffer(); | |
201 | return r; | |
202 | } | |
203 | ||
204 | ptr get_ptr(size_t len) { | |
205 | if (deep) { | |
206 | return buffer::copy(get_pos_add(len), len); | |
207 | } else { | |
208 | size_t off = pos - bp->c_str(); | |
209 | pos += len; | |
210 | if (pos > end_ptr) | |
211 | throw end_of_buffer(); | |
212 | return ptr(*bp, off, len); | |
213 | } | |
214 | } | |
215 | ptr get_preceding_ptr(size_t len) { | |
216 | if (deep) { | |
217 | return buffer::copy(get_pos() - len, len); | |
218 | } else { | |
219 | size_t off = pos - bp->c_str(); | |
220 | return ptr(*bp, off - len, len); | |
221 | } | |
222 | } | |
223 | ||
224 | void advance(size_t len) { | |
225 | pos += len; | |
226 | if (pos > end_ptr) | |
227 | throw end_of_buffer(); | |
228 | } | |
229 | ||
230 | const char *get_pos() { | |
231 | return pos; | |
232 | } | |
233 | const char *get_end() { | |
234 | return end_ptr; | |
235 | } | |
236 | ||
237 | size_t get_offset() { | |
238 | return pos - start; | |
239 | } | |
240 | ||
241 | bool end() const { | |
242 | return pos == end_ptr; | |
243 | } | |
244 | }; | |
245 | ||
246 | ptr() : _raw(0), _off(0), _len(0) {} | |
247 | // cppcheck-suppress noExplicitConstructor | |
248 | ptr(raw *r); | |
249 | // cppcheck-suppress noExplicitConstructor | |
250 | ptr(unsigned l); | |
251 | ptr(const char *d, unsigned l); | |
252 | ptr(const ptr& p); | |
253 | ptr(ptr&& p) noexcept; | |
254 | ptr(const ptr& p, unsigned o, unsigned l); | |
255 | ptr& operator= (const ptr& p); | |
256 | ptr& operator= (ptr&& p) noexcept; | |
257 | ~ptr() { | |
258 | release(); | |
259 | } | |
260 | ||
261 | bool have_raw() const { return _raw ? true:false; } | |
262 | ||
263 | raw *clone(); | |
264 | void swap(ptr& other); | |
265 | ptr& make_shareable(); | |
266 | ||
267 | iterator begin(size_t offset=0) const { | |
268 | return iterator(this, offset, false); | |
269 | } | |
270 | iterator begin_deep(size_t offset=0) const { | |
271 | return iterator(this, offset, true); | |
272 | } | |
273 | ||
274 | // misc | |
275 | bool at_buffer_head() const { return _off == 0; } | |
276 | bool at_buffer_tail() const; | |
277 | ||
278 | bool is_aligned(unsigned align) const { | |
279 | return ((long)c_str() & (align-1)) == 0; | |
280 | } | |
281 | bool is_page_aligned() const { return is_aligned(CEPH_PAGE_SIZE); } | |
282 | bool is_n_align_sized(unsigned align) const | |
283 | { | |
284 | return (length() % align) == 0; | |
285 | } | |
286 | bool is_n_page_sized() const { return is_n_align_sized(CEPH_PAGE_SIZE); } | |
287 | bool is_partial() const { | |
288 | return have_raw() && (start() > 0 || end() < raw_length()); | |
289 | } | |
290 | ||
291 | // accessors | |
292 | raw *get_raw() const { return _raw; } | |
293 | const char *c_str() const; | |
294 | char *c_str(); | |
295 | const char *end_c_str() const; | |
296 | char *end_c_str(); | |
297 | unsigned length() const { return _len; } | |
298 | unsigned offset() const { return _off; } | |
299 | unsigned start() const { return _off; } | |
300 | unsigned end() const { return _off + _len; } | |
301 | unsigned unused_tail_length() const; | |
302 | const char& operator[](unsigned n) const; | |
303 | char& operator[](unsigned n); | |
304 | ||
305 | const char *raw_c_str() const; | |
306 | unsigned raw_length() const; | |
307 | int raw_nref() const; | |
308 | ||
309 | void copy_out(unsigned o, unsigned l, char *dest) const; | |
310 | ||
311 | bool can_zero_copy() const; | |
312 | int zero_copy_to_fd(int fd, int64_t *offset) const; | |
313 | ||
31f18b77 | 314 | unsigned wasted() const; |
7c673cae FG |
315 | |
316 | int cmp(const ptr& o) const; | |
317 | bool is_zero() const; | |
318 | ||
319 | // modifiers | |
320 | void set_offset(unsigned o) { | |
321 | assert(raw_length() >= o); | |
322 | _off = o; | |
323 | } | |
324 | void set_length(unsigned l) { | |
325 | assert(raw_length() >= l); | |
326 | _len = l; | |
327 | } | |
328 | ||
329 | unsigned append(char c); | |
330 | unsigned append(const char *p, unsigned l); | |
331 | void copy_in(unsigned o, unsigned l, const char *src); | |
332 | void copy_in(unsigned o, unsigned l, const char *src, bool crc_reset); | |
333 | void zero(); | |
334 | void zero(bool crc_reset); | |
335 | void zero(unsigned o, unsigned l); | |
336 | void zero(unsigned o, unsigned l, bool crc_reset); | |
337 | ||
338 | }; | |
339 | ||
340 | ||
341 | /* | |
342 | * list - the useful bit! | |
343 | */ | |
344 | ||
345 | class CEPH_BUFFER_API list { | |
346 | // my private bits | |
347 | std::list<ptr> _buffers; | |
348 | unsigned _len; | |
349 | unsigned _memcopy_count; //the total of memcopy using rebuild(). | |
350 | ptr append_buffer; // where i put small appends. | |
31f18b77 | 351 | int _mempool = -1; |
7c673cae FG |
352 | |
353 | public: | |
354 | class iterator; | |
355 | ||
356 | private: | |
357 | template <bool is_const> | |
358 | class CEPH_BUFFER_API iterator_impl | |
359 | : public std::iterator<std::forward_iterator_tag, char> { | |
360 | protected: | |
361 | typedef typename std::conditional<is_const, | |
362 | const list, | |
363 | list>::type bl_t; | |
364 | typedef typename std::conditional<is_const, | |
365 | const std::list<ptr>, | |
366 | std::list<ptr> >::type list_t; | |
367 | typedef typename std::conditional<is_const, | |
368 | typename std::list<ptr>::const_iterator, | |
369 | typename std::list<ptr>::iterator>::type list_iter_t; | |
370 | bl_t* bl; | |
371 | list_t* ls; // meh.. just here to avoid an extra pointer dereference.. | |
372 | unsigned off; // in bl | |
373 | list_iter_t p; | |
374 | unsigned p_off; // in *p | |
375 | friend class iterator_impl<true>; | |
376 | ||
377 | public: | |
378 | // constructor. position. | |
379 | iterator_impl() | |
380 | : bl(0), ls(0), off(0), p_off(0) {} | |
381 | iterator_impl(bl_t *l, unsigned o=0); | |
382 | iterator_impl(bl_t *l, unsigned o, list_iter_t ip, unsigned po) | |
383 | : bl(l), ls(&bl->_buffers), off(o), p(ip), p_off(po) {} | |
384 | iterator_impl(const list::iterator& i); | |
385 | ||
386 | /// get current iterator offset in buffer::list | |
387 | unsigned get_off() const { return off; } | |
388 | ||
389 | /// get number of bytes remaining from iterator position to the end of the buffer::list | |
390 | unsigned get_remaining() const { return bl->length() - off; } | |
391 | ||
392 | /// true if iterator is at the end of the buffer::list | |
393 | bool end() const { | |
394 | return p == ls->end(); | |
395 | //return off == bl->length(); | |
396 | } | |
397 | ||
398 | void advance(int o); | |
399 | void seek(unsigned o); | |
400 | char operator*() const; | |
401 | iterator_impl& operator++(); | |
402 | ptr get_current_ptr() const; | |
403 | ||
404 | bl_t& get_bl() const { return *bl; } | |
405 | ||
406 | // copy data out. | |
407 | // note that these all _append_ to dest! | |
408 | void copy(unsigned len, char *dest); | |
409 | // deprecated, use copy_deep() | |
410 | void copy(unsigned len, ptr &dest) __attribute__((deprecated)); | |
411 | void copy_deep(unsigned len, ptr &dest); | |
412 | void copy_shallow(unsigned len, ptr &dest); | |
413 | void copy(unsigned len, list &dest); | |
414 | void copy(unsigned len, std::string &dest); | |
415 | void copy_all(list &dest); | |
416 | ||
417 | // get a pointer to the currenet iterator position, return the | |
418 | // number of bytes we can read from that position (up to want), | |
419 | // and advance the iterator by that amount. | |
420 | size_t get_ptr_and_advance(size_t want, const char **p); | |
421 | ||
422 | /// calculate crc from iterator position | |
423 | uint32_t crc32c(size_t length, uint32_t crc); | |
424 | ||
425 | friend bool operator==(const iterator_impl& lhs, | |
426 | const iterator_impl& rhs) { | |
427 | return &lhs.get_bl() == &rhs.get_bl() && lhs.get_off() == rhs.get_off(); | |
428 | } | |
429 | friend bool operator!=(const iterator_impl& lhs, | |
430 | const iterator_impl& rhs) { | |
431 | return &lhs.get_bl() != &rhs.get_bl() || lhs.get_off() != rhs.get_off(); | |
432 | } | |
433 | }; | |
434 | ||
435 | public: | |
436 | typedef iterator_impl<true> const_iterator; | |
437 | ||
438 | class CEPH_BUFFER_API iterator : public iterator_impl<false> { | |
439 | public: | |
440 | iterator() = default; | |
441 | iterator(bl_t *l, unsigned o=0); | |
442 | iterator(bl_t *l, unsigned o, list_iter_t ip, unsigned po); | |
443 | ||
444 | void advance(int o); | |
445 | void seek(unsigned o); | |
446 | char operator*(); | |
447 | iterator& operator++(); | |
448 | ptr get_current_ptr(); | |
449 | ||
450 | // copy data out | |
451 | void copy(unsigned len, char *dest); | |
452 | // deprecated, use copy_deep() | |
453 | void copy(unsigned len, ptr &dest) __attribute__((deprecated)); | |
454 | void copy_deep(unsigned len, ptr &dest); | |
455 | void copy_shallow(unsigned len, ptr &dest); | |
456 | void copy(unsigned len, list &dest); | |
457 | void copy(unsigned len, std::string &dest); | |
458 | void copy_all(list &dest); | |
459 | ||
460 | // copy data in | |
461 | void copy_in(unsigned len, const char *src); | |
462 | void copy_in(unsigned len, const char *src, bool crc_reset); | |
463 | void copy_in(unsigned len, const list& otherl); | |
464 | ||
465 | bool operator==(const iterator& rhs) const { | |
466 | return bl == rhs.bl && off == rhs.off; | |
467 | } | |
468 | bool operator!=(const iterator& rhs) const { | |
469 | return bl != rhs.bl || off != rhs.off; | |
470 | } | |
471 | }; | |
472 | ||
473 | class contiguous_appender { | |
474 | bufferlist *pbl; | |
475 | char *pos; | |
476 | ptr bp; | |
477 | bool deep; | |
478 | ||
479 | /// running count of bytes appended that are not reflected by @pos | |
480 | size_t out_of_band_offset = 0; | |
481 | ||
482 | contiguous_appender(bufferlist *l, size_t len, bool d) | |
483 | : pbl(l), | |
484 | deep(d) { | |
485 | size_t unused = pbl->append_buffer.unused_tail_length(); | |
486 | if (len > unused) { | |
487 | // note: if len < the normal append_buffer size it *might* | |
488 | // be better to allocate a normal-sized append_buffer and | |
489 | // use part of it. however, that optimizes for the case of | |
490 | // old-style types including new-style types. and in most | |
491 | // such cases, this won't be the very first thing encoded to | |
492 | // the list, so append_buffer will already be allocated. | |
493 | // OTOH if everything is new-style, we *should* allocate | |
494 | // only what we need and conserve memory. | |
495 | bp = buffer::create(len); | |
496 | pos = bp.c_str(); | |
497 | } else { | |
498 | pos = pbl->append_buffer.end_c_str(); | |
499 | } | |
500 | } | |
501 | ||
502 | void flush_and_continue() { | |
503 | if (bp.have_raw()) { | |
504 | // we allocated a new buffer | |
505 | size_t l = pos - bp.c_str(); | |
506 | pbl->append(bufferptr(bp, 0, l)); | |
507 | bp.set_length(bp.length() - l); | |
508 | bp.set_offset(bp.offset() + l); | |
509 | } else { | |
510 | // we are using pbl's append_buffer | |
511 | size_t l = pos - pbl->append_buffer.end_c_str(); | |
512 | if (l) { | |
513 | pbl->append_buffer.set_length(pbl->append_buffer.length() + l); | |
514 | pbl->append(pbl->append_buffer, pbl->append_buffer.end() - l, l); | |
515 | pos = pbl->append_buffer.end_c_str(); | |
516 | } | |
517 | } | |
518 | } | |
519 | ||
520 | friend class list; | |
521 | ||
522 | public: | |
523 | ~contiguous_appender() { | |
524 | if (bp.have_raw()) { | |
525 | // we allocated a new buffer | |
526 | bp.set_length(pos - bp.c_str()); | |
527 | pbl->append(std::move(bp)); | |
528 | } else { | |
529 | // we are using pbl's append_buffer | |
530 | size_t l = pos - pbl->append_buffer.end_c_str(); | |
531 | if (l) { | |
532 | pbl->append_buffer.set_length(pbl->append_buffer.length() + l); | |
533 | pbl->append(pbl->append_buffer, pbl->append_buffer.end() - l, l); | |
534 | } | |
535 | } | |
536 | } | |
537 | ||
538 | size_t get_out_of_band_offset() const { | |
539 | return out_of_band_offset; | |
540 | } | |
541 | void append(const char *p, size_t l) { | |
542 | maybe_inline_memcpy(pos, p, l, 16); | |
543 | pos += l; | |
544 | } | |
545 | char *get_pos_add(size_t len) { | |
546 | char *r = pos; | |
547 | pos += len; | |
548 | return r; | |
549 | } | |
550 | char *get_pos() { | |
551 | return pos; | |
552 | } | |
553 | ||
554 | void append(const bufferptr& p) { | |
555 | if (!p.length()) { | |
556 | return; | |
557 | } | |
558 | if (deep) { | |
559 | append(p.c_str(), p.length()); | |
560 | } else { | |
561 | flush_and_continue(); | |
562 | pbl->append(p); | |
563 | out_of_band_offset += p.length(); | |
564 | } | |
565 | } | |
566 | void append(const bufferlist& l) { | |
567 | if (!l.length()) { | |
568 | return; | |
569 | } | |
570 | if (deep) { | |
571 | for (const auto &p : l._buffers) { | |
572 | append(p.c_str(), p.length()); | |
573 | } | |
574 | } else { | |
575 | flush_and_continue(); | |
576 | pbl->append(l); | |
577 | out_of_band_offset += l.length(); | |
578 | } | |
579 | } | |
580 | ||
581 | size_t get_logical_offset() { | |
582 | if (bp.have_raw()) { | |
583 | return out_of_band_offset + (pos - bp.c_str()); | |
584 | } else { | |
585 | return out_of_band_offset + (pos - pbl->append_buffer.end_c_str()); | |
586 | } | |
587 | } | |
588 | }; | |
589 | ||
590 | contiguous_appender get_contiguous_appender(size_t len, bool deep=false) { | |
591 | return contiguous_appender(this, len, deep); | |
592 | } | |
593 | ||
594 | class page_aligned_appender { | |
595 | bufferlist *pbl; | |
596 | size_t offset; | |
597 | unsigned min_alloc; | |
598 | ptr buffer; | |
599 | char *pos, *end; | |
600 | ||
601 | page_aligned_appender(list *l, unsigned min_pages) | |
602 | : pbl(l), | |
603 | min_alloc(min_pages * CEPH_PAGE_SIZE), | |
604 | pos(nullptr), end(nullptr) {} | |
605 | ||
606 | friend class list; | |
607 | ||
608 | public: | |
609 | ~page_aligned_appender() { | |
610 | flush(); | |
611 | } | |
612 | ||
613 | void flush() { | |
614 | if (pos && pos != buffer.c_str()) { | |
615 | size_t len = pos - buffer.c_str(); | |
616 | pbl->append(buffer, 0, len); | |
617 | buffer.set_length(buffer.length() - len); | |
618 | buffer.set_offset(buffer.offset() + len); | |
619 | } | |
620 | } | |
621 | ||
622 | void append(const char *buf, size_t len) { | |
623 | while (len > 0) { | |
624 | if (!pos) { | |
625 | size_t alloc = (len + CEPH_PAGE_SIZE - 1) & CEPH_PAGE_MASK; | |
626 | if (alloc < min_alloc) { | |
627 | alloc = min_alloc; | |
628 | } | |
629 | buffer = create_page_aligned(alloc); | |
630 | pos = buffer.c_str(); | |
631 | end = buffer.end_c_str(); | |
632 | } | |
633 | size_t l = len; | |
634 | if (l > (size_t)(end - pos)) { | |
635 | l = end - pos; | |
636 | } | |
637 | memcpy(pos, buf, l); | |
638 | pos += l; | |
639 | buf += l; | |
640 | len -= l; | |
641 | if (pos == end) { | |
642 | pbl->append(buffer, 0, buffer.length()); | |
643 | pos = end = nullptr; | |
644 | } | |
645 | } | |
646 | } | |
647 | }; | |
648 | ||
649 | page_aligned_appender get_page_aligned_appender(unsigned min_pages=1) { | |
650 | return page_aligned_appender(this, min_pages); | |
651 | } | |
652 | ||
653 | private: | |
654 | mutable iterator last_p; | |
655 | int zero_copy_to_fd(int fd) const; | |
656 | ||
657 | public: | |
658 | // cons/des | |
659 | list() : _len(0), _memcopy_count(0), last_p(this) {} | |
660 | // cppcheck-suppress noExplicitConstructor | |
661 | list(unsigned prealloc) : _len(0), _memcopy_count(0), last_p(this) { | |
662 | reserve(prealloc); | |
663 | } | |
664 | ||
665 | list(const list& other) : _buffers(other._buffers), _len(other._len), | |
666 | _memcopy_count(other._memcopy_count), last_p(this) { | |
667 | make_shareable(); | |
668 | } | |
669 | list(list&& other); | |
670 | list& operator= (const list& other) { | |
671 | if (this != &other) { | |
672 | _buffers = other._buffers; | |
673 | _len = other._len; | |
674 | make_shareable(); | |
675 | } | |
676 | return *this; | |
677 | } | |
678 | ||
679 | list& operator= (list&& other) { | |
680 | _buffers = std::move(other._buffers); | |
681 | _len = other._len; | |
682 | _memcopy_count = other._memcopy_count; | |
683 | last_p = begin(); | |
684 | append_buffer.swap(other.append_buffer); | |
31f18b77 | 685 | _mempool = other._mempool; |
7c673cae FG |
686 | other.clear(); |
687 | return *this; | |
688 | } | |
689 | ||
690 | unsigned get_num_buffers() const { return _buffers.size(); } | |
691 | const ptr& front() const { return _buffers.front(); } | |
692 | const ptr& back() const { return _buffers.back(); } | |
693 | ||
31f18b77 FG |
694 | void reassign_to_mempool(int pool); |
695 | void try_assign_to_mempool(int pool); | |
696 | ||
697 | size_t get_append_buffer_unused_tail_length() const { | |
698 | return append_buffer.unused_tail_length(); | |
699 | } | |
700 | ||
7c673cae FG |
701 | unsigned get_memcopy_count() const {return _memcopy_count; } |
702 | const std::list<ptr>& buffers() const { return _buffers; } | |
703 | void swap(list& other); | |
704 | unsigned length() const { | |
705 | #if 0 | |
706 | // DEBUG: verify _len | |
707 | unsigned len = 0; | |
708 | for (std::list<ptr>::const_iterator it = _buffers.begin(); | |
709 | it != _buffers.end(); | |
710 | it++) { | |
711 | len += (*it).length(); | |
712 | } | |
713 | assert(len == _len); | |
714 | #endif | |
715 | return _len; | |
716 | } | |
717 | ||
718 | bool contents_equal(buffer::list& other); | |
719 | bool contents_equal(const buffer::list& other) const; | |
720 | ||
721 | bool can_zero_copy() const; | |
722 | bool is_provided_buffer(const char *dst) const; | |
723 | bool is_aligned(unsigned align) const; | |
724 | bool is_page_aligned() const; | |
725 | bool is_n_align_sized(unsigned align) const; | |
726 | bool is_n_page_sized() const; | |
727 | bool is_aligned_size_and_memory(unsigned align_size, | |
728 | unsigned align_memory) const; | |
729 | ||
730 | bool is_zero() const; | |
731 | ||
732 | // modifiers | |
733 | void clear() { | |
734 | _buffers.clear(); | |
735 | _len = 0; | |
736 | _memcopy_count = 0; | |
737 | last_p = begin(); | |
738 | append_buffer = ptr(); | |
739 | } | |
740 | void push_front(ptr& bp) { | |
741 | if (bp.length() == 0) | |
742 | return; | |
743 | _buffers.push_front(bp); | |
744 | _len += bp.length(); | |
745 | } | |
746 | void push_front(ptr&& bp) { | |
747 | if (bp.length() == 0) | |
748 | return; | |
749 | _len += bp.length(); | |
750 | _buffers.push_front(std::move(bp)); | |
751 | } | |
752 | void push_front(raw *r) { | |
753 | push_front(ptr(r)); | |
754 | } | |
755 | void push_back(const ptr& bp) { | |
756 | if (bp.length() == 0) | |
757 | return; | |
758 | _buffers.push_back(bp); | |
759 | _len += bp.length(); | |
760 | } | |
761 | void push_back(ptr&& bp) { | |
762 | if (bp.length() == 0) | |
763 | return; | |
764 | _len += bp.length(); | |
765 | _buffers.push_back(std::move(bp)); | |
766 | } | |
767 | void push_back(raw *r) { | |
768 | push_back(ptr(r)); | |
769 | } | |
770 | ||
771 | void zero(); | |
772 | void zero(unsigned o, unsigned l); | |
773 | ||
774 | bool is_contiguous() const; | |
775 | void rebuild(); | |
776 | void rebuild(ptr& nb); | |
777 | bool rebuild_aligned(unsigned align); | |
778 | bool rebuild_aligned_size_and_memory(unsigned align_size, | |
779 | unsigned align_memory); | |
780 | bool rebuild_page_aligned(); | |
781 | ||
31f18b77 | 782 | void reserve(size_t prealloc); |
7c673cae FG |
783 | |
784 | // assignment-op with move semantics | |
785 | const static unsigned int CLAIM_DEFAULT = 0; | |
786 | const static unsigned int CLAIM_ALLOW_NONSHAREABLE = 1; | |
787 | ||
788 | void claim(list& bl, unsigned int flags = CLAIM_DEFAULT); | |
789 | void claim_append(list& bl, unsigned int flags = CLAIM_DEFAULT); | |
790 | void claim_prepend(list& bl, unsigned int flags = CLAIM_DEFAULT); | |
31f18b77 FG |
791 | // only for bl is bufferlist::page_aligned_appender |
792 | void claim_append_piecewise(list& bl); | |
7c673cae FG |
793 | |
794 | // clone non-shareable buffers (make shareable) | |
795 | void make_shareable() { | |
796 | std::list<buffer::ptr>::iterator pb; | |
797 | for (pb = _buffers.begin(); pb != _buffers.end(); ++pb) { | |
798 | (void) pb->make_shareable(); | |
799 | } | |
800 | } | |
801 | ||
802 | // copy with explicit volatile-sharing semantics | |
803 | void share(const list& bl) | |
804 | { | |
805 | if (this != &bl) { | |
806 | clear(); | |
807 | std::list<buffer::ptr>::const_iterator pb; | |
808 | for (pb = bl._buffers.begin(); pb != bl._buffers.end(); ++pb) { | |
809 | push_back(*pb); | |
810 | } | |
811 | } | |
812 | } | |
813 | ||
814 | iterator begin() { | |
815 | return iterator(this, 0); | |
816 | } | |
817 | iterator end() { | |
818 | return iterator(this, _len, _buffers.end(), 0); | |
819 | } | |
820 | ||
821 | const_iterator begin() const { | |
822 | return const_iterator(this, 0); | |
823 | } | |
824 | const_iterator end() const { | |
825 | return const_iterator(this, _len, _buffers.end(), 0); | |
826 | } | |
827 | ||
828 | // crope lookalikes. | |
829 | // **** WARNING: this are horribly inefficient for large bufferlists. **** | |
830 | void copy(unsigned off, unsigned len, char *dest) const; | |
831 | void copy(unsigned off, unsigned len, list &dest) const; | |
832 | void copy(unsigned off, unsigned len, std::string& dest) const; | |
833 | void copy_in(unsigned off, unsigned len, const char *src); | |
834 | void copy_in(unsigned off, unsigned len, const char *src, bool crc_reset); | |
835 | void copy_in(unsigned off, unsigned len, const list& src); | |
836 | ||
837 | void append(char c); | |
838 | void append(const char *data, unsigned len); | |
839 | void append(const std::string& s) { | |
840 | append(s.data(), s.length()); | |
841 | } | |
842 | void append(const ptr& bp); | |
843 | void append(ptr&& bp); | |
844 | void append(const ptr& bp, unsigned off, unsigned len); | |
845 | void append(const list& bl); | |
846 | void append(std::istream& in); | |
847 | void append_zero(unsigned len); | |
848 | void prepend_zero(unsigned len); | |
849 | ||
850 | /* | |
851 | * get a char | |
852 | */ | |
853 | const char& operator[](unsigned n) const; | |
854 | char *c_str(); | |
855 | std::string to_str() const; | |
856 | ||
857 | void substr_of(const list& other, unsigned off, unsigned len); | |
858 | ||
859 | /// return a pointer to a contiguous extent of the buffer, | |
860 | /// reallocating as needed | |
861 | char *get_contiguous(unsigned off, ///< offset | |
862 | unsigned len); ///< length | |
863 | ||
864 | // funky modifer | |
865 | void splice(unsigned off, unsigned len, list *claim_by=0 /*, bufferlist& replace_with */); | |
866 | void write(int off, int len, std::ostream& out) const; | |
867 | ||
868 | void encode_base64(list& o); | |
869 | void decode_base64(list& o); | |
870 | ||
871 | void write_stream(std::ostream &out) const; | |
872 | void hexdump(std::ostream &out, bool trailing_newline = true) const; | |
873 | int read_file(const char *fn, std::string *error); | |
874 | ssize_t read_fd(int fd, size_t len); | |
875 | int read_fd_zero_copy(int fd, size_t len); | |
876 | int write_file(const char *fn, int mode=0644); | |
877 | int write_fd(int fd) const; | |
878 | int write_fd(int fd, uint64_t offset) const; | |
879 | int write_fd_zero_copy(int fd) const; | |
880 | template<typename VectorT> | |
881 | void prepare_iov(VectorT *piov) const { | |
882 | assert(_buffers.size() <= IOV_MAX); | |
883 | piov->resize(_buffers.size()); | |
884 | unsigned n = 0; | |
885 | for (auto& p : _buffers) { | |
886 | (*piov)[n].iov_base = (void *)p.c_str(); | |
887 | (*piov)[n].iov_len = p.length(); | |
888 | ++n; | |
889 | } | |
890 | } | |
891 | uint32_t crc32c(uint32_t crc) const; | |
892 | void invalidate_crc(); | |
31f18b77 FG |
893 | |
894 | // These functions return a bufferlist with a pointer to a single | |
895 | // static buffer. They /must/ not outlive the memory they | |
896 | // reference. | |
897 | static list static_from_mem(char* c, size_t l); | |
898 | static list static_from_cstring(char* c); | |
899 | static list static_from_string(std::string& s); | |
7c673cae FG |
900 | }; |
901 | ||
902 | /* | |
903 | * efficient hash of one or more bufferlists | |
904 | */ | |
905 | ||
906 | class hash { | |
907 | uint32_t crc; | |
908 | ||
909 | public: | |
910 | hash() : crc(0) { } | |
911 | // cppcheck-suppress noExplicitConstructor | |
912 | hash(uint32_t init) : crc(init) { } | |
913 | ||
31f18b77 | 914 | void update(const buffer::list& bl) { |
7c673cae FG |
915 | crc = bl.crc32c(crc); |
916 | } | |
917 | ||
918 | uint32_t digest() { | |
919 | return crc; | |
920 | } | |
921 | }; | |
922 | ||
923 | inline bool operator>(bufferlist& l, bufferlist& r) { | |
924 | for (unsigned p = 0; ; p++) { | |
925 | if (l.length() > p && r.length() == p) return true; | |
926 | if (l.length() == p) return false; | |
927 | if (l[p] > r[p]) return true; | |
928 | if (l[p] < r[p]) return false; | |
929 | } | |
930 | } | |
931 | inline bool operator>=(bufferlist& l, bufferlist& r) { | |
932 | for (unsigned p = 0; ; p++) { | |
933 | if (l.length() > p && r.length() == p) return true; | |
934 | if (r.length() == p && l.length() == p) return true; | |
935 | if (l.length() == p && r.length() > p) return false; | |
936 | if (l[p] > r[p]) return true; | |
937 | if (l[p] < r[p]) return false; | |
938 | } | |
939 | } | |
940 | ||
941 | inline bool operator==(const bufferlist &l, const bufferlist &r) { | |
942 | if (l.length() != r.length()) | |
943 | return false; | |
944 | for (unsigned p = 0; p < l.length(); p++) { | |
945 | if (l[p] != r[p]) | |
946 | return false; | |
947 | } | |
948 | return true; | |
949 | } | |
950 | inline bool operator<(bufferlist& l, bufferlist& r) { | |
951 | return r > l; | |
952 | } | |
953 | inline bool operator<=(bufferlist& l, bufferlist& r) { | |
954 | return r >= l; | |
955 | } | |
956 | ||
957 | ||
958 | std::ostream& operator<<(std::ostream& out, const buffer::ptr& bp); | |
959 | ||
960 | std::ostream& operator<<(std::ostream& out, const raw &r); | |
961 | ||
962 | std::ostream& operator<<(std::ostream& out, const buffer::list& bl); | |
963 | ||
964 | std::ostream& operator<<(std::ostream& out, const buffer::error& e); | |
965 | ||
31f18b77 | 966 | inline bufferhash& operator<<(bufferhash& l, const bufferlist &r) { |
7c673cae FG |
967 | l.update(r); |
968 | return l; | |
969 | } | |
970 | ||
971 | } | |
972 | ||
973 | #if defined(HAVE_XIO) | |
974 | xio_reg_mem* get_xio_mp(const buffer::ptr& bp); | |
975 | #endif | |
976 | ||
977 | } | |
978 | ||
979 | #endif |