Merge #16889: Add some general std::vector utility functions

7d8d3e6a2a Add tests for util/vector.h's Cat and Vector (Pieter Wuille)
e65e61c812 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 7d8d3e6a2a
  MarcoFalke:
    ACK 7d8d3e6a2a (enjoyed reading the tests, but did not compile)

Tree-SHA512: 92325f14e90d7e7d9d920421979aec22bb0d730e0291362b4326cccc76f9c2d865bec33a797c5c0201773468c3773cb50ce52c8eee4c1ec1a4d10db5cf2b9d2a
This commit is contained in:
MarcoFalke 2019-10-18 09:56:43 -04:00
commit 0ff7cd7d0c
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
7 changed files with 179 additions and 32 deletions

View file

@ -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 \

View file

@ -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. */

View file

@ -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));
}
}

View file

@ -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") {}
};

View file

@ -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()

View file

@ -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) {

51
src/util/vector.h Normal file
View file

@ -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