Skip to content

Commit f138422

Browse files
committed
Merge bitcoin/bitcoin#28203: refactor: serialization simplifications
f054bd0 refactor: use "if constexpr" in std::vector's Unserialize() (Martin Leitner-Ankerl) 088caa6 refactor: use "if constexpr" in std::vector's Serialize() (Martin Leitner-Ankerl) 0fafaca refactor: use "if constexpr" in prevector's Unserialize() (Martin Leitner-Ankerl) c8839ec refactor: use "if constexpr" in prevector's Serialize() (Martin Leitner-Ankerl) 1403d18 refactor: use fold expressions instead of recursive calls in UnserializeMany() (Martin Leitner-Ankerl) bd08a00 refactor: use fold expressions instead of recursive calls in SerializeMany() (Martin Leitner-Ankerl) Pull request description: This simplifies the serialization code a bit and should also make it a bit faster. * use fold expressions instead of recursive calls. This simplifies the code, makes it most likely faster because it reduces the number of function calls, and compiles faster because there are fewer template instantiations. * use `if constexpr` instead of unnecessarily creating a temporary object only to call the right overload. This is used for `std::vector` and `prevector` serialization. ACKs for top commit: MarcoFalke: only change is to add a missing `&`. lgtm, re-ACK f054bd0 📦 jonatack: ACK f054bd0 sipa: utACK f054bd0 john-moffett: ACK f054bd0 Tree-SHA512: 0417bf2d6be486c581732297945449211fc3481bac82964e27628b38ef55a47dfa58d730148aeaf1b19fa8eb1076489cc646ceebb178162a9afa59034601501d
2 parents a4ca497 + f054bd0 commit f138422

File tree

1 file changed

+63
-124
lines changed

1 file changed

+63
-124
lines changed

src/serialize.h

Lines changed: 63 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -642,23 +642,14 @@ template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_st
642642
* prevector
643643
* prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
644644
*/
645-
template<typename Stream, unsigned int N, typename T> void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&);
646-
template<typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&);
647645
template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v);
648-
template<typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&);
649-
template<typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&);
650646
template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v);
651647

652648
/**
653649
* vector
654650
* vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
655651
*/
656-
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&);
657-
template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&);
658-
template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&);
659652
template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v);
660-
template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&);
661-
template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&);
662653
template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v);
663654

664655
/**
@@ -751,122 +742,82 @@ void Unserialize(Stream& is, std::basic_string<C>& str)
751742
/**
752743
* prevector
753744
*/
754-
template<typename Stream, unsigned int N, typename T>
755-
void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&)
756-
{
757-
WriteCompactSize(os, v.size());
758-
if (!v.empty())
759-
os.write(MakeByteSpan(v));
760-
}
761-
762-
template<typename Stream, unsigned int N, typename T, typename V>
763-
void Serialize_impl(Stream& os, const prevector<N, T>& v, const V&)
764-
{
765-
Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
766-
}
767-
768-
template<typename Stream, unsigned int N, typename T>
769-
inline void Serialize(Stream& os, const prevector<N, T>& v)
770-
{
771-
Serialize_impl(os, v, T());
772-
}
773-
774-
775-
template<typename Stream, unsigned int N, typename T>
776-
void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
777-
{
778-
// Limit size per read so bogus size value won't cause out of memory
779-
v.clear();
780-
unsigned int nSize = ReadCompactSize(is);
781-
unsigned int i = 0;
782-
while (i < nSize)
783-
{
784-
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
785-
v.resize_uninitialized(i + blk);
786-
is.read(AsWritableBytes(Span{&v[i], blk}));
787-
i += blk;
745+
template <typename Stream, unsigned int N, typename T>
746+
void Serialize(Stream& os, const prevector<N, T>& v)
747+
{
748+
if constexpr (std::is_same_v<T, unsigned char>) {
749+
WriteCompactSize(os, v.size());
750+
if (!v.empty())
751+
os.write(MakeByteSpan(v));
752+
} else {
753+
Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
788754
}
789755
}
790756

791-
template<typename Stream, unsigned int N, typename T, typename V>
792-
void Unserialize_impl(Stream& is, prevector<N, T>& v, const V&)
793-
{
794-
Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
795-
}
796757

797-
template<typename Stream, unsigned int N, typename T>
798-
inline void Unserialize(Stream& is, prevector<N, T>& v)
758+
template <typename Stream, unsigned int N, typename T>
759+
void Unserialize(Stream& is, prevector<N, T>& v)
799760
{
800-
Unserialize_impl(is, v, T());
761+
if constexpr (std::is_same_v<T, unsigned char>) {
762+
// Limit size per read so bogus size value won't cause out of memory
763+
v.clear();
764+
unsigned int nSize = ReadCompactSize(is);
765+
unsigned int i = 0;
766+
while (i < nSize) {
767+
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
768+
v.resize_uninitialized(i + blk);
769+
is.read(AsWritableBytes(Span{&v[i], blk}));
770+
i += blk;
771+
}
772+
} else {
773+
Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
774+
}
801775
}
802776

803777

804-
805778
/**
806779
* vector
807780
*/
808-
template<typename Stream, typename T, typename A>
809-
void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&)
810-
{
811-
WriteCompactSize(os, v.size());
812-
if (!v.empty())
813-
os.write(MakeByteSpan(v));
814-
}
815-
816-
template<typename Stream, typename T, typename A>
817-
void Serialize_impl(Stream& os, const std::vector<T, A>& v, const bool&)
818-
{
819-
// A special case for std::vector<bool>, as dereferencing
820-
// std::vector<bool>::const_iterator does not result in a const bool&
821-
// due to std::vector's special casing for bool arguments.
822-
WriteCompactSize(os, v.size());
823-
for (bool elem : v) {
824-
::Serialize(os, elem);
781+
template <typename Stream, typename T, typename A>
782+
void Serialize(Stream& os, const std::vector<T, A>& v)
783+
{
784+
if constexpr (std::is_same_v<T, unsigned char>) {
785+
WriteCompactSize(os, v.size());
786+
if (!v.empty())
787+
os.write(MakeByteSpan(v));
788+
} else if constexpr (std::is_same_v<T, bool>) {
789+
// A special case for std::vector<bool>, as dereferencing
790+
// std::vector<bool>::const_iterator does not result in a const bool&
791+
// due to std::vector's special casing for bool arguments.
792+
WriteCompactSize(os, v.size());
793+
for (bool elem : v) {
794+
::Serialize(os, elem);
795+
}
796+
} else {
797+
Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
825798
}
826799
}
827800

828-
template<typename Stream, typename T, typename A, typename V>
829-
void Serialize_impl(Stream& os, const std::vector<T, A>& v, const V&)
830-
{
831-
Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
832-
}
833801

834-
template<typename Stream, typename T, typename A>
835-
inline void Serialize(Stream& os, const std::vector<T, A>& v)
802+
template <typename Stream, typename T, typename A>
803+
void Unserialize(Stream& is, std::vector<T, A>& v)
836804
{
837-
Serialize_impl(os, v, T());
838-
}
839-
840-
841-
template<typename Stream, typename T, typename A>
842-
void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&)
843-
{
844-
// Limit size per read so bogus size value won't cause out of memory
845-
v.clear();
846-
unsigned int nSize = ReadCompactSize(is);
847-
unsigned int i = 0;
848-
while (i < nSize)
849-
{
850-
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
851-
v.resize(i + blk);
852-
is.read(AsWritableBytes(Span{&v[i], blk}));
853-
i += blk;
805+
if constexpr (std::is_same_v<T, unsigned char>) {
806+
// Limit size per read so bogus size value won't cause out of memory
807+
v.clear();
808+
unsigned int nSize = ReadCompactSize(is);
809+
unsigned int i = 0;
810+
while (i < nSize) {
811+
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
812+
v.resize(i + blk);
813+
is.read(AsWritableBytes(Span{&v[i], blk}));
814+
i += blk;
815+
}
816+
} else {
817+
Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
854818
}
855819
}
856820

857-
template<typename Stream, typename T, typename A, typename V>
858-
void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&)
859-
{
860-
Unserialize(is, Using<VectorFormatter<DefaultFormatter>>(v));
861-
}
862-
863-
template<typename Stream, typename T, typename A>
864-
inline void Unserialize(Stream& is, std::vector<T, A>& v)
865-
{
866-
Unserialize_impl(is, v, T());
867-
}
868-
869-
870821

871822
/**
872823
* pair
@@ -1039,28 +990,16 @@ class CSizeComputer
1039990
int GetVersion() const { return nVersion; }
1040991
};
1041992

1042-
template<typename Stream>
1043-
void SerializeMany(Stream& s)
1044-
{
1045-
}
1046-
1047-
template<typename Stream, typename Arg, typename... Args>
1048-
void SerializeMany(Stream& s, const Arg& arg, const Args&... args)
1049-
{
1050-
::Serialize(s, arg);
1051-
::SerializeMany(s, args...);
1052-
}
1053-
1054-
template<typename Stream>
1055-
inline void UnserializeMany(Stream& s)
993+
template <typename Stream, typename... Args>
994+
void SerializeMany(Stream& s, const Args&... args)
1056995
{
996+
(::Serialize(s, args), ...);
1057997
}
1058998

1059-
template<typename Stream, typename Arg, typename... Args>
1060-
inline void UnserializeMany(Stream& s, Arg&& arg, Args&&... args)
999+
template <typename Stream, typename... Args>
1000+
inline void UnserializeMany(Stream& s, Args&&... args)
10611001
{
1062-
::Unserialize(s, arg);
1063-
::UnserializeMany(s, args...);
1002+
(::Unserialize(s, args), ...);
10641003
}
10651004

10661005
template<typename Stream, typename... Args>

0 commit comments

Comments
 (0)