--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <atomic>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/uio.h>
+
+#include "include/compat.h"
+#include "include/mempool.h"
+#include "armor.h"
+#include "common/environment.h"
+#include "common/errno.h"
+#include "common/safe_io.h"
+#include "common/simple_spin.h"
+#include "common/strtol.h"
+#include "common/likely.h"
+#include "common/valgrind.h"
+#include "common/deleter.h"
+#include "common/RWLock.h"
+#include "include/types.h"
+#include "include/scope_guard.h"
+
+#if defined(HAVE_XIO)
+#include "msg/xio/XioMsg.h"
+#endif
+
+using namespace ceph;
+
+#define CEPH_BUFFER_ALLOC_UNIT (MIN(CEPH_PAGE_SIZE, 4096))
+#define CEPH_BUFFER_APPEND_SIZE (CEPH_BUFFER_ALLOC_UNIT - sizeof(raw_combined))
+
+#ifdef BUFFER_DEBUG
+static std::atomic_flag buffer_debug_lock = ATOMIC_FLAG_INIT;
+# define bdout { simple_spin_lock(&buffer_debug_lock); std::cout
+# define bendl std::endl; simple_spin_unlock(&buffer_debug_lock); }
+#else
+# define bdout if (0) { std::cout
+# define bendl std::endl; }
+#endif
+
+ static std::atomic<uint64_t> buffer_total_alloc { 0 };
+ static std::atomic<uint64_t> buffer_history_alloc_bytes { 0 };
+ static std::atomic<uint64_t> buffer_history_alloc_num { 0 };
+
+ const bool buffer_track_alloc = get_env_bool("CEPH_BUFFER_TRACK");
+
+ namespace {
+ void inc_total_alloc(unsigned len) {
+ if (buffer_track_alloc)
+ buffer_total_alloc += len;
+ }
+
+ void dec_total_alloc(unsigned len) {
+ if (buffer_track_alloc)
+ buffer_total_alloc -= len;
+ }
+
+ void inc_history_alloc(uint64_t len) {
+ if (buffer_track_alloc) {
+ buffer_history_alloc_bytes += len;
+ buffer_history_alloc_num++;
+ }
+ }
+ } // namespace
+
+ int buffer::get_total_alloc() {
+ return buffer_total_alloc;
+ }
+ uint64_t buffer::get_history_alloc_bytes() {
+ return buffer_history_alloc_bytes;
+ }
+ uint64_t buffer::get_history_alloc_num() {
+ return buffer_history_alloc_num;
+ }
+
+ static std::atomic<unsigned> buffer_cached_crc { 0 };
+ static std::atomic<unsigned> buffer_cached_crc_adjusted { 0 };
+ static std::atomic<unsigned> buffer_missed_crc { 0 };
+
+ static bool buffer_track_crc = get_env_bool("CEPH_BUFFER_TRACK");
+
+ void buffer::track_cached_crc(bool b) {
+ buffer_track_crc = b;
+ }
+ int buffer::get_cached_crc() {
+ return buffer_cached_crc;
+ }
+ int buffer::get_cached_crc_adjusted() {
+ return buffer_cached_crc_adjusted;
+ }
+
+ int buffer::get_missed_crc() {
+ return buffer_missed_crc;
+ }
+
+ static std::atomic<unsigned> buffer_c_str_accesses { 0 };
+
+ static bool buffer_track_c_str = get_env_bool("CEPH_BUFFER_TRACK");
+
+ void buffer::track_c_str(bool b) {
+ buffer_track_c_str = b;
+ }
+ int buffer::get_c_str_accesses() {
+ return buffer_c_str_accesses;
+ }
+
+#ifdef CEPH_HAVE_SETPIPE_SZ
+ static std::atomic<unsigned> buffer_max_pipe_size { 0 };
+ int update_max_pipe_size() {
+ char buf[32];
+ int r;
+ std::string err;
+ struct stat stat_result;
+ if (::stat(PROCPREFIX "/proc/sys/fs/pipe-max-size", &stat_result) == -1)
+ return -errno;
+ r = safe_read_file(PROCPREFIX "/proc/sys/fs/", "pipe-max-size",
+ buf, sizeof(buf) - 1);
+ if (r < 0)
+ return r;
+ buf[r] = '\0';
+ size_t size = strict_strtol(buf, 10, &err);
+ if (!err.empty())
+ return -EIO;
+ buffer_max_pipe_size = size;
+ return 0;
+ }
+
+ size_t get_max_pipe_size() {
+ size_t size = buffer_max_pipe_size;
+ if (size)
+ return size;
+ if (update_max_pipe_size() == 0)
+ return buffer_max_pipe_size;
+ // this is the max size hardcoded in linux before 2.6.35
+ return 65536;
+ }
+#else
+ size_t get_max_pipe_size() { return 65536; }
+#endif
+
+
+ const char * buffer::error::what() const throw () {
+ return "buffer::exception";
+ }
+ const char * buffer::bad_alloc::what() const throw () {
+ return "buffer::bad_alloc";
+ }
+ const char * buffer::end_of_buffer::what() const throw () {
+ return "buffer::end_of_buffer";
+ }
+ const char * buffer::malformed_input::what() const throw () {
+ return buf;
+ }
+ buffer::error_code::error_code(int error) :
+ buffer::malformed_input(cpp_strerror(error).c_str()), code(error) {}
+
+ class buffer::raw {
+ public:
+ char *data;
+ unsigned len;
+ std::atomic<unsigned> nref { 0 };
+ int mempool;
+
+ mutable std::atomic_flag crc_spinlock = ATOMIC_FLAG_INIT;
+ map<pair<size_t, size_t>, pair<uint32_t, uint32_t> > crc_map;
+
+ explicit raw(unsigned l, int mempool=mempool::mempool_buffer_anon)
+ : data(NULL), len(l), nref(0), mempool(mempool) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+ raw(char *c, unsigned l, int mempool=mempool::mempool_buffer_anon)
+ : data(c), len(l), nref(0), mempool(mempool) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+ virtual ~raw() {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ }
+
+ void _set_len(unsigned l) {
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ len = l;
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(1, len);
+ }
+
+ void reassign_to_mempool(int pool) {
+ if (pool == mempool) {
+ return;
+ }
+ mempool::get_pool(mempool::pool_index_t(mempool)).adjust_count(
+ -1, -(int)len);
+ mempool = pool;
+ mempool::get_pool(mempool::pool_index_t(pool)).adjust_count(1, len);
+ }
+
+ void try_assign_to_mempool(int pool) {
+ if (mempool == mempool::mempool_buffer_anon) {
+ reassign_to_mempool(pool);
+ }
+ }
+
+ // no copying.
+ // cppcheck-suppress noExplicitConstructor
+ raw(const raw &other);
+ const raw& operator=(const raw &other);
+
+ virtual char *get_data() {
+ return data;
+ }
+ virtual raw* clone_empty() = 0;
+ raw *clone() {
+ raw *c = clone_empty();
+ memcpy(c->data, data, len);
+ return c;
+ }
+ virtual bool can_zero_copy() const {
+ return false;
+ }
+ virtual int zero_copy_to_fd(int fd, loff_t *offset) {
+ return -ENOTSUP;
+ }
+ virtual bool is_page_aligned() {
+ return ((long)data & ~CEPH_PAGE_MASK) == 0;
+ }
+ bool is_n_page_sized() {
+ return (len & ~CEPH_PAGE_MASK) == 0;
+ }
+ virtual bool is_shareable() {
+ // true if safe to reference/share the existing buffer copy
+ // false if it is not safe to share the buffer, e.g., due to special
+ // and/or registered memory that is scarce
+ return true;
+ }
+ bool get_crc(const pair<size_t, size_t> &fromto,
+ pair<uint32_t, uint32_t> *crc) const {
+ simple_spin_lock(&crc_spinlock);
+ map<pair<size_t, size_t>, pair<uint32_t, uint32_t> >::const_iterator i =
+ crc_map.find(fromto);
+ if (i == crc_map.end()) {
+ simple_spin_unlock(&crc_spinlock);
+ return false;
+ }
+ *crc = i->second;
+ simple_spin_unlock(&crc_spinlock);
+ return true;
+ }
+ void set_crc(const pair<size_t, size_t> &fromto,
+ const pair<uint32_t, uint32_t> &crc) {
+ simple_spin_lock(&crc_spinlock);
+ crc_map[fromto] = crc;
+ simple_spin_unlock(&crc_spinlock);
+ }
+ void invalidate_crc() {
+ simple_spin_lock(&crc_spinlock);
+ if (crc_map.size() != 0) {
+ crc_map.clear();
+ }
+ simple_spin_unlock(&crc_spinlock);
+ }
+ };
+
+ /*
+ * raw_combined is always placed within a single allocation along
+ * with the data buffer. the data goes at the beginning, and
+ * raw_combined at the end.
+ */
+ class buffer::raw_combined : public buffer::raw {
+ size_t alignment;
+ public:
+ raw_combined(char *dataptr, unsigned l, unsigned align,
+ int mempool)
+ : raw(dataptr, l, mempool),
+ alignment(align) {
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ }
+ ~raw_combined() override {
+ dec_total_alloc(len);
+ }
+ raw* clone_empty() override {
+ return create(len, alignment);
+ }
+
+ static raw_combined *create(unsigned len,
+ unsigned align,
+ int mempool = mempool::mempool_buffer_anon) {
+ if (!align)
+ align = sizeof(size_t);
+ size_t rawlen = ROUND_UP_TO(sizeof(buffer::raw_combined),
+ alignof(buffer::raw_combined));
+ size_t datalen = ROUND_UP_TO(len, alignof(buffer::raw_combined));
+
+#ifdef DARWIN
+ char *ptr = (char *) valloc(rawlen + datalen);
+#else
+ char *ptr = 0;
+ int r = ::posix_memalign((void**)(void*)&ptr, align, rawlen + datalen);
+ if (r)
+ throw bad_alloc();
+#endif /* DARWIN */
+ if (!ptr)
+ throw bad_alloc();
+
+ // actual data first, since it has presumably larger alignment restriction
+ // then put the raw_combined at the end
+ return new (ptr + datalen) raw_combined(ptr, len, align, mempool);
+ }
+
+ static void operator delete(void *ptr) {
+ raw_combined *raw = (raw_combined *)ptr;
+ ::free((void *)raw->data);
+ }
+ };
+
+ class buffer::raw_malloc : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_malloc(unsigned l) : raw(l) {
+ if (len) {
+ data = (char *)malloc(len);
+ if (!data)
+ throw bad_alloc();
+ } else {
+ data = 0;
+ }
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ bdout << "raw_malloc " << this << " alloc " << (void *)data << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw_malloc(unsigned l, char *b) : raw(b, l) {
+ inc_total_alloc(len);
+ bdout << "raw_malloc " << this << " alloc " << (void *)data << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ ~raw_malloc() override {
+ free(data);
+ dec_total_alloc(len);
+ bdout << "raw_malloc " << this << " free " << (void *)data << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw* clone_empty() override {
+ return new raw_malloc(len);
+ }
+ };
+
+#ifndef __CYGWIN__
+ class buffer::raw_mmap_pages : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_mmap_pages(unsigned l) : raw(l) {
+ data = (char*)::mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (!data)
+ throw bad_alloc();
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ bdout << "raw_mmap " << this << " alloc " << (void *)data << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ ~raw_mmap_pages() override {
+ ::munmap(data, len);
+ dec_total_alloc(len);
+ bdout << "raw_mmap " << this << " free " << (void *)data << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw* clone_empty() override {
+ return new raw_mmap_pages(len);
+ }
+ };
+
+ class buffer::raw_posix_aligned : public buffer::raw {
+ unsigned align;
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ raw_posix_aligned(unsigned l, unsigned _align) : raw(l) {
+ align = _align;
+ assert((align >= sizeof(void *)) && (align & (align - 1)) == 0);
+#ifdef DARWIN
+ data = (char *) valloc(len);
+#else
+ int r = ::posix_memalign((void**)(void*)&data, align, len);
+ if (r)
+ throw bad_alloc();
+#endif /* DARWIN */
+ if (!data)
+ throw bad_alloc();
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ bdout << "raw_posix_aligned " << this << " alloc " << (void *)data << " l=" << l << ", align=" << align << " total_alloc=" << buffer::get_total_alloc() << bendl;
+ }
+ ~raw_posix_aligned() override {
+ ::free(data);
+ dec_total_alloc(len);
+ bdout << "raw_posix_aligned " << this << " free " << (void *)data << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw* clone_empty() override {
+ return new raw_posix_aligned(len, align);
+ }
+ };
+#endif
+
+#ifdef __CYGWIN__
+ class buffer::raw_hack_aligned : public buffer::raw {
+ unsigned align;
+ char *realdata;
+ public:
+ raw_hack_aligned(unsigned l, unsigned _align) : raw(l) {
+ align = _align;
+ realdata = new char[len+align-1];
+ unsigned off = ((unsigned)realdata) & (align-1);
+ if (off)
+ data = realdata + align - off;
+ else
+ data = realdata;
+ inc_total_alloc(len+align-1);
+ inc_history_alloc(len+align-1);
+ //cout << "hack aligned " << (unsigned)data
+ //<< " in raw " << (unsigned)realdata
+ //<< " off " << off << std::endl;
+ assert(((unsigned)data & (align-1)) == 0);
+ }
+ ~raw_hack_aligned() {
+ delete[] realdata;
+ dec_total_alloc(len+align-1);
+ }
+ raw* clone_empty() {
+ return new raw_hack_aligned(len, align);
+ }
+ };
+#endif
+
+#ifdef CEPH_HAVE_SPLICE
+ class buffer::raw_pipe : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_pipe(unsigned len) : raw(len), source_consumed(false) {
+ size_t max = get_max_pipe_size();
+ if (len > max) {
+ bdout << "raw_pipe: requested length " << len
+ << " > max length " << max << bendl;
+ throw malformed_input("length larger than max pipe size");
+ }
+ pipefds[0] = -1;
+ pipefds[1] = -1;
+
+ int r;
+ if (::pipe(pipefds) == -1) {
+ r = -errno;
+ bdout << "raw_pipe: error creating pipe: " << cpp_strerror(r) << bendl;
+ throw error_code(r);
+ }
+
+ r = set_nonblocking(pipefds);
+ if (r < 0) {
+ bdout << "raw_pipe: error setting nonblocking flag on temp pipe: "
+ << cpp_strerror(r) << bendl;
+ throw error_code(r);
+ }
+
+ r = set_pipe_size(pipefds, len);
+ if (r < 0) {
+ bdout << "raw_pipe: could not set pipe size" << bendl;
+ // continue, since the pipe should become large enough as needed
+ }
+
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ bdout << "raw_pipe " << this << " alloc " << len << " "
+ << buffer::get_total_alloc() << bendl;
+ }
+
+ ~raw_pipe() override {
+ if (data)
+ free(data);
+ close_pipe(pipefds);
+ dec_total_alloc(len);
+ bdout << "raw_pipe " << this << " free " << (void *)data << " "
+ << buffer::get_total_alloc() << bendl;
+ }
+
+ bool can_zero_copy() const override {
+ return true;
+ }
+
+ int set_source(int fd, loff_t *off) {
+ int flags = SPLICE_F_NONBLOCK;
+ ssize_t r = safe_splice(fd, off, pipefds[1], NULL, len, flags);
+ if (r < 0) {
+ bdout << "raw_pipe: error splicing into pipe: " << cpp_strerror(r)
+ << bendl;
+ return r;
+ }
+ // update length with actual amount read
+ _set_len(r);
+ return 0;
+ }
+
+ int zero_copy_to_fd(int fd, loff_t *offset) override {
+ assert(!source_consumed);
+ int flags = SPLICE_F_NONBLOCK;
+ ssize_t r = safe_splice_exact(pipefds[0], NULL, fd, offset, len, flags);
+ if (r < 0) {
+ bdout << "raw_pipe: error splicing from pipe to fd: "
+ << cpp_strerror(r) << bendl;
+ return r;
+ }
+ source_consumed = true;
+ return 0;
+ }
+
+ buffer::raw* clone_empty() override {
+ // cloning doesn't make sense for pipe-based buffers,
+ // and is only used by unit tests for other types of buffers
+ return NULL;
+ }
+
+ char *get_data() override {
+ if (data)
+ return data;
+ return copy_pipe(pipefds);
+ }
+
+ private:
+ int set_pipe_size(int *fds, long length) {
+#ifdef CEPH_HAVE_SETPIPE_SZ
+ if (::fcntl(fds[1], F_SETPIPE_SZ, length) == -1) {
+ int r = -errno;
+ if (r == -EPERM) {
+ // pipe limit must have changed - EPERM means we requested
+ // more than the maximum size as an unprivileged user
+ update_max_pipe_size();
+ throw malformed_input("length larger than new max pipe size");
+ }
+ return r;
+ }
+#endif
+ return 0;
+ }
+
+ int set_nonblocking(int *fds) {
+ if (::fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1)
+ return -errno;
+ if (::fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1)
+ return -errno;
+ return 0;
+ }
+
+ static void close_pipe(const int *fds) {
+ if (fds[0] >= 0)
+ VOID_TEMP_FAILURE_RETRY(::close(fds[0]));
+ if (fds[1] >= 0)
+ VOID_TEMP_FAILURE_RETRY(::close(fds[1]));
+ }
+ char *copy_pipe(int *fds) {
+ /* preserve original pipe contents by copying into a temporary
+ * pipe before reading.
+ */
+ int tmpfd[2];
+ int r;
+
+ assert(!source_consumed);
+ assert(fds[0] >= 0);
+
+ if (::pipe(tmpfd) == -1) {
+ r = -errno;
+ bdout << "raw_pipe: error creating temp pipe: " << cpp_strerror(r)
+ << bendl;
+ throw error_code(r);
+ }
+ auto sg = make_scope_guard([=] { close_pipe(tmpfd); });
+ r = set_nonblocking(tmpfd);
+ if (r < 0) {
+ bdout << "raw_pipe: error setting nonblocking flag on temp pipe: "
+ << cpp_strerror(r) << bendl;
+ throw error_code(r);
+ }
+ r = set_pipe_size(tmpfd, len);
+ if (r < 0) {
+ bdout << "raw_pipe: error setting pipe size on temp pipe: "
+ << cpp_strerror(r) << bendl;
+ }
+ int flags = SPLICE_F_NONBLOCK;
+ if (::tee(fds[0], tmpfd[1], len, flags) == -1) {
+ r = errno;
+ bdout << "raw_pipe: error tee'ing into temp pipe: " << cpp_strerror(r)
+ << bendl;
+ throw error_code(r);
+ }
+ data = (char *)malloc(len);
+ if (!data) {
+ throw bad_alloc();
+ }
+ r = safe_read(tmpfd[0], data, len);
+ if (r < (ssize_t)len) {
+ bdout << "raw_pipe: error reading from temp pipe:" << cpp_strerror(r)
+ << bendl;
+ free(data);
+ data = NULL;
+ throw error_code(r);
+ }
+ return data;
+ }
+ bool source_consumed;
+ int pipefds[2];
+ };
+#endif // CEPH_HAVE_SPLICE
+
+ /*
+ * primitive buffer types
+ */
+ class buffer::raw_char : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_char(unsigned l) : raw(l) {
+ if (len)
+ data = new char[len];
+ else
+ data = 0;
+ inc_total_alloc(len);
+ inc_history_alloc(len);
+ bdout << "raw_char " << this << " alloc " << (void *)data << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw_char(unsigned l, char *b) : raw(b, l) {
+ inc_total_alloc(len);
+ bdout << "raw_char " << this << " alloc " << (void *)data << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ ~raw_char() override {
+ delete[] data;
+ dec_total_alloc(len);
+ bdout << "raw_char " << this << " free " << (void *)data << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw* clone_empty() override {
+ return new raw_char(len);
+ }
+ };
+
+ class buffer::raw_claimed_char : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_claimed_char(unsigned l, char *b) : raw(b, l) {
+ inc_total_alloc(len);
+ bdout << "raw_claimed_char " << this << " alloc " << (void *)data
+ << " " << l << " " << buffer::get_total_alloc() << bendl;
+ }
+ ~raw_claimed_char() override {
+ dec_total_alloc(len);
+ bdout << "raw_claimed_char " << this << " free " << (void *)data
+ << " " << buffer::get_total_alloc() << bendl;
+ }
+ raw* clone_empty() override {
+ return new raw_char(len);
+ }
+ };
+
+ class buffer::raw_unshareable : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ explicit raw_unshareable(unsigned l) : raw(l) {
+ if (len)
+ data = new char[len];
+ else
+ data = 0;
+ }
+ raw_unshareable(unsigned l, char *b) : raw(b, l) {
+ }
+ raw* clone_empty() override {
+ return new raw_char(len);
+ }
+ bool is_shareable() override {
+ return false; // !shareable, will force make_shareable()
+ }
+ ~raw_unshareable() override {
+ delete[] data;
+ }
+ };
+
+ class buffer::raw_static : public buffer::raw {
+ public:
+ MEMPOOL_CLASS_HELPERS();
+
+ raw_static(const char *d, unsigned l) : raw((char*)d, l) { }
+ ~raw_static() override {}
+ raw* clone_empty() override {
+ return new buffer::raw_char(len);
+ }
+ };
+
+ class buffer::raw_claim_buffer : public buffer::raw {
+ deleter del;
+ public:
+ raw_claim_buffer(const char *b, unsigned l, deleter d)
+ : raw((char*)b, l), del(std::move(d)) { }
+ ~raw_claim_buffer() override {}
+ raw* clone_empty() override {
+ return new buffer::raw_char(len);
+ }
+ };
+
+#if defined(HAVE_XIO)
+ class buffer::xio_msg_buffer : public buffer::raw {
+ private:
+ XioDispatchHook* m_hook;
+ public:
+ xio_msg_buffer(XioDispatchHook* _m_hook, const char *d,
+ unsigned l) :
+ raw((char*)d, l), m_hook(_m_hook->get()) {}
+
+ bool is_shareable() { return false; }
+ static void operator delete(void *p)
+ {
+ xio_msg_buffer *buf = static_cast<xio_msg_buffer*>(p);
+ // return hook ref (counts against pool); it appears illegal
+ // to do this in our dtor, because this fires after that
+ buf->m_hook->put();
+ }
+ raw* clone_empty() {
+ return new buffer::raw_char(len);
+ }
+ };
+
+ class buffer::xio_mempool : public buffer::raw {
+ public:
+ struct xio_reg_mem *mp;
+ xio_mempool(struct xio_reg_mem *_mp, unsigned l) :
+ raw((char*)_mp->addr, l), mp(_mp)
+ { }
+ ~xio_mempool() {}
+ raw* clone_empty() {
+ return new buffer::raw_char(len);
+ }
+ };
+
+ struct xio_reg_mem* get_xio_mp(const buffer::ptr& bp)
+ {
+ buffer::xio_mempool *mb = dynamic_cast<buffer::xio_mempool*>(bp.get_raw());
+ if (mb) {
+ return mb->mp;
+ }
+ return NULL;
+ }
+
+ buffer::raw* buffer::create_msg(
+ unsigned len, char *buf, XioDispatchHook* m_hook) {
+ XioPool& pool = m_hook->get_pool();
+ buffer::raw* bp =
+ static_cast<buffer::raw*>(pool.alloc(sizeof(xio_msg_buffer)));
+ new (bp) xio_msg_buffer(m_hook, buf, len);
+ return bp;
+ }
+#endif /* HAVE_XIO */
+
+ buffer::raw* buffer::copy(const char *c, unsigned len) {
+ raw* r = buffer::create_aligned(len, sizeof(size_t));
+ memcpy(r->data, c, len);
+ return r;
+ }
+
+ buffer::raw* buffer::create(unsigned len) {
+ return buffer::create_aligned(len, sizeof(size_t));
+ }
+ buffer::raw* buffer::create_in_mempool(unsigned len, int mempool) {
+ return buffer::create_aligned_in_mempool(len, sizeof(size_t), mempool);
+ }
+ buffer::raw* buffer::claim_char(unsigned len, char *buf) {
+ return new raw_claimed_char(len, buf);
+ }
+ buffer::raw* buffer::create_malloc(unsigned len) {
+ return new raw_malloc(len);
+ }
+ buffer::raw* buffer::claim_malloc(unsigned len, char *buf) {
+ return new raw_malloc(len, buf);
+ }
+ buffer::raw* buffer::create_static(unsigned len, char *buf) {
+ return new raw_static(buf, len);
+ }
+ buffer::raw* buffer::claim_buffer(unsigned len, char *buf, deleter del) {
+ return new raw_claim_buffer(buf, len, std::move(del));
+ }
+
+ buffer::raw* buffer::create_aligned_in_mempool(
+ unsigned len, unsigned align, int mempool) {
+ // If alignment is a page multiple, use a separate buffer::raw to
+ // avoid fragmenting the heap.
+ //
+ // Somewhat unexpectedly, I see consistently better performance
+ // from raw_combined than from raw even when the allocation size is
+ // a page multiple (but alignment is not).
+ //
+ // I also see better performance from a separate buffer::raw once the
+ // size passes 8KB.
+ if ((align & ~CEPH_PAGE_MASK) == 0 ||
+ len >= CEPH_PAGE_SIZE * 2) {
+#ifndef __CYGWIN__
+ return new raw_posix_aligned(len, align);
+#else
+ return new raw_hack_aligned(len, align);
+#endif
+ }
+ return raw_combined::create(len, align, mempool);
+ }
+ buffer::raw* buffer::create_aligned(
+ unsigned len, unsigned align) {
+ return create_aligned_in_mempool(len, align,
+ mempool::mempool_buffer_anon);
+ }
+
+ buffer::raw* buffer::create_page_aligned(unsigned len) {
+ return create_aligned(len, CEPH_PAGE_SIZE);
+ }
+
+ buffer::raw* buffer::create_zero_copy(unsigned len, int fd, int64_t *offset) {
+#ifdef CEPH_HAVE_SPLICE
+ buffer::raw_pipe* buf = new raw_pipe(len);
+ int r = buf->set_source(fd, (loff_t*)offset);
+ if (r < 0) {
+ delete buf;
+ throw error_code(r);
+ }
+ return buf;
+#else
+ throw error_code(-ENOTSUP);
+#endif
+ }
+
+ buffer::raw* buffer::create_unshareable(unsigned len) {
+ return new raw_unshareable(len);
+ }
+
+ buffer::ptr::ptr(raw *r) : _raw(r), _off(0), _len(r->len) // no lock needed; this is an unref raw.
+ {
+ r->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ buffer::ptr::ptr(unsigned l) : _off(0), _len(l)
+ {
+ _raw = create(l);
+ _raw->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ buffer::ptr::ptr(const char *d, unsigned l) : _off(0), _len(l) // ditto.
+ {
+ _raw = copy(d, l);
+ _raw->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ buffer::ptr::ptr(const ptr& p) : _raw(p._raw), _off(p._off), _len(p._len)
+ {
+ if (_raw) {
+ _raw->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ }
+ buffer::ptr::ptr(ptr&& p) noexcept : _raw(p._raw), _off(p._off), _len(p._len)
+ {
+ p._raw = nullptr;
+ p._off = p._len = 0;
+ }
+ buffer::ptr::ptr(const ptr& p, unsigned o, unsigned l)
+ : _raw(p._raw), _off(p._off + o), _len(l)
+ {
+ assert(o+l <= p._len);
+ assert(_raw);
+ _raw->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ buffer::ptr& buffer::ptr::operator= (const ptr& p)
+ {
+ if (p._raw) {
+ p._raw->nref++;
+ bdout << "ptr " << this << " get " << _raw << bendl;
+ }
+ buffer::raw *raw = p._raw;
+ release();
+ if (raw) {
+ _raw = raw;
+ _off = p._off;
+ _len = p._len;
+ } else {
+ _off = _len = 0;
+ }
+ return *this;
+ }
+ buffer::ptr& buffer::ptr::operator= (ptr&& p) noexcept
+ {
+ release();
+ buffer::raw *raw = p._raw;
+ if (raw) {
+ _raw = raw;
+ _off = p._off;
+ _len = p._len;
+ p._raw = nullptr;
+ p._off = p._len = 0;
+ } else {
+ _off = _len = 0;
+ }
+ return *this;
+ }
+
+ buffer::raw *buffer::ptr::clone()
+ {
+ return _raw->clone();
+ }
+
+ buffer::ptr& buffer::ptr::make_shareable() {
+ if (_raw && !_raw->is_shareable()) {
+ buffer::raw *tr = _raw;
+ _raw = tr->clone();
+ _raw->nref = 1;
+ if (unlikely(--tr->nref == 0)) {
+ ANNOTATE_HAPPENS_AFTER(&tr->nref);
+ ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(&tr->nref);
+ delete tr;
+ } else {
+ ANNOTATE_HAPPENS_BEFORE(&tr->nref);
+ }
+ }
+ return *this;
+ }
+
+ void buffer::ptr::swap(ptr& other)
+ {
+ raw *r = _raw;
+ unsigned o = _off;
+ unsigned l = _len;
+ _raw = other._raw;
+ _off = other._off;
+ _len = other._len;
+ other._raw = r;
+ other._off = o;
+ other._len = l;
+ }
+
+ void buffer::ptr::release()
+ {
+ if (_raw) {
+ bdout << "ptr " << this << " release " << _raw << bendl;
+ if (--_raw->nref == 0) {
+ //cout << "hosing raw " << (void*)_raw << " len " << _raw->len << std::endl;
+ ANNOTATE_HAPPENS_AFTER(&_raw->nref);
+ ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(&_raw->nref);
+ delete _raw; // dealloc old (if any)
+ } else {
+ ANNOTATE_HAPPENS_BEFORE(&_raw->nref);
+ }
+ _raw = 0;
+ }
+ }
+
+ bool buffer::ptr::at_buffer_tail() const { return _off + _len == _raw->len; }
+
+ int buffer::ptr::get_mempool() const {
+ if (_raw) {
+ return _raw->mempool;
+ }
+ return mempool::mempool_buffer_anon;
+ }
+
+ void buffer::ptr::reassign_to_mempool(int pool) {
+ if (_raw) {
+ _raw->reassign_to_mempool(pool);
+ }
+ }
+ void buffer::ptr::try_assign_to_mempool(int pool) {
+ if (_raw) {
+ _raw->try_assign_to_mempool(pool);
+ }
+ }
+
+ const char *buffer::ptr::c_str() const {
+ assert(_raw);
+ if (buffer_track_c_str)
+ buffer_c_str_accesses++;
+ return _raw->get_data() + _off;
+ }
+ char *buffer::ptr::c_str() {
+ assert(_raw);
+ if (buffer_track_c_str)
+ buffer_c_str_accesses++;
+ return _raw->get_data() + _off;
+ }
+ const char *buffer::ptr::end_c_str() const {
+ assert(_raw);
+ if (buffer_track_c_str)
+ buffer_c_str_accesses++;
+ return _raw->get_data() + _off + _len;
+ }
+ char *buffer::ptr::end_c_str() {
+ assert(_raw);
+ if (buffer_track_c_str)
+ buffer_c_str_accesses++;
+ return _raw->get_data() + _off + _len;
+ }
+
+ unsigned buffer::ptr::unused_tail_length() const
+ {
+ if (_raw)
+ return _raw->len - (_off+_len);
+ else
+ return 0;
+ }
+ const char& buffer::ptr::operator[](unsigned n) const
+ {
+ assert(_raw);
+ assert(n < _len);
+ return _raw->get_data()[_off + n];
+ }
+ char& buffer::ptr::operator[](unsigned n)
+ {
+ assert(_raw);
+ assert(n < _len);
+ return _raw->get_data()[_off + n];
+ }
+
+ const char *buffer::ptr::raw_c_str() const { assert(_raw); return _raw->data; }
+ unsigned buffer::ptr::raw_length() const { assert(_raw); return _raw->len; }
+ int buffer::ptr::raw_nref() const { assert(_raw); return _raw->nref; }
+
+ void buffer::ptr::copy_out(unsigned o, unsigned l, char *dest) const {
+ assert(_raw);
+ if (o+l > _len)
+ throw end_of_buffer();
+ char* src = _raw->data + _off + o;
+ maybe_inline_memcpy(dest, src, l, 8);
+ }
+
+ unsigned buffer::ptr::wasted() const
+ {
+ return _raw->len - _len;
+ }
+
+ int buffer::ptr::cmp(const ptr& o) const
+ {
+ int l = _len < o._len ? _len : o._len;
+ if (l) {
+ int r = memcmp(c_str(), o.c_str(), l);
+ if (r)
+ return r;
+ }
+ if (_len < o._len)
+ return -1;
+ if (_len > o._len)
+ return 1;
+ return 0;
+ }
+
+ bool buffer::ptr::is_zero() const
+ {
+ return mem_is_zero(c_str(), _len);
+ }
+
+ unsigned buffer::ptr::append(char c)
+ {
+ assert(_raw);
+ assert(1 <= unused_tail_length());
+ char* ptr = _raw->data + _off + _len;
+ *ptr = c;
+ _len++;
+ return _len + _off;
+ }
+
+ unsigned buffer::ptr::append(const char *p, unsigned l)
+ {
+ assert(_raw);
+ assert(l <= unused_tail_length());
+ char* c = _raw->data + _off + _len;
+ maybe_inline_memcpy(c, p, l, 32);
+ _len += l;
+ return _len + _off;
+ }
+
+ void buffer::ptr::copy_in(unsigned o, unsigned l, const char *src)
+ {
+ copy_in(o, l, src, true);
+ }
+
+ void buffer::ptr::copy_in(unsigned o, unsigned l, const char *src, bool crc_reset)
+ {
+ assert(_raw);
+ assert(o <= _len);
+ assert(o+l <= _len);
+ char* dest = _raw->data + _off + o;
+ if (crc_reset)
+ _raw->invalidate_crc();
+ maybe_inline_memcpy(dest, src, l, 64);
+ }
+
+ void buffer::ptr::zero()
+ {
+ zero(true);
+ }
+
+ void buffer::ptr::zero(bool crc_reset)
+ {
+ if (crc_reset)
+ _raw->invalidate_crc();
+ memset(c_str(), 0, _len);
+ }
+
+ void buffer::ptr::zero(unsigned o, unsigned l)
+ {
+ zero(o, l, true);
+ }
+
+ void buffer::ptr::zero(unsigned o, unsigned l, bool crc_reset)
+ {
+ assert(o+l <= _len);
+ if (crc_reset)
+ _raw->invalidate_crc();
+ memset(c_str()+o, 0, l);
+ }
+ bool buffer::ptr::can_zero_copy() const
+ {
+ return _raw->can_zero_copy();
+ }
+
+ int buffer::ptr::zero_copy_to_fd(int fd, int64_t *offset) const
+ {
+ return _raw->zero_copy_to_fd(fd, (loff_t*)offset);
+ }
+
+ // -- buffer::list::iterator --
+ /*
+ buffer::list::iterator operator=(const buffer::list::iterator& other)
+ {
+ if (this != &other) {
+ bl = other.bl;
+ ls = other.ls;
+ off = other.off;
+ p = other.p;
+ p_off = other.p_off;
+ }
+ return *this;
+ }*/
+
+ template<bool is_const>
+ buffer::list::iterator_impl<is_const>::iterator_impl(bl_t *l, unsigned o)
+ : bl(l), ls(&bl->_buffers), off(0), p(ls->begin()), p_off(0)
+ {
+ advance(o);
+ }
+
+ template<bool is_const>
+ buffer::list::iterator_impl<is_const>::iterator_impl(const buffer::list::iterator& i)
+ : iterator_impl<is_const>(i.bl, i.off, i.p, i.p_off) {}
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::advance(int o)
+ {
+ //cout << this << " advance " << o << " from " << off << " (p_off " << p_off << " in " << p->length() << ")" << std::endl;
+ if (o > 0) {
+ p_off += o;
+ while (p_off > 0) {
+ if (p == ls->end())
+ throw end_of_buffer();
+ if (p_off >= p->length()) {
+ // skip this buffer
+ p_off -= p->length();
+ p++;
+ } else {
+ // somewhere in this buffer!
+ break;
+ }
+ }
+ off += o;
+ return;
+ }
+ while (o < 0) {
+ if (p_off) {
+ unsigned d = -o;
+ if (d > p_off)
+ d = p_off;
+ p_off -= d;
+ off -= d;
+ o += d;
+ } else if (off > 0) {
+ assert(p != ls->begin());
+ p--;
+ p_off = p->length();
+ } else {
+ throw end_of_buffer();
+ }
+ }
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::seek(unsigned o)
+ {
+ p = ls->begin();
+ off = p_off = 0;
+ advance(o);
+ }
+
+ template<bool is_const>
+ char buffer::list::iterator_impl<is_const>::operator*() const
+ {
+ if (p == ls->end())
+ throw end_of_buffer();
+ return (*p)[p_off];
+ }
+
+ template<bool is_const>
+ buffer::list::iterator_impl<is_const>&
+ buffer::list::iterator_impl<is_const>::operator++()
+ {
+ if (p == ls->end())
+ throw end_of_buffer();
+ advance(1);
+ return *this;
+ }
+
+ template<bool is_const>
+ buffer::ptr buffer::list::iterator_impl<is_const>::get_current_ptr() const
+ {
+ if (p == ls->end())
+ throw end_of_buffer();
+ return ptr(*p, p_off, p->length() - p_off);
+ }
+
+ // copy data out.
+ // note that these all _append_ to dest!
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy(unsigned len, char *dest)
+ {
+ if (p == ls->end()) seek(off);
+ while (len > 0) {
+ if (p == ls->end())
+ throw end_of_buffer();
+ assert(p->length() > 0);
+
+ unsigned howmuch = p->length() - p_off;
+ if (len < howmuch) howmuch = len;
+ p->copy_out(p_off, howmuch, dest);
+ dest += howmuch;
+
+ len -= howmuch;
+ advance(howmuch);
+ }
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy(unsigned len, ptr &dest)
+ {
+ copy_deep(len, dest);
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy_deep(unsigned len, ptr &dest)
+ {
+ if (!len) {
+ return;
+ }
+ if (p == ls->end())
+ throw end_of_buffer();
+ assert(p->length() > 0);
+ dest = create(len);
+ copy(len, dest.c_str());
+ }
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy_shallow(unsigned len,
+ ptr &dest)
+ {
+ if (!len) {
+ return;
+ }
+ if (p == ls->end())
+ throw end_of_buffer();
+ assert(p->length() > 0);
+ unsigned howmuch = p->length() - p_off;
+ if (howmuch < len) {
+ dest = create(len);
+ copy(len, dest.c_str());
+ } else {
+ dest = ptr(*p, p_off, len);
+ advance(len);
+ }
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy(unsigned len, list &dest)
+ {
+ if (p == ls->end())
+ seek(off);
+ while (len > 0) {
+ if (p == ls->end())
+ throw end_of_buffer();
+
+ unsigned howmuch = p->length() - p_off;
+ if (len < howmuch)
+ howmuch = len;
+ dest.append(*p, p_off, howmuch);
+
+ len -= howmuch;
+ advance(howmuch);
+ }
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy(unsigned len, std::string &dest)
+ {
+ if (p == ls->end())
+ seek(off);
+ while (len > 0) {
+ if (p == ls->end())
+ throw end_of_buffer();
+
+ unsigned howmuch = p->length() - p_off;
+ const char *c_str = p->c_str();
+ if (len < howmuch)
+ howmuch = len;
+ dest.append(c_str + p_off, howmuch);
+
+ len -= howmuch;
+ advance(howmuch);
+ }
+ }
+
+ template<bool is_const>
+ void buffer::list::iterator_impl<is_const>::copy_all(list &dest)
+ {
+ if (p == ls->end())
+ seek(off);
+ while (1) {
+ if (p == ls->end())
+ return;
+ assert(p->length() > 0);
+
+ unsigned howmuch = p->length() - p_off;
+ const char *c_str = p->c_str();
+ dest.append(c_str + p_off, howmuch);
+
+ advance(howmuch);
+ }
+ }
+
+ template<bool is_const>
+ size_t buffer::list::iterator_impl<is_const>::get_ptr_and_advance(
+ size_t want, const char **data)
+ {
+ if (p == ls->end()) {
+ seek(off);
+ if (p == ls->end()) {
+ return 0;
+ }
+ }
+ *data = p->c_str() + p_off;
+ size_t l = MIN(p->length() - p_off, want);
+ p_off += l;
+ if (p_off == p->length()) {
+ ++p;
+ p_off = 0;
+ }
+ off += l;
+ return l;
+ }
+
+ template<bool is_const>
+ uint32_t buffer::list::iterator_impl<is_const>::crc32c(
+ size_t length, uint32_t crc)
+ {
+ length = MIN( length, get_remaining());
+ while (length > 0) {
+ const char *p;
+ size_t l = get_ptr_and_advance(length, &p);
+ crc = ceph_crc32c(crc, (unsigned char*)p, l);
+ length -= l;
+ }
+ return crc;
+ }
+
+ // explicitly instantiate only the iterator types we need, so we can hide the
+ // details in this compilation unit without introducing unnecessary link time
+ // dependencies.
+ template class buffer::list::iterator_impl<true>;
+ template class buffer::list::iterator_impl<false>;
+
+ buffer::list::iterator::iterator(bl_t *l, unsigned o)
+ : iterator_impl(l, o)
+ {}
+
+ buffer::list::iterator::iterator(bl_t *l, unsigned o, list_iter_t ip, unsigned po)
+ : iterator_impl(l, o, ip, po)
+ {}
+
+ void buffer::list::iterator::advance(int o)
+ {
+ buffer::list::iterator_impl<false>::advance(o);
+ }
+
+ void buffer::list::iterator::seek(unsigned o)
+ {
+ buffer::list::iterator_impl<false>::seek(o);
+ }
+
+ char buffer::list::iterator::operator*()
+ {
+ if (p == ls->end()) {
+ throw end_of_buffer();
+ }
+ return (*p)[p_off];
+ }
+
+ buffer::list::iterator& buffer::list::iterator::operator++()
+ {
+ buffer::list::iterator_impl<false>::operator++();
+ return *this;
+ }
+
+ buffer::ptr buffer::list::iterator::get_current_ptr()
+ {
+ if (p == ls->end()) {
+ throw end_of_buffer();
+ }
+ return ptr(*p, p_off, p->length() - p_off);
+ }
+
+ void buffer::list::iterator::copy(unsigned len, char *dest)
+ {
+ return buffer::list::iterator_impl<false>::copy(len, dest);
+ }
+
+ void buffer::list::iterator::copy(unsigned len, ptr &dest)
+ {
+ return buffer::list::iterator_impl<false>::copy_deep(len, dest);
+ }
+
+ void buffer::list::iterator::copy_deep(unsigned len, ptr &dest)
+ {
+ buffer::list::iterator_impl<false>::copy_deep(len, dest);
+ }
+
+ void buffer::list::iterator::copy_shallow(unsigned len, ptr &dest)
+ {
+ buffer::list::iterator_impl<false>::copy_shallow(len, dest);
+ }
+
+ void buffer::list::iterator::copy(unsigned len, list &dest)
+ {
+ buffer::list::iterator_impl<false>::copy(len, dest);
+ }
+
+ void buffer::list::iterator::copy(unsigned len, std::string &dest)
+ {
+ buffer::list::iterator_impl<false>::copy(len, dest);
+ }
+
+ void buffer::list::iterator::copy_all(list &dest)
+ {
+ buffer::list::iterator_impl<false>::copy_all(dest);
+ }
+
+ void buffer::list::iterator::copy_in(unsigned len, const char *src)
+ {
+ copy_in(len, src, true);
+ }
+
+ // copy data in
+ void buffer::list::iterator::copy_in(unsigned len, const char *src, bool crc_reset)
+ {
+ // copy
+ if (p == ls->end())
+ seek(off);
+ while (len > 0) {
+ if (p == ls->end())
+ throw end_of_buffer();
+
+ unsigned howmuch = p->length() - p_off;
+ if (len < howmuch)
+ howmuch = len;
+ p->copy_in(p_off, howmuch, src, crc_reset);
+
+ src += howmuch;
+ len -= howmuch;
+ advance(howmuch);
+ }
+ }
+
+ void buffer::list::iterator::copy_in(unsigned len, const list& otherl)
+ {
+ if (p == ls->end())
+ seek(off);
+ unsigned left = len;
+ for (std::list<ptr>::const_iterator i = otherl._buffers.begin();
+ i != otherl._buffers.end();
+ ++i) {
+ unsigned l = (*i).length();
+ if (left < l)
+ l = left;
+ copy_in(l, i->c_str());
+ left -= l;
+ if (left == 0)
+ break;
+ }
+ }
+
+ // -- buffer::list --
+
+ buffer::list::list(list&& other)
+ : _buffers(std::move(other._buffers)),
+ _len(other._len),
+ _memcopy_count(other._memcopy_count),
+ last_p(this) {
+ append_buffer.swap(other.append_buffer);
+ other.clear();
+ }
+
+ void buffer::list::swap(list& other)
+ {
+ std::swap(_len, other._len);
+ std::swap(_memcopy_count, other._memcopy_count);
+ _buffers.swap(other._buffers);
+ append_buffer.swap(other.append_buffer);
+ //last_p.swap(other.last_p);
+ last_p = begin();
+ other.last_p = other.begin();
+ }
+
+ bool buffer::list::contents_equal(buffer::list& other)
+ {
+ return static_cast<const buffer::list*>(this)->contents_equal(other);
+ }
+
+ bool buffer::list::contents_equal(const ceph::buffer::list& other) const
+ {
+ if (length() != other.length())
+ return false;
+
+ // buffer-wise comparison
+ if (true) {
+ std::list<ptr>::const_iterator a = _buffers.begin();
+ std::list<ptr>::const_iterator b = other._buffers.begin();
+ unsigned aoff = 0, boff = 0;
+ while (a != _buffers.end()) {
+ unsigned len = a->length() - aoff;
+ if (len > b->length() - boff)
+ len = b->length() - boff;
+ if (memcmp(a->c_str() + aoff, b->c_str() + boff, len) != 0)
+ return false;
+ aoff += len;
+ if (aoff == a->length()) {
+ aoff = 0;
+ ++a;
+ }
+ boff += len;
+ if (boff == b->length()) {
+ boff = 0;
+ ++b;
+ }
+ }
+ assert(b == other._buffers.end());
+ return true;
+ }
+
+ // byte-wise comparison
+ if (false) {
+ bufferlist::const_iterator me = begin();
+ bufferlist::const_iterator him = other.begin();
+ while (!me.end()) {
+ if (*me != *him)
+ return false;
+ ++me;
+ ++him;
+ }
+ return true;
+ }
+ }
+
+ bool buffer::list::can_zero_copy() const
+ {
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it)
+ if (!it->can_zero_copy())
+ return false;
+ return true;
+ }
+
+ bool buffer::list::is_provided_buffer(const char *dst) const
+ {
+ if (_buffers.empty())
+ return false;
+ return (is_contiguous() && (_buffers.front().c_str() == dst));
+ }
+
+ bool buffer::list::is_aligned(unsigned align) const
+ {
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it)
+ if (!it->is_aligned(align))
+ return false;
+ return true;
+ }
+
+ bool buffer::list::is_n_align_sized(unsigned align) const
+ {
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it)
+ if (!it->is_n_align_sized(align))
+ return false;
+ return true;
+ }
+
+ bool buffer::list::is_aligned_size_and_memory(unsigned align_size,
+ unsigned align_memory) const
+ {
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it) {
+ if (!it->is_aligned(align_memory) || !it->is_n_align_sized(align_size))
+ return false;
+ }
+ return true;
+ }
+
+ bool buffer::list::is_zero() const {
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it) {
+ if (!it->is_zero()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void buffer::list::zero()
+ {
+ for (std::list<ptr>::iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it)
+ it->zero();
+ }
+
+ void buffer::list::zero(unsigned o, unsigned l)
+ {
+ assert(o+l <= _len);
+ unsigned p = 0;
+ for (std::list<ptr>::iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it) {
+ if (p + it->length() > o) {
+ if (p >= o && p+it->length() <= o+l) {
+ // 'o'------------- l -----------|
+ // 'p'-- it->length() --|
+ it->zero();
+ } else if (p >= o) {
+ // 'o'------------- l -----------|
+ // 'p'------- it->length() -------|
+ it->zero(0, o+l-p);
+ } else if (p + it->length() <= o+l) {
+ // 'o'------------- l -----------|
+ // 'p'------- it->length() -------|
+ it->zero(o-p, it->length()-(o-p));
+ } else {
+ // 'o'----------- l -----------|
+ // 'p'---------- it->length() ----------|
+ it->zero(o-p, l);
+ }
+ }
+ p += it->length();
+ if (o+l <= p)
+ break; // done
+ }
+ }
+
+ bool buffer::list::is_contiguous() const
+ {
+ return &(*_buffers.begin()) == &(*_buffers.rbegin());
+ }
+
+ bool buffer::list::is_n_page_sized() const
+ {
+ return is_n_align_sized(CEPH_PAGE_SIZE);
+ }
+
+ bool buffer::list::is_page_aligned() const
+ {
+ return is_aligned(CEPH_PAGE_SIZE);
+ }
+
+ int buffer::list::get_mempool() const
+ {
+ if (_buffers.empty()) {
+ return mempool::mempool_buffer_anon;
+ }
+ return _buffers.back().get_mempool();
+ }
+
+ void buffer::list::reassign_to_mempool(int pool)
+ {
+ if (append_buffer.get_raw()) {
+ append_buffer.get_raw()->reassign_to_mempool(pool);
+ }
+ for (auto& p : _buffers) {
+ p.get_raw()->reassign_to_mempool(pool);
+ }
+ }
+
+ void buffer::list::try_assign_to_mempool(int pool)
+ {
+ if (append_buffer.get_raw()) {
+ append_buffer.get_raw()->try_assign_to_mempool(pool);
+ }
+ for (auto& p : _buffers) {
+ p.get_raw()->try_assign_to_mempool(pool);
+ }
+ }
+
+ void buffer::list::rebuild()
+ {
+ if (_len == 0) {
+ _buffers.clear();
+ return;
+ }
+ ptr nb;
+ if ((_len & ~CEPH_PAGE_MASK) == 0)
+ nb = buffer::create_page_aligned(_len);
+ else
+ nb = buffer::create(_len);
+ rebuild(nb);
+ }
+
+ void buffer::list::rebuild(ptr& nb)
+ {
+ unsigned pos = 0;
+ for (std::list<ptr>::iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it) {
+ nb.copy_in(pos, it->length(), it->c_str(), false);
+ pos += it->length();
+ }
+ _memcopy_count += pos;
+ _buffers.clear();
+ if (nb.length())
+ _buffers.push_back(nb);
+ invalidate_crc();
+ last_p = begin();
+ }
+
+ bool buffer::list::rebuild_aligned(unsigned align)
+ {
+ return rebuild_aligned_size_and_memory(align, align);
+ }
+
+ bool buffer::list::rebuild_aligned_size_and_memory(unsigned align_size,
+ unsigned align_memory)
+ {
+ unsigned old_memcopy_count = _memcopy_count;
+ std::list<ptr>::iterator p = _buffers.begin();
+ while (p != _buffers.end()) {
+ // keep anything that's already align and sized aligned
+ if (p->is_aligned(align_memory) && p->is_n_align_sized(align_size)) {
+ /*cout << " segment " << (void*)p->c_str()
+ << " offset " << ((unsigned long)p->c_str() & (align - 1))
+ << " length " << p->length()
+ << " " << (p->length() & (align - 1)) << " ok" << std::endl;
+ */
+ ++p;
+ continue;
+ }
+
+ // consolidate unaligned items, until we get something that is sized+aligned
+ list unaligned;
+ unsigned offset = 0;
+ do {
+ /*cout << " segment " << (void*)p->c_str()
+ << " offset " << ((unsigned long)p->c_str() & (align - 1))
+ << " length " << p->length() << " " << (p->length() & (align - 1))
+ << " overall offset " << offset << " " << (offset & (align - 1))
+ << " not ok" << std::endl;
+ */
+ offset += p->length();
+ unaligned.push_back(*p);
+ _buffers.erase(p++);
+ } while (p != _buffers.end() &&
+ (!p->is_aligned(align_memory) ||
+ !p->is_n_align_sized(align_size) ||
+ (offset % align_size)));
+ if (!(unaligned.is_contiguous() && unaligned._buffers.front().is_aligned(align_memory))) {
+ ptr nb(buffer::create_aligned(unaligned._len, align_memory));
+ unaligned.rebuild(nb);
+ _memcopy_count += unaligned._len;
+ }
+ _buffers.insert(p, unaligned._buffers.front());
+ }
+ last_p = begin();
+
+ return (old_memcopy_count != _memcopy_count);
+ }
+
+ bool buffer::list::rebuild_page_aligned()
+ {
+ return rebuild_aligned(CEPH_PAGE_SIZE);
+ }
+
+ void buffer::list::reserve(size_t prealloc)
+ {
+ if (append_buffer.unused_tail_length() < prealloc) {
+ append_buffer = buffer::create_in_mempool(prealloc, get_mempool());
+ append_buffer.set_length(0); // unused, so far.
+ }
+ }
+
+ // sort-of-like-assignment-op
+ void buffer::list::claim(list& bl, unsigned int flags)
+ {
+ // free my buffers
+ clear();
+ claim_append(bl, flags);
+ }
+
+ void buffer::list::claim_append(list& bl, unsigned int flags)
+ {
+ // steal the other guy's buffers
+ _len += bl._len;
+ if (!(flags & CLAIM_ALLOW_NONSHAREABLE))
+ bl.make_shareable();
+ _buffers.splice(_buffers.end(), bl._buffers );
+ bl._len = 0;
+ bl.last_p = bl.begin();
+ }
+
+ void buffer::list::claim_prepend(list& bl, unsigned int flags)
+ {
+ // steal the other guy's buffers
+ _len += bl._len;
+ if (!(flags & CLAIM_ALLOW_NONSHAREABLE))
+ bl.make_shareable();
+ _buffers.splice(_buffers.begin(), bl._buffers );
+ bl._len = 0;
+ bl.last_p = bl.begin();
+ }
+
+ void buffer::list::claim_append_piecewise(list& bl)
+ {
+ // steal the other guy's buffers
+ for (std::list<buffer::ptr>::const_iterator i = bl.buffers().begin();
+ i != bl.buffers().end(); i++) {
+ append(*i, 0, i->length());
+ }
+ bl.clear();
+ }
+
+ void buffer::list::copy(unsigned off, unsigned len, char *dest) const
+ {
+ if (off + len > length())
+ throw end_of_buffer();
+ if (last_p.get_off() != off)
+ last_p.seek(off);
+ last_p.copy(len, dest);
+ }
+
+ void buffer::list::copy(unsigned off, unsigned len, list &dest) const
+ {
+ if (off + len > length())
+ throw end_of_buffer();
+ if (last_p.get_off() != off)
+ last_p.seek(off);
+ last_p.copy(len, dest);
+ }
+
+ void buffer::list::copy(unsigned off, unsigned len, std::string& dest) const
+ {
+ if (last_p.get_off() != off)
+ last_p.seek(off);
+ return last_p.copy(len, dest);
+ }
+
+ void buffer::list::copy_in(unsigned off, unsigned len, const char *src)
+ {
+ copy_in(off, len, src, true);
+ }
+
+ void buffer::list::copy_in(unsigned off, unsigned len, const char *src, bool crc_reset)
+ {
+ if (off + len > length())
+ throw end_of_buffer();
+
+ if (last_p.get_off() != off)
+ last_p.seek(off);
+ last_p.copy_in(len, src, crc_reset);
+ }
+
+ void buffer::list::copy_in(unsigned off, unsigned len, const list& src)
+ {
+ if (last_p.get_off() != off)
+ last_p.seek(off);
+ last_p.copy_in(len, src);
+ }
+
+ void buffer::list::append(char c)
+ {
+ // put what we can into the existing append_buffer.
+ unsigned gap = append_buffer.unused_tail_length();
+ if (!gap) {
+ // make a new append_buffer!
+ append_buffer = raw_combined::create(CEPH_BUFFER_APPEND_SIZE, 0,
+ get_mempool());
+ append_buffer.set_length(0); // unused, so far.
+ }
+ append(append_buffer, append_buffer.append(c) - 1, 1); // add segment to the list
+ }
+
+ void buffer::list::append(const char *data, unsigned len)
+ {
+ while (len > 0) {
+ // put what we can into the existing append_buffer.
+ unsigned gap = append_buffer.unused_tail_length();
+ if (gap > 0) {
+ if (gap > len) gap = len;
+ //cout << "append first char is " << data[0] << ", last char is " << data[len-1] << std::endl;
+ append_buffer.append(data, gap);
+ append(append_buffer, append_buffer.length() - gap, gap); // add segment to the list
+ len -= gap;
+ data += gap;
+ }
+ if (len == 0)
+ break; // done!
+
+ // make a new append_buffer. fill out a complete page, factoring in the
+ // raw_combined overhead.
+ size_t need = ROUND_UP_TO(len, sizeof(size_t)) + sizeof(raw_combined);
+ size_t alen = ROUND_UP_TO(need, CEPH_BUFFER_ALLOC_UNIT) -
+ sizeof(raw_combined);
+ append_buffer = raw_combined::create(alen, 0, get_mempool());
+ append_buffer.set_length(0); // unused, so far.
+ }
+ }
+
+ void buffer::list::append(const ptr& bp)
+ {
+ if (bp.length())
+ push_back(bp);
+ }
+
+ void buffer::list::append(ptr&& bp)
+ {
+ if (bp.length())
+ push_back(std::move(bp));
+ }
+
+ void buffer::list::append(const ptr& bp, unsigned off, unsigned len)
+ {
+ assert(len+off <= bp.length());
+ if (!_buffers.empty()) {
+ ptr &l = _buffers.back();
+ if (l.get_raw() == bp.get_raw() &&
+ l.end() == bp.start() + off) {
+ // yay contiguous with tail bp!
+ l.set_length(l.length()+len);
+ _len += len;
+ return;
+ }
+ }
+ // add new item to list
+ push_back(ptr(bp, off, len));
+ }
+
+ void buffer::list::append(const list& bl)
+ {
+ _len += bl._len;
+ for (std::list<ptr>::const_iterator p = bl._buffers.begin();
+ p != bl._buffers.end();
+ ++p)
+ _buffers.push_back(*p);
+ }
+
+ void buffer::list::append(std::istream& in)
+ {
+ while (!in.eof()) {
+ std::string s;
+ getline(in, s);
+ append(s.c_str(), s.length());
+ if (s.length())
+ append("\n", 1);
+ }
+ }
+
+ void buffer::list::prepend_zero(unsigned len)
+ {
+ ptr bp(len);
+ bp.zero(false);
+ _len += len;
+ _buffers.emplace_front(std::move(bp));
+ }
+
+ void buffer::list::append_zero(unsigned len)
+ {
+ ptr bp(len);
+ bp.zero(false);
+ append(std::move(bp));
+ }
+
+
+ /*
+ * get a char
+ */
+ const char& buffer::list::operator[](unsigned n) const
+ {
+ if (n >= _len)
+ throw end_of_buffer();
+
+ for (std::list<ptr>::const_iterator p = _buffers.begin();
+ p != _buffers.end();
+ ++p) {
+ if (n >= p->length()) {
+ n -= p->length();
+ continue;
+ }
+ return (*p)[n];
+ }
+ ceph_abort();
+ }
+
+ /*
+ * return a contiguous ptr to whole bufferlist contents.
+ */
+ char *buffer::list::c_str()
+ {
+ if (_buffers.empty())
+ return 0; // no buffers
+
+ std::list<ptr>::const_iterator iter = _buffers.begin();
+ ++iter;
+
+ if (iter != _buffers.end())
+ rebuild();
+ return _buffers.front().c_str(); // good, we're already contiguous.
+ }
+
+ string buffer::list::to_str() const {
+ string s;
+ s.reserve(length());
+ for (std::list<ptr>::const_iterator p = _buffers.begin();
+ p != _buffers.end();
+ ++p) {
+ if (p->length()) {
+ s.append(p->c_str(), p->length());
+ }
+ }
+ return s;
+ }
+
+ char *buffer::list::get_contiguous(unsigned orig_off, unsigned len)
+ {
+ if (orig_off + len > length())
+ throw end_of_buffer();
+
+ if (len == 0) {
+ return 0;
+ }
+
+ unsigned off = orig_off;
+ std::list<ptr>::iterator curbuf = _buffers.begin();
+ while (off > 0 && off >= curbuf->length()) {
+ off -= curbuf->length();
+ ++curbuf;
+ }
+
+ if (off + len > curbuf->length()) {
+ bufferlist tmp;
+ unsigned l = off + len;
+
+ do {
+ if (l >= curbuf->length())
+ l -= curbuf->length();
+ else
+ l = 0;
+ tmp.append(*curbuf);
+ curbuf = _buffers.erase(curbuf);
+
+ } while (curbuf != _buffers.end() && l > 0);
+
+ assert(l == 0);
+
+ tmp.rebuild();
+ _buffers.insert(curbuf, tmp._buffers.front());
+ return tmp.c_str() + off;
+ }
+
+ last_p = begin(); // we modified _buffers
+
+ return curbuf->c_str() + off;
+ }
+
+ void buffer::list::substr_of(const list& other, unsigned off, unsigned len)
+ {
+ if (off + len > other.length())
+ throw end_of_buffer();
+
+ clear();
+
+ // skip off
+ std::list<ptr>::const_iterator curbuf = other._buffers.begin();
+ while (off > 0 &&
+ off >= curbuf->length()) {
+ // skip this buffer
+ //cout << "skipping over " << *curbuf << std::endl;
+ off -= (*curbuf).length();
+ ++curbuf;
+ }
+ assert(len == 0 || curbuf != other._buffers.end());
+
+ while (len > 0) {
+ // partial?
+ if (off + len < curbuf->length()) {
+ //cout << "copying partial of " << *curbuf << std::endl;
+ _buffers.push_back( ptr( *curbuf, off, len ) );
+ _len += len;
+ break;
+ }
+
+ // through end
+ //cout << "copying end (all?) of " << *curbuf << std::endl;
+ unsigned howmuch = curbuf->length() - off;
+ _buffers.push_back( ptr( *curbuf, off, howmuch ) );
+ _len += howmuch;
+ len -= howmuch;
+ off = 0;
+ ++curbuf;
+ }
+ }
+
+ // funky modifer
+ void buffer::list::splice(unsigned off, unsigned len, list *claim_by /*, bufferlist& replace_with */)
+ { // fixme?
+ if (len == 0)
+ return;
+
+ if (off >= length())
+ throw end_of_buffer();
+
+ assert(len > 0);
+ //cout << "splice off " << off << " len " << len << " ... mylen = " << length() << std::endl;
+
+ // skip off
+ std::list<ptr>::iterator curbuf = _buffers.begin();
+ while (off > 0) {
+ assert(curbuf != _buffers.end());
+ if (off >= (*curbuf).length()) {
+ // skip this buffer
+ //cout << "off = " << off << " skipping over " << *curbuf << std::endl;
+ off -= (*curbuf).length();
+ ++curbuf;
+ } else {
+ // somewhere in this buffer!
+ //cout << "off = " << off << " somewhere in " << *curbuf << std::endl;
+ break;
+ }
+ }
+
+ if (off) {
+ // add a reference to the front bit
+ // insert it before curbuf (which we'll hose)
+ //cout << "keeping front " << off << " of " << *curbuf << std::endl;
+ _buffers.insert( curbuf, ptr( *curbuf, 0, off ) );
+ _len += off;
+ }
+
+ while (len > 0) {
+ // partial?
+ if (off + len < (*curbuf).length()) {
+ //cout << "keeping end of " << *curbuf << ", losing first " << off+len << std::endl;
+ if (claim_by)
+ claim_by->append( *curbuf, off, len );
+ (*curbuf).set_offset( off+len + (*curbuf).offset() ); // ignore beginning big
+ (*curbuf).set_length( (*curbuf).length() - (len+off) );
+ _len -= off+len;
+ //cout << " now " << *curbuf << std::endl;
+ break;
+ }
+
+ // hose though the end
+ unsigned howmuch = (*curbuf).length() - off;
+ //cout << "discarding " << howmuch << " of " << *curbuf << std::endl;
+ if (claim_by)
+ claim_by->append( *curbuf, off, howmuch );
+ _len -= (*curbuf).length();
+ _buffers.erase( curbuf++ );
+ len -= howmuch;
+ off = 0;
+ }
+
+ // splice in *replace (implement me later?)
+
+ last_p = begin(); // just in case we were in the removed region.
+ }
+
+ void buffer::list::write(int off, int len, std::ostream& out) const
+ {
+ list s;
+ s.substr_of(*this, off, len);
+ for (std::list<ptr>::const_iterator it = s._buffers.begin();
+ it != s._buffers.end();
+ ++it)
+ if (it->length())
+ out.write(it->c_str(), it->length());
+ /*iterator p(this, off);
+ while (len > 0 && !p.end()) {
+ int l = p.left_in_this_buf();
+ if (l > len)
+ l = len;
+ out.write(p.c_str(), l);
+ len -= l;
+ }*/
+ }
+
+void buffer::list::encode_base64(buffer::list& o)
+{
+ bufferptr bp(length() * 4 / 3 + 3);
+ int l = ceph_armor(bp.c_str(), bp.c_str() + bp.length(), c_str(), c_str() + length());
+ bp.set_length(l);
+ o.push_back(std::move(bp));
+}
+
+void buffer::list::decode_base64(buffer::list& e)
+{
+ bufferptr bp(4 + ((e.length() * 3) / 4));
+ int l = ceph_unarmor(bp.c_str(), bp.c_str() + bp.length(), e.c_str(), e.c_str() + e.length());
+ if (l < 0) {
+ std::ostringstream oss;
+ oss << "decode_base64: decoding failed:\n";
+ hexdump(oss);
+ throw buffer::malformed_input(oss.str().c_str());
+ }
+ assert(l <= (int)bp.length());
+ bp.set_length(l);
+ push_back(std::move(bp));
+}
+
+
+
+int buffer::list::read_file(const char *fn, std::string *error)
+{
+ int fd = TEMP_FAILURE_RETRY(::open(fn, O_RDONLY));
+ if (fd < 0) {
+ int err = errno;
+ std::ostringstream oss;
+ oss << "can't open " << fn << ": " << cpp_strerror(err);
+ *error = oss.str();
+ return -err;
+ }
+
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ if (::fstat(fd, &st) < 0) {
+ int err = errno;
+ std::ostringstream oss;
+ oss << "bufferlist::read_file(" << fn << "): stat error: "
+ << cpp_strerror(err);
+ *error = oss.str();
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
+ return -err;
+ }
+
+ ssize_t ret = read_fd(fd, st.st_size);
+ if (ret < 0) {
+ std::ostringstream oss;
+ oss << "bufferlist::read_file(" << fn << "): read error:"
+ << cpp_strerror(ret);
+ *error = oss.str();
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
+ return ret;
+ }
+ else if (ret != st.st_size) {
+ // Premature EOF.
+ // Perhaps the file changed between stat() and read()?
+ std::ostringstream oss;
+ oss << "bufferlist::read_file(" << fn << "): warning: got premature EOF.";
+ *error = oss.str();
+ // not actually an error, but weird
+ }
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
+ return 0;
+}
+
+ssize_t buffer::list::read_fd(int fd, size_t len)
+{
+ // try zero copy first
+ if (false && read_fd_zero_copy(fd, len) == 0) {
+ // TODO fix callers to not require correct read size, which is not
+ // available for raw_pipe until we actually inspect the data
+ return 0;
+ }
+ bufferptr bp = buffer::create(len);
+ ssize_t ret = safe_read(fd, (void*)bp.c_str(), len);
+ if (ret >= 0) {
+ bp.set_length(ret);
+ append(std::move(bp));
+ }
+ return ret;
+}
+
+int buffer::list::read_fd_zero_copy(int fd, size_t len)
+{
+#ifdef CEPH_HAVE_SPLICE
+ try {
+ append(buffer::create_zero_copy(len, fd, NULL));
+ } catch (buffer::error_code &e) {
+ return e.code;
+ } catch (buffer::malformed_input &e) {
+ return -EIO;
+ }
+ return 0;
+#else
+ return -ENOTSUP;
+#endif
+}
+
+int buffer::list::write_file(const char *fn, int mode)
+{
+ int fd = TEMP_FAILURE_RETRY(::open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode));
+ if (fd < 0) {
+ int err = errno;
+ cerr << "bufferlist::write_file(" << fn << "): failed to open file: "
+ << cpp_strerror(err) << std::endl;
+ return -err;
+ }
+ int ret = write_fd(fd);
+ if (ret) {
+ cerr << "bufferlist::write_fd(" << fn << "): write_fd error: "
+ << cpp_strerror(ret) << std::endl;
+ VOID_TEMP_FAILURE_RETRY(::close(fd));
+ return ret;
+ }
+ if (TEMP_FAILURE_RETRY(::close(fd))) {
+ int err = errno;
+ cerr << "bufferlist::write_file(" << fn << "): close error: "
+ << cpp_strerror(err) << std::endl;
+ return -err;
+ }
+ return 0;
+}
+
+static int do_writev(int fd, struct iovec *vec, uint64_t offset, unsigned veclen, unsigned bytes)
+{
+ ssize_t r = 0;
+ while (bytes > 0) {
+#ifdef HAVE_PWRITEV
+ r = ::pwritev(fd, vec, veclen, offset);
+#else
+ r = ::lseek64(fd, offset, SEEK_SET);
+ if (r != offset) {
+ r = -errno;
+ return r;
+ }
+ r = ::writev(fd, vec, veclen);
+#endif
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ return -errno;
+ }
+
+ bytes -= r;
+ offset += r;
+ if (bytes == 0) break;
+
+ while (r > 0) {
+ if (vec[0].iov_len <= (size_t)r) {
+ // drain this whole item
+ r -= vec[0].iov_len;
+ ++vec;
+ --veclen;
+ } else {
+ vec[0].iov_base = (char *)vec[0].iov_base + r;
+ vec[0].iov_len -= r;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int buffer::list::write_fd(int fd) const
+{
+ if (can_zero_copy())
+ return write_fd_zero_copy(fd);
+
+ // use writev!
+ iovec iov[IOV_MAX];
+ int iovlen = 0;
+ ssize_t bytes = 0;
+
+ std::list<ptr>::const_iterator p = _buffers.begin();
+ while (p != _buffers.end()) {
+ if (p->length() > 0) {
+ iov[iovlen].iov_base = (void *)p->c_str();
+ iov[iovlen].iov_len = p->length();
+ bytes += p->length();
+ iovlen++;
+ }
+ ++p;
+
+ if (iovlen == IOV_MAX ||
+ p == _buffers.end()) {
+ iovec *start = iov;
+ int num = iovlen;
+ ssize_t wrote;
+ retry:
+ wrote = ::writev(fd, start, num);
+ if (wrote < 0) {
+ int err = errno;
+ if (err == EINTR)
+ goto retry;
+ return -err;
+ }
+ if (wrote < bytes) {
+ // partial write, recover!
+ while ((size_t)wrote >= start[0].iov_len) {
+ wrote -= start[0].iov_len;
+ bytes -= start[0].iov_len;
+ start++;
+ num--;
+ }
+ if (wrote > 0) {
+ start[0].iov_len -= wrote;
+ start[0].iov_base = (char *)start[0].iov_base + wrote;
+ bytes -= wrote;
+ }
+ goto retry;
+ }
+ iovlen = 0;
+ bytes = 0;
+ }
+ }
+ return 0;
+}
+
+int buffer::list::write_fd(int fd, uint64_t offset) const
+{
+ iovec iov[IOV_MAX];
+
+ std::list<ptr>::const_iterator p = _buffers.begin();
+ uint64_t left_pbrs = _buffers.size();
+ while (left_pbrs) {
+ ssize_t bytes = 0;
+ unsigned iovlen = 0;
+ uint64_t size = MIN(left_pbrs, IOV_MAX);
+ left_pbrs -= size;
+ while (size > 0) {
+ iov[iovlen].iov_base = (void *)p->c_str();
+ iov[iovlen].iov_len = p->length();
+ iovlen++;
+ bytes += p->length();
+ ++p;
+ size--;
+ }
+
+ int r = do_writev(fd, iov, offset, iovlen, bytes);
+ if (r < 0)
+ return r;
+ offset += bytes;
+ }
+ return 0;
+}
+
+int buffer::list::write_fd_zero_copy(int fd) const
+{
+ if (!can_zero_copy())
+ return -ENOTSUP;
+ /* pass offset to each call to avoid races updating the fd seek
+ * position, since the I/O may be non-blocking
+ */
+ int64_t offset = ::lseek(fd, 0, SEEK_CUR);
+ int64_t *off_p = &offset;
+ if (offset < 0 && errno != ESPIPE)
+ return -errno;
+ if (errno == ESPIPE)
+ off_p = NULL;
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end(); ++it) {
+ int r = it->zero_copy_to_fd(fd, off_p);
+ if (r < 0)
+ return r;
+ if (off_p)
+ offset += it->length();
+ }
+ return 0;
+}
+
+__u32 buffer::list::crc32c(__u32 crc) const
+{
+ for (std::list<ptr>::const_iterator it = _buffers.begin();
+ it != _buffers.end();
+ ++it) {
+ if (it->length()) {
+ raw *r = it->get_raw();
+ pair<size_t, size_t> ofs(it->offset(), it->offset() + it->length());
+ pair<uint32_t, uint32_t> ccrc;
+ if (r->get_crc(ofs, &ccrc)) {
+ if (ccrc.first == crc) {
+ // got it already
+ crc = ccrc.second;
+ if (buffer_track_crc)
+ buffer_cached_crc++;
+ } else {
+ /* If we have cached crc32c(buf, v) for initial value v,
+ * we can convert this to a different initial value v' by:
+ * crc32c(buf, v') = crc32c(buf, v) ^ adjustment
+ * where adjustment = crc32c(0*len(buf), v ^ v')
+ *
+ * http://crcutil.googlecode.com/files/crc-doc.1.0.pdf
+ * note, u for our crc32c implementation is 0
+ */
+ crc = ccrc.second ^ ceph_crc32c(ccrc.first ^ crc, NULL, it->length());
+ if (buffer_track_crc)
+ buffer_cached_crc_adjusted++;
+ }
+ } else {
+ if (buffer_track_crc)
+ buffer_missed_crc++;
+ uint32_t base = crc;
+ crc = ceph_crc32c(crc, (unsigned char*)it->c_str(), it->length());
+ r->set_crc(ofs, make_pair(base, crc));
+ }
+ }
+ }
+ return crc;
+}
+
+void buffer::list::invalidate_crc()
+{
+ for (std::list<ptr>::const_iterator p = _buffers.begin(); p != _buffers.end(); ++p) {
+ raw *r = p->get_raw();
+ if (r) {
+ r->invalidate_crc();
+ }
+ }
+}
+
+/**
+ * Binary write all contents to a C++ stream
+ */
+void buffer::list::write_stream(std::ostream &out) const
+{
+ for (std::list<ptr>::const_iterator p = _buffers.begin(); p != _buffers.end(); ++p) {
+ if (p->length() > 0) {
+ out.write(p->c_str(), p->length());
+ }
+ }
+}
+
+
+void buffer::list::hexdump(std::ostream &out, bool trailing_newline) const
+{
+ if (!length())
+ return;
+
+ std::ios_base::fmtflags original_flags = out.flags();
+
+ // do our best to match the output of hexdump -C, for better
+ // diff'ing!
+
+ out.setf(std::ios::right);
+ out.fill('0');
+
+ unsigned per = 16;
+ bool was_zeros = false, did_star = false;
+ for (unsigned o=0; o<length(); o += per) {
+ bool row_is_zeros = false;
+ if (o + per < length()) {
+ row_is_zeros = true;
+ for (unsigned i=0; i<per && o+i<length(); i++) {
+ if ((*this)[o+i]) {
+ row_is_zeros = false;
+ }
+ }
+ if (row_is_zeros) {
+ if (was_zeros) {
+ if (!did_star) {
+ out << "\n*";
+ did_star = true;
+ }
+ continue;
+ }
+ was_zeros = true;
+ } else {
+ was_zeros = false;
+ did_star = false;
+ }
+ }
+ if (o)
+ out << "\n";
+ out << std::hex << std::setw(8) << o << " ";
+
+ unsigned i;
+ for (i=0; i<per && o+i<length(); i++) {
+ if (i == 8)
+ out << ' ';
+ out << " " << std::setw(2) << ((unsigned)(*this)[o+i] & 0xff);
+ }
+ for (; i<per; i++) {
+ if (i == 8)
+ out << ' ';
+ out << " ";
+ }
+
+ out << " |";
+ for (i=0; i<per && o+i<length(); i++) {
+ char c = (*this)[o+i];
+ if (isupper(c) || islower(c) || isdigit(c) || c == ' ' || ispunct(c))
+ out << c;
+ else
+ out << '.';
+ }
+ out << '|' << std::dec;
+ }
+ if (trailing_newline) {
+ out << "\n" << std::hex << std::setw(8) << length();
+ out << "\n";
+ }
+
+ out.flags(original_flags);
+}
+
+
+buffer::list buffer::list::static_from_mem(char* c, size_t l) {
+ list bl;
+ bl.push_back(ptr(create_static(l, c)));
+ return bl;
+}
+
+buffer::list buffer::list::static_from_cstring(char* c) {
+ return static_from_mem(c, std::strlen(c));
+}
+
+buffer::list buffer::list::static_from_string(string& s) {
+ // C++14 just has string::data return a char* from a non-const
+ // string.
+ return static_from_mem(const_cast<char*>(s.data()), s.length());
+ // But the way buffer::list mostly doesn't work in a sane way with
+ // const makes me generally sad.
+}
+
+std::ostream& buffer::operator<<(std::ostream& out, const buffer::raw &r) {
+ return out << "buffer::raw(" << (void*)r.data << " len " << r.len << " nref " << r.nref.load() << ")";
+}
+
+std::ostream& buffer::operator<<(std::ostream& out, const buffer::ptr& bp) {
+ if (bp.have_raw())
+ out << "buffer::ptr(" << bp.offset() << "~" << bp.length()
+ << " " << (void*)bp.c_str()
+ << " in raw " << (void*)bp.raw_c_str()
+ << " len " << bp.raw_length()
+ << " nref " << bp.raw_nref() << ")";
+ else
+ out << "buffer:ptr(" << bp.offset() << "~" << bp.length() << " no raw)";
+ return out;
+}
+
+std::ostream& buffer::operator<<(std::ostream& out, const buffer::list& bl) {
+ out << "buffer::list(len=" << bl.length() << "," << std::endl;
+
+ std::list<buffer::ptr>::const_iterator it = bl.buffers().begin();
+ while (it != bl.buffers().end()) {
+ out << "\t" << *it;
+ if (++it == bl.buffers().end()) break;
+ out << "," << std::endl;
+ }
+ out << std::endl << ")";
+ return out;
+}
+
+std::ostream& buffer::operator<<(std::ostream& out, const buffer::error& e)
+{
+ return out << e.what();
+}
+
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_malloc, buffer_raw_malloc,
+ buffer_meta);
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_mmap_pages, buffer_raw_mmap_pagse,
+ buffer_meta);
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_posix_aligned,
+ buffer_raw_posix_aligned, buffer_meta);
+#ifdef CEPH_HAVE_SPLICE
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_pipe, buffer_raw_pipe, buffer_meta);
+#endif
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_char, buffer_raw_char, buffer_meta);
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_claimed_char, buffer_raw_claimed_char,
+ buffer_meta);
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_unshareable, buffer_raw_unshareable,
+ buffer_meta);
+MEMPOOL_DEFINE_OBJECT_FACTORY(buffer::raw_static, buffer_raw_static,
+ buffer_meta);
+