// -*- 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 * * 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. * */ #ifndef CEPH_ENCODING_H #define CEPH_ENCODING_H #include #include #include #include #include #include #include #include #include #include #include #include "include/unordered_map.h" #include "include/unordered_set.h" #include "common/ceph_time.h" #include "include/int_types.h" #include "common/convenience.h" #include "byteorder.h" #include "buffer.h" // pull in the new-style encoding so that we get the denc_traits<> definition. #include "denc.h" #include "assert.h" using namespace ceph; namespace ceph { /* * Notes on feature encoding: * * - The default encode() methods have a features argument with a default parameter * (which goes to zero). * - Normal classes will use WRITE_CLASS_ENCODER, with that features=0 default. * - Classes that _require_ features will use WRITE_CLASS_ENCODER_FEATURES, which * does not define the default. Any caller must explicitly pass it in. * - STL container macros have two encode variants: one with a features arg, and one * without. * * The result: * - A feature encode() method will fail to compile if a value is not * passed in. * - The feature varianet of the STL templates will be used when the feature arg is * provided. It will be passed through to any template arg types, but it will be * ignored when not needed. */ // -------------------------------------- // base types template inline void encode_raw(const T& t, bufferlist& bl) { bl.append((char*)&t, sizeof(t)); } template inline void decode_raw(T& t, bufferlist::const_iterator &p) { p.copy(sizeof(t), (char*)&t); } #define WRITE_RAW_ENCODER(type) \ inline void encode(const type &v, ::ceph::bufferlist& bl, uint64_t features=0) { ::ceph::encode_raw(v, bl); } \ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { ::ceph::decode_raw(v, p); } WRITE_RAW_ENCODER(__u8) #ifndef _CHAR_IS_SIGNED WRITE_RAW_ENCODER(__s8) #endif WRITE_RAW_ENCODER(char) WRITE_RAW_ENCODER(ceph_le64) WRITE_RAW_ENCODER(ceph_le32) WRITE_RAW_ENCODER(ceph_le16) inline void encode(const bool &v, bufferlist& bl) { __u8 vv = v; encode_raw(vv, bl); } inline void decode(bool &v, bufferlist::const_iterator& p) { __u8 vv; decode_raw(vv, p); v = vv; } // ----------------------------------- // int types #define WRITE_INTTYPE_ENCODER(type, etype) \ inline void encode(type v, ::ceph::bufferlist& bl, uint64_t features=0) { \ ceph_##etype e; \ e = v; \ ::ceph::encode_raw(e, bl); \ } \ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { \ ceph_##etype e; \ ::ceph::decode_raw(e, p); \ v = e; \ } WRITE_INTTYPE_ENCODER(uint64_t, le64) WRITE_INTTYPE_ENCODER(int64_t, le64) WRITE_INTTYPE_ENCODER(uint32_t, le32) WRITE_INTTYPE_ENCODER(int32_t, le32) WRITE_INTTYPE_ENCODER(uint16_t, le16) WRITE_INTTYPE_ENCODER(int16_t, le16) // ----------------------------------- // float types // // NOTE: The following code assumes all supported platforms use IEEE binary32 // as float and IEEE binary64 as double floating-point format. The assumption // is verified by the assertions below. // // Under this assumption, we can use raw encoding of floating-point types // on little-endian machines, but we still need to perform a byte swap // on big-endian machines to ensure cross-architecture compatibility. // To achive that, we reinterpret the values as integers first, which are // byte-swapped via the ceph_le types as above. The extra conversions // are optimized away on little-endian machines by the compiler. #define WRITE_FLTTYPE_ENCODER(type, itype, etype) \ static_assert(sizeof(type) == sizeof(itype)); \ static_assert(std::numeric_limits::is_iec559, \ "floating-point type not using IEEE754 format"); \ inline void encode(type v, ::ceph::bufferlist& bl, uint64_t features=0) { \ ceph_##etype e; \ e = *reinterpret_cast(&v); \ ::ceph::encode_raw(e, bl); \ } \ inline void decode(type &v, ::ceph::bufferlist::const_iterator& p) { \ ceph_##etype e; \ ::ceph::decode_raw(e, p); \ *reinterpret_cast(&v) = e; \ } WRITE_FLTTYPE_ENCODER(float, uint32_t, le32) WRITE_FLTTYPE_ENCODER(double, uint64_t, le64) // see denc.h for ENCODE_DUMP_PATH discussion and definition. #ifdef ENCODE_DUMP_PATH # define ENCODE_DUMP_PRE() \ unsigned pre_off = bl.length() # define ENCODE_DUMP_POST(cl) \ do { \ static int i = 0; \ i++; \ int bits = 0; \ for (unsigned t = i; t; bits++) \ t &= t - 1; \ if (bits > 2) \ break; \ char fn[PATH_MAX]; \ snprintf(fn, sizeof(fn), ENCODE_STRINGIFY(ENCODE_DUMP_PATH) "/%s__%d.%x", #cl, getpid(), i++); \ int fd = ::open(fn, O_WRONLY|O_TRUNC|O_CREAT|O_CLOEXEC|O_BINARY, 0644); \ if (fd >= 0) { \ ::ceph::bufferlist sub; \ sub.substr_of(bl, pre_off, bl.length() - pre_off); \ sub.write_fd(fd); \ ::close(fd); \ } \ } while (0) #else # define ENCODE_DUMP_PRE() # define ENCODE_DUMP_POST(cl) #endif #define WRITE_CLASS_ENCODER(cl) \ inline void encode(const cl& c, ::ceph::buffer::list &bl, uint64_t features=0) { \ ENCODE_DUMP_PRE(); c.encode(bl); ENCODE_DUMP_POST(cl); } \ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); } #define WRITE_CLASS_MEMBER_ENCODER(cl) \ inline void encode(const cl &c, ::ceph::bufferlist &bl) const { \ ENCODE_DUMP_PRE(); c.encode(bl); ENCODE_DUMP_POST(cl); } \ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); } #define WRITE_CLASS_ENCODER_FEATURES(cl) \ inline void encode(const cl &c, ::ceph::bufferlist &bl, uint64_t features) { \ ENCODE_DUMP_PRE(); c.encode(bl, features); ENCODE_DUMP_POST(cl); } \ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); } #define WRITE_CLASS_ENCODER_OPTIONAL_FEATURES(cl) \ inline void encode(const cl &c, ::ceph::bufferlist &bl, uint64_t features = 0) { \ ENCODE_DUMP_PRE(); c.encode(bl, features); ENCODE_DUMP_POST(cl); } \ inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); } // string inline void encode(std::string_view s, bufferlist& bl, uint64_t features=0) { __u32 len = s.length(); encode(len, bl); if (len) bl.append(s.data(), len); } inline void encode(const std::string& s, bufferlist& bl, uint64_t features=0) { return encode(std::string_view(s), bl, features); } inline void decode(std::string& s, bufferlist::const_iterator& p) { __u32 len; decode(len, p); s.clear(); p.copy(len, s); } inline void encode_nohead(std::string_view s, bufferlist& bl) { bl.append(s.data(), s.length()); } inline void encode_nohead(const std::string& s, bufferlist& bl) { encode_nohead(std::string_view(s), bl); } inline void decode_nohead(int len, std::string& s, bufferlist::const_iterator& p) { s.clear(); p.copy(len, s); } // const char* (encode only, string compatible) inline void encode(const char *s, bufferlist& bl) { encode(std::string_view(s, strlen(s)), bl); } // opaque byte vectors inline void encode(std::vector& v, bufferlist& bl) { uint32_t len = v.size(); encode(len, bl); if (len) bl.append((char *)v.data(), len); } inline void decode(std::vector& v, bufferlist::const_iterator& p) { uint32_t len; decode(len, p); v.resize(len); p.copy(len, (char *)v.data()); } // ----------------------------- // buffers // bufferptr (encapsulated) inline void encode(const buffer::ptr& bp, bufferlist& bl) { __u32 len = bp.length(); encode(len, bl); if (len) bl.append(bp); } inline void decode(buffer::ptr& bp, bufferlist::const_iterator& p) { __u32 len; decode(len, p); bufferlist s; p.copy(len, s); if (len) { if (s.get_num_buffers() == 1) bp = s.front(); else bp = buffer::copy(s.c_str(), s.length()); } } // bufferlist (encapsulated) inline void encode(const bufferlist& s, bufferlist& bl) { __u32 len = s.length(); encode(len, bl); bl.append(s); } inline void encode_destructively(bufferlist& s, bufferlist& bl) { __u32 len = s.length(); encode(len, bl); bl.claim_append(s); } inline void decode(bufferlist& s, bufferlist::const_iterator& p) { __u32 len; decode(len, p); s.clear(); p.copy(len, s); } inline void encode_nohead(const bufferlist& s, bufferlist& bl) { bl.append(s); } inline void decode_nohead(int len, bufferlist& s, bufferlist::const_iterator& p) { s.clear(); p.copy(len, s); } // Time, since the templates are defined in std::chrono template>* = nullptr> void encode(const std::chrono::time_point& t, ceph::bufferlist &bl) { auto ts = Clock::to_timespec(t); // A 32 bit count of seconds causes me vast unhappiness. uint32_t s = ts.tv_sec; uint32_t ns = ts.tv_nsec; encode(s, bl); encode(ns, bl); } template>* = nullptr> void decode(std::chrono::time_point& t, bufferlist::const_iterator& p) { uint32_t s; uint32_t ns; decode(s, p); decode(ns, p); struct timespec ts = { static_cast(s), static_cast(ns)}; t = Clock::from_timespec(ts); } template>* = nullptr> void encode(const std::chrono::duration& d, ceph::bufferlist &bl) { using namespace std::chrono; int32_t s = duration_cast(d).count(); int32_t ns = (duration_cast(d) % seconds(1)).count(); encode(s, bl); encode(ns, bl); } template>* = nullptr> void decode(std::chrono::duration& d, bufferlist::const_iterator& p) { int32_t s; int32_t ns; decode(s, p); decode(ns, p); d = std::chrono::seconds(s) + std::chrono::nanoseconds(ns); } // ----------------------------- // STL container types template inline void encode(const boost::optional &p, bufferlist &bl); template inline void decode(boost::optional &p, bufferlist::const_iterator &bp); template inline void encode(const std::optional &p, bufferlist &bl); template inline void decode(std::optional &p, bufferlist::const_iterator &bp); template inline void encode(const boost::tuple &t, bufferlist& bl); template inline void decode(boost::tuple &t, bufferlist::const_iterator &bp); template, typename b_traits=denc_traits> inline std::enable_if_t encode(const std::pair &p, bufferlist &bl, uint64_t features); template, typename b_traits=denc_traits> inline std::enable_if_t encode(const std::pair &p, bufferlist &bl); template, typename b_traits=denc_traits> inline std::enable_if_t decode(std::pair &pa, bufferlist::const_iterator &p); template> inline std::enable_if_t encode(const std::list& ls, bufferlist& bl); template> inline std::enable_if_t encode(const std::list& ls, bufferlist& bl, uint64_t features); template> inline std::enable_if_t decode(std::list& ls, bufferlist::const_iterator& p); template inline void encode(const std::list, Alloc>& ls, bufferlist& bl); template inline void encode(const std::list, Alloc>& ls, bufferlist& bl, uint64_t features); template inline void decode(std::list, Alloc>& ls, bufferlist::const_iterator& p); template> inline std::enable_if_t encode(const std::set& s, bufferlist& bl); template> inline std::enable_if_t decode(std::set& s, bufferlist::const_iterator& p); template> inline std::enable_if_t encode_nohead(const std::set& s, bufferlist& bl); template> inline std::enable_if_t decode_nohead(int len, std::set& s, bufferlist::iterator& p); template> inline std::enable_if_t encode(const boost::container::flat_set& s, bufferlist& bl); template> inline std::enable_if_t decode(boost::container::flat_set& s, bufferlist::const_iterator& p); template> inline std::enable_if_t encode_nohead(const boost::container::flat_set& s, bufferlist& bl); template> inline std::enable_if_t decode_nohead(int len, boost::container::flat_set& s, bufferlist::iterator& p); template inline void encode(const std::multiset& s, bufferlist& bl); template inline void decode(std::multiset& s, bufferlist::const_iterator& p); template> inline std::enable_if_t encode(const std::vector& v, bufferlist& bl, uint64_t features); template> inline std::enable_if_t encode(const std::vector& v, bufferlist& bl); template> inline std::enable_if_t decode(std::vector& v, bufferlist::const_iterator& p); template> inline std::enable_if_t encode_nohead(const std::vector& v, bufferlist& bl); template> inline std::enable_if_t decode_nohead(int len, std::vector& v, bufferlist::const_iterator& p); template inline void encode(const std::vector,Alloc>& v, bufferlist& bl, uint64_t features); template inline void encode(const std::vector,Alloc>& v, bufferlist& bl); template inline void decode(std::vector,Alloc>& v, bufferlist::const_iterator& p); // small_vector template> inline std::enable_if_t encode(const boost::container::small_vector& v, bufferlist& bl, uint64_t features); template> inline std::enable_if_t encode(const boost::container::small_vector& v, bufferlist& bl); template> inline std::enable_if_t decode(boost::container::small_vector& v, bufferlist::const_iterator& p); template> inline std::enable_if_t encode_nohead(const boost::container::small_vector& v, bufferlist& bl); template> inline std::enable_if_t decode_nohead(int len, boost::container::small_vector& v, bufferlist::const_iterator& p); // std::map template, typename u_traits=denc_traits> inline std::enable_if_t encode(const std::map& m, bufferlist& bl); template, typename u_traits=denc_traits> inline std::enable_if_t encode(const std::map& m, bufferlist& bl, uint64_t features); template, typename u_traits=denc_traits> inline std::enable_if_t decode(std::map& m, bufferlist::const_iterator& p); template inline void decode_noclear(std::map& m, bufferlist::const_iterator& p); template, typename u_traits=denc_traits> inline std::enable_if_t encode_nohead(const std::map& m, bufferlist& bl); template, typename u_traits=denc_traits> inline std::enable_if_t encode_nohead(const std::map& m, bufferlist& bl, uint64_t features); template, typename u_traits=denc_traits> inline std::enable_if_t decode_nohead(int n, std::map& m, bufferlist::const_iterator& p); template, typename u_traits=denc_traits> inline std::enable_if_t encode(const boost::container::flat_map& m, bufferlist& bl); template, typename u_traits=denc_traits> inline std::enable_if_t encode(const boost::container::flat_map& m, bufferlist& bl, uint64_t features); template, typename u_traits=denc_traits> inline std::enable_if_t decode(boost::container::flat_map& m, bufferlist::const_iterator& p); template inline void decode_noclear(boost::container::flat_map& m, bufferlist::const_iterator& p); template, typename u_traits=denc_traits> inline std::enable_if_t encode_nohead(const boost::container::flat_map& m, bufferlist& bl); template, typename u_traits=denc_traits> inline std::enable_if_t encode_nohead(const boost::container::flat_map& m, bufferlist& bl, uint64_t features); template, typename u_traits=denc_traits> inline std::enable_if_t decode_nohead(int n, boost::container::flat_map& m, bufferlist::const_iterator& p); template inline void encode(const std::multimap& m, bufferlist& bl); template inline void decode(std::multimap& m, bufferlist::const_iterator& p); template inline void encode(const unordered_map& m, bufferlist& bl, uint64_t features); template inline void encode(const unordered_map& m, bufferlist& bl); template inline void decode(unordered_map& m, bufferlist::const_iterator& p); template inline void encode(const ceph::unordered_set& m, bufferlist& bl); template inline void decode(ceph::unordered_set& m, bufferlist::const_iterator& p); template inline void encode(const std::deque& ls, bufferlist& bl, uint64_t features); template inline void encode(const std::deque& ls, bufferlist& bl); template inline void decode(std::deque& ls, bufferlist::const_iterator& p); template> inline std::enable_if_t encode(const std::array& v, bufferlist& bl, uint64_t features); template> inline std::enable_if_t encode(const std::array& v, bufferlist& bl); template> inline std::enable_if_t decode(std::array& v, bufferlist::const_iterator& p); // full bl decoder template inline void decode(T &o, const bufferlist& bl) { auto p = bl.begin(); decode(o, p); ceph_assert(p.end()); } // boost optional template inline void encode(const boost::optional &p, bufferlist &bl) { __u8 present = static_cast(p); encode(present, bl); if (p) encode(p.get(), bl); } #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuninitialized" template inline void decode(boost::optional &p, bufferlist::const_iterator &bp) { __u8 present; decode(present, bp); if (present) { p = T{}; decode(p.get(), bp); } else { p = boost::none; } } #pragma GCC diagnostic pop #pragma GCC diagnostic warning "-Wpragmas" // std optional template inline void encode(const std::optional &p, bufferlist &bl) { __u8 present = static_cast(p); encode(present, bl); if (p) encode(*p, bl); } #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuninitialized" template inline void decode(std::optional &p, bufferlist::const_iterator &bp) { __u8 present; decode(present, bp); if (present) { p = T{}; decode(*p, bp); } else { p = std::nullopt; } } // std::tuple template inline void encode(const std::tuple &t, bufferlist& bl) { ceph::for_each(t, [&bl](const auto& e) { encode(e, bl); }); } template inline void decode(std::tuple &t, bufferlist::const_iterator &bp) { ceph::for_each(t, [&bp](auto& e) { decode(e, bp); }); } //triple boost::tuple template inline void encode(const boost::tuple &t, bufferlist& bl) { encode(boost::get<0>(t), bl); encode(boost::get<1>(t), bl); encode(boost::get<2>(t), bl); } template inline void decode(boost::tuple &t, bufferlist::const_iterator &bp) { decode(boost::get<0>(t), bp); decode(boost::get<1>(t), bp); decode(boost::get<2>(t), bp); } // std::pair template inline std::enable_if_t encode(const std::pair &p, bufferlist &bl, uint64_t features) { encode(p.first, bl, features); encode(p.second, bl, features); } template inline std::enable_if_t encode(const std::pair &p, bufferlist &bl) { encode(p.first, bl); encode(p.second, bl); } template inline std::enable_if_t decode(std::pair &pa, bufferlist::const_iterator &p) { decode(pa.first, p); decode(pa.second, p); } // std::list template inline std::enable_if_t encode(const std::list& ls, bufferlist& bl) { __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1) encode(n, bl); for (auto p = ls.begin(); p != ls.end(); ++p) encode(*p, bl); } template inline std::enable_if_t encode(const std::list& ls, bufferlist& bl, uint64_t features) { using counter_encode_t = ceph_le32; unsigned n = 0; auto filler = bl.append_hole(sizeof(counter_encode_t)); for (const auto& item : ls) { // we count on our own because of buggy std::list::size() implementation // which doesn't follow the O(1) complexity constraint C++11 has brought. ++n; encode(item, bl, features); } counter_encode_t en; en = n; filler.copy_in(sizeof(en), reinterpret_cast(&en)); } template inline std::enable_if_t decode(std::list& ls, bufferlist::const_iterator& p) { __u32 n; decode(n, p); ls.clear(); while (n--) { ls.emplace_back(); decode(ls.back(), p); } } // std::list> template inline void encode(const std::list, Alloc>& ls, bufferlist& bl) { __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1) encode(n, bl); for (const auto& ref : ls) { encode(*ref, bl); } } template inline void encode(const std::list, Alloc>& ls, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(ls.size()); // c++11 std::list::size() is O(1) encode(n, bl); for (const auto& ref : ls) { encode(*ref, bl, features); } } template inline void decode(std::list, Alloc>& ls, bufferlist::const_iterator& p) { __u32 n; decode(n, p); ls.clear(); while (n--) { auto ref = std::make_shared(); decode(*ref, p); ls.emplace_back(std::move(ref)); } } // std::set template inline std::enable_if_t encode(const std::set& s, bufferlist& bl) { __u32 n = (__u32)(s.size()); encode(n, bl); for (auto p = s.begin(); p != s.end(); ++p) encode(*p, bl); } template inline std::enable_if_t decode(std::set& s, bufferlist::const_iterator& p) { __u32 n; decode(n, p); s.clear(); while (n--) { T v; decode(v, p); s.insert(v); } } template inline typename std::enable_if::type encode_nohead(const std::set& s, bufferlist& bl) { for (auto p = s.begin(); p != s.end(); ++p) encode(*p, bl); } template inline std::enable_if_t decode_nohead(int len, std::set& s, bufferlist::const_iterator& p) { for (int i=0; i template inline std::enable_if_t encode(const boost::container::flat_set& s, bufferlist& bl) { __u32 n = (__u32)(s.size()); encode(n, bl); for (const auto& e : s) encode(e, bl); } template inline std::enable_if_t decode(boost::container::flat_set& s, bufferlist::const_iterator& p) { __u32 n; decode(n, p); s.clear(); s.reserve(n); while (n--) { T v; decode(v, p); s.insert(v); } } template inline std::enable_if_t encode_nohead(const boost::container::flat_set& s, bufferlist& bl) { for (const auto& e : s) encode(e, bl); } template inline std::enable_if_t decode_nohead(int len, boost::container::flat_set& s, bufferlist::iterator& p) { s.reserve(len); for (int i=0; i inline void encode(const std::multiset& s, bufferlist& bl) { __u32 n = (__u32)(s.size()); encode(n, bl); for (auto p = s.begin(); p != s.end(); ++p) encode(*p, bl); } template inline void decode(std::multiset& s, bufferlist::const_iterator& p) { __u32 n; decode(n, p); s.clear(); while (n--) { T v; decode(v, p); s.insert(v); } } template inline std::enable_if_t encode(const std::vector& v, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(v.size()); encode(n, bl); for (auto p = v.begin(); p != v.end(); ++p) encode(*p, bl, features); } template inline std::enable_if_t encode(const std::vector& v, bufferlist& bl) { __u32 n = (__u32)(v.size()); encode(n, bl); for (auto p = v.begin(); p != v.end(); ++p) encode(*p, bl); } template inline std::enable_if_t decode(std::vector& v, bufferlist::const_iterator& p) { __u32 n; decode(n, p); v.resize(n); for (__u32 i=0; i inline std::enable_if_t encode_nohead(const std::vector& v, bufferlist& bl) { for (auto p = v.begin(); p != v.end(); ++p) encode(*p, bl); } template inline std::enable_if_t decode_nohead(int len, std::vector& v, bufferlist::const_iterator& p) { v.resize(len); for (__u32 i=0; i inline std::enable_if_t encode(const boost::container::small_vector& v, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(v.size()); encode(n, bl); for (const auto& i : v) encode(i, bl, features); } template inline std::enable_if_t encode(const boost::container::small_vector& v, bufferlist& bl) { __u32 n = (__u32)(v.size()); encode(n, bl); for (const auto& i : v) encode(i, bl); } template inline std::enable_if_t decode(boost::container::small_vector& v, bufferlist::const_iterator& p) { __u32 n; decode(n, p); v.resize(n); for (auto& i : v) decode(i, p); } template inline std::enable_if_t encode_nohead(const boost::container::small_vector& v, bufferlist& bl) { for (const auto& i : v) encode(i, bl); } template inline std::enable_if_t decode_nohead(int len, boost::container::small_vector& v, bufferlist::const_iterator& p) { v.resize(len); for (auto& i : v) decode(i, p); } // vector (shared_ptr) template inline void encode(const std::vector,Alloc>& v, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(v.size()); encode(n, bl); for (const auto& ref : v) { if (ref) encode(*ref, bl, features); else encode(T(), bl, features); } } template inline void encode(const std::vector,Alloc>& v, bufferlist& bl) { __u32 n = (__u32)(v.size()); encode(n, bl); for (const auto& ref : v) { if (ref) encode(*ref, bl); else encode(T(), bl); } } template inline void decode(std::vector,Alloc>& v, bufferlist::const_iterator& p) { __u32 n; decode(n, p); v.clear(); v.reserve(n); while (n--) { auto ref = std::make_shared(); decode(*ref, p); v.emplace_back(std::move(ref)); } } // map template inline std::enable_if_t encode(const std::map& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline std::enable_if_t encode(const std::map& m, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl, features); encode(p->second, bl, features); } } template inline std::enable_if_t decode(std::map& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.clear(); while (n--) { T k; decode(k, p); decode(m[k], p); } } template inline void decode_noclear(std::map& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); while (n--) { T k; decode(k, p); decode(m[k], p); } } template inline std::enable_if_t encode_nohead(const std::map& m, bufferlist& bl) { for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline std::enable_if_t encode_nohead(const std::map& m, bufferlist& bl, uint64_t features) { for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl, features); encode(p->second, bl, features); } } template inline std::enable_if_t decode_nohead(int n, std::map& m, bufferlist::const_iterator& p) { m.clear(); while (n--) { T k; decode(k, p); decode(m[k], p); } } // boost::container::flat-map template inline std::enable_if_t encode(const boost::container::flat_map& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); for (typename boost::container::flat_map::const_iterator p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline std::enable_if_t encode(const boost::container::flat_map& m, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl, features); encode(p->second, bl, features); } } template inline std::enable_if_t decode(boost::container::flat_map& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.clear(); m.reserve(n); while (n--) { T k; decode(k, p); decode(m[k], p); } } template inline void decode_noclear(boost::container::flat_map& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.reserve(m.size() + n); while (n--) { T k; decode(k, p); decode(m[k], p); } } template inline std::enable_if_t encode_nohead(const boost::container::flat_map& m, bufferlist& bl) { for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline std::enable_if_t encode_nohead(const boost::container::flat_map& m, bufferlist& bl, uint64_t features) { for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl, features); encode(p->second, bl, features); } } template inline std::enable_if_t decode_nohead(int n, boost::container::flat_map& m, bufferlist::const_iterator& p) { m.clear(); while (n--) { T k; decode(k, p); decode(m[k], p); } } // multimap template inline void encode(const std::multimap& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline void decode(std::multimap& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.clear(); while (n--) { typename std::pair tu = std::pair(); decode(tu.first, p); typename std::multimap::iterator it = m.insert(tu); decode(it->second, p); } } // ceph::unordered_map template inline void encode(const unordered_map& m, bufferlist& bl, uint64_t features) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl, features); encode(p->second, bl, features); } } template inline void encode(const unordered_map& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) { encode(p->first, bl); encode(p->second, bl); } } template inline void decode(unordered_map& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.clear(); while (n--) { T k; decode(k, p); decode(m[k], p); } } // ceph::unordered_set template inline void encode(const ceph::unordered_set& m, bufferlist& bl) { __u32 n = (__u32)(m.size()); encode(n, bl); for (auto p = m.begin(); p != m.end(); ++p) encode(*p, bl); } template inline void decode(ceph::unordered_set& m, bufferlist::const_iterator& p) { __u32 n; decode(n, p); m.clear(); while (n--) { T k; decode(k, p); m.insert(k); } } // deque template inline void encode(const std::deque& ls, bufferlist& bl, uint64_t features) { __u32 n = ls.size(); encode(n, bl); for (auto p = ls.begin(); p != ls.end(); ++p) encode(*p, bl, features); } template inline void encode(const std::deque& ls, bufferlist& bl) { __u32 n = ls.size(); encode(n, bl); for (auto p = ls.begin(); p != ls.end(); ++p) encode(*p, bl); } template inline void decode(std::deque& ls, bufferlist::const_iterator& p) { __u32 n; decode(n, p); ls.clear(); while (n--) { ls.emplace_back(); decode(ls.back(), p); } } // std::array template inline std::enable_if_t encode(const std::array& v, bufferlist& bl, uint64_t features) { for (const auto& e : v) encode(e, bl, features); } template inline std::enable_if_t encode(const std::array& v, bufferlist& bl) { for (const auto& e : v) encode(e, bl); } template inline std::enable_if_t decode(std::array& v, bufferlist::const_iterator& p) { for (auto& e : v) decode(e, p); } } /* * guards */ /** * start encoding block * * @param v current (code) version of the encoding * @param compat oldest code version that can decode it * @param bl bufferlist to encode to * */ #define ENCODE_START(v, compat, bl) \ __u8 struct_v = v; \ __u8 struct_compat = compat; \ ceph_le32 struct_len; \ auto filler = (bl).append_hole(sizeof(struct_v) + \ sizeof(struct_compat) + sizeof(struct_len)); \ const auto starting_bl_len = (bl).length(); \ using ::ceph::encode; \ do { /** * finish encoding block * * @param bl bufferlist we were encoding to * @param new_struct_compat struct-compat value to use */ #define ENCODE_FINISH_NEW_COMPAT(bl, new_struct_compat) \ } while (false); \ if (new_struct_compat) { \ struct_compat = new_struct_compat; \ } \ struct_len = (bl).length() - starting_bl_len; \ filler.copy_in(sizeof(struct_v), (char *)&struct_v); \ filler.copy_in(sizeof(struct_compat), \ (char *)&struct_compat); \ filler.copy_in(sizeof(struct_len), (char *)&struct_len); #define ENCODE_FINISH(bl) ENCODE_FINISH_NEW_COMPAT(bl, 0) #define DECODE_ERR_OLDVERSION(func, v, compatv) \ (std::string(func) + " no longer understand old encoding version " #v " < " + std::to_string(compatv)) #define DECODE_ERR_PAST(func) \ (std::string(func) + " decode past end of struct encoding") /** * check for very old encoding * * If the encoded data is older than oldestv, raise an exception. * * @param oldestv oldest version of the code we can successfully decode. */ #define DECODE_OLDEST(oldestv) \ if (struct_v < oldestv) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, oldestv)); /** * start a decoding block * * @param v current version of the encoding that the code supports/encodes * @param bl bufferlist::iterator for the encoded data */ #define DECODE_START(v, bl) \ __u8 struct_v, struct_compat; \ using ::ceph::decode; \ decode(struct_v, bl); \ decode(struct_compat, bl); \ if (v < struct_compat) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, struct_compat)); \ __u32 struct_len; \ decode(struct_len, bl); \ if (struct_len > bl.get_remaining()) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ unsigned struct_end = bl.get_off() + struct_len; \ do { /* BEWARE: any change to this macro MUST be also reflected in the duplicative * DECODE_START_LEGACY_COMPAT_LEN! */ #define __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, skip_v, bl) \ using ::ceph::decode; \ __u8 struct_v; \ decode(struct_v, bl); \ if (struct_v >= compatv) { \ __u8 struct_compat; \ decode(struct_compat, bl); \ if (v < struct_compat) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, struct_compat)); \ } else if (skip_v) { \ if (bl.get_remaining() < skip_v) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ bl += skip_v; \ } \ unsigned struct_end = 0; \ if (struct_v >= lenv) { \ __u32 struct_len; \ decode(struct_len, bl); \ if (struct_len > bl.get_remaining()) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ struct_end = bl.get_off() + struct_len; \ } \ do { /** * start a decoding block with legacy support for older encoding schemes * * The old encoding schemes has a __u8 struct_v only, or lacked either * the compat version or length. Skip those fields conditionally. * * Most of the time, v, compatv, and lenv will all match the version * where the structure was switched over to the new macros. * * @param v current version of the encoding that the code supports/encodes * @param compatv oldest version that includes a __u8 compat version field * @param lenv oldest version that includes a __u32 length wrapper * @param bl bufferlist::iterator containing the encoded data */ /* BEWARE: this is duplication of __DECODE_START_LEGACY_COMPAT_LEN which * MUST be changed altogether. For the rationale behind code duplication, * please `git blame` and refer to the commit message. */ #define DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, bl) \ using ::ceph::decode; \ __u8 struct_v; \ decode(struct_v, bl); \ if (struct_v >= compatv) { \ __u8 struct_compat; \ decode(struct_compat, bl); \ if (v < struct_compat) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION( \ __PRETTY_FUNCTION__, v, struct_compat)); \ } \ unsigned struct_end = 0; \ if (struct_v >= lenv) { \ __u32 struct_len; \ decode(struct_len, bl); \ if (struct_len > bl.get_remaining()) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ struct_end = bl.get_off() + struct_len; \ } \ do { /** * start a decoding block with legacy support for older encoding schemes * * This version of the macro assumes the legacy encoding had a 32 bit * version * * The old encoding schemes has a __u8 struct_v only, or lacked either * the compat version or length. Skip those fields conditionally. * * Most of the time, v, compatv, and lenv will all match the version * where the structure was switched over to the new macros. * * @param v current version of the encoding that the code supports/encodes * @param compatv oldest version that includes a __u8 compat version field * @param lenv oldest version that includes a __u32 length wrapper * @param bl bufferlist::iterator containing the encoded data */ #define DECODE_START_LEGACY_COMPAT_LEN_32(v, compatv, lenv, bl) \ __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, 3u, bl) #define DECODE_START_LEGACY_COMPAT_LEN_16(v, compatv, lenv, bl) \ __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, 1u, bl) /** * finish decode block * * @param bl bufferlist::iterator we were decoding from */ #define DECODE_FINISH(bl) \ } while (false); \ if (struct_end) { \ if (bl.get_off() > struct_end) \ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ if (bl.get_off() < struct_end) \ bl += struct_end - bl.get_off(); \ } namespace ceph { /* * Encoders/decoders to read from current offset in a file handle and * encode/decode the data according to argument types. */ inline ssize_t decode_file(int fd, std::string &str) { bufferlist bl; __u32 len = 0; bl.read_fd(fd, sizeof(len)); decode(len, bl); bl.read_fd(fd, len); decode(str, bl); return bl.length(); } inline ssize_t decode_file(int fd, bufferptr &bp) { bufferlist bl; __u32 len = 0; bl.read_fd(fd, sizeof(len)); decode(len, bl); bl.read_fd(fd, len); auto bli = std::cbegin(bl); decode(bp, bli); return bl.length(); } } #endif