Skip to content

Commit faec591

Browse files
MarcoFalkesipa
andcommitted
Support for serialization parameters
The moved part can be reviewed with the git options --ignore-all-space --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space (Modified by Marco Falke) Co-authored-by: Pieter Wuille <[email protected]>
1 parent fac42e9 commit faec591

File tree

1 file changed

+124
-13
lines changed

1 file changed

+124
-13
lines changed

src/serialize.h

Lines changed: 124 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef BITCOIN_SERIALIZE_H
77
#define BITCOIN_SERIALIZE_H
88

9+
#include <attributes.h>
910
#include <compat/endian.h>
1011

1112
#include <algorithm>
@@ -194,28 +195,81 @@ const Out& AsBase(const In& x)
194195
template<typename Stream, typename Type, typename Operation> \
195196
static void SerializationOps(Type& obj, Stream& s, Operation ser_action)
196197

198+
/**
199+
* Variant of FORMATTER_METHODS that supports a declared parameter type.
200+
*
201+
* If a formatter has a declared parameter type, it must be invoked directly or
202+
* indirectly with a parameter of that type. This permits making serialization
203+
* depend on run-time context in a type-safe way.
204+
*
205+
* Example use:
206+
* struct BarParameter { bool fancy; ... };
207+
* struct Bar { ... };
208+
* struct FooFormatter {
209+
* FORMATTER_METHODS(Bar, obj, BarParameter, param) {
210+
* if (param.fancy) {
211+
* READWRITE(VARINT(obj.value));
212+
* } else {
213+
* READWRITE(obj.value);
214+
* }
215+
* }
216+
* };
217+
* which would then be invoked as
218+
* READWRITE(WithParams(BarParameter{...}, Using<FooFormatter>(obj.foo)))
219+
*
220+
* WithParams(parameter, obj) can be invoked anywhere in the call stack; it is
221+
* passed down recursively into all serialization code, until another
222+
* WithParams overrides it.
223+
*
224+
* Parameters will be implicitly converted where appropriate. This means that
225+
* "parent" serialization code can use a parameter that derives from, or is
226+
* convertible to, a "child" formatter's parameter type.
227+
*
228+
* Compilation will fail in any context where serialization is invoked but
229+
* no parameter of a type convertible to BarParameter is provided.
230+
*/
231+
#define FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj) \
232+
template <typename Stream> \
233+
static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, ActionSerialize{}, s.GetParams()); } \
234+
template <typename Stream> \
235+
static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, ActionUnserialize{}, s.GetParams()); } \
236+
template <typename Stream, typename Type, typename Operation> \
237+
static void SerializationOps(Type& obj, Stream& s, Operation ser_action, const paramcls& paramobj)
238+
239+
#define BASE_SERIALIZE_METHODS(cls) \
240+
template <typename Stream> \
241+
void Serialize(Stream& s) const \
242+
{ \
243+
static_assert(std::is_same<const cls&, decltype(*this)>::value, "Serialize type mismatch"); \
244+
Ser(s, *this); \
245+
} \
246+
template <typename Stream> \
247+
void Unserialize(Stream& s) \
248+
{ \
249+
static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch"); \
250+
Unser(s, *this); \
251+
}
252+
197253
/**
198254
* Implement the Serialize and Unserialize methods by delegating to a single templated
199255
* static method that takes the to-be-(de)serialized object as a parameter. This approach
200256
* has the advantage that the constness of the object becomes a template parameter, and
201257
* thus allows a single implementation that sees the object as const for serializing
202258
* and non-const for deserializing, without casts.
203259
*/
204-
#define SERIALIZE_METHODS(cls, obj) \
205-
template<typename Stream> \
206-
void Serialize(Stream& s) const \
207-
{ \
208-
static_assert(std::is_same<const cls&, decltype(*this)>::value, "Serialize type mismatch"); \
209-
Ser(s, *this); \
210-
} \
211-
template<typename Stream> \
212-
void Unserialize(Stream& s) \
213-
{ \
214-
static_assert(std::is_same<cls&, decltype(*this)>::value, "Unserialize type mismatch"); \
215-
Unser(s, *this); \
216-
} \
260+
#define SERIALIZE_METHODS(cls, obj) \
261+
BASE_SERIALIZE_METHODS(cls) \
217262
FORMATTER_METHODS(cls, obj)
218263

264+
/**
265+
* Variant of SERIALIZE_METHODS that supports a declared parameter type.
266+
*
267+
* See FORMATTER_METHODS_PARAMS for more information on parameters.
268+
*/
269+
#define SERIALIZE_METHODS_PARAMS(cls, obj, paramcls, paramobj) \
270+
BASE_SERIALIZE_METHODS(cls) \
271+
FORMATTER_METHODS_PARAMS(cls, obj, paramcls, paramobj)
272+
219273
// clang-format off
220274
#ifndef CHAR_EQUALS_INT8
221275
template <typename Stream> void Serialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t
@@ -1080,4 +1134,61 @@ size_t GetSerializeSizeMany(int nVersion, const T&... t)
10801134
return sc.size();
10811135
}
10821136

1137+
/** Wrapper that overrides the GetParams() function of a stream (and hides GetVersion/GetType). */
1138+
template <typename Params, typename SubStream>
1139+
class ParamsStream
1140+
{
1141+
const Params& m_params;
1142+
SubStream& m_substream; // private to avoid leaking version/type into serialization code that shouldn't see it
1143+
1144+
public:
1145+
ParamsStream(const Params& params LIFETIMEBOUND, SubStream& substream LIFETIMEBOUND) : m_params{params}, m_substream{substream} {}
1146+
template <typename U> ParamsStream& operator<<(const U& obj) { ::Serialize(*this, obj); return *this; }
1147+
template <typename U> ParamsStream& operator>>(U&& obj) { ::Unserialize(*this, obj); return *this; }
1148+
void write(Span<const std::byte> src) { m_substream.write(src); }
1149+
void read(Span<std::byte> dst) { m_substream.read(dst); }
1150+
void ignore(size_t num) { m_substream.ignore(num); }
1151+
bool eof() const { return m_substream.eof(); }
1152+
size_t size() const { return m_substream.size(); }
1153+
const Params& GetParams() const { return m_params; }
1154+
int GetVersion() = delete; // Deprecated with Params usage
1155+
int GetType() = delete; // Deprecated with Params usage
1156+
};
1157+
1158+
/** Wrapper that serializes objects with the specified parameters. */
1159+
template <typename Params, typename T>
1160+
class ParamsWrapper
1161+
{
1162+
static_assert(std::is_lvalue_reference<T>::value, "ParamsWrapper needs an lvalue reference type T");
1163+
const Params& m_params;
1164+
T m_object;
1165+
1166+
public:
1167+
explicit ParamsWrapper(const Params& params, T obj) : m_params{params}, m_object{obj} {}
1168+
1169+
template <typename Stream>
1170+
void Serialize(Stream& s) const
1171+
{
1172+
ParamsStream ss{m_params, s};
1173+
::Serialize(ss, m_object);
1174+
}
1175+
template <typename Stream>
1176+
void Unserialize(Stream& s)
1177+
{
1178+
ParamsStream ss{m_params, s};
1179+
::Unserialize(ss, m_object);
1180+
}
1181+
};
1182+
1183+
/**
1184+
* Return a wrapper around t that (de)serializes it with specified parameter params.
1185+
*
1186+
* See FORMATTER_METHODS_PARAMS for more information on serialization parameters.
1187+
*/
1188+
template <typename Params, typename T>
1189+
static auto WithParams(const Params& params, T&& t)
1190+
{
1191+
return ParamsWrapper<Params, T&>{params, t};
1192+
}
1193+
10831194
#endif // BITCOIN_SERIALIZE_H

0 commit comments

Comments
 (0)