aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2019-10-18 09:56:43 -0400
committerMarcoFalke <falke.marco@gmail.com>2019-10-18 09:57:00 -0400
commit0ff7cd7d0c074448db636bcc73e7879b7e4b21ec (patch)
tree8c6513a72dbdd555ce5350036b2e26a2a2e265ae
parentf2a094884d987465437a6300daa84de6f39c2322 (diff)
parent7d8d3e6a2ad827fa916e3909a18dedb9f7fdce43 (diff)
downloadbitcoin-0ff7cd7d0c074448db636bcc73e7879b7e4b21ec.tar.xz
Merge #16889: Add some general std::vector utility functions
7d8d3e6a2ad827fa916e3909a18dedb9f7fdce43 Add tests for util/vector.h's Cat and Vector (Pieter Wuille) e65e61c812df90a56e3ce4a8e76c4b746766f387 Add some general std::vector utility functions (Pieter Wuille) Pull request description: This is another general improvement extracted from #16800 . Two functions are added are: * Vector(arg1,arg2,arg3,...) constructs a vector with the specified arguments as elements. The vector's type is derived from the arguments. If some of the arguments are rvalue references, they will be moved into place rather than copied (which can't be achieved using list initialization). * Cat(vector1,vector2) returns a concatenation of the two vectors, efficiently moving elements when relevant. Vector generalizes (and replaces) the `Singleton` function in src/descriptor.cpp, and `Cat` replaces the function in bech32.cpp ACKs for top commit: laanwj: ACK 7d8d3e6a2ad827fa916e3909a18dedb9f7fdce43 MarcoFalke: ACK 7d8d3e6a2ad827fa916e3909a18dedb9f7fdce43 (enjoyed reading the tests, but did not compile) Tree-SHA512: 92325f14e90d7e7d9d920421979aec22bb0d730e0291362b4326cccc76f9c2d865bec33a797c5c0201773468c3773cb50ce52c8eee4c1ec1a4d10db5cf2b9d2a
-rw-r--r--src/Makefile.am1
-rw-r--r--src/bech32.cpp8
-rw-r--r--src/outputtype.cpp6
-rw-r--r--src/script/descriptor.cpp36
-rw-r--r--src/test/util_tests.cpp106
-rw-r--r--src/txdb.cpp3
-rw-r--r--src/util/vector.h51
7 files changed, 179 insertions, 32 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index eec84122ae..d50524a8ae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -220,6 +220,7 @@ BITCOIN_CORE_H = \
util/translation.h \
util/url.h \
util/validation.h \
+ util/vector.h \
validation.h \
validationinterface.h \
versionbits.h \
diff --git a/src/bech32.cpp b/src/bech32.cpp
index 4c966350b4..1e0471f110 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
+#include <util/vector.h>
#include <assert.h>
@@ -26,13 +27,6 @@ const int8_t CHARSET_REV[128] = {
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
};
-/** Concatenate two byte arrays. */
-data Cat(data x, const data& y)
-{
- x.insert(x.end(), y.begin(), y.end());
- return x;
-}
-
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
* bits correspond to earlier values. */
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index bcaa05f4b6..5cc43898a7 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -10,6 +10,7 @@
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
+#include <util/vector.h>
#include <assert.h>
#include <string>
@@ -65,12 +66,13 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
{
PKHash keyid(key);
+ CTxDestination p2pkh{keyid};
if (key.IsCompressed()) {
CTxDestination segwit = WitnessV0KeyHash(keyid);
CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
- return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
+ return Vector(std::move(p2pkh), std::move(p2sh), std::move(segwit));
} else {
- return std::vector<CTxDestination>{std::move(keyid)};
+ return Vector(std::move(p2pkh));
}
}
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index ed1bd4cda9..536807e1d8 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -14,6 +14,7 @@
#include <util/spanparsing.h>
#include <util/system.h>
#include <util/strencodings.h>
+#include <util/vector.h>
#include <memory>
#include <string>
@@ -501,22 +502,13 @@ public:
}
};
-/** Construct a vector with one element, which is moved into it. */
-template<typename T>
-std::vector<T> Singleton(T elem)
-{
- std::vector<T> ret;
- ret.emplace_back(std::move(elem));
- return ret;
-}
-
/** A parsed addr(A) descriptor. */
class AddressDescriptor final : public DescriptorImpl
{
const CTxDestination m_destination;
protected:
std::string ToStringExtra() const override { return EncodeDestination(m_destination); }
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(m_destination)); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(m_destination)); }
public:
AddressDescriptor(CTxDestination destination) : DescriptorImpl({}, {}, "addr"), m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
@@ -528,7 +520,7 @@ class RawDescriptor final : public DescriptorImpl
const CScript m_script;
protected:
std::string ToStringExtra() const override { return HexStr(m_script.begin(), m_script.end()); }
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Singleton(m_script); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); }
public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
@@ -538,9 +530,9 @@ public:
class PKDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForRawPubKey(keys[0])); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); }
public:
- PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pk") {}
+ PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pk") {}
};
/** A parsed pkh(P) descriptor. */
@@ -551,10 +543,10 @@ protected:
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
- return Singleton(GetScriptForDestination(PKHash(id)));
+ return Vector(GetScriptForDestination(PKHash(id)));
}
public:
- PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
+ PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "pkh") {}
};
/** A parsed wpkh(P) descriptor. */
@@ -565,10 +557,10 @@ protected:
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
- return Singleton(GetScriptForDestination(WitnessV0KeyHash(id)));
+ return Vector(GetScriptForDestination(WitnessV0KeyHash(id)));
}
public:
- WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "wpkh") {}
+ WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "wpkh") {}
};
/** A parsed combo(P) descriptor. */
@@ -591,7 +583,7 @@ protected:
return ret;
}
public:
- ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "combo") {}
+ ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), {}, "combo") {}
};
/** A parsed multi(...) or sortedmulti(...) descriptor */
@@ -605,9 +597,9 @@ protected:
if (m_sorted) {
std::vector<CPubKey> sorted_keys(keys);
std::sort(sorted_keys.begin(), sorted_keys.end());
- return Singleton(GetScriptForMultisig(m_threshold, sorted_keys));
+ return Vector(GetScriptForMultisig(m_threshold, sorted_keys));
}
- return Singleton(GetScriptForMultisig(m_threshold, keys));
+ return Vector(GetScriptForMultisig(m_threshold, keys));
}
public:
MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), {}, sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {}
@@ -617,7 +609,7 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(ScriptHash(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(ScriptHash(*script))); }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
};
@@ -626,7 +618,7 @@ public:
class WSHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Vector(GetScriptForDestination(WitnessV0ScriptHash(*script))); }
public:
WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {}
};
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 3995a3c732..02303d0f65 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -13,6 +13,7 @@
#include <util/string.h>
#include <util/time.h>
#include <util/spanparsing.h>
+#include <util/vector.h>
#include <stdint.h>
#include <thread>
@@ -1714,4 +1715,109 @@ BOOST_AUTO_TEST_CASE(test_LogEscapeMessage)
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage(NUL), R"(O\x00O)");
}
+namespace {
+
+struct Tracker
+{
+ //! Points to the original object (possibly itself) we moved/copied from
+ const Tracker* origin;
+ //! How many copies where involved between the original object and this one (moves are not counted)
+ int copies;
+
+ Tracker() noexcept : origin(this), copies(0) {}
+ Tracker(const Tracker& t) noexcept : origin(t.origin), copies(t.copies + 1) {}
+ Tracker(Tracker&& t) noexcept : origin(t.origin), copies(t.copies) {}
+ Tracker& operator=(const Tracker& t) noexcept
+ {
+ origin = t.origin;
+ copies = t.copies + 1;
+ return *this;
+ }
+ Tracker& operator=(Tracker&& t) noexcept
+ {
+ origin = t.origin;
+ copies = t.copies;
+ return *this;
+ }
+};
+
+}
+
+BOOST_AUTO_TEST_CASE(test_tracked_vector)
+{
+ Tracker t1;
+ Tracker t2;
+ Tracker t3;
+
+ BOOST_CHECK(t1.origin == &t1);
+ BOOST_CHECK(t2.origin == &t2);
+ BOOST_CHECK(t3.origin == &t3);
+
+ auto v1 = Vector(t1);
+ BOOST_CHECK_EQUAL(v1.size(), 1);
+ BOOST_CHECK(v1[0].origin == &t1);
+ BOOST_CHECK_EQUAL(v1[0].copies, 1);
+
+ auto v2 = Vector(std::move(t2));
+ BOOST_CHECK_EQUAL(v2.size(), 1);
+ BOOST_CHECK(v2[0].origin == &t2);
+ BOOST_CHECK_EQUAL(v2[0].copies, 0);
+
+ auto v3 = Vector(t1, std::move(t2));
+ BOOST_CHECK_EQUAL(v3.size(), 2);
+ BOOST_CHECK(v3[0].origin == &t1);
+ BOOST_CHECK(v3[1].origin == &t2);
+ BOOST_CHECK_EQUAL(v3[0].copies, 1);
+ BOOST_CHECK_EQUAL(v3[1].copies, 0);
+
+ auto v4 = Vector(std::move(v3[0]), v3[1], std::move(t3));
+ BOOST_CHECK_EQUAL(v4.size(), 3);
+ BOOST_CHECK(v4[0].origin == &t1);
+ BOOST_CHECK(v4[1].origin == &t2);
+ BOOST_CHECK(v4[2].origin == &t3);
+ BOOST_CHECK_EQUAL(v4[0].copies, 1);
+ BOOST_CHECK_EQUAL(v4[1].copies, 1);
+ BOOST_CHECK_EQUAL(v4[2].copies, 0);
+
+ auto v5 = Cat(v1, v4);
+ BOOST_CHECK_EQUAL(v5.size(), 4);
+ BOOST_CHECK(v5[0].origin == &t1);
+ BOOST_CHECK(v5[1].origin == &t1);
+ BOOST_CHECK(v5[2].origin == &t2);
+ BOOST_CHECK(v5[3].origin == &t3);
+ BOOST_CHECK_EQUAL(v5[0].copies, 2);
+ BOOST_CHECK_EQUAL(v5[1].copies, 2);
+ BOOST_CHECK_EQUAL(v5[2].copies, 2);
+ BOOST_CHECK_EQUAL(v5[3].copies, 1);
+
+ auto v6 = Cat(std::move(v1), v3);
+ BOOST_CHECK_EQUAL(v6.size(), 3);
+ BOOST_CHECK(v6[0].origin == &t1);
+ BOOST_CHECK(v6[1].origin == &t1);
+ BOOST_CHECK(v6[2].origin == &t2);
+ BOOST_CHECK_EQUAL(v6[0].copies, 1);
+ BOOST_CHECK_EQUAL(v6[1].copies, 2);
+ BOOST_CHECK_EQUAL(v6[2].copies, 1);
+
+ auto v7 = Cat(v2, std::move(v4));
+ BOOST_CHECK_EQUAL(v7.size(), 4);
+ BOOST_CHECK(v7[0].origin == &t2);
+ BOOST_CHECK(v7[1].origin == &t1);
+ BOOST_CHECK(v7[2].origin == &t2);
+ BOOST_CHECK(v7[3].origin == &t3);
+ BOOST_CHECK_EQUAL(v7[0].copies, 1);
+ BOOST_CHECK_EQUAL(v7[1].copies, 1);
+ BOOST_CHECK_EQUAL(v7[2].copies, 1);
+ BOOST_CHECK_EQUAL(v7[3].copies, 0);
+
+ auto v8 = Cat(std::move(v2), std::move(v3));
+ BOOST_CHECK_EQUAL(v8.size(), 3);
+ BOOST_CHECK(v8[0].origin == &t2);
+ BOOST_CHECK(v8[1].origin == &t1);
+ BOOST_CHECK(v8[2].origin == &t2);
+ BOOST_CHECK_EQUAL(v8[0].copies, 0);
+ BOOST_CHECK_EQUAL(v8[1].copies, 1);
+ BOOST_CHECK_EQUAL(v8[2].copies, 0);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 536bfee901..a7eb5f9f67 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -12,6 +12,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/translation.h>
+#include <util/vector.h>
#include <stdint.h>
@@ -102,7 +103,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
// A vector is used for future extensibility, as we may want to support
// interrupting after partial writes from multiple independent reorgs.
batch.Erase(DB_BEST_BLOCK);
- batch.Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip});
+ batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
diff --git a/src/util/vector.h b/src/util/vector.h
new file mode 100644
index 0000000000..dab65ded2a
--- /dev/null
+++ b/src/util/vector.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_VECTOR_H
+#define BITCOIN_UTIL_VECTOR_H
+
+#include <initializer_list>
+#include <type_traits>
+#include <vector>
+
+/** Construct a vector with the specified elements.
+ *
+ * This is preferable over the list initializing constructor of std::vector:
+ * - It automatically infers the element type from its arguments.
+ * - If any arguments are rvalue references, they will be moved into the vector
+ * (list initialization always copies).
+ */
+template<typename... Args>
+inline std::vector<typename std::common_type<Args...>::type> Vector(Args&&... args)
+{
+ std::vector<typename std::common_type<Args...>::type> ret;
+ ret.reserve(sizeof...(args));
+ // The line below uses the trick from https://www.experts-exchange.com/articles/32502/None-recursive-variadic-templates-with-std-initializer-list.html
+ (void)std::initializer_list<int>{(ret.emplace_back(std::forward<Args>(args)), 0)...};
+ return ret;
+}
+
+/** Concatenate two vectors, moving elements. */
+template<typename V>
+inline V Cat(V v1, V&& v2)
+{
+ v1.reserve(v1.size() + v2.size());
+ for (auto& arg : v2) {
+ v1.push_back(std::move(arg));
+ }
+ return v1;
+}
+
+/** Concatenate two vectors. */
+template<typename V>
+inline V Cat(V v1, const V& v2)
+{
+ v1.reserve(v1.size() + v2.size());
+ for (const auto& arg : v2) {
+ v1.push_back(arg);
+ }
+ return v1;
+}
+
+#endif // BITCOIN_UTIL_VECTOR_H