diff --git a/src/Makefile.am b/src/Makefile.am index ab0c3d0f6a248..12d8bac346c80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -146,6 +146,7 @@ BITCOIN_CORE_H = \ net.h \ noui.h \ pow.h \ + prevector.h \ protocol.h \ pubkey.h \ random.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index dfc5705eb3d57..d38079f8f2aed 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -61,6 +61,7 @@ BITCOIN_TESTS =\ test/net_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ + test/prevector_tests.cpp \ test/random_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ diff --git a/src/coins.h b/src/coins.h index a22acd5319dc6..0fd3676d9ecfc 100644 --- a/src/coins.h +++ b/src/coins.h @@ -289,8 +289,7 @@ class CCoins size_t DynamicMemoryUsage() const { size_t ret = memusage::DynamicUsage(vout); for(const CTxOut &out : vout) { - const std::vector *script = &out.scriptPubKey; - ret += memusage::DynamicUsage(*script); + ret += memusage::DynamicUsage(*static_cast(&out.scriptPubKey)); } return ret; } diff --git a/src/hash.h b/src/hash.h index 68f5635fe1d19..173e7df081565 100644 --- a/src/hash.h +++ b/src/hash.h @@ -10,6 +10,7 @@ #include "crypto/ripemd160.h" #include "crypto/sha256.h" +#include "prevector.h" #include "serialize.h" #include "uint256.h" #include "version.h" @@ -265,6 +266,13 @@ inline uint160 Hash160(const std::vector& vch) return Hash160(vch.begin(), vch.end()); } +/** Compute the 160-bit hash of a vector. */ +template +inline uint160 Hash160(const prevector& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + /** A writer stream (for serialization) that computes a 256-bit hash. */ class CHashWriter { diff --git a/src/masternode-budget.h b/src/masternode-budget.h index 7873a56e19c1c..95aab0ea02238 100644 --- a/src/masternode-budget.h +++ b/src/masternode-budget.h @@ -298,7 +298,7 @@ class CTxBudgetPayment template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(payee); + READWRITE(*(CScriptBase*)(&payee)); READWRITE(nAmount); READWRITE(nProposalHash); } @@ -519,7 +519,7 @@ class CBudgetProposal ss << nBlockStart; ss << nBlockEnd; ss << nAmount; - ss << address; + ss << std::vector(address.begin(), address.end()); uint256 h1 = ss.GetHash(); return h1; @@ -537,7 +537,7 @@ class CBudgetProposal READWRITE(nBlockStart); READWRITE(nBlockEnd); READWRITE(nAmount); - READWRITE(address); + READWRITE(*(CScriptBase*)(&address)); READWRITE(nTime); READWRITE(nFeeTXHash); @@ -594,7 +594,7 @@ class CBudgetProposalBroadcast : public CBudgetProposal READWRITE(nBlockStart); READWRITE(nBlockEnd); READWRITE(nAmount); - READWRITE(address); + READWRITE(*(CScriptBase*)(&address)); READWRITE(nFeeTXHash); } }; diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 4ba22257bde62..f0cf6fe139c02 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -148,7 +148,7 @@ CMasternodePaymentDB::ReadResult CMasternodePaymentDB::Read(CMasternodePayments& uint256 CMasternodePaymentWinner::GetHash() const { CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - ss << payee; + ss << std::vector(payee.begin(), payee.end()); ss << nBlockHeight; ss << vinMasternode.prevout; return ss.GetHash(); diff --git a/src/masternode-payments.h b/src/masternode-payments.h index 6c053a978ce56..5970d6414cd50 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -79,7 +79,7 @@ class CMasternodePayee template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(scriptPubKey); + READWRITE(*(CScriptBase*)(&scriptPubKey)); READWRITE(nVotes); } }; @@ -200,7 +200,7 @@ class CMasternodePaymentWinner : public CSignedMessage { READWRITE(vinMasternode); READWRITE(nBlockHeight); - READWRITE(payee); + READWRITE(*(CScriptBase*)(&payee)); READWRITE(vchSig); try { diff --git a/src/memusage.h b/src/memusage.h index 9f7de9e2e165d..41717caecd48f 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -37,7 +37,9 @@ template static size_t DynamicUsage(const X& x); static inline size_t MallocUsage(size_t alloc) { // Measured on libc6 2.19 on Linux. - if (sizeof(void*) == 8) { + if (alloc == 0) { + return 0; + } else if (sizeof(void*) == 8) { return ((alloc + 31) >> 4) << 4; } else if (sizeof(void*) == 4) { return ((alloc + 15) >> 3) << 3; @@ -65,6 +67,12 @@ static inline size_t DynamicUsage(const std::vector& v) return MallocUsage(v.capacity() * sizeof(X)); } +template +static inline size_t DynamicUsage(const prevector& v) +{ + return MallocUsage(v.allocated_memory()); +} + template static inline size_t DynamicUsage(const std::set& s) { diff --git a/src/prevector.h b/src/prevector.h new file mode 100644 index 0000000000000..fa688cf886bce --- /dev/null +++ b/src/prevector.h @@ -0,0 +1,486 @@ +#ifndef _BITCOIN_PREVECTOR_H_ +#define _BITCOIN_PREVECTOR_H_ + +#include +#include +#include + +#include + +#pragma pack(push, 1) +/** Implements a drop-in replacement for std::vector which stores up to N + * elements directly (without heap allocation). The types Size and Diff are + * used to store element counts, and can be any unsigned + signed type. + * + * Storage layout is either: + * - Direct allocation: + * - Size _size: the number of used elements (between 0 and N) + * - T direct[N]: an array of N elements of type T + * (only the first _size are initialized). + * - Indirect allocation: + * - Size _size: the number of used elements plus N + 1 + * - Size capacity: the number of allocated elements + * - T* indirect: a pointer to an array of capacity elements of type T + * (only the first _size are initialized). + * + * The data type T must be movable by memmove/realloc(). Once we switch to C++, + * move constructors can be used instead. + */ +template +class prevector { +public: + typedef Size size_type; + typedef Diff difference_type; + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + class iterator { + T* ptr; + public: + typedef Diff difference_type; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef std::random_access_iterator_tag iterator_category; + iterator(T* ptr_) : ptr(ptr_) {} + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + T& operator[](size_type pos) { return ptr[pos]; } + const T& operator[](size_type pos) const { return ptr[pos]; } + iterator& operator++() { ptr++; return *this; } + iterator& operator--() { ptr--; return *this; } + iterator operator++(int) { iterator copy(*this); ++(*this); return copy; } + iterator operator--(int) { iterator copy(*this); --(*this); return copy; } + difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); } + iterator operator+(size_type n) { return iterator(ptr + n); } + iterator& operator+=(size_type n) { ptr += n; return *this; } + iterator operator-(size_type n) { return iterator(ptr - n); } + iterator& operator-=(size_type n) { ptr -= n; return *this; } + bool operator==(iterator x) const { return ptr == x.ptr; } + bool operator!=(iterator x) const { return ptr != x.ptr; } + bool operator>=(iterator x) const { return ptr >= x.ptr; } + bool operator<=(iterator x) const { return ptr <= x.ptr; } + bool operator>(iterator x) const { return ptr > x.ptr; } + bool operator<(iterator x) const { return ptr < x.ptr; } + }; + + class reverse_iterator { + T* ptr; + public: + typedef Diff difference_type; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + reverse_iterator(T* ptr_) : ptr(ptr_) {} + T& operator*() { return *ptr; } + const T& operator*() const { return *ptr; } + T* operator->() { return ptr; } + const T* operator->() const { return ptr; } + reverse_iterator& operator--() { ptr++; return *this; } + reverse_iterator& operator++() { ptr--; return *this; } + reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; } + reverse_iterator operator--(int) { reverse_iterator copy(*this); --(*this); return copy; } + bool operator==(reverse_iterator x) const { return ptr == x.ptr; } + bool operator!=(reverse_iterator x) const { return ptr != x.ptr; } + }; + + class const_iterator { + const T* ptr; + public: + typedef Diff difference_type; + typedef const T value_type; + typedef const T* pointer; + typedef const T& reference; + typedef std::random_access_iterator_tag iterator_category; + const_iterator(const T* ptr_) : ptr(ptr_) {} + const_iterator(iterator x) : ptr(&(*x)) {} + const T& operator*() const { return *ptr; } + const T* operator->() const { return ptr; } + const T& operator[](size_type pos) const { return ptr[pos]; } + const_iterator& operator++() { ptr++; return *this; } + const_iterator& operator--() { ptr--; return *this; } + const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; } + const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; } + difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); } + const_iterator operator+(size_type n) { return const_iterator(ptr + n); } + const_iterator& operator+=(size_type n) { ptr += n; return *this; } + const_iterator operator-(size_type n) { return const_iterator(ptr - n); } + const_iterator& operator-=(size_type n) { ptr -= n; return *this; } + bool operator==(const_iterator x) const { return ptr == x.ptr; } + bool operator!=(const_iterator x) const { return ptr != x.ptr; } + bool operator>=(const_iterator x) const { return ptr >= x.ptr; } + bool operator<=(const_iterator x) const { return ptr <= x.ptr; } + bool operator>(const_iterator x) const { return ptr > x.ptr; } + bool operator<(const_iterator x) const { return ptr < x.ptr; } + }; + + class const_reverse_iterator { + const T* ptr; + public: + typedef Diff difference_type; + typedef const T value_type; + typedef const T* pointer; + typedef const T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + const_reverse_iterator(T* ptr_) : ptr(ptr_) {} + const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {} + const T& operator*() const { return *ptr; } + const T* operator->() const { return ptr; } + const_reverse_iterator& operator--() { ptr++; return *this; } + const_reverse_iterator& operator++() { ptr--; return *this; } + const_reverse_iterator operator++(int) { const_reverse_iterator copy(*this); ++(*this); return copy; } + const_reverse_iterator operator--(int) { const_reverse_iterator copy(*this); --(*this); return copy; } + bool operator==(const_reverse_iterator x) const { return ptr == x.ptr; } + bool operator!=(const_reverse_iterator x) const { return ptr != x.ptr; } + }; + +private: + size_type _size; + union { + char direct[sizeof(T) * N]; + struct { + size_type capacity; + char* indirect; + }; + } _union; + + T* direct_ptr(difference_type pos) { return reinterpret_cast(_union.direct) + pos; } + const T* direct_ptr(difference_type pos) const { return reinterpret_cast(_union.direct) + pos; } + T* indirect_ptr(difference_type pos) { return reinterpret_cast(_union.indirect) + pos; } + const T* indirect_ptr(difference_type pos) const { return reinterpret_cast(_union.indirect) + pos; } + bool is_direct() const { return _size <= N; } + + void change_capacity(size_type new_capacity) { + if (new_capacity <= N) { + if (!is_direct()) { + T* indirect = indirect_ptr(0); + T* src = indirect; + T* dst = direct_ptr(0); + memcpy(dst, src, size() * sizeof(T)); + free(indirect); + _size -= N + 1; + } + } else { + if (!is_direct()) { + _union.indirect = static_cast(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity)); + _union.capacity = new_capacity; + } else { + char* new_indirect = static_cast(malloc(((size_t)sizeof(T)) * new_capacity)); + T* src = direct_ptr(0); + T* dst = reinterpret_cast(new_indirect); + memcpy(dst, src, size() * sizeof(T)); + _union.indirect = new_indirect; + _union.capacity = new_capacity; + _size += N + 1; + } + } + } + + T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + +public: + void assign(size_type n, const T& val) { + clear(); + if (capacity() < n) { + change_capacity(n); + } + while (size() < n) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(val); + } + } + + template + void assign(InputIterator first, InputIterator last) { + size_type n = last - first; + clear(); + if (capacity() < n) { + change_capacity(n); + } + while (first != last) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(*first); + ++first; + } + } + + prevector() : _size(0) {} + + explicit prevector(size_type n) : _size(0) { + resize(n); + } + + explicit prevector(size_type n, const T& val = T()) : _size(0) { + change_capacity(n); + while (size() < n) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(val); + } + } + + template + prevector(InputIterator first, InputIterator last) : _size(0) { + size_type n = last - first; + change_capacity(n); + while (first != last) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(*first); + ++first; + } + } + + prevector(const prevector& other) : _size(0) { + change_capacity(other.size()); + const_iterator it = other.begin(); + while (it != other.end()) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(*it); + ++it; + } + } + + prevector& operator=(const prevector& other) { + if (&other == this) { + return *this; + } + resize(0); + change_capacity(other.size()); + const_iterator it = other.begin(); + while (it != other.end()) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(*it); + ++it; + } + return *this; + } + + size_type size() const { + return is_direct() ? _size : _size - N - 1; + } + + bool empty() const { + return size() == 0; + } + + iterator begin() { return iterator(item_ptr(0)); } + const_iterator begin() const { return const_iterator(item_ptr(0)); } + iterator end() { return iterator(item_ptr(size())); } + const_iterator end() const { return const_iterator(item_ptr(size())); } + + reverse_iterator rbegin() { return reverse_iterator(item_ptr(size() - 1)); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); } + reverse_iterator rend() { return reverse_iterator(item_ptr(-1)); } + const_reverse_iterator rend() const { return const_reverse_iterator(item_ptr(-1)); } + + size_t capacity() const { + if (is_direct()) { + return N; + } else { + return _union.capacity; + } + } + + T& operator[](size_type pos) { + return *item_ptr(pos); + } + + const T& operator[](size_type pos) const { + return *item_ptr(pos); + } + + void resize(size_type new_size) { + while (size() > new_size) { + item_ptr(size() - 1)->~T(); + _size--; + } + if (new_size > capacity()) { + change_capacity(new_size); + } + while (size() < new_size) { + _size++; + new(static_cast(item_ptr(size() - 1))) T(); + } + } + + void reserve(size_type new_capacity) { + if (new_capacity > capacity()) { + change_capacity(new_capacity); + } + } + + void shrink_to_fit() { + change_capacity(size()); + } + + void clear() { + resize(0); + } + + iterator insert(iterator pos, const T& value) { + size_type p = pos - begin(); + size_type new_size = size() + 1; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T)); + _size++; + new(static_cast(item_ptr(p))) T(value); + return iterator(item_ptr(p)); + } + + void insert(iterator pos, size_type count, const T& value) { + size_type p = pos - begin(); + size_type new_size = size() + count; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + _size += count; + for (size_type i = 0; i < count; i++) { + new(static_cast(item_ptr(p + i))) T(value); + } + } + + template + void insert(iterator pos, InputIterator first, InputIterator last) { + size_type p = pos - begin(); + difference_type count = last - first; + size_type new_size = size() + count; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + _size += count; + while (first != last) { + new(static_cast(item_ptr(p))) T(*first); + ++p; + ++first; + } + } + + iterator erase(iterator pos) { + (*pos).~T(); + memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos)))); + _size--; + return pos; + } + + iterator erase(iterator first, iterator last) { + iterator p = first; + char* endp = (char*)&(*end()); + while (p != last) { + (*p).~T(); + _size--; + ++p; + } + memmove(&(*first), &(*last), endp - ((char*)(&(*last)))); + return first; + } + + void push_back(const T& value) { + size_type new_size = size() + 1; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + new(item_ptr(size())) T(value); + _size++; + } + + void pop_back() { + _size--; + } + + T& front() { + return *item_ptr(0); + } + + const T& front() const { + return *item_ptr(0); + } + + T& back() { + return *item_ptr(size() - 1); + } + + const T& back() const { + return *item_ptr(size() - 1); + } + + void swap(prevector& other) { + if (_size & other._size & 1) { + std::swap(_union.capacity, other._union.capacity); + std::swap(_union.indirect, other._union.indirect); + } else { + std::swap(_union, other._union); + } + std::swap(_size, other._size); + } + + ~prevector() { + clear(); + if (!is_direct()) { + free(_union.indirect); + _union.indirect = NULL; + } + } + + bool operator==(const prevector& other) const { + if (other.size() != size()) { + return false; + } + const_iterator b1 = begin(); + const_iterator b2 = other.begin(); + const_iterator e1 = end(); + while (b1 != e1) { + if ((*b1) != (*b2)) { + return false; + } + ++b1; + ++b2; + } + return true; + } + + bool operator!=(const prevector& other) const { + return !(*this == other); + } + + bool operator<(const prevector& other) const { + if (size() < other.size()) { + return true; + } + if (size() > other.size()) { + return false; + } + const_iterator b1 = begin(); + const_iterator b2 = other.begin(); + const_iterator e1 = end(); + while (b1 != e1) { + if ((*b1) < (*b2)) { + return true; + } + if ((*b2) < (*b1)) { + return false; + } + ++b1; + ++b2; + } + return false; + } + + size_t allocated_memory() const { + if (is_direct()) { + return 0; + } else { + return ((size_t)(sizeof(T))) * _union.capacity; + } + } +}; +#pragma pack(pop) + +#endif \ No newline at end of file diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 265cb0a4144ed..123efff1588b6 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -84,7 +84,7 @@ class CTxIn template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(prevout); - READWRITE(scriptSig); + READWRITE(*(CScriptBase*)(&scriptSig)); READWRITE(nSequence); } @@ -133,7 +133,7 @@ class CTxOut template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(nValue); - READWRITE(scriptPubKey); + READWRITE(*(CScriptBase*)(&scriptPubKey)); } void SetNull() diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index beb96054098a1..bed1706ea5338 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -8,6 +8,7 @@ #define BITCOIN_QT_TRANSACTIONRECORD_H #include "amount.h" +#include "script/script.h" #include "uint256.h" #include diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index b1c448d327a96..84324446653af 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1044,7 +1044,7 @@ class CTransactionSignatureSerializer { // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScript(), nType, nVersion); + ::Serialize(s, CScriptBase(), nType, nVersion); else SerializeScriptCode(s, nType, nVersion); // Serialize the nSequence diff --git a/src/script/script.cpp b/src/script/script.cpp index 160c4adb6c504..48018c3d347d9 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -246,26 +246,26 @@ bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); + (*this)[0] == OP_HASH160 && + (*this)[1] == 0x14 && + (*this)[22] == OP_EQUAL); } bool CScript::IsPayToColdStaking() const { // Extra-fast test for pay-to-cold-staking CScripts: return (this->size() == 51 && - this->at(2) == OP_ROT && - this->at(4) == OP_CHECKCOLDSTAKEVERIFY && - this->at(5) == 0x14 && - this->at(27) == 0x14 && - this->at(49) == OP_EQUALVERIFY && - this->at(50) == OP_CHECKSIG); + (*this)[2] == OP_ROT && + (*this)[4] == OP_CHECKCOLDSTAKEVERIFY && + (*this)[5] == 0x14 && + (*this)[27] == 0x14 && + (*this)[49] == OP_EQUALVERIFY && + (*this)[50] == OP_CHECKSIG); } bool CScript::StartsWithOpcode(const opcodetype opcode) const { - return (!this->empty() && this->at(0) == opcode); + return (!this->empty() && (*this)[0] == opcode); } bool CScript::IsZerocoinMint() const diff --git a/src/script/script.h b/src/script/script.h index e8fc830c56f12..986c0031079b2 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -19,6 +19,7 @@ #include #include "crypto/common.h" +#include "prevector.h" typedef std::vector valtype; @@ -362,8 +363,10 @@ class CScriptNum int64_t m_value; }; +typedef prevector<28, unsigned char> CScriptBase; + /** Serialized script, used inside transaction inputs and outputs */ -class CScript : public std::vector +class CScript : public CScriptBase { protected: CScript& push_int64(int64_t n) @@ -384,9 +387,10 @@ class CScript : public std::vector } public: CScript() { } - CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } - CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } - CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } + CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(std::vector::const_iterator pbegin, std::vector::const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } CScript& operator+=(const CScript& b) { @@ -641,7 +645,7 @@ class CScript : public std::vector void clear() { // The default std::vector::clear() does not release memory. - std::vector().swap(*this); + CScriptBase().swap(*this); } }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 7d643c37c7223..85362f4111d16 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -153,7 +153,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl bool fSolved = Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; // Append serialized subscript whether or not it is completely signed: - txin.scriptSig << static_cast(subscript); + txin.scriptSig << valtype(subscript.begin(), subscript.end()); if (!fSolved) return false; } diff --git a/src/serialize.h b/src/serialize.h index af82d494224cf..58da8b2bb0b34 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -22,9 +22,9 @@ #include "compat/endian.h" #include "libzerocoin/Denominations.h" #include "libzerocoin/SpendType.h" +#include "prevector.h" #include "sporkid.h" -class CScript; static const unsigned int MAX_SIZE = 0x02000000; @@ -48,6 +48,34 @@ inline T* NCONST_PTR(const T* val) return const_cast(val); } +/** + * Get begin pointer of vector (non-const version). + * @note These functions avoid the undefined case of indexing into an empty + * vector, as well as that of indexing after the end of the vector. + */ +template +inline typename V::value_type* begin_ptr(V& v) +{ + return v.empty() ? nullptr : &v[0]; +} +/** Get begin pointer of vector (const version) */ +template +inline const typename V::value_type* begin_ptr(const V& v) +{ + return v.empty() ? nullptr : &v[0]; +} +/** Get end pointer of vector (non-const version) */ +template +inline typename V::value_type* end_ptr(V& v) +{ + return v.empty() ? nullptr : (&v[0] + v.size()); +} +/** Get end pointer of vector (const version) */ +template +inline const typename V::value_type* end_ptr(const V& v) +{ + return v.empty() ? nullptr : (&v[0] + v.size()); +} /* * Lowest-level serialization and conversion. @@ -405,6 +433,12 @@ class CFlatData pbegin = (char*)v.data(); pend = (char*)(v.data() + v.size()); } + template + explicit CFlatData(prevector &v) + { + pbegin = (char*)begin_ptr(v); + pend = (char*)end_ptr(v); + } char* begin() { return pbegin; } const char* begin() const { return pbegin; } char* end() { return pend; } @@ -510,6 +544,20 @@ void Serialize(Stream& os, const std::basic_string& str, int, int = 0); template void Unserialize(Stream& is, std::basic_string& str, int, int = 0); +/** + * prevector + * prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. + */ +template unsigned int GetSerializeSize_impl(const prevector& v, int nType, int nVersion, const unsigned char&); +template unsigned int GetSerializeSize_impl(const prevector& v, int nType, int nVersion, const V&); +template inline unsigned int GetSerializeSize(const prevector& v, int nType, int nVersion); +template void Serialize_impl(Stream& os, const prevector& v, int nType, int nVersion, const unsigned char&); +template void Serialize_impl(Stream& os, const prevector& v, int nType, int nVersion, const V&); +template inline void Serialize(Stream& os, const prevector& v, int nType, int nVersion); +template void Unserialize_impl(Stream& is, prevector& v, int nType, int nVersion, const unsigned char&); +template void Unserialize_impl(Stream& is, prevector& v, int nType, int nVersion, const V&); +template inline void Unserialize(Stream& is, prevector& v, int nType, int nVersion); + /** * vector * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. @@ -533,15 +581,6 @@ void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion); -/** - * others derived from vector - */ -extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); -template -void Serialize(Stream& os, const CScript& v, int nType, int nVersion); -template -void Unserialize(Stream& is, CScript& v, int nType, int nVersion); - /** * pair */ @@ -625,6 +664,97 @@ void Unserialize(Stream& is, std::basic_string& str, int, int) } + +/** + * prevector + */ +template +unsigned int GetSerializeSize_impl(const prevector& v, int nType, int nVersion, const unsigned char&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const prevector& v, int nType, int nVersion, const V&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename prevector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const prevector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, T()); +} + + +template +void Serialize_impl(Stream& os, const prevector& v, int nType, int nVersion, const unsigned char&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const prevector& v, int nType, int nVersion, const V&) +{ + WriteCompactSize(os, v.size()); + for (typename prevector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const prevector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, T()); +} + + +template +void Unserialize_impl(Stream& is, prevector& v, int nType, int nVersion, const unsigned char&) +{ + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, prevector& v, int nType, int nVersion, const V&) +{ + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, prevector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, T()); +} + + + /** * vector */ @@ -712,26 +842,6 @@ inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersio } -/** - * others derived from vector - */ -inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) -{ - return GetSerializeSize((const std::vector&)v, nType, nVersion); -} - -template -void Serialize(Stream& os, const CScript& v, int nType, int nVersion) -{ - Serialize(os, (const std::vector&)v, nType, nVersion); -} - -template -void Unserialize(Stream& is, CScript& v, int nType, int nVersion) -{ - Unserialize(is, (std::vector&)v, nType, nVersion); -} - /** * pair diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index edaeced376521..4fc0e030f2232 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; - tx.vin[0].scriptSig = CScript() << (std::vector)script; + tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp new file mode 100644 index 0000000000000..aa3a449346b74 --- /dev/null +++ b/src/test/prevector_tests.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include "prevector.h" +#include "random.h" + +#include "serialize.h" +#include "streams.h" + +#include "test/test_pivx.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(PrevectorTests, TestingSetup) + +template +class prevector_tester { + typedef std::vector realtype; + realtype real_vector; + + typedef prevector pretype; + pretype pre_vector; + + typedef typename pretype::size_type Size; + + void test() { + const pretype& const_pre_vector = pre_vector; + BOOST_CHECK_EQUAL(real_vector.size(), pre_vector.size()); + BOOST_CHECK_EQUAL(real_vector.empty(), pre_vector.empty()); + for (Size s = 0; s < real_vector.size(); s++) { + BOOST_CHECK(real_vector[s] == pre_vector[s]); + BOOST_CHECK(&(pre_vector[s]) == &(pre_vector.begin()[s])); + BOOST_CHECK(&(pre_vector[s]) == &*(pre_vector.begin() + s)); + BOOST_CHECK(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size())); + } + // BOOST_CHECK(realtype(pre_vector) == real_vector); + BOOST_CHECK(pretype(real_vector.begin(), real_vector.end()) == pre_vector); + BOOST_CHECK(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector); + size_t pos = 0; + for (const T& v : pre_vector) { + BOOST_CHECK(v == real_vector[pos++]); + } + pos = 0; + for (const T& v : const_pre_vector) { + BOOST_CHECK(v == real_vector[pos++]); + } + + CDataStream ss1(SER_DISK, 0); + CDataStream ss2(SER_DISK, 0); + ss1 << real_vector; + ss2 << pre_vector; + BOOST_CHECK_EQUAL(ss1.size(), ss2.size()); + for (Size s = 0; s < ss1.size(); s++) { + BOOST_CHECK_EQUAL(ss1[s], ss2[s]); + } + } + +public: + void resize(Size s) { + real_vector.resize(s); + BOOST_CHECK_EQUAL(real_vector.size(), s); + pre_vector.resize(s); + BOOST_CHECK_EQUAL(pre_vector.size(), s); + test(); + } + + void reserve(Size s) { + real_vector.reserve(s); + BOOST_CHECK(real_vector.capacity() >= s); + pre_vector.reserve(s); + BOOST_CHECK(pre_vector.capacity() >= s); + test(); + } + + void insert(Size position, const T& value) { + real_vector.insert(real_vector.begin() + position, value); + pre_vector.insert(pre_vector.begin() + position, value); + test(); + } + + void insert(Size position, Size count, const T& value) { + real_vector.insert(real_vector.begin() + position, count, value); + pre_vector.insert(pre_vector.begin() + position, count, value); + test(); + } + + template + void insert_range(Size position, I first, I last) { + real_vector.insert(real_vector.begin() + position, first, last); + pre_vector.insert(pre_vector.begin() + position, first, last); + test(); + } + + void erase(Size position) { + real_vector.erase(real_vector.begin() + position); + pre_vector.erase(pre_vector.begin() + position); + test(); + } + + void erase(Size first, Size last) { + real_vector.erase(real_vector.begin() + first, real_vector.begin() + last); + pre_vector.erase(pre_vector.begin() + first, pre_vector.begin() + last); + test(); + } + + void update(Size pos, const T& value) { + real_vector[pos] = value; + pre_vector[pos] = value; + test(); + } + + void push_back(const T& value) { + real_vector.push_back(value); + pre_vector.push_back(value); + test(); + } + + void pop_back() { + real_vector.pop_back(); + pre_vector.pop_back(); + test(); + } + + void clear() { + real_vector.clear(); + pre_vector.clear(); + } + + void assign(Size n, const T& value) { + real_vector.assign(n, value); + pre_vector.assign(n, value); + } + + Size size() { + return real_vector.size(); + } + + Size capacity() { + return pre_vector.capacity(); + } + + void shrink_to_fit() { + pre_vector.shrink_to_fit(); + test(); + } +}; + +BOOST_AUTO_TEST_CASE(PrevectorTestInt) +{ + for (int j = 0; j < 64; j++) { + prevector_tester<8, int> test; + for (int i = 0; i < 2048; i++) { + int r = InsecureRand32(); + if ((r % 4) == 0) { + test.insert(InsecureRand32() % (test.size() + 1), InsecureRand32()); + } + if (test.size() > 0 && ((r >> 2) % 4) == 1) { + test.erase(InsecureRand32() % test.size()); + } + if (((r >> 4) % 8) == 2) { + int new_size = std::max(0, std::min(30, test.size() + (InsecureRand32() % 5) - 2)); + test.resize(new_size); + } + if (((r >> 7) % 8) == 3) { + test.insert(InsecureRand32() % (test.size() + 1), 1 + (InsecureRand32() % 2), InsecureRand32()); + } + if (((r >> 10) % 8) == 4) { + int del = std::min(test.size(), 1 + (InsecureRand32() % 2)); + int beg = InsecureRand32() % (test.size() + 1 - del); + test.erase(beg, beg + del); + } + if (((r >> 13) % 16) == 5) { + test.push_back(InsecureRand32()); + } + if (test.size() > 0 && ((r >> 17) % 16) == 6) { + test.pop_back(); + } + if (((r >> 21) % 32) == 7) { + int values[4]; + int num = 1 + (InsecureRand32() % 4); + for (int i = 0; i < num; i++) { + values[i] = InsecureRand32(); + } + test.insert_range(InsecureRand32() % (test.size() + 1), values, values + num); + } + if (((r >> 26) % 32) == 8) { + int del = std::min(test.size(), 1 + (InsecureRand32() % 4)); + int beg = InsecureRand32() % (test.size() + 1 - del); + test.erase(beg, beg + del); + } + r = InsecureRand32(); + if (r % 32 == 9) { + test.reserve(InsecureRand32() % 32); + } + if ((r >> 5) % 64 == 10) { + test.shrink_to_fit(); + } + if (test.size() > 0) { + test.update(InsecureRand32() % test.size(), InsecureRand32()); + } + if (((r >> 11) & 1024) == 11) { + test.clear(); + } + if (((r >> 21) & 512) == 12) { + test.assign(InsecureRand32() % 32, InsecureRand32()); + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 2c50c534e77a7..e1d3e61189745 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -25,7 +25,7 @@ static std::vector Serialize(const CScript& s) { - std::vector sSerialized(s); + std::vector sSerialized(s.begin(), s.end()); return sSerialized; } @@ -338,8 +338,8 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) // SignSignature doesn't know how to sign these. We're // not testing validating signatures, so just create // dummy signatures that DO include the correct P2SH scripts: - txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast >(oneAndTwo); - txTo.vin[4].scriptSig << static_cast >(fifteenSigops); + txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector(oneAndTwo.begin(), oneAndTwo.end()); + txTo.vin[4].scriptSig << std::vector(fifteenSigops.begin(), fifteenSigops.end()); BOOST_CHECK(::AreInputsStandard(txTo, coins)); // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] @@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd1.vin.resize(1); txToNonStd1.vin[0].prevout.n = 5; txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); - txToNonStd1.vin[0].scriptSig << static_cast >(sixteenSigops); + txToNonStd1.vin[0].scriptSig << std::vector(sixteenSigops.begin(), sixteenSigops.end()); BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U); @@ -373,7 +373,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd2.vin.resize(1); txToNonStd2.vin[0].prevout.n = 6; txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); - txToNonStd2.vin[0].scriptSig << static_cast >(twentySigops); + txToNonStd2.vin[0].scriptSig << std::vector(twentySigops.begin(), twentySigops.end()); BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index d89903e1e8f07..f054abbbc63e8 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -267,7 +267,7 @@ class TestBuilder TestBuilder& PushRedeem() { - DoPush(static_cast >(scriptPubKey)); + DoPush(std::vector(scriptPubKey.begin(), scriptPubKey.end())); return *this; } @@ -877,7 +877,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: - scriptSigCopy = CScript() << OP_0 << static_cast >(pkSingle); + scriptSigCopy = CScript() << OP_0 << std::vector(pkSingle.begin(), pkSingle.end()); combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); BOOST_CHECK(combined == scriptSig); combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 03504fa708dae..fb7912386b388 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -19,7 +19,7 @@ static std::vector Serialize(const CScript& s) { - std::vector sSerialized(s); + std::vector sSerialized(s.begin(), s.end()); return sSerialized; } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index a9832b2f10627..fae767532afd7 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -113,31 +113,31 @@ bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); + return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript& dest) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("watchs"), dest), '1'); + return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript& dest) { nWalletDBUpdated++; - return Erase(std::make_pair(std::string("watchs"), dest)); + return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteMultiSig(const CScript& dest) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("multisig"), dest), '1'); + return Write(std::make_pair(std::string("multisig"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseMultiSig(const CScript& dest) { nWalletDBUpdated++; - return Erase(std::make_pair(std::string("multisig"), dest)); + return Erase(std::make_pair(std::string("multisig"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) @@ -502,7 +502,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW } } else if (strType == "watchs") { CScript script; - ssKey >> script; + ssKey >> *(CScriptBase*)(&script); char fYes; ssValue >> fYes; if (fYes == '1') @@ -513,7 +513,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW pwallet->nTimeFirstKey = 1; } else if (strType == "multisig") { CScript script; - ssKey >> script; + ssKey >> *(CScriptBase*)(&script); char fYes; ssValue >> fYes; if (fYes == '1') @@ -637,7 +637,7 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CW uint160 hash; ssKey >> hash; CScript script; - ssValue >> script; + ssValue >> *(CScriptBase*)(&script); if (!pwallet->LoadCScript(script)) { strErr = "Error reading wallet database: LoadCScript failed"; return false;