-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 sts=2 expandtab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ * Copyright (C) 2026 International Business Machines Corp. (IBM)
*
* 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
+ * License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
- *
+ *
*/
#ifndef CEPH_ENCODING_H
#define CEPH_ENCODING_H
#include <optional>
#include <unordered_map>
#include <unordered_set>
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <chrono>
+#include <climits>
+#include <cstdio>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <limits>
+#include <list>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+#include <utility>
#include <boost/container/small_vector.hpp>
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/tuple/tuple.hpp>
+#include <fcntl.h>
+#include <unistd.h>
+
#include "common/ceph_time.h"
+#include "include/compat.h"
#include "include/int_types.h"
#include "common/convenience.h"
// 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
+// To achieve that, we bit-cast 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)); \
"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<itype *>(&v); \
+ e = std::bit_cast<itype>(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<itype *>(&v) = e; \
+ itype raw = e; \
+ v = std::bit_cast<type>(raw); \
}
WRITE_FLTTYPE_ENCODER(float, uint32_t, le32)
ENCODE_DUMP_PRE(); c.encode(bl, features); ENCODE_DUMP_POST(cl); } \
inline void decode(cl &c, ::ceph::bufferlist::const_iterator &p) { c.decode(p); }
+namespace encoding_detail {
+
+inline void encode_count(size_t n, bufferlist& bl)
+{
+ // Legacy container/string lengths are stored as 32-bit values on the wire.
+ encode(static_cast<__u32>(n), bl);
+}
+
+inline __u32 decode_count(bufferlist::const_iterator& p)
+{
+ __u32 n;
+ decode(n, p);
+ return n;
+}
+
+inline void append_bytes(const void *data, size_t len, bufferlist& bl)
+{
+ if (len)
+ bl.append(static_cast<const char *>(data), len);
+}
+
+inline void encode_bytes(const void *data, size_t len, bufferlist& bl)
+{
+ encode_count(len, bl);
+ append_bytes(data, len, bl);
+}
+
+} // namespace encoding_detail
// 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);
+ encoding_detail::encode_bytes(s.data(), s.length(), bl);
}
inline void encode(const std::string& s, bufferlist& bl, uint64_t features=0)
{
}
inline void decode(std::string& s, bufferlist::const_iterator& p)
{
- __u32 len;
- decode(len, p);
+ const auto len = encoding_detail::decode_count(p);
s.clear();
p.copy(len, s);
}
inline void encode_nohead(std::string_view s, bufferlist& bl)
{
- bl.append(s.data(), s.length());
+ encoding_detail::append_bytes(s.data(), s.length(), bl);
}
inline void encode_nohead(const std::string& s, bufferlist& bl)
{
}
// const char* (encode only, string compatible)
-inline void encode(const char *s, bufferlist& bl)
+inline void encode(const char *s, bufferlist& bl)
{
encode(std::string_view(s, strlen(s)), bl);
}
// opaque byte vectors
inline void encode(std::vector<uint8_t>& v, bufferlist& bl)
{
- uint32_t len = v.size();
- encode(len, bl);
- if (len)
- bl.append((char *)v.data(), len);
+ encoding_detail::encode_bytes(v.data(), v.size(), bl);
}
inline void decode(std::vector<uint8_t>& v, bufferlist::const_iterator& p)
{
- uint32_t len;
-
- decode(len, p);
+ const auto len = encoding_detail::decode_count(p);
v.resize(len);
p.copy(len, (char *)v.data());
}
// buffers
// bufferptr (encapsulated)
-inline void encode(const buffer::ptr& bp, bufferlist& bl)
+inline void encode(const buffer::ptr& bp, bufferlist& bl)
{
- __u32 len = bp.length();
- encode(len, bl);
+ const auto len = bp.length();
+ encoding_detail::encode_count(len, bl);
if (len)
bl.append(bp);
}
inline void decode(buffer::ptr& bp, bufferlist::const_iterator& p)
{
- __u32 len;
- decode(len, p);
+ const auto len = encoding_detail::decode_count(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());
+ if (!len) {
+ return;
+ }
+
+ if (1 == s.get_num_buffers()) {
+ bp = s.front();
+ return;
}
+
+ bp = buffer::copy(s.c_str(), s.length());
}
// bufferlist (encapsulated)
-inline void encode(const bufferlist& s, bufferlist& bl)
+inline void encode(const bufferlist& s, bufferlist& bl)
{
- __u32 len = s.length();
- encode(len, bl);
+ encoding_detail::encode_count(s.length(), bl);
bl.append(s);
}
-inline void encode_destructively(bufferlist& s, bufferlist& bl)
+inline void encode_destructively(bufferlist& s, bufferlist& bl)
{
- __u32 len = s.length();
- encode(len, bl);
+ encoding_detail::encode_count(s.length(), bl);
bl.claim_append(s);
}
inline void decode(bufferlist& s, bufferlist::const_iterator& p)
{
- __u32 len;
- decode(len, p);
+ const auto len = encoding_detail::decode_count(p);
s.clear();
p.copy(len, s);
}
-inline void encode_nohead(const bufferlist& s, bufferlist& bl)
+inline void encode_nohead(const bufferlist& s, bufferlist& bl)
{
bl.append(s);
}
// -----------------------------
// STL container types
+namespace encoding_detail {
+
+template<typename TraitsT>
+concept needs_legacy_encoding = !TraitsT::supported;
+
+template<typename FirstTraitsT, typename SecondTraitsT>
+concept pair_needs_legacy_encoding =
+ needs_legacy_encoding<FirstTraitsT> ||
+ needs_legacy_encoding<SecondTraitsT>;
+
+template<typename KeyT, typename ValueT>
+concept map_decodes_by_emplace =
+ std::move_constructible<KeyT> &&
+ std::move_constructible<ValueT>;
+
+template<typename OptionalT>
+void encode_optional(const OptionalT& p, bufferlist& bl);
+
template<typename T>
-inline void encode(const boost::optional<T> &p, bufferlist &bl);
-template<typename T>
-inline void decode(boost::optional<T> &p, bufferlist::const_iterator &bp);
-template<typename T>
-inline void encode(const std::optional<T> &p, bufferlist &bl);
-template<typename T>
-inline void decode(std::optional<T> &p, bufferlist::const_iterator &bp);
-template<class A, class B, class C>
-inline void encode(const boost::tuple<A, B, C> &t, bufferlist& bl);
-template<class A, class B, class C>
-inline void decode(boost::tuple<A, B, C> &t, bufferlist::const_iterator &bp);
-template<class A, class B,
- typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
-inline std::enable_if_t<!a_traits::supported || !b_traits::supported>
-encode(const std::pair<A,B> &p, bufferlist &bl, uint64_t features);
-template<class A, class B,
- typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
-inline std::enable_if_t<!a_traits::supported ||
- !b_traits::supported>
-encode(const std::pair<A,B> &p, bufferlist &bl);
-template<class A, class B,
- typename a_traits=denc_traits<A>, typename b_traits=denc_traits<B>>
-inline std::enable_if_t<!a_traits::supported ||
- !b_traits::supported>
-decode(std::pair<A,B> &pa, bufferlist::const_iterator &p);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::list<T, Alloc>& ls, bufferlist& bl);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::list<T,Alloc>& ls, bufferlist& bl, uint64_t features);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(std::list<T,Alloc>& ls, bufferlist::const_iterator& p);
-template<class T, class Alloc>
-inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist& bl);
-template<class T, class Alloc>
-inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist& bl, uint64_t features);
-template<class T, class Alloc>
-inline void decode(std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist::const_iterator& p);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::set<T,Comp,Alloc>& s, bufferlist& bl);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode_nohead(const std::set<T,Comp,Alloc>& s, bufferlist& bl);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode_nohead(unsigned len, std::set<T,Comp,Alloc>& s, bufferlist::iterator& p);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const boost::container::flat_set<T, Comp, Alloc>& s, bufferlist& bl);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(boost::container::flat_set<T, Comp, Alloc>& s, bufferlist::const_iterator& p);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode_nohead(const boost::container::flat_set<T, Comp, Alloc>& s,
- bufferlist& bl);
-template<class T, class Comp, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode_nohead(unsigned len, boost::container::flat_set<T, Comp, Alloc>& s,
- bufferlist::iterator& p);
-template<class T, class Comp, class Alloc>
-inline void encode(const std::multiset<T,Comp,Alloc>& s, bufferlist& bl);
-template<class T, class Comp, class Alloc>
-inline void decode(std::multiset<T,Comp,Alloc>& s, bufferlist::const_iterator& p);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::vector<T,Alloc>& v, bufferlist& bl, uint64_t features);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::vector<T,Alloc>& v, bufferlist& bl);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(std::vector<T,Alloc>& v, bufferlist::const_iterator& p);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode_nohead(const std::vector<T,Alloc>& v, bufferlist& bl);
-template<class T, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode_nohead(unsigned len, std::vector<T,Alloc>& v, bufferlist::const_iterator& p);
-template<class T,class Alloc>
-inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
- bufferlist& bl,
- uint64_t features);
-template<class T, class Alloc>
-inline void encode(const std::vector<std::shared_ptr<T>,Alloc>& v,
- bufferlist& bl);
-template<class T, class Alloc>
-inline void decode(std::vector<std::shared_ptr<T>,Alloc>& v,
- bufferlist::const_iterator& p);
-// small_vector
-template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl, uint64_t features);
-template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl);
-template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p);
-template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode_nohead(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl);
-template<class T, std::size_t N, class Alloc, typename traits=denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode_nohead(unsigned len, boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p);
-// std::map
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported ||
- !u_traits::supported>
-encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc>
-inline void decode_noclear(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode_nohead(unsigned n, std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl,
- uint64_t features);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode(boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc>
-inline void decode_noclear(boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist& bl);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist& bl, uint64_t features);
-template<class T, class U, class Comp, class Alloc,
- typename t_traits=denc_traits<T>, typename u_traits=denc_traits<U>>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode_nohead(unsigned n, boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist::const_iterator& p);
-template<class T, class U, class Comp, class Alloc>
-inline void encode(const std::multimap<T,U,Comp,Alloc>& m, bufferlist& bl);
-template<class T, class U, class Comp, class Alloc>
-inline void decode(std::multimap<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class U, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl,
- uint64_t features);
-template<class T, class U, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl);
-template<class T, class U, class Hash, class Pred, class Alloc>
-inline void decode(std::unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist& bl);
-template<class T, class Hash, class Pred, class Alloc>
-inline void decode(std::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p);
-template<class T, class Alloc>
-inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl, uint64_t features);
-template<class T, class Alloc>
-inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl);
-template<class T, class Alloc>
-inline void decode(std::deque<T,Alloc>& ls, bufferlist::const_iterator& p);
-template<class T, size_t N, typename traits = denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::array<T, N>& v, bufferlist& bl, uint64_t features);
-template<class T, size_t N, typename traits = denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-encode(const std::array<T, N>& v, bufferlist& bl);
-template<class T, size_t N, typename traits = denc_traits<T>>
-inline std::enable_if_t<!traits::supported>
-decode(std::array<T, N>& v, bufferlist::const_iterator& p);
+std::optional<T> decode_optional(bufferlist::const_iterator& p);
-// full bl decoder
-template<class T>
-inline void decode(T &o, const bufferlist& bl)
-{
- auto p = bl.begin();
- decode(o, p);
- ceph_assert(p.end());
-}
+template<typename FnT>
+void for_each_count(unsigned n, FnT&& fn);
+
+template<typename RangeT>
+void encode_range_nohead(const RangeT& r, bufferlist& bl);
+
+template<typename RangeT>
+void encode_range_nohead(const RangeT& r, bufferlist& bl, uint64_t features);
+
+template<typename RangeT, typename IteratorT>
+void decode_range_nohead(RangeT& r, IteratorT& p);
+
+template<typename RangeT>
+void encode_range(const RangeT& r, bufferlist& bl);
+
+template<typename RangeT>
+void encode_range(const RangeT& r, bufferlist& bl, uint64_t features);
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_resize_nohead(unsigned len, ContainerT& c,
+ IteratorT& p);
+
+template<typename ContainerT>
+void decode_by_resize(ContainerT& c, bufferlist::const_iterator& p);
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_emplace_back(unsigned len, ContainerT& c,
+ IteratorT& p);
+
+template<typename ContainerT>
+void decode_by_emplace_back(ContainerT& c, bufferlist::const_iterator& p);
+
+template<typename ContainerT>
+void clear_and_reserve(ContainerT& c, size_t n);
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_insert(unsigned len, ContainerT& c,
+ IteratorT& p);
+
+template<typename ContainerT>
+void decode_by_insert(ContainerT& c, bufferlist::const_iterator& p);
+
+template<typename RangeT>
+void encode_shared_ptr_range(const RangeT& r, bufferlist& bl);
+
+template<typename RangeT>
+void encode_shared_ptr_range(const RangeT& r, bufferlist& bl,
+ uint64_t features);
+
+template<typename ValueT, typename EncodeFnT>
+void append_cached_default_encoding(std::optional<std::string>& bytes,
+ bufferlist& bl,
+ EncodeFnT&& encode_value);
+
+template<typename RangeT, typename EncodeFnT>
+void encode_shared_ptr_range_with(const RangeT& r, bufferlist& bl,
+ EncodeFnT&& encode_value);
+
+template<typename ContainerT>
+void decode_shared_ptr_sequence(ContainerT& c,
+ bufferlist::const_iterator& p);
+
+template<typename PairT>
+void encode_pair_members(const PairT& item, bufferlist& bl);
+
+template<typename PairT>
+void encode_pair_members(const PairT& item, bufferlist& bl,
+ uint64_t features);
+
+template<typename PairT>
+void decode_pair_members(PairT& item, bufferlist::const_iterator& p);
+
+template<typename MapT>
+void encode_pair_range_nohead(const MapT& m, bufferlist& bl);
+
+template<typename MapT>
+void encode_pair_range_nohead(const MapT& m, bufferlist& bl,
+ uint64_t features);
+
+template<typename MapT>
+void encode_pair_range(const MapT& m, bufferlist& bl);
+
+template<typename MapT>
+void encode_pair_range(const MapT& m, bufferlist& bl, uint64_t features);
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries_by_subscript(unsigned n, MapT& m,
+ IteratorT& p);
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries_by_emplace(unsigned n, MapT& m,
+ IteratorT& p);
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries(unsigned n, MapT& m, IteratorT& p);
+
+template<typename MapT>
+void decode_map_by_subscript(unsigned n, MapT& m,
+ bufferlist::const_iterator& p);
+
+template<typename MapT>
+void decode_map_by_subscript(MapT& m, bufferlist::const_iterator& p);
+
+template<typename MapT>
+void decode_map(unsigned n, MapT& m, bufferlist::const_iterator& p);
+
+template<typename MapT>
+void decode_map(MapT& m, bufferlist::const_iterator& p);
+
+} // namespace encoding_detail
// boost optional
template<typename T>
-inline void encode(const boost::optional<T> &p, bufferlist &bl)
+inline void encode(const boost::optional<T>& p, bufferlist& bl)
{
- __u8 present = static_cast<bool>(p);
- encode(present, bl);
- if (p)
- encode(p.get(), bl);
+ encoding_detail::encode_optional(p, bl);
}
-#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wuninitialized"
template<typename T>
-inline void decode(boost::optional<T> &p, bufferlist::const_iterator &bp)
+inline void decode(boost::optional<T>& p, bufferlist::const_iterator& bp)
{
- __u8 present;
- decode(present, bp);
- if (present) {
- p = T{};
- decode(p.get(), bp);
- } else {
- p = boost::none;
+ auto decoded = encoding_detail::decode_optional<T>(bp);
+ if (decoded) {
+ p = std::move(*decoded);
+ return;
}
+
+ p.reset();
}
-#pragma GCC diagnostic pop
-#pragma GCC diagnostic warning "-Wpragmas"
// std optional
template<typename T>
-inline void encode(const std::optional<T> &p, bufferlist &bl)
+inline void encode(const std::optional<T>& p, bufferlist& bl)
{
- __u8 present = static_cast<bool>(p);
- encode(present, bl);
- if (p)
- encode(*p, bl);
+ encoding_detail::encode_optional(p, bl);
}
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuninitialized"
template<typename T>
-inline void decode(std::optional<T> &p, bufferlist::const_iterator &bp)
+inline void decode(std::optional<T>& p, bufferlist::const_iterator& bp)
{
- __u8 present;
- decode(present, bp);
- if (present) {
- p = T{};
- decode(*p, bp);
- } else {
- p = std::nullopt;
- }
+ p = encoding_detail::decode_optional<T>(bp);
}
+#pragma GCC diagnostic pop
// std::tuple
template<typename... Ts>
-inline void encode(const std::tuple<Ts...> &t, bufferlist& bl)
+inline void encode(const std::tuple<Ts...>& t, bufferlist& bl)
{
ceph::for_each(t, [&bl](const auto& e) {
- encode(e, bl);
- });
+ encode(e, bl);
+ });
}
template<typename... Ts>
-inline void decode(std::tuple<Ts...> &t, bufferlist::const_iterator &bp)
+inline void decode(std::tuple<Ts...>& t, bufferlist::const_iterator& bp)
{
ceph::for_each(t, [&bp](auto& e) {
- decode(e, bp);
- });
+ decode(e, bp);
+ });
}
-//triple boost::tuple
+// triple boost::tuple
template<class A, class B, class C>
-inline void encode(const boost::tuple<A, B, C> &t, bufferlist& bl)
+inline void encode(const boost::tuple<A, B, C>& t, bufferlist& bl)
{
encode(boost::get<0>(t), bl);
encode(boost::get<1>(t), bl);
encode(boost::get<2>(t), bl);
}
template<class A, class B, class C>
-inline void decode(boost::tuple<A, B, C> &t, bufferlist::const_iterator &bp)
+inline void decode(boost::tuple<A, B, C>& 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<A,B>
+// std::pair<A, B>
template<class A, class B,
- typename a_traits, typename b_traits>
-inline std::enable_if_t<!a_traits::supported || !b_traits::supported>
- encode(const std::pair<A,B> &p, bufferlist &bl, uint64_t features)
+ typename a_traits = denc_traits<A>, typename b_traits = denc_traits<B>>
+requires encoding_detail::pair_needs_legacy_encoding<a_traits, b_traits>
+inline void encode(const std::pair<A, B>& p, bufferlist& bl, uint64_t features)
{
- encode(p.first, bl, features);
- encode(p.second, bl, features);
+ encoding_detail::encode_pair_members(p, bl, features);
}
template<class A, class B,
- typename a_traits, typename b_traits>
-inline std::enable_if_t<!a_traits::supported ||
- !b_traits::supported>
- encode(const std::pair<A,B> &p, bufferlist &bl)
+ typename a_traits = denc_traits<A>, typename b_traits = denc_traits<B>>
+requires encoding_detail::pair_needs_legacy_encoding<a_traits, b_traits>
+inline void encode(const std::pair<A, B>& p, bufferlist& bl)
{
- encode(p.first, bl);
- encode(p.second, bl);
+ encoding_detail::encode_pair_members(p, bl);
}
-template<class A, class B, typename a_traits, typename b_traits>
-inline std::enable_if_t<!a_traits::supported ||
- !b_traits::supported>
- decode(std::pair<A,B> &pa, bufferlist::const_iterator &p)
+template<class A, class B,
+ typename a_traits = denc_traits<A>, typename b_traits = denc_traits<B>>
+requires encoding_detail::pair_needs_legacy_encoding<a_traits, b_traits>
+inline void decode(std::pair<A, B>& pa, bufferlist::const_iterator& p)
{
- decode(pa.first, p);
- decode(pa.second, p);
+ encoding_detail::decode_pair_members(pa, p);
}
// std::list<T>
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const std::list<T, Alloc>& 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<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const std::list<T,Alloc>& 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<char*>(&en));
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::list<T, Alloc>& ls, bufferlist& bl)
+{
+ encoding_detail::encode_range(ls, bl);
+}
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::list<T, Alloc>& ls, bufferlist& bl, uint64_t features)
+{
+ encoding_detail::encode_range(ls, bl, features);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode(std::list<T,Alloc>& ls, bufferlist::const_iterator& p)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(std::list<T, Alloc>& ls, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- ls.clear();
- while (n--) {
- ls.emplace_back();
- decode(ls.back(), p);
- }
+ encoding_detail::decode_by_emplace_back(ls, p);
}
// std::list<std::shared_ptr<T>>
template<class T, class Alloc>
inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist& bl)
+ 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);
- }
+ encoding_detail::encode_shared_ptr_range(ls, bl);
}
template<class T, class Alloc>
inline void encode(const std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist& bl, uint64_t features)
+ 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);
- }
+ encoding_detail::encode_shared_ptr_range(ls, bl, features);
}
template<class T, class Alloc>
inline void decode(std::list<std::shared_ptr<T>, Alloc>& ls,
- bufferlist::const_iterator& p)
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- ls.clear();
- while (n--) {
- auto ref = std::make_shared<T>();
- decode(*ref, p);
- ls.emplace_back(std::move(ref));
- }
+ encoding_detail::decode_shared_ptr_sequence(ls, p);
}
// std::set<T>
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const std::set<T,Comp,Alloc>& s, bufferlist& bl)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::set<T, Comp, Alloc>& s, bufferlist& bl)
{
- __u32 n = (__u32)(s.size());
- encode(n, bl);
- for (auto p = s.begin(); p != s.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range(s, bl);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode(std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(std::set<T, Comp, Alloc>& s, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- s.clear();
- while (n--) {
- T v;
- decode(v, p);
- s.insert(v);
- }
+ encoding_detail::decode_by_insert(s, p);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline typename std::enable_if<!traits::supported>::type
- encode_nohead(const std::set<T,Comp,Alloc>& s, bufferlist& bl)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode_nohead(const std::set<T, Comp, Alloc>& s, bufferlist& bl)
{
- for (auto p = s.begin(); p != s.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range_nohead(s, bl);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode_nohead(unsigned len, std::set<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode_nohead(unsigned len, std::set<T, Comp, Alloc>& s,
+ bufferlist::const_iterator& p)
{
- for (unsigned i=0; i<len; i++) {
- T v;
- decode(v, p);
- s.insert(v);
- }
+ encoding_detail::decode_by_insert(len, s, p);
}
// boost::container::flat_set<T>
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
-encode(const boost::container::flat_set<T, Comp, Alloc>& s, bufferlist& bl)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist& bl)
{
- __u32 n = (__u32)(s.size());
- encode(n, bl);
- for (const auto& e : s)
- encode(e, bl);
+ encoding_detail::encode_range(s, bl);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
-decode(boost::container::flat_set<T, Comp, Alloc>& s, bufferlist::const_iterator& p)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(boost::container::flat_set<T, Comp, Alloc>& 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);
- }
+ encoding_detail::decode_by_insert(s, p);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
-encode_nohead(const boost::container::flat_set<T, Comp, Alloc>& s,
- bufferlist& bl)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode_nohead(const boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist& bl)
{
- for (const auto& e : s)
- encode(e, bl);
+ encoding_detail::encode_range_nohead(s, bl);
}
-template<class T, class Comp, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
-decode_nohead(unsigned len, boost::container::flat_set<T, Comp, Alloc>& s,
- bufferlist::iterator& p)
+template<class T, class Comp, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode_nohead(unsigned len,
+ boost::container::flat_set<T, Comp, Alloc>& s,
+ bufferlist::const_iterator& p)
{
- s.reserve(len);
- for (unsigned i=0; i<len; i++) {
- T v;
- decode(v, p);
- s.insert(v);
- }
+ encoding_detail::decode_by_insert(len, s, p);
}
// multiset
template<class T, class Comp, class Alloc>
-inline void encode(const std::multiset<T,Comp,Alloc>& s, bufferlist& bl)
+inline void encode(const std::multiset<T, Comp, Alloc>& s, bufferlist& bl)
{
- __u32 n = (__u32)(s.size());
- encode(n, bl);
- for (auto p = s.begin(); p != s.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range(s, bl);
}
template<class T, class Comp, class Alloc>
-inline void decode(std::multiset<T,Comp,Alloc>& s, bufferlist::const_iterator& p)
+inline void decode(std::multiset<T, Comp, Alloc>& s, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- s.clear();
- while (n--) {
- T v;
- decode(v, p);
- s.insert(v);
- }
+ encoding_detail::decode_by_insert(s, p);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const std::vector<T,Alloc>& v, bufferlist& bl, uint64_t features)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::vector<T, Alloc>& 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);
+ encoding_detail::encode_range(v, bl, features);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const std::vector<T,Alloc>& v, bufferlist& bl)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::vector<T, Alloc>& v, bufferlist& bl)
{
- __u32 n = (__u32)(v.size());
- encode(n, bl);
- for (auto p = v.begin(); p != v.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range(v, bl);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode(std::vector<T,Alloc>& v, bufferlist::const_iterator& p)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(std::vector<T, Alloc>& v, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- v.resize(n);
- for (__u32 i=0; i<n; i++)
- decode(v[i], p);
+ encoding_detail::decode_by_resize(v, p);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode_nohead(const std::vector<T,Alloc>& v, bufferlist& bl)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode_nohead(const std::vector<T, Alloc>& v, bufferlist& bl)
{
- for (auto p = v.begin(); p != v.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range_nohead(v, bl);
}
-template<class T, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode_nohead(unsigned len, std::vector<T,Alloc>& v, bufferlist::const_iterator& p)
+template<class T, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode_nohead(unsigned len, std::vector<T, Alloc>& v,
+ bufferlist::const_iterator& p)
{
- v.resize(len);
- for (__u32 i=0; i<v.size(); i++)
- decode(v[i], p);
+ encoding_detail::decode_by_resize_nohead(len, v, p);
}
// small vector
-template<class T, std::size_t N, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl, uint64_t features)
+template<class T, std::size_t N, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const boost::container::small_vector<T, N, Alloc>& v,
+ bufferlist& bl, uint64_t features)
{
- __u32 n = (__u32)(v.size());
- encode(n, bl);
- for (const auto& i : v)
- encode(i, bl, features);
+ encoding_detail::encode_range(v, bl, features);
}
-template<class T, std::size_t N, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl)
+template<class T, std::size_t N, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const boost::container::small_vector<T, N, Alloc>& v,
+ bufferlist& bl)
{
- __u32 n = (__u32)(v.size());
- encode(n, bl);
- for (const auto& i : v)
- encode(i, bl);
+ encoding_detail::encode_range(v, bl);
}
-template<class T, std::size_t N, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode(boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p)
+template<class T, std::size_t N, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(boost::container::small_vector<T, N, Alloc>& v,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- v.resize(n);
- for (auto& i : v)
- decode(i, p);
+ encoding_detail::decode_by_resize(v, p);
}
-template<class T, std::size_t N, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- encode_nohead(const boost::container::small_vector<T,N,Alloc>& v, bufferlist& bl)
+template<class T, std::size_t N, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode_nohead(const boost::container::small_vector<T, N, Alloc>& v,
+ bufferlist& bl)
{
- for (const auto& i : v)
- encode(i, bl);
+ encoding_detail::encode_range_nohead(v, bl);
}
-template<class T, std::size_t N, class Alloc, typename traits>
-inline std::enable_if_t<!traits::supported>
- decode_nohead(unsigned len, boost::container::small_vector<T,N,Alloc>& v, bufferlist::const_iterator& p)
+template<class T, std::size_t N, class Alloc, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode_nohead(unsigned len,
+ boost::container::small_vector<T, N, Alloc>& v,
+ bufferlist::const_iterator& p)
{
- v.resize(len);
- for (auto& i : v)
- decode(i, p);
+ encoding_detail::decode_by_resize_nohead(len, v, p);
}
// vector (shared_ptr)
-template<class T,class Alloc>
-inline void encode(const std::vector<std::shared_ptr<T>,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<class T, class Alloc>
+inline void encode(const std::vector<std::shared_ptr<T>, Alloc>& v,
+ bufferlist& bl,
+ uint64_t features)
+{
+ encoding_detail::encode_shared_ptr_range(v, bl, features);
}
template<class T, class Alloc>
-inline void encode(const std::vector<std::shared_ptr<T>,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);
- }
+inline void encode(const std::vector<std::shared_ptr<T>, Alloc>& v,
+ bufferlist& bl)
+{
+ encoding_detail::encode_shared_ptr_range(v, bl);
}
template<class T, class Alloc>
-inline void decode(std::vector<std::shared_ptr<T>,Alloc>& v,
- bufferlist::const_iterator& p)
+inline void decode(std::vector<std::shared_ptr<T>, Alloc>& v,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- v.clear();
- v.reserve(n);
- while (n--) {
- auto ref = std::make_shared<T>();
- decode(*ref, p);
- v.emplace_back(std::move(ref));
- }
+ encoding_detail::decode_shared_ptr_sequence(v, p);
}
// map
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported ||
- !u_traits::supported>
- encode(const std::map<T,U,Comp,Alloc>& 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<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode(const std::map<T,U,Comp,Alloc>& 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);
- }
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode(const std::map<T, U, Comp, Alloc>& m, bufferlist& bl)
+{
+ encoding_detail::encode_pair_range(m, bl);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- decode(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode(const std::map<T, U, Comp, Alloc>& m, bufferlist& bl,
+ uint64_t features)
{
- __u32 n;
- decode(n, p);
- m.clear();
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
+ encoding_detail::encode_pair_range(m, bl, features);
}
-template <std::move_constructible T, std::move_constructible U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode(std::map<T, U, Comp, Alloc>& m, bufferlist::const_iterator& p)
+template<class T, class U, class Comp, class Alloc,
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void decode(std::map<T, U, Comp, Alloc>& m, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- m.clear();
- while (n--) {
- T k;
- U v;
- decode(k, p);
- decode(v, p);
- m.emplace(std::move(k), std::move(v));
- }
+ encoding_detail::decode_map(m, p);
}
+
+// Compatibility-only: no production callers were found in-tree. Do not add new
+// callers unless preserving the destination's current entries is necessary.
+// Actual deprecation is a separate public-header compatibility decision.
+// Duplicate decoded keys intentionally preserve the existing mapped value for
+// std::map.
template<class T, class U, class Comp, class Alloc>
-inline void decode_noclear(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+[[maybe_unused]] inline void decode_noclear(std::map<T, U, Comp, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
-}
-template<std::move_constructible T, std::move_constructible U, class Comp, class Alloc>
-inline void decode_noclear(std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
-{
- __u32 n;
- decode(n, p);
- while (n--) {
- T k;
- U v;
- decode(k, p);
- decode(v, p);
- m.emplace(std::move(k), std::move(v));
- }
+ encoding_detail::decode_map_entries(encoding_detail::decode_count(p), m, p);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode_nohead(const std::map<T, U, Comp, Alloc>& m, bufferlist& bl)
{
- for (auto p = m.begin(); p != m.end(); ++p) {
- encode(p->first, bl);
- encode(p->second, bl);
- }
+ encoding_detail::encode_pair_range_nohead(m, bl);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode_nohead(const std::map<T,U,Comp,Alloc>& m, bufferlist& bl, uint64_t features)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode_nohead(const std::map<T, U, Comp, Alloc>& 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);
- }
+ encoding_detail::encode_pair_range_nohead(m, bl, features);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- decode_nohead(unsigned n, std::map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void decode_nohead(unsigned n, std::map<T, U, Comp, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- m.clear();
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
-}
-
-template <std::move_constructible T, std::move_constructible U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
-decode_nohead(unsigned n, std::map<T, U, Comp, Alloc>& m, bufferlist::const_iterator& p)
-{
- m.clear();
- while (n--) {
- T k;
- U v;
- decode(k, p);
- decode(v, p);
- m.emplace(std::move(k), std::move(v));
- }
+ encoding_detail::decode_map(n, m, p);
}
// boost::container::flat-map
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode(const boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist& bl)
-{
- __u32 n = (__u32)(m.size());
- encode(n, bl);
- for (typename boost::container::flat_map<T,U,Comp>::const_iterator p
- = m.begin(); p != m.end(); ++p) {
- encode(p->first, bl);
- encode(p->second, bl);
- }
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode(const boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist& bl)
+{
+ encoding_detail::encode_pair_range(m, bl);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode(const boost::container::flat_map<T,U,Comp,Alloc>& 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);
- }
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode(const boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist& bl, uint64_t features)
+{
+ encoding_detail::encode_pair_range(m, bl, features);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- decode(boost::container::flat_map<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void decode(boost::container::flat_map<T, U, Comp, Alloc>& 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);
- }
+ encoding_detail::decode_map_by_subscript(m, p);
}
+
+// Compatibility-only: no production callers were found in-tree. Do not add new
+// callers unless preserving the destination's current entries is necessary.
+// Actual deprecation is a separate public-header compatibility decision.
+// Duplicate decoded keys intentionally replace the existing mapped value for
+// boost::container::flat_map.
template<class T, class U, class Comp, class Alloc>
-inline void decode_noclear(boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist::const_iterator& p)
+[[maybe_unused]] inline void decode_noclear(
+ boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
+ const auto n = encoding_detail::decode_count(p);
m.reserve(m.size() + n);
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
+ encoding_detail::decode_map_entries_by_subscript(n, m, p);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist& bl)
-{
- for (auto p = m.begin(); p != m.end(); ++p) {
- encode(p->first, bl);
- encode(p->second, bl);
- }
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode_nohead(const boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist& bl)
+{
+ encoding_detail::encode_pair_range_nohead(m, bl);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
- inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- encode_nohead(const boost::container::flat_map<T,U,Comp,Alloc>& 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);
- }
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void encode_nohead(const boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist& bl, uint64_t features)
+{
+ encoding_detail::encode_pair_range_nohead(m, bl, features);
}
template<class T, class U, class Comp, class Alloc,
- typename t_traits, typename u_traits>
-inline std::enable_if_t<!t_traits::supported || !u_traits::supported>
- decode_nohead(unsigned n, boost::container::flat_map<T,U,Comp,Alloc>& m,
- bufferlist::const_iterator& p)
+ typename t_traits = denc_traits<T>, typename u_traits = denc_traits<U>>
+requires encoding_detail::pair_needs_legacy_encoding<t_traits, u_traits>
+inline void decode_nohead(unsigned n,
+ boost::container::flat_map<T, U, Comp, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- m.clear();
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
+ encoding_detail::decode_map_by_subscript(n, m, p);
}
// multimap
template<class T, class U, class Comp, class Alloc>
-inline void encode(const std::multimap<T,U,Comp,Alloc>& m, bufferlist& bl)
+inline void encode(const std::multimap<T, U, Comp, Alloc>& 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);
- }
+ encoding_detail::encode_pair_range(m, bl);
}
template<class T, class U, class Comp, class Alloc>
-inline void decode(std::multimap<T,U,Comp,Alloc>& m, bufferlist::const_iterator& p)
+inline void decode(std::multimap<T, U, Comp, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
m.clear();
- while (n--) {
- typename std::pair<T,U> tu = std::pair<T,U>();
+
+ encoding_detail::for_each_count(encoding_detail::decode_count(p), [&m, &p] {
+ auto tu = std::pair<T, U> {};
decode(tu.first, p);
- typename std::multimap<T,U,Comp,Alloc>::iterator it = m.insert(tu);
+ auto it = m.insert(std::move(tu));
decode(it->second, p);
- }
+ });
}
// std::unordered_map
template<class T, class U, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_map<T,U,Hash,Pred,Alloc>& 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<class T, class U, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist& bl)
+inline void encode(const std::unordered_map<T, U, Hash, Pred, Alloc>& 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);
- encode(p->second, bl);
- }
+ encoding_detail::encode_pair_range(m, bl, features);
}
template<class T, class U, class Hash, class Pred, class Alloc>
-inline void decode(std::unordered_map<T,U,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p)
+inline void encode(const std::unordered_map<T, U, Hash, Pred, Alloc>& m,
+ bufferlist& bl)
{
- __u32 n;
- decode(n, p);
- m.clear();
- while (n--) {
- T k;
- decode(k, p);
- decode(m[k], p);
- }
+ encoding_detail::encode_pair_range(m, bl);
}
-
-template <std::move_constructible T, std::move_constructible U, class Hash, class Pred, class Alloc>
-inline void decode(std::unordered_map<T, U, Hash, Pred, Alloc>& m, bufferlist::const_iterator& p)
+template<class T, class U, class Hash, class Pred, class Alloc>
+inline void decode(std::unordered_map<T, U, Hash, Pred, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- m.clear();
- while (n--) {
- T k;
- U v;
- decode(k, p);
- decode(v, p);
- m.emplace(std::move(k), std::move(v));
- }
+ encoding_detail::decode_map(m, p);
}
// std::unordered_set
template<class T, class Hash, class Pred, class Alloc>
-inline void encode(const std::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist& bl)
+inline void encode(const std::unordered_set<T, Hash, Pred, Alloc>& m,
+ bufferlist& bl)
{
- __u32 n = (__u32)(m.size());
- encode(n, bl);
- for (auto p = m.begin(); p != m.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range(m, bl);
}
template<class T, class Hash, class Pred, class Alloc>
-inline void decode(std::unordered_set<T,Hash,Pred,Alloc>& m, bufferlist::const_iterator& p)
+inline void decode(std::unordered_set<T, Hash, Pred, Alloc>& m,
+ bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- m.clear();
- while (n--) {
- T k;
- decode(k, p);
- m.insert(k);
- }
+ encoding_detail::decode_by_insert(m, p);
}
// deque
template<class T, class Alloc>
-inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl, uint64_t features)
+inline void encode(const std::deque<T, Alloc>& 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);
+ encoding_detail::encode_range(ls, bl, features);
}
template<class T, class Alloc>
-inline void encode(const std::deque<T,Alloc>& ls, bufferlist& bl)
+inline void encode(const std::deque<T, Alloc>& ls, bufferlist& bl)
{
- __u32 n = ls.size();
- encode(n, bl);
- for (auto p = ls.begin(); p != ls.end(); ++p)
- encode(*p, bl);
+ encoding_detail::encode_range(ls, bl);
}
template<class T, class Alloc>
-inline void decode(std::deque<T,Alloc>& ls, bufferlist::const_iterator& p)
+inline void decode(std::deque<T, Alloc>& ls, bufferlist::const_iterator& p)
{
- __u32 n;
- decode(n, p);
- ls.clear();
- while (n--) {
- ls.emplace_back();
- decode(ls.back(), p);
- }
+ encoding_detail::decode_by_emplace_back(ls, p);
}
// std::array<T, N>
-template<class T, size_t N, typename traits>
-inline std::enable_if_t<!traits::supported>
-encode(const std::array<T, N>& v, bufferlist& bl, uint64_t features)
+template<class T, size_t N, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::array<T, N>& v, bufferlist& bl, uint64_t features)
{
- for (const auto& e : v)
- encode(e, bl, features);
+ encoding_detail::encode_range_nohead(v, bl, features);
}
-template<class T, size_t N, typename traits>
-inline std::enable_if_t<!traits::supported>
-encode(const std::array<T, N>& v, bufferlist& bl)
+template<class T, size_t N, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void encode(const std::array<T, N>& v, bufferlist& bl)
{
- for (const auto& e : v)
- encode(e, bl);
+ encoding_detail::encode_range_nohead(v, bl);
+}
+template<class T, size_t N, typename traits = denc_traits<T>>
+requires encoding_detail::needs_legacy_encoding<traits>
+inline void decode(std::array<T, N>& v, bufferlist::const_iterator& p)
+{
+ encoding_detail::decode_range_nohead(v, p);
+}
+
+namespace encoding_detail {
+
+template<typename OptionalT>
+void encode_optional(const OptionalT& p, bufferlist& bl)
+{
+ __u8 present = static_cast<bool>(p);
+ encode(present, bl);
+ if (!p) {
+ return;
+ }
+
+ encode(*p, bl);
+}
+
+template<typename T>
+std::optional<T> decode_optional(bufferlist::const_iterator& p)
+{
+ __u8 present;
+ decode(present, p);
+ if (!present) {
+ return std::nullopt;
+ }
+
+ T value{};
+ decode(value, p);
+ return value;
+}
+
+template<typename FnT>
+void for_each_count(unsigned n, FnT&& fn)
+{
+ std::ranges::for_each(std::views::iota(0u, n), [&fn](auto) {
+ std::invoke(fn);
+ });
+}
+
+template<typename RangeT>
+void encode_range_nohead(const RangeT& r, bufferlist& bl)
+{
+ std::ranges::for_each(r, [&bl](const auto& item) {
+ encode(item, bl);
+ });
+}
+
+template<typename RangeT>
+void encode_range_nohead(const RangeT& r, bufferlist& bl, uint64_t features)
+{
+ std::ranges::for_each(r, [&bl, features](const auto& item) {
+ encode(item, bl, features);
+ });
+}
+
+template<typename RangeT, typename IteratorT>
+void decode_range_nohead(RangeT& r, IteratorT& p)
+{
+ std::ranges::for_each(r, [&p](auto& item) {
+ decode(item, p);
+ });
+}
+
+template<typename RangeT>
+void encode_range(const RangeT& r, bufferlist& bl)
+{
+ encode_count(std::ranges::size(r), bl);
+ encode_range_nohead(r, bl);
+}
+
+template<typename RangeT>
+void encode_range(const RangeT& r, bufferlist& bl, uint64_t features)
+{
+ encode_count(std::ranges::size(r), bl);
+ encode_range_nohead(r, bl, features);
+}
+
+template<typename ContainerT>
+void reserve_if_possible(ContainerT& c, size_t n)
+{
+ if constexpr (requires { c.reserve(n); }) {
+ c.reserve(n);
+ }
+}
+
+template<typename ContainerT>
+void clear_and_reserve(ContainerT& c, size_t n)
+{
+ c.clear();
+ reserve_if_possible(c, n);
+}
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_resize_nohead(unsigned len, ContainerT& c,
+ IteratorT& p)
+{
+ c.resize(len);
+ decode_range_nohead(c, p);
+}
+
+template<typename ContainerT>
+void decode_by_resize(ContainerT& c, bufferlist::const_iterator& p)
+{
+ decode_by_resize_nohead(decode_count(p), c, p);
+}
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_emplace_back(unsigned len, ContainerT& c,
+ IteratorT& p)
+{
+ clear_and_reserve(c, len);
+
+ for_each_count(len, [&c, &p] {
+ c.emplace_back();
+ decode(c.back(), p);
+ });
+}
+
+template<typename ContainerT>
+void decode_by_emplace_back(ContainerT& c, bufferlist::const_iterator& p)
+{
+ decode_by_emplace_back(decode_count(p), c, p);
+}
+
+template<typename ContainerT, typename IteratorT>
+void decode_by_insert(unsigned len, ContainerT& c,
+ IteratorT& p)
+{
+ clear_and_reserve(c, len);
+
+ for_each_count(len, [&c, &p] {
+ typename ContainerT::value_type v;
+ decode(v, p);
+ c.insert(std::move(v));
+ });
+}
+
+template<typename ContainerT>
+void decode_by_insert(ContainerT& c, bufferlist::const_iterator& p)
+{
+ decode_by_insert(decode_count(p), c, p);
+}
+
+template<typename RangeT>
+void encode_shared_ptr_range(const RangeT& r, bufferlist& bl)
+{
+ encode_shared_ptr_range_with(r, bl, [](const auto& value, auto& out) {
+ encode(value, out);
+ });
+}
+
+template<typename RangeT>
+void encode_shared_ptr_range(const RangeT& r, bufferlist& bl,
+ uint64_t features)
+{
+ encode_shared_ptr_range_with(r, bl, [features](const auto& value, auto& out) {
+ encode(value, out, features);
+ });
+}
+
+template<typename ValueT, typename EncodeFnT>
+void append_cached_default_encoding(std::optional<std::string>& bytes,
+ bufferlist& bl,
+ EncodeFnT&& encode_value)
+{
+ if (!bytes) {
+ auto tmp = bufferlist {};
+ std::invoke(encode_value, ValueT {}, tmp);
+ bytes = tmp.to_str();
+ }
+
+ bl.append(std::string_view { *bytes });
+}
+
+template<typename RangeT, typename EncodeFnT>
+void encode_shared_ptr_range_with(const RangeT& r, bufferlist& bl,
+ EncodeFnT&& encode_value)
+{
+ using value_type = typename std::ranges::range_value_t<RangeT>::element_type;
+
+ encode_count(std::ranges::size(r), bl);
+
+ auto null_bytes = std::optional<std::string> {};
+
+ std::ranges::for_each(r, [&bl, &encode_value, &null_bytes](const auto& ref) {
+ if (ref) {
+ std::invoke(encode_value, *ref, bl);
+ return;
+ }
+
+ append_cached_default_encoding<value_type>(null_bytes, bl, encode_value);
+ });
+}
+
+template<typename ContainerT>
+void decode_shared_ptr_sequence(ContainerT& c,
+ bufferlist::const_iterator& p)
+{
+ const auto n = decode_count(p);
+ clear_and_reserve(c, n);
+
+ for_each_count(n, [&c, &p] {
+ auto ref = std::make_shared<typename ContainerT::value_type::element_type>();
+ decode(*ref, p);
+ c.emplace_back(std::move(ref));
+ });
+}
+
+template<typename PairT>
+void encode_pair_members(const PairT& item, bufferlist& bl)
+{
+ encode(item.first, bl);
+ encode(item.second, bl);
+}
+
+template<typename PairT>
+void encode_pair_members(const PairT& item, bufferlist& bl,
+ uint64_t features)
+{
+ encode(item.first, bl, features);
+ encode(item.second, bl, features);
+}
+
+template<typename PairT>
+void decode_pair_members(PairT& item, bufferlist::const_iterator& p)
+{
+ decode(item.first, p);
+ decode(item.second, p);
+}
+
+template<typename MapT>
+void encode_pair_range_nohead(const MapT& m, bufferlist& bl)
+{
+ std::ranges::for_each(m, [&bl](const auto& item) {
+ encode_pair_members(item, bl);
+ });
}
-template<class T, size_t N, typename traits>
-inline std::enable_if_t<!traits::supported>
-decode(std::array<T, N>& v, bufferlist::const_iterator& p)
+
+template<typename MapT>
+void encode_pair_range_nohead(const MapT& m, bufferlist& bl,
+ uint64_t features)
{
- for (auto& e : v)
- decode(e, p);
+ std::ranges::for_each(m, [&bl, features](const auto& item) {
+ encode_pair_members(item, bl, features);
+ });
+}
+
+template<typename MapT>
+void encode_pair_range(const MapT& m, bufferlist& bl)
+{
+ encode_count(std::ranges::size(m), bl);
+ encode_pair_range_nohead(m, bl);
+}
+
+template<typename MapT>
+void encode_pair_range(const MapT& m, bufferlist& bl, uint64_t features)
+{
+ encode_count(std::ranges::size(m), bl);
+ encode_pair_range_nohead(m, bl, features);
+}
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries_by_subscript(unsigned n, MapT& m,
+ IteratorT& p)
+{
+ for_each_count(n, [&m, &p] {
+ typename MapT::key_type k;
+ decode(k, p);
+ decode(m[std::move(k)], p);
+ });
+}
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries_by_emplace(unsigned n, MapT& m,
+ IteratorT& p)
+{
+ for_each_count(n, [&m, &p] {
+ typename MapT::key_type k;
+ typename MapT::mapped_type v;
+ decode(k, p);
+ decode(v, p);
+ m.emplace(std::move(k), std::move(v));
+ });
+}
+
+template<typename MapT, typename IteratorT>
+void decode_map_entries(unsigned n, MapT& m, IteratorT& p)
+{
+ if constexpr (map_decodes_by_emplace<typename MapT::key_type,
+ typename MapT::mapped_type>) {
+ decode_map_entries_by_emplace(n, m, p);
+ return;
+ }
+
+ decode_map_entries_by_subscript(n, m, p);
+}
+
+template<typename MapT>
+void decode_map_by_subscript(unsigned n, MapT& m,
+ bufferlist::const_iterator& p)
+{
+ clear_and_reserve(m, n);
+ decode_map_entries_by_subscript(n, m, p);
+}
+
+template<typename MapT>
+void decode_map_by_subscript(MapT& m, bufferlist::const_iterator& p)
+{
+ decode_map_by_subscript(decode_count(p), m, p);
+}
+
+template<typename MapT>
+void decode_map(unsigned n, MapT& m, bufferlist::const_iterator& p)
+{
+ clear_and_reserve(m, n);
+ decode_map_entries(n, m, p);
+}
+
+template<typename MapT>
+void decode_map(MapT& m, bufferlist::const_iterator& p)
+{
+ decode_map(decode_count(p), m, p);
+}
+
+} // namespace encoding_detail
+
+// full bl decoder
+template<class T>
+inline void decode(T &o, const bufferlist& bl)
+{
+ auto p = bl.begin();
+ decode(o, p);
+ ceph_assert(p.end());
}
}
__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)); \
+ auto filler = (bl).append_hole( \
+ ::ceph::encoding_detail::struct_header_len()); \
const auto starting_bl_len = (bl).length(); \
using ::ceph::encode; \
do {
*/
#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);
+ ::ceph::encoding_detail::finish_encode_struct( \
+ filler, struct_v, struct_compat, struct_len, \
+ new_struct_compat, (bl), starting_bl_len);
#define ENCODE_FINISH(bl) ENCODE_FINISH_NEW_COMPAT(bl, 0)
-#define DECODE_ERR_OLDVERSION(func, v, compatv) \
- (std::string(func) + " no longer understands old encoding version " #v " < " + std::to_string(compatv))
+#define DECODE_ERR_OLDVERSION(func, v, compatv) \
+ (std::string(func) + " no longer understands old encoding version " #v \
+ " < " + std::to_string(compatv))
#define DECODE_ERR_NO_COMPAT(func, code_v, v, compatv) \
("Decoder at '" + std::string(func) + "' v=" + std::to_string(code_v) + \
#define DECODE_ERR_PAST(func) \
(std::string(func) + " decode past end of struct encoding")
+namespace ceph::encoding_detail {
+
+inline constexpr auto struct_header_len() noexcept
+{
+ return sizeof(__u8) + sizeof(__u8) + sizeof(ceph_le32);
+}
+
+struct struct_header final {
+ __u8 v = 0;
+ __u8 compat = 0;
+ __u32 len = 0;
+};
+
+template<typename IteratorT>
+struct_header read_struct_header(IteratorT& bl)
+{
+ using ::ceph::decode;
+
+ struct_header header;
+ decode(header.v, bl);
+ decode(header.compat, bl);
+ decode(header.len, bl);
+
+ return header;
+}
+
+inline void check_decode_compat(__u8 code_v, __u8 struct_v,
+ __u8 struct_compat,
+ const char *func)
+{
+ if (struct_compat > code_v) {
+ throw ::ceph::buffer::malformed_input(
+ DECODE_ERR_NO_COMPAT(func, code_v, struct_v, struct_compat));
+ }
+}
+
+template<typename IteratorT>
+unsigned checked_struct_end(IteratorT& bl, __u32 struct_len,
+ const char *func)
+{
+ if (struct_len > bl.get_remaining()) {
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(func));
+ }
+
+ return bl.get_off() + struct_len;
+}
+
+template<typename VersionT, typename IteratorT>
+unsigned decode_struct_start(__u8 code_v, VersionT& struct_v,
+ __u8& struct_compat, __u32& struct_len,
+ IteratorT& bl, const char *func)
+{
+ const auto header = read_struct_header(bl);
+ struct_v = header.v;
+ struct_compat = header.compat;
+ struct_len = header.len;
+
+ check_decode_compat(code_v, struct_v, struct_compat, func);
+ return checked_struct_end(bl, struct_len, func);
+}
+
+template<typename VersionT, typename IteratorT>
+unsigned decode_legacy_struct_start(__u8 code_v, VersionT& struct_v,
+ __u8 compat_v, __u8 len_v,
+ unsigned skip_v, IteratorT& bl,
+ const char *func)
+{
+ using ::ceph::decode;
+
+ decode(struct_v, bl);
+
+ if (compat_v <= struct_v) {
+ __u8 struct_compat;
+ decode(struct_compat, bl);
+ check_decode_compat(code_v, struct_v, struct_compat, func);
+ }
+
+ if (skip_v && compat_v > struct_v) {
+ if (skip_v > bl.get_remaining()) {
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(func));
+ }
+
+ bl += skip_v;
+ }
+
+ if (len_v > struct_v) {
+ return 0;
+ }
+
+ __u32 struct_len;
+ decode(struct_len, bl);
+ return checked_struct_end(bl, struct_len, func);
+}
+
+template<typename FillerT>
+void finish_encode_struct(FillerT& filler, __u8 struct_v,
+ __u8& struct_compat, ceph_le32& struct_len,
+ __u8 new_struct_compat, const bufferlist& bl,
+ size_t starting_bl_len)
+{
+ if (new_struct_compat) {
+ struct_compat = new_struct_compat;
+ }
+
+ struct_len = static_cast<__u32>(bl.length() - starting_bl_len);
+ filler.copy_in(sizeof(struct_v),
+ reinterpret_cast<const char *>(&struct_v));
+ filler.copy_in(sizeof(struct_compat),
+ reinterpret_cast<const char *>(&struct_compat));
+ filler.copy_in(sizeof(struct_len),
+ reinterpret_cast<const char *>(&struct_len));
+}
+
+template<typename IteratorT>
+void finish_decode_struct(IteratorT& bl, unsigned struct_end,
+ const char *func)
+{
+ if (!struct_end) {
+ return;
+ }
+
+ if (struct_end < bl.get_off()) {
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(func));
+ }
+
+ if (struct_end > bl.get_off()) {
+ bl += struct_end - bl.get_off();
+ }
+}
+
+template<typename PayloadT, typename IteratorT>
+void decode_unknown(PayloadT& payload, IteratorT& bl, const char *func)
+{
+ const auto header = read_struct_header(bl);
+ checked_struct_end(bl, header.len, func);
+
+ payload.clear();
+
+ using ::ceph::encode;
+
+ encode(header.v, payload);
+ encode(header.compat, payload);
+ encode(header.len, payload);
+ bl.copy(header.len, payload);
+}
+
+} // namespace ceph::encoding_detail
+
/**
* check for very old encoding
*
* @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));
+ if (oldestv > struct_v) \
+ throw ::ceph::buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, v, oldestv));
/**
* start a decoding block
#define DECODE_START(_v, bl) \
StructVChecker<_v> struct_v; \
__u8 struct_compat; \
- using ::ceph::decode; \
- decode(struct_v.v, bl); \
- decode(struct_compat, bl); \
- if (_v < struct_compat) \
- throw ::ceph::buffer::malformed_input(DECODE_ERR_NO_COMPAT(__PRETTY_FUNCTION__, _v, struct_v.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; \
+ using ::ceph::decode; \
+ unsigned struct_end = ::ceph::encoding_detail::decode_struct_start( \
+ _v, struct_v.v, struct_compat, struct_len, bl, __PRETTY_FUNCTION__); \
do {
#define DECODE_START_UNCHECKED(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_NO_COMPAT(__PRETTY_FUNCTION__, v, struct_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; \
+ using ::ceph::decode; \
+ unsigned struct_end = ::ceph::encoding_detail::decode_struct_start( \
+ v, struct_v, struct_compat, struct_len, bl, __PRETTY_FUNCTION__); \
do {
#define DECODE_UNKNOWN(payload, bl) \
do { \
- __u8 struct_v, struct_compat; \
- using ::ceph::decode; \
- decode(struct_v, bl); \
- decode(struct_compat, bl); \
- __u32 struct_len; \
- decode(struct_len, bl); \
- if (struct_len > bl.get_remaining()) \
- throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
- payload.clear(); \
- using ::ceph::encode; \
- encode(struct_v, payload); \
- encode(struct_compat, payload); \
- encode(struct_len, payload); \
- bl.copy(struct_len, payload); \
+ ::ceph::encoding_detail::decode_unknown( \
+ payload, bl, __PRETTY_FUNCTION__); \
} while (0)
-/* BEWARE: any change to this macro MUST be also reflected in the duplicative
- * DECODE_START_LEGACY_COMPAT_LEN! */
+/* The checked and unchecked legacy wrappers intentionally differ only in the
+ * local struct_v type. Keep shared behavior in decode_legacy_struct_start(). */
#define __DECODE_START_LEGACY_COMPAT_LEN(_v, compatv, lenv, skip_v, bl) \
using ::ceph::decode; \
StructVChecker<_v> struct_v; \
- decode(struct_v.v, bl); \
- if (struct_v.v >= compatv) { \
- __u8 struct_compat; \
- decode(struct_compat, bl); \
- if (_v < struct_compat) \
- throw ::ceph::buffer::malformed_input(DECODE_ERR_NO_COMPAT(__PRETTY_FUNCTION__, _v, struct_v.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.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; \
- } \
+ unsigned struct_end = ::ceph::encoding_detail::decode_legacy_struct_start( \
+ _v, struct_v.v, compatv, lenv, skip_v, bl, __PRETTY_FUNCTION__); \
do {
/**
* @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_NO_COMPAT( \
- __PRETTY_FUNCTION__, v, struct_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; \
- } \
+ unsigned struct_end = ::ceph::encoding_detail::decode_legacy_struct_start( \
+ v, struct_v, compatv, lenv, 0, bl, __PRETTY_FUNCTION__); \
do {
/**
*/
#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(); \
- }
+ ::ceph::encoding_detail::finish_decode_struct( \
+ bl, struct_end, __PRETTY_FUNCTION__);
namespace ceph {
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2026 International Business Machines Corp. (IBM)
+ *
+ * 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 "include/buffer.h"
#include "include/encoding.h"
#include <fmt/format.h>
#include "gtest/gtest.h"
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <cstdint>
+#include <functional>
#include <iostream> // for std::cout
+#include <list>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/container/small_vector.hpp>
+#include <boost/optional.hpp>
+#include <boost/tuple/tuple.hpp>
using namespace std;
template < typename T >
static void test_encode_and_decode(const T& src)
{
- bufferlist bl(1000000);
+ buffer::list bl(1000000);
encode(src, bl);
T dst;
auto i = bl.cbegin();
decode(dst, i);
- ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst;
+ ASSERT_EQ(src, dst)
+ << "Encoding roundtrip changed the string: orig=" << src
+ << ", but new=" << dst;
}
TEST(EncodingRoundTrip, StringSimple) {
template <typename Size, typename T>
static void test_encode_and_nohead_nohead(Size len, const T& src)
{
- bufferlist bl(1000000);
+ buffer::list bl(1000000);
encode(len, bl);
encode_nohead(src, bl);
T dst;
auto i = bl.cbegin();
decode(len, i);
decode_nohead(len, dst, i);
- ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst;
+ ASSERT_EQ(src, dst)
+ << "Encoding roundtrip changed the string: orig=" << src
+ << ", but new=" << dst;
}
TEST(EncodingRoundTrip, StringNoHead) {
}
TEST(EncodingRoundTrip, BufferListNoHead) {
- bufferlist bl;
+ buffer::list bl;
bl.append("is this a dagger which i see before me?");
auto size = bl.length();
test_encode_and_nohead_nohead(static_cast<int>(size), bl);
return data == rhs.data;
}
- friend void decode(ConstructorCounter &s, bufferlist::const_iterator& p)
+ friend void decode(ConstructorCounter &s, buffer::list::const_iterator& p)
{
decode(s.data, p);
}
- friend void encode(const ConstructorCounter &s, bufferlist& p)
+ friend void encode(const ConstructorCounter &s, buffer::list& p)
{
encode(s.data, p);
}
my_val_t::init();
test_encode_and_decode < multimap2_t >(multimap2);
- EXPECT_EQ(my_key_t::get_default_ctor(), 5);
- EXPECT_EQ(my_key_t::get_one_arg_ctor(), 0);
- EXPECT_EQ(my_key_t::get_copy_ctor(), 5);
- EXPECT_EQ(my_key_t::get_assigns(), 0);
+ EXPECT_EQ(5, my_key_t::get_default_ctor());
+ EXPECT_EQ(0, my_key_t::get_one_arg_ctor());
+ EXPECT_EQ(5, my_key_t::get_copy_ctor());
+ EXPECT_EQ(0, my_key_t::get_assigns());
- EXPECT_EQ(my_val_t::get_default_ctor(), 5);
- EXPECT_EQ(my_val_t::get_one_arg_ctor(), 0);
- EXPECT_EQ(my_val_t::get_copy_ctor(), 5);
- EXPECT_EQ(my_val_t::get_assigns(), 0);
+ EXPECT_EQ(5, my_val_t::get_default_ctor());
+ EXPECT_EQ(0, my_val_t::get_one_arg_ctor());
+ EXPECT_EQ(5, my_val_t::get_copy_ctor());
+ EXPECT_EQ(0, my_val_t::get_assigns());
}
namespace ceph {
// include/denc.h
template<>
void encode<uint64_t, denc_traits<uint64_t>>(const uint64_t&,
- bufferlist&,
- uint64_t f) {
+ buffer::list&,
+ uint64_t features) {
static_assert(denc_traits<uint64_t>::supported,
"should support new encoder");
static_assert(!denc_traits<uint64_t>::featured,
"should not be featured");
- ASSERT_EQ(0UL, f);
+ ASSERT_EQ(0UL, features);
// make sure the test fails if i get called
ASSERT_TRUE(false);
}
template<>
void encode<ceph_le64, denc_traits<ceph_le64>>(const ceph_le64&,
- bufferlist&,
- uint64_t f) {
+ buffer::list&,
+ uint64_t features) {
static_assert(denc_traits<ceph_le64>::supported,
"should support new encoder");
static_assert(!denc_traits<ceph_le64>::featured,
"should not be featured");
- ASSERT_EQ(0UL, f);
+ ASSERT_EQ(0UL, features);
// make sure the test fails if i get called
ASSERT_TRUE(false);
}
try {
throw exec;
} catch (const exception& e) {
- ASSERT_NE(string(e.what()).find(expected_what), string::npos);
+ ASSERT_NE(string::npos, string(e.what()).find(expected_what));
}
}
}
};
for (unsigned i=0; v[i][1]; ++i) {
{
- bufferlist bl;
+ buffer::list bl;
{
auto app = bl.get_contiguous_appender(16, true);
denc_varint(v[i][0], app);
cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][1]);
+ ASSERT_EQ(v[i][1], bl.length());
uint32_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_varint(u, p);
ASSERT_EQ(v[i][0], u);
}
{
- bufferlist bl;
+ buffer::list bl;
{
auto app = bl.get_contiguous_appender(16, true);
denc_signed_varint(v[i][0], app);
cout << std::hex << v[i][0] << "\t" << v[i][2] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][2]);
+ ASSERT_EQ(v[i][2], bl.length());
int32_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_signed_varint(u, p);
ASSERT_EQ((int32_t)v[i][0], u);
}
{
- bufferlist bl;
+ buffer::list bl;
int64_t x = -(int64_t)v[i][0];
{
auto app = bl.get_contiguous_appender(16, true);
cout << std::dec << x << std::hex << "\t" << v[i][3] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][3]);
+ ASSERT_EQ(v[i][3], bl.length());
int64_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_signed_varint(u, p);
};
for (unsigned i=0; v[i][1]; ++i) {
{
- bufferlist bl;
+ buffer::list bl;
{
auto app = bl.get_contiguous_appender(16, true);
denc_varint_lowz(v[i][0], app);
cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][1]);
+ ASSERT_EQ(v[i][1], bl.length());
uint32_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_varint_lowz(u, p);
ASSERT_EQ(v[i][0], u);
}
{
- bufferlist bl;
+ buffer::list bl;
int64_t x = v[i][0];
{
auto app = bl.get_contiguous_appender(16, true);
cout << std::hex << x << "\t" << v[i][1] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][2]);
+ ASSERT_EQ(v[i][2], bl.length());
int64_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_signed_varint_lowz(u, p);
ASSERT_EQ(x, u);
}
{
- bufferlist bl;
+ buffer::list bl;
int64_t x = -(int64_t)v[i][0];
{
auto app = bl.get_contiguous_appender(16, true);
cout << std::dec << x << "\t" << v[i][1] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][3]);
+ ASSERT_EQ(v[i][3], bl.length());
int64_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_signed_varint_lowz(u, p);
{0, 0}
};
for (unsigned i=0; v[i][1]; ++i) {
- bufferlist bl;
+ buffer::list bl;
{
auto app = bl.get_contiguous_appender(16, true);
denc_lba(v[i][0], app);
cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t";
bl.hexdump(cout, false);
cout << std::endl;
- ASSERT_EQ(bl.length(), v[i][1]);
+ ASSERT_EQ(v[i][1], bl.length());
uint64_t u;
auto p = bl.begin().get_current_ptr().cbegin();
denc_lba(u, p);
}
}
+
+/*
+ * encoding.h Compatibility Coverage
+ *
+ * New tests for interface-preserving modernization of
+ * include/encoding.h belong below this section.
+ */
+
+namespace {
+
+struct encoding_compat_record final {
+ uint32_t n = 0;
+ std::string s;
+
+ static inline uint64_t last_features = 0;
+ static inline unsigned feature_encode_calls = 0;
+
+ static void reset_counters()
+ {
+ last_features = 0;
+ feature_encode_calls = 0;
+ }
+
+ friend bool operator==(const encoding_compat_record& lhs,
+ const encoding_compat_record& rhs)
+ {
+ return lhs.n == rhs.n && lhs.s == rhs.s;
+ }
+
+ friend bool operator<(const encoding_compat_record& lhs,
+ const encoding_compat_record& rhs)
+ {
+ return std::tie(lhs.n, lhs.s) < std::tie(rhs.n, rhs.s);
+ }
+
+ friend void encode(const encoding_compat_record& v, buffer::list& bl)
+ {
+ encode(v.n, bl);
+ encode(v.s, bl);
+ }
+
+ friend void encode(const encoding_compat_record& v,
+ buffer::list& bl,
+ uint64_t features)
+ {
+ last_features = features;
+ ++feature_encode_calls;
+ encode(v, bl);
+ }
+
+ friend void decode(encoding_compat_record& v, buffer::list::const_iterator& p)
+ {
+ decode(v.n, p);
+ decode(v.s, p);
+ }
+
+ friend std::ostream& operator<<(std::ostream& os,
+ const encoding_compat_record& v)
+ {
+ return os << "{n=" << v.n << ", s=" << v.s << "}";
+ }
+};
+
+struct encoding_compat_record_hash final {
+ std::size_t operator()(const encoding_compat_record& v) const
+ {
+ return std::hash<uint32_t> {}(v.n) ^ (std::hash<std::string> {}(v.s) << 1);
+ }
+};
+
+struct encoding_compat_v1_record final {
+ uint32_t n = 0;
+ std::string s;
+
+ void decode(buffer::list::const_iterator& p)
+ {
+ DECODE_START(1, p);
+ decode(n, p);
+ decode(s, p);
+ DECODE_FINISH(p);
+ }
+
+ friend bool operator==(const encoding_compat_v1_record& lhs,
+ const encoding_compat_v1_record& rhs)
+ {
+ return lhs.n == rhs.n && lhs.s == rhs.s;
+ }
+};
+
+struct encoding_compat_v2_record final {
+ uint32_t n = 0;
+ std::string s;
+ std::string extra;
+
+ void encode(buffer::list& bl) const
+ {
+ ENCODE_START(2, 1, bl);
+ encode(n, bl);
+ encode(s, bl);
+ encode(extra, bl);
+ ENCODE_FINISH(bl);
+ }
+};
+
+struct encoding_macro_record final {
+ __u8 value = 0;
+
+ void encode(buffer::list& bl) const
+ {
+ ENCODE_START(1, 1, bl);
+ encode(value, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(buffer::list::const_iterator& p)
+ {
+ DECODE_START(1, p);
+ decode(value, p);
+ DECODE_FINISH(p);
+ }
+};
+
+struct encoding_new_compat_record final {
+ __u8 value = 0;
+
+ void encode(buffer::list& bl) const
+ {
+ ENCODE_START(1, 1, bl);
+ encode(value, bl);
+ ENCODE_FINISH_NEW_COMPAT(bl, 2);
+ }
+};
+
+static const encoding_compat_record alpha { 1, "alpha" };
+static const encoding_compat_record bravo { 2, "bravo" };
+static const encoding_compat_record bravo_alt { 2, "bravo-alt" };
+static const encoding_compat_record charlie { 3, "charlie" };
+
+template <typename T>
+T encoding_compat_round_trip(const T& src)
+{
+ buffer::list bl;
+ encode(src, bl);
+
+ T dst;
+ auto p = bl.cbegin();
+ decode(dst, p);
+
+ EXPECT_TRUE(p.end());
+ return dst;
+}
+
+template <typename T>
+void expect_encoding_compat_round_trip(const T& src)
+{
+ EXPECT_EQ(src, encoding_compat_round_trip(src));
+}
+
+template <typename T>
+void expect_encoding_compat_nohead_round_trip(const T& src, unsigned n)
+{
+ buffer::list bl;
+ encode_nohead(src, bl);
+
+ T dst;
+ auto p = bl.cbegin();
+ decode_nohead(n, dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(src, dst);
+}
+
+template <typename T>
+void expect_nohead_decode_replaces_existing(const T& src, T dst)
+{
+ buffer::list bl;
+ encode_nohead(src, bl);
+
+ auto p = bl.cbegin();
+ decode_nohead(src.size(), dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(src, dst);
+}
+
+void append_compat_le32(std::string& out, uint32_t v)
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ out.push_back(static_cast<char>(v >> (i * 8)));
+ }
+}
+
+void append_compat_le64(std::string& out, uint64_t v)
+{
+ for (unsigned i = 0; i < 8; ++i) {
+ out.push_back(static_cast<char>(v >> (i * 8)));
+ }
+}
+
+std::string float_wire_bytes(uint32_t raw)
+{
+ auto out = std::string {};
+ append_compat_le32(out, raw);
+ return out;
+}
+
+std::string float_wire_bytes(uint64_t raw)
+{
+ auto out = std::string {};
+ append_compat_le64(out, raw);
+ return out;
+}
+
+void expect_float_wire_format(float v, uint32_t raw)
+{
+ buffer::list bl;
+ encode(v, bl);
+
+ EXPECT_EQ(float_wire_bytes(raw), bl.to_str());
+
+ float out = 0;
+ auto p = bl.cbegin();
+ decode(out, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(raw, std::bit_cast<uint32_t>(out));
+}
+
+void expect_float_wire_format(double v, uint64_t raw)
+{
+ buffer::list bl;
+ encode(v, bl);
+
+ EXPECT_EQ(float_wire_bytes(raw), bl.to_str());
+
+ double out = 0;
+ auto p = bl.cbegin();
+ decode(out, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(raw, std::bit_cast<uint64_t>(out));
+}
+
+void append_compat_string(std::string& out, std::string_view v)
+{
+ append_compat_le32(out, v.size());
+ out.append(v);
+}
+
+void append_compat_record(std::string& out, const encoding_compat_record& v)
+{
+ append_compat_le32(out, v.n);
+ append_compat_string(out, v.s);
+}
+
+void encode_macro_header(buffer::list& bl, __u8 struct_v,
+ __u8 struct_compat, __u32 struct_len)
+{
+ encode(struct_v, bl);
+ encode(struct_compat, bl);
+ encode(struct_len, bl);
+}
+
+void decode_macro_overread(buffer::list::const_iterator& p)
+{
+ DECODE_START(1, p);
+ p += 2;
+ DECODE_FINISH(p);
+}
+
+void decode_macro_unknown(buffer::list& payload,
+ buffer::list::const_iterator& p)
+{
+ DECODE_UNKNOWN(payload, p);
+}
+
+std::pair<__u8, __u32> decode_unchecked_macro_header(buffer::list::const_iterator& p)
+{
+ DECODE_START_UNCHECKED(3, p);
+ DECODE_FINISH(p);
+
+ return { struct_v, struct_len };
+}
+
+__u8 decode_legacy_macro_value(buffer::list::const_iterator& p)
+{
+ __u8 value = 0;
+
+ DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, p);
+ decode(value, p);
+ DECODE_FINISH(p);
+
+ return value;
+}
+
+__u8 decode_legacy_macro_value_16(buffer::list::const_iterator& p)
+{
+ __u8 value = 0;
+
+ DECODE_START_LEGACY_COMPAT_LEN_16(2, 2, 2, p);
+ decode(value, p);
+ DECODE_FINISH(p);
+
+ return value;
+}
+
+__u8 decode_legacy_macro_value_32(buffer::list::const_iterator& p)
+{
+ __u8 value = 0;
+
+ DECODE_START_LEGACY_COMPAT_LEN_32(2, 2, 2, p);
+ decode(value, p);
+ DECODE_FINISH(p);
+
+ return value;
+}
+
+template <typename T>
+std::string encode_compat_to_string(const T& v)
+{
+ buffer::list bl;
+ encode(v, bl);
+ return bl.to_str();
+}
+
+template <typename T>
+void expect_compat_wire_format(const T& v, std::string_view expected)
+{
+ EXPECT_EQ(expected, encode_compat_to_string(v));
+}
+
+template <typename T>
+void expect_feature_compat_wire_format(const T& v, std::string_view expected)
+{
+ buffer::list bl;
+ encode(v, bl, 123);
+
+ EXPECT_EQ(expected, bl.to_str());
+}
+
+template <typename T>
+void expect_feature_encode_calls(const T& v, std::size_t expected_calls)
+{
+ buffer::list bl;
+
+ encoding_compat_record::reset_counters();
+ encode(v, bl, 123);
+
+ EXPECT_EQ(uint64_t { 123 }, encoding_compat_record::last_features);
+ EXPECT_EQ(expected_calls, encoding_compat_record::feature_encode_calls);
+}
+
+template <std::ranges::sized_range RangeT>
+buffer::list make_decode_noclear_payload(const RangeT& entries)
+{
+ auto bl = buffer::list {};
+ encode(static_cast<__u32>(std::ranges::size(entries)), bl);
+
+ std::ranges::for_each(entries, [&bl](const auto& item) {
+ encode(item.first, bl);
+ encode(item.second, bl);
+ });
+
+ return bl;
+}
+
+} // namespace
+
+TEST(EncodingCompatibility, LegacySequenceContainersRoundTrip)
+{
+ expect_encoding_compat_round_trip(
+ std::vector<encoding_compat_record> { alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ std::list<encoding_compat_record> { alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ std::deque<encoding_compat_record> { alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ boost::container::small_vector<encoding_compat_record, 4> {
+ alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ std::array<encoding_compat_record, 3> { alpha, bravo, charlie });
+}
+
+TEST(EncodingCompatibility, BufferTypesRoundTrip)
+{
+ bufferptr ptr_src("bufferptr-value", 15);
+ auto ptr_dst = encoding_compat_round_trip(ptr_src);
+
+ ASSERT_EQ(ptr_src.length(), ptr_dst.length());
+ EXPECT_EQ(std::string_view(ptr_src.c_str(), ptr_src.length()),
+ std::string_view(ptr_dst.c_str(), ptr_dst.length()));
+
+ buffer::list list_src;
+ list_src.append("first-", 6);
+ list_src.append("second", 6);
+ auto list_dst = encoding_compat_round_trip(list_src);
+
+ EXPECT_EQ(list_src.length(), list_dst.length());
+ EXPECT_EQ(list_src.to_str(), list_dst.to_str());
+}
+
+TEST(EncodingCompatibility, FloatingPointWireFormat)
+{
+ expect_float_wire_format(std::bit_cast<float>(uint32_t { 0x00000000 }),
+ uint32_t { 0x00000000 });
+ expect_float_wire_format(std::bit_cast<float>(uint32_t { 0x80000000 }),
+ uint32_t { 0x80000000 });
+ expect_float_wire_format(std::bit_cast<float>(uint32_t { 0x3f800000 }),
+ uint32_t { 0x3f800000 });
+ expect_float_wire_format(std::bit_cast<float>(uint32_t { 0x3dfbe76d }),
+ uint32_t { 0x3dfbe76d });
+ expect_float_wire_format(std::bit_cast<float>(uint32_t { 0x7fc00001 }),
+ uint32_t { 0x7fc00001 });
+
+ expect_float_wire_format(std::bit_cast<double>(uint64_t { 0x0000000000000000 }),
+ uint64_t { 0x0000000000000000 });
+ expect_float_wire_format(std::bit_cast<double>(uint64_t { 0x8000000000000000 }),
+ uint64_t { 0x8000000000000000 });
+ expect_float_wire_format(std::bit_cast<double>(uint64_t { 0x3ff0000000000000 }),
+ uint64_t { 0x3ff0000000000000 });
+ expect_float_wire_format(std::bit_cast<double>(uint64_t { 0x3fbf7ced916872b0 }),
+ uint64_t { 0x3fbf7ced916872b0 });
+ expect_float_wire_format(std::bit_cast<double>(uint64_t { 0x7ff8000000000001 }),
+ uint64_t { 0x7ff8000000000001 });
+}
+
+TEST(EncodingCompatibility, LegacySetContainersRoundTrip)
+{
+ expect_encoding_compat_round_trip(
+ std::set<encoding_compat_record> { alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ boost::container::flat_set<encoding_compat_record> { alpha, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ std::multiset<encoding_compat_record> { alpha, bravo, bravo, charlie });
+ expect_encoding_compat_round_trip(
+ std::unordered_set<encoding_compat_record, encoding_compat_record_hash> {
+ alpha, bravo, charlie });
+}
+
+TEST(EncodingCompatibility, LegacyMapContainersRoundTrip)
+{
+ expect_encoding_compat_round_trip(
+ std::map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ });
+ expect_encoding_compat_round_trip(
+ boost::container::flat_map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ });
+ expect_encoding_compat_round_trip(
+ std::multimap<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { alpha, bravo_alt },
+ { bravo, charlie },
+ });
+ expect_encoding_compat_round_trip(
+ std::unordered_map<encoding_compat_record,
+ encoding_compat_record,
+ encoding_compat_record_hash> {
+ { alpha, bravo },
+ { bravo, charlie },
+ });
+}
+
+TEST(EncodingCompatibility, LegacyNoHeadRoundTrip)
+{
+ expect_encoding_compat_nohead_round_trip(
+ std::vector<encoding_compat_record> { alpha, bravo, charlie }, 3);
+ expect_encoding_compat_nohead_round_trip(
+ boost::container::small_vector<encoding_compat_record, 4> {
+ alpha, bravo, charlie }, 3);
+ expect_encoding_compat_nohead_round_trip(
+ std::set<encoding_compat_record> { alpha, bravo, charlie }, 3);
+ expect_encoding_compat_nohead_round_trip(
+ boost::container::flat_set<encoding_compat_record> {
+ alpha, bravo, charlie }, 3);
+ expect_encoding_compat_nohead_round_trip(
+ std::map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ }, 2);
+ expect_encoding_compat_nohead_round_trip(
+ boost::container::flat_map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ }, 2);
+}
+
+TEST(EncodingCompatibility, LegacyNoHeadDecodeClearsDestination)
+{
+ expect_nohead_decode_replaces_existing(
+ std::vector<encoding_compat_record> { alpha, bravo },
+ std::vector<encoding_compat_record> { charlie });
+ expect_nohead_decode_replaces_existing(
+ boost::container::small_vector<encoding_compat_record, 4> { alpha, bravo },
+ boost::container::small_vector<encoding_compat_record, 4> { charlie });
+ expect_nohead_decode_replaces_existing(
+ std::set<encoding_compat_record> { alpha, bravo },
+ std::set<encoding_compat_record> { charlie });
+ expect_nohead_decode_replaces_existing(
+ boost::container::flat_set<encoding_compat_record> { alpha, bravo },
+ boost::container::flat_set<encoding_compat_record> { charlie });
+ expect_nohead_decode_replaces_existing(
+ std::map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ },
+ std::map<encoding_compat_record, encoding_compat_record> {
+ { charlie, alpha },
+ });
+ expect_nohead_decode_replaces_existing(
+ boost::container::flat_map<encoding_compat_record,
+ encoding_compat_record> {
+ { alpha, bravo },
+ },
+ boost::container::flat_map<encoding_compat_record,
+ encoding_compat_record> {
+ { charlie, alpha },
+ });
+}
+
+TEST(EncodingCompatibility, LegacySetNoHeadEncodingWireFormat)
+{
+ const std::set<encoding_compat_record> src { alpha, bravo };
+ std::string expected;
+ append_compat_record(expected, alpha);
+ append_compat_record(expected, bravo);
+
+ buffer::list bl;
+ encode_nohead(src, bl);
+
+ EXPECT_EQ(expected, bl.to_str());
+}
+
+TEST(EncodingCompatibility, LegacyFlatSetNoHeadEncodingWireFormat)
+{
+ const boost::container::flat_set<encoding_compat_record> src { alpha, bravo };
+ std::string expected;
+ append_compat_record(expected, alpha);
+ append_compat_record(expected, bravo);
+
+ buffer::list bl;
+ encode_nohead(src, bl);
+
+ EXPECT_EQ(expected, bl.to_str());
+}
+
+TEST(EncodingCompatibility, LegacyOptionalTupleAndPairRoundTrip)
+{
+ expect_encoding_compat_round_trip(
+ std::optional<encoding_compat_record> { alpha });
+ expect_encoding_compat_round_trip(
+ std::optional<encoding_compat_record> {});
+ expect_encoding_compat_round_trip(
+ boost::optional<encoding_compat_record> { bravo });
+ expect_encoding_compat_round_trip(
+ boost::optional<encoding_compat_record> {});
+ expect_encoding_compat_round_trip(
+ std::tuple<encoding_compat_record, std::string, uint32_t> {
+ alpha, "tuple", 7 });
+ expect_encoding_compat_round_trip(
+ std::pair<encoding_compat_record, encoding_compat_record> {
+ alpha, bravo });
+
+ buffer::list bl;
+ encode(std::string_view { "string-view" }, bl);
+
+ std::string string_view_out;
+ auto p = bl.cbegin();
+ decode(string_view_out, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ("string-view", string_view_out);
+
+ const auto src = boost::make_tuple(alpha, bravo, charlie);
+ const auto dst = encoding_compat_round_trip(src);
+ EXPECT_EQ(boost::get<0>(src), boost::get<0>(dst));
+ EXPECT_EQ(boost::get<1>(src), boost::get<1>(dst));
+ EXPECT_EQ(boost::get<2>(src), boost::get<2>(dst));
+}
+
+TEST(EncodingCompatibility, SharedPtrContainersRoundTrip)
+{
+ using compat_ptr = std::shared_ptr<encoding_compat_record>;
+
+ const std::list<compat_ptr> list_src {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ };
+ auto list_dst = encoding_compat_round_trip(list_src);
+
+ ASSERT_EQ(list_src.size(), list_dst.size());
+ auto list_it = list_dst.begin();
+ ASSERT_TRUE(*list_it);
+ EXPECT_EQ(alpha, **list_it);
+ ++list_it;
+ ASSERT_TRUE(*list_it);
+ EXPECT_EQ(encoding_compat_record {}, **list_it);
+ ++list_it;
+ ASSERT_TRUE(*list_it);
+ EXPECT_EQ(bravo, **list_it);
+
+ const std::vector<compat_ptr> vector_src {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ };
+ auto vector_dst = encoding_compat_round_trip(vector_src);
+
+ ASSERT_EQ(vector_src.size(), vector_dst.size());
+ ASSERT_TRUE(vector_dst[0]);
+ ASSERT_TRUE(vector_dst[1]);
+ ASSERT_TRUE(vector_dst[2]);
+ EXPECT_EQ(alpha, *vector_dst[0]);
+ EXPECT_EQ(encoding_compat_record {}, *vector_dst[1]);
+ EXPECT_EQ(bravo, *vector_dst[2]);
+}
+
+TEST(EncodingCompatibility, FeatureEncodingPropagatesToLegacyContainerElements)
+{
+ expect_feature_encode_calls(
+ std::vector<encoding_compat_record> { alpha, bravo, charlie }, 3);
+ expect_feature_encode_calls(
+ std::list<encoding_compat_record> { alpha, bravo, charlie }, 3);
+ expect_feature_encode_calls(
+ std::deque<encoding_compat_record> { alpha, bravo, charlie }, 3);
+ expect_feature_encode_calls(
+ boost::container::small_vector<encoding_compat_record, 4> {
+ alpha, bravo, charlie }, 3);
+ expect_feature_encode_calls(
+ std::array<encoding_compat_record, 3> { alpha, bravo, charlie }, 3);
+ expect_feature_encode_calls(
+ std::map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ }, 4);
+ expect_feature_encode_calls(
+ boost::container::flat_map<encoding_compat_record, encoding_compat_record> {
+ { alpha, bravo },
+ { bravo, charlie },
+ }, 4);
+ expect_feature_encode_calls(
+ std::unordered_map<encoding_compat_record,
+ encoding_compat_record,
+ encoding_compat_record_hash> {
+ { alpha, bravo },
+ { bravo, charlie },
+ }, 4);
+ expect_feature_encode_calls(
+ std::vector<std::shared_ptr<encoding_compat_record>> {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ }, 3);
+}
+
+TEST(EncodingCompatibility, DecodeNoClearMapPreservesDuplicateKeys)
+{
+ const std::map<encoding_compat_record, encoding_compat_record> src {
+ { alpha, bravo },
+ };
+ buffer::list bl;
+ encode(src, bl);
+
+ std::map<encoding_compat_record, encoding_compat_record> dst {
+ { alpha, charlie },
+ { charlie, alpha },
+ };
+ auto p = bl.cbegin();
+ decode_noclear(dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(charlie, dst.at(alpha));
+ EXPECT_EQ(alpha, dst.at(charlie));
+}
+
+TEST(EncodingCompatibility, DecodeNoClearFlatMapReplacesDuplicateKeys)
+{
+ const boost::container::flat_map<encoding_compat_record,
+ encoding_compat_record> src {
+ { alpha, bravo },
+ };
+ buffer::list bl;
+ encode(src, bl);
+
+ boost::container::flat_map<encoding_compat_record,
+ encoding_compat_record> dst {
+ { alpha, charlie },
+ { charlie, alpha },
+ };
+ auto p = bl.cbegin();
+ decode_noclear(dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(bravo, dst.at(alpha));
+ EXPECT_EQ(alpha, dst.at(charlie));
+}
+
+TEST(EncodingCompatibility, DecodeNoClearPrimitiveMapPreservesDuplicateKeys)
+{
+ const auto entries = std::array {
+ std::pair { uint64_t { 7 }, std::bit_cast<float>(uint32_t { 0x3f800000 }) },
+ std::pair { uint64_t { 11 }, std::bit_cast<float>(uint32_t { 0x40000000 }) },
+ };
+ auto bl = make_decode_noclear_payload(entries);
+
+ std::map<uint64_t, float> dst {
+ { uint64_t { 7 }, std::bit_cast<float>(uint32_t { 0x40400000 }) },
+ { uint64_t { 13 }, std::bit_cast<float>(uint32_t { 0x40800000 }) },
+ };
+ auto p = bl.cbegin();
+ decode_noclear(dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(3u, dst.size());
+ EXPECT_EQ(uint32_t { 0x40400000 }, std::bit_cast<uint32_t>(dst.at(7)));
+ EXPECT_EQ(uint32_t { 0x40000000 }, std::bit_cast<uint32_t>(dst.at(11)));
+ EXPECT_EQ(uint32_t { 0x40800000 }, std::bit_cast<uint32_t>(dst.at(13)));
+}
+
+TEST(EncodingCompatibility, DecodeNoClearPrimitiveFlatMapReplacesDuplicateKeys)
+{
+ const auto entries = std::array {
+ std::pair { uint64_t { 7 }, std::bit_cast<float>(uint32_t { 0x3f800000 }) },
+ std::pair { uint64_t { 11 }, std::bit_cast<float>(uint32_t { 0x40000000 }) },
+ };
+ auto bl = make_decode_noclear_payload(entries);
+
+ boost::container::flat_map<uint64_t, float> dst {
+ { uint64_t { 7 }, std::bit_cast<float>(uint32_t { 0x40400000 }) },
+ { uint64_t { 13 }, std::bit_cast<float>(uint32_t { 0x40800000 }) },
+ };
+ auto p = bl.cbegin();
+ decode_noclear(dst, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(3u, dst.size());
+ EXPECT_EQ(uint32_t { 0x3f800000 }, std::bit_cast<uint32_t>(dst.at(7)));
+ EXPECT_EQ(uint32_t { 0x40000000 }, std::bit_cast<uint32_t>(dst.at(11)));
+ EXPECT_EQ(uint32_t { 0x40800000 }, std::bit_cast<uint32_t>(dst.at(13)));
+}
+
+TEST(EncodingCompatibility, DecodeFinishSkipsUnknownTrailingFields)
+{
+ const encoding_compat_v2_record src { 7, "known", "unknown" };
+ buffer::list bl;
+ src.encode(bl);
+
+ encoding_compat_v1_record dst;
+ auto p = bl.cbegin();
+ dst.decode(p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ((encoding_compat_v1_record { 7, "known" }), dst);
+}
+
+TEST(EncodingCompatibility, MacroDecodeRejectsNewerCompat)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 2, 2, 0);
+
+ encoding_macro_record dst;
+ auto p = bl.cbegin();
+
+ EXPECT_THROW(dst.decode(p), buffer::malformed_input);
+}
+
+TEST(EncodingCompatibility, MacroDecodeRejectsShortPayload)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 1, 1, 1);
+
+ encoding_macro_record dst;
+ auto p = bl.cbegin();
+
+ EXPECT_THROW(dst.decode(p), buffer::malformed_input);
+}
+
+TEST(EncodingCompatibility, MacroDecodeFinishSkipsUnreadBytes)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 1, 1, 2);
+ encode(__u8 { 17 }, bl);
+ encode(__u8 { 19 }, bl);
+
+ encoding_macro_record dst;
+ auto p = bl.cbegin();
+ dst.decode(p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(__u8 { 17 }, dst.value);
+}
+
+TEST(EncodingCompatibility, MacroDecodeFinishRejectsOverread)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 1, 1, 1);
+ encode(__u8 { 17 }, bl);
+ encode(__u8 { 19 }, bl);
+
+ auto p = bl.cbegin();
+
+ EXPECT_THROW(decode_macro_overread(p), buffer::malformed_input);
+}
+
+TEST(EncodingCompatibility, MacroDecodeUnknownPreservesEnvelope)
+{
+ const encoding_macro_record src { 42 };
+ buffer::list bl;
+ src.encode(bl);
+
+ buffer::list payload;
+ auto p = bl.cbegin();
+ decode_macro_unknown(payload, p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(bl.to_str(), payload.to_str());
+}
+
+TEST(EncodingCompatibility, MacroEncodeFinishNewCompatUpdatesEnvelope)
+{
+ const encoding_new_compat_record src { 43 };
+ buffer::list bl;
+ src.encode(bl);
+
+ encoding_macro_record dst;
+ auto p = bl.cbegin();
+
+ EXPECT_THROW(dst.decode(p), buffer::malformed_input);
+}
+
+TEST(EncodingCompatibility, MacroDecodeStartUncheckedExposesHeaderFields)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 2, 1, 0);
+
+ auto p = bl.cbegin();
+ const auto [struct_v, struct_len] = decode_unchecked_macro_header(p);
+
+ EXPECT_TRUE(p.end());
+ EXPECT_EQ(__u8 { 2 }, struct_v);
+ EXPECT_EQ(__u32 { 0 }, struct_len);
+}
+
+TEST(EncodingCompatibility, MacroLegacyCompatLenDecodesOldPayload)
+{
+ buffer::list bl;
+ encode(__u8 { 1 }, bl);
+ encode(__u8 { 23 }, bl);
+
+ auto p = bl.cbegin();
+
+ EXPECT_EQ(__u8 { 23 }, decode_legacy_macro_value(p));
+ EXPECT_TRUE(p.end());
+}
+
+TEST(EncodingCompatibility, MacroLegacyCompatLenDecodesCurrentPayload)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 2, 1, 1);
+ encode(__u8 { 29 }, bl);
+
+ auto p = bl.cbegin();
+
+ EXPECT_EQ(__u8 { 29 }, decode_legacy_macro_value(p));
+ EXPECT_TRUE(p.end());
+}
+
+TEST(EncodingCompatibility, MacroLegacyCompatLenRejectsNewerCompat)
+{
+ buffer::list bl;
+ encode_macro_header(bl, 2, 3, 0);
+
+ auto p = bl.cbegin();
+
+ EXPECT_THROW(decode_legacy_macro_value(p), buffer::malformed_input);
+}
+
+TEST(EncodingCompatibility, MacroLegacyCompatLen16SkipsOldVersionBytes)
+{
+ buffer::list bl;
+ encode(__u16 { 1 }, bl);
+ encode(__u8 { 31 }, bl);
+
+ auto p = bl.cbegin();
+
+ EXPECT_EQ(__u8 { 31 }, decode_legacy_macro_value_16(p));
+ EXPECT_TRUE(p.end());
+}
+
+TEST(EncodingCompatibility, MacroLegacyCompatLen32SkipsOldVersionBytes)
+{
+ buffer::list bl;
+ encode(__u32 { 1 }, bl);
+ encode(__u8 { 37 }, bl);
+
+ auto p = bl.cbegin();
+
+ EXPECT_EQ(__u8 { 37 }, decode_legacy_macro_value_32(p));
+ EXPECT_TRUE(p.end());
+}
+
+TEST(EncodingCompatibility, RepresentativeWireFormats)
+{
+ std::string string_wire;
+ append_compat_string(string_wire, "hello");
+ expect_compat_wire_format(std::string { "hello" }, string_wire);
+
+ std::string vector_u64_wire;
+ append_compat_le32(vector_u64_wire, 2);
+ append_compat_le64(vector_u64_wire, 11);
+ append_compat_le64(vector_u64_wire, 22);
+ expect_compat_wire_format(std::vector<uint64_t> { 11, 22 },
+ vector_u64_wire);
+
+ std::string vector_record_wire;
+ append_compat_le32(vector_record_wire, 2);
+ append_compat_record(vector_record_wire, alpha);
+ append_compat_record(vector_record_wire, bravo);
+ expect_compat_wire_format(std::vector<encoding_compat_record> { alpha, bravo },
+ vector_record_wire);
+
+ std::string array_record_wire;
+ append_compat_record(array_record_wire, alpha);
+ append_compat_record(array_record_wire, bravo);
+ expect_compat_wire_format(std::array<encoding_compat_record, 2> { alpha, bravo },
+ array_record_wire);
+
+ std::string shared_ptr_vector_wire;
+ append_compat_le32(shared_ptr_vector_wire, 5);
+ append_compat_record(shared_ptr_vector_wire, alpha);
+ append_compat_record(shared_ptr_vector_wire, encoding_compat_record {});
+ append_compat_record(shared_ptr_vector_wire, encoding_compat_record {});
+ append_compat_record(shared_ptr_vector_wire, bravo);
+ append_compat_record(shared_ptr_vector_wire, encoding_compat_record {});
+ expect_compat_wire_format(
+ std::vector<std::shared_ptr<encoding_compat_record>> {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ nullptr,
+ },
+ shared_ptr_vector_wire);
+
+ std::string shared_ptr_list_wire;
+ append_compat_le32(shared_ptr_list_wire, 5);
+ append_compat_record(shared_ptr_list_wire, alpha);
+ append_compat_record(shared_ptr_list_wire, encoding_compat_record {});
+ append_compat_record(shared_ptr_list_wire, encoding_compat_record {});
+ append_compat_record(shared_ptr_list_wire, bravo);
+ append_compat_record(shared_ptr_list_wire, encoding_compat_record {});
+ expect_compat_wire_format(
+ std::list<std::shared_ptr<encoding_compat_record>> {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ nullptr,
+ },
+ shared_ptr_list_wire);
+
+ expect_feature_compat_wire_format(
+ std::vector<std::shared_ptr<encoding_compat_record>> {
+ std::make_shared<encoding_compat_record>(alpha),
+ nullptr,
+ nullptr,
+ std::make_shared<encoding_compat_record>(bravo),
+ nullptr,
+ },
+ shared_ptr_vector_wire);
+}