From d1ec81e6061b463ef2474bb8ba729765362a795b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 3 Jan 2014 01:13:52 +0000 Subject: [PATCH] Code cleanups. --- Common.h | 43 ++++ PatriciaTree.cpp | 473 ++++++++++++++++++++++++++++++++++++++++++-- PatriciaTree.h | 501 ++--------------------------------------------- 3 files changed, 516 insertions(+), 501 deletions(-) diff --git a/Common.h b/Common.h index bf57cd557..d5b6d7146 100644 --- a/Common.h +++ b/Common.h @@ -31,4 +31,47 @@ template inline std::string asHex(_T const& _data, int _w = 2) return ret.str(); } +template void trimFront(_T& _t, uint _elements) +{ + memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); + _t.resize(_t.size() - _elements); +} + +template void pushFront(_T& _t, _U _e) +{ + _t.push_back(_e); + memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); + _t[0] = _e; +} + +inline bytes toHex(std::string const& _s) +{ + std::vector ret; + ret.reserve(_s.size() * 2); + for (auto i: _s) + { + ret.push_back(i / 16); + ret.push_back(i % 16); + } + return ret; +} + +inline std::string toBigEndianString(u256 _val) +{ + std::string ret; + ret.resize(32); + for (int i = 0; i <32; ++i, _val >>= 8) + ret[31 - i] = (char)(uint8_t)_val; + return ret; +} + +template uint commonPrefix(_T const& _t, _U const& _u) +{ + uint s = std::min(_t.size(), _u.size()); + for (uint i = 0;; ++i) + if (i == s || _t[i] != _u[i]) + return i; + return s; +} + } diff --git a/PatriciaTree.cpp b/PatriciaTree.cpp index fdd3c5b20..dd0e79317 100644 --- a/PatriciaTree.cpp +++ b/PatriciaTree.cpp @@ -3,27 +3,302 @@ using namespace std; using namespace eth; -bool eth::g_hashDebug = false; +namespace eth +{ + +#define ENABLE_DEBUG_PRINT 0 + +#if ENABLE_DEBUG_PRINT +bool g_hashDebug = false; +#endif /* -PatriciaTree::PatriciaTree(RLP const& _data) + * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1 + * [0,0,1,2,3,4,5] 0x10012345 + * [0,1,2,3,4,5] 0x00012345 + * [1,2,3,4,5] 0x112345 + * [0,0,1,2,3,4] 0x00001234 + * [0,1,2,3,4] 0x101234 + * [1,2,3,4] 0x001234 + * [0,0,1,2,3,4,5,T] 0x30012345 + * [0,0,1,2,3,4,T] 0x20001234 + * [0,1,2,3,4,5,T] 0x20012345 + * [1,2,3,4,5,T] 0x312345 + * [1,2,3,4,T] 0x201234 + */ + +std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated, int _begin, int _end) { - // Make tree based on _data - assert(_data.isList()); - if (_data.isEmpty()) + uint begin = _begin; + uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end; + bool termed = _terminated; + bool odd = (end - begin) % 2; + + std::string ret(1, ((termed ? 2 : 0) | (odd ? 1 : 0)) * 16); + if (odd) { - // NULL node. + ret[0] |= _hexVector[begin]; + ++begin; } - else if (_data.isList() && _data.itemCount() == 2) + for (uint i = begin; i < end; i += 2) + ret += _hexVector[i] * 16 + _hexVector[i + 1]; + return ret; +} + +u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen) +{ +#if ENABLE_DEBUG_PRINT + static std::string s_indent; + if (_preLen) + s_indent += " "; +#endif + + RLPStream rlp; + if (_begin == _end) + rlp << ""; // NULL + else if (std::next(_begin) == _end) { - // Key-value pair + // only one left - terminate with the pair. + rlp << RLPList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha256(rlp.out()) << std::endl; +#endif } - else if (_data.isList() && _data.itemCount() == 17) + else { - // Branch + // find the number of common prefix nibbles shared + // i.e. the minimum number of nibbles shared at the beginning between the first hex string and each successive. + uint sharedPre = (uint)-1; + uint c = 0; + for (auto i = std::next(_begin); i != _end && sharedPre; ++i, ++c) + { + uint x = std::min(sharedPre, std::min(_begin->first.size(), i->first.size())); + uint shared = _preLen; + for (; shared < x && _begin->first[shared] == i->first[shared]; ++shared) {} + sharedPre = std::min(shared, sharedPre); + } + if (sharedPre > _preLen) + { + // if they all have the same next nibble, we also want a pair. +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; +#endif + rlp << RLPList(2) << hexPrefixEncode(_begin->first, false, _preLen, sharedPre) << toBigEndianString(hash256aux(_s, _begin, _end, sharedPre)); +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "= " << sha256(rlp.out()) << std::endl; +#endif + } + else + { + // otherwise enumerate all 16+1 entries. + rlp << RLPList(17); + auto b = _begin; + if (_preLen == b->first.size()) + { +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "@: " << b->second << std::endl; +#endif + ++b; + } + for (auto i = 0; i < 16; ++i) + { + auto n = b; + for (; n != _end && n->first[_preLen] == i; ++n) {} + if (b == n) + rlp << ""; + else + { +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << std::hex << i << ": " << std::endl; +#endif + rlp << toBigEndianString(hash256aux(_s, b, n, _preLen + 1)); + } + b = n; + } + if (_preLen == _begin->first.size()) + rlp << _begin->second; + else + rlp << ""; + +#if ENABLE_DEBUG_PRINT + if (g_hashDebug) + std::cerr << s_indent << "= " << sha256(rlp.out()) << std::endl; +#endif + } } +#if ENABLE_DEBUG_PRINT + if (_preLen) + s_indent.resize(s_indent.size() - 2); +#endif + return sha256(rlp.out()); +} + +u256 hash256(StringMap const& _s) +{ + // build patricia tree. + if (_s.empty()) + return sha256(RLPNull); + HexMap hexMap; + for (auto i = _s.rbegin(); i != _s.rend(); ++i) + hexMap[toHex(i->first)] = i->second; + return hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0); } -*/ + + +class TrieNode +{ +public: + TrieNode() {} + virtual ~TrieNode() {} + + virtual std::string const& at(fConstBytes _key) const = 0; + virtual TrieNode* insert(fConstBytes _key, std::string const& _value) = 0; + virtual TrieNode* remove(fConstBytes _key) = 0; + virtual bytes rlp() const = 0; + +#if ENABLE_DEBUG_PRINT + void debugPrint(std::string const& _indent = "") const { std::cerr << std::hex << sha256() << ":" << std::endl; debugPrintBody(_indent); } +#endif + + u256 sha256() const { /*if (!m_sha256)*/ m_sha256 = eth::sha256(rlp()); return m_sha256; } + void mark() { m_sha256 = 0; } + +protected: +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent = "") const = 0; +#endif + + static TrieNode* newBranch(fConstBytes _k1, std::string const& _v1, fConstBytes _k2, std::string const& _v2); + +private: + mutable u256 m_sha256 = 0; +}; + +static const std::string c_nullString; + +class TrieExtNode: public TrieNode +{ +public: + TrieExtNode(fConstBytes _bytes): m_ext(_bytes.begin(), _bytes.end()) {} + + bytes m_ext; +}; + +class TrieBranchNode: public TrieNode +{ +public: + TrieBranchNode(std::string const& _value): m_value(_value) + { + memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); + } + + TrieBranchNode(byte _i1, TrieNode* _n1, std::string const& _value = std::string()): m_value(_value) + { + memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); + m_nodes[_i1] = _n1; + } + + TrieBranchNode(byte _i1, TrieNode* _n1, byte _i2, TrieNode* _n2) + { + memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); + m_nodes[_i1] = _n1; + m_nodes[_i2] = _n2; + } + + virtual ~TrieBranchNode() + { + for (auto i: m_nodes) + delete i; + } + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + + if (m_value.size()) + std::cerr << _indent << "@: " << m_value << std::endl; + for (auto i = 0; i < 16; ++i) + if (m_nodes[i]) + { + std::cerr << _indent << std::hex << i << ": "; + m_nodes[i]->debugPrint(_indent + " "); + } + } +#endif + + virtual std::string const& at(fConstBytes _key) const override; + virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; + virtual TrieNode* remove(fConstBytes _key) override; + virtual bytes rlp() const override; + +private: + /// @returns (byte)-1 when no active branches, 16 when multiple active and the index of the active branch otherwise. + byte activeBranch() const; + + TrieNode* rejig(); + + std::array m_nodes; + std::string m_value; +}; + +class TrieLeafNode: public TrieExtNode +{ +public: + TrieLeafNode(fConstBytes _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + assert(m_value.size()); + std::cerr << _indent; + if (m_ext.size()) + std::cerr << asHex(m_ext, 1) << ": "; + else + std::cerr << "@: "; + std::cerr << m_value << std::endl; + } +#endif + + virtual std::string const& at(fConstBytes _key) const override { return contains(_key) ? m_value : c_nullString; } + virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; + virtual TrieNode* remove(fConstBytes _key) override; + virtual bytes rlp() const override { return rlpListBytes(hexPrefixEncode(m_ext, true), m_value); } + +private: + bool contains(fConstBytes _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } + + std::string m_value; +}; + +class TrieInfixNode: public TrieExtNode +{ +public: + TrieInfixNode(fConstBytes _key, TrieNode* _next): TrieExtNode(_key), m_next(_next) {} + virtual ~TrieInfixNode() { delete m_next; } + +#if ENABLE_DEBUG_PRINT + virtual void debugPrintBody(std::string const& _indent) const + { + std::cerr << _indent << asHex(m_ext, 1) << ": "; + m_next->debugPrint(_indent + " "); + } +#endif + + virtual std::string const& at(fConstBytes _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; } + virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; + virtual TrieNode* remove(fConstBytes _key) override; + virtual bytes rlp() const override { assert(m_next); return rlpListBytes(hexPrefixEncode(m_ext, false), toBigEndianString(m_next->sha256())); } + +private: + bool contains(fConstBytes _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } + + TrieNode* m_next; +}; TrieNode* TrieNode::newBranch(fConstBytes _k1, std::string const& _v1, fConstBytes _k2, std::string const& _v2) { @@ -44,6 +319,15 @@ TrieNode* TrieNode::newBranch(fConstBytes _k1, std::string const& _v1, fConstByt return ret; } +std::string const& TrieBranchNode::at(fConstBytes _key) const +{ + if (_key.empty()) + return m_value; + else if (m_nodes[_key[0]] != nullptr) + return m_nodes[_key[0]]->at(_key.cropped(1)); + return c_nullString; +} + TrieNode* TrieBranchNode::insert(fConstBytes _key, std::string const& _value) { assert(_value.size()); @@ -111,3 +395,170 @@ TrieNode* TrieBranchNode::rejig() return this; } + +bytes TrieBranchNode::rlp() const +{ + RLPStream s; + s << RLPList(17); + for (auto i: m_nodes) + s << (i ? toBigEndianString(i->sha256()) : ""); + s << m_value; + return s.out(); +} + +byte TrieBranchNode::activeBranch() const +{ + byte n = (byte)-1; + for (int i = 0; i < 16; ++i) + if (m_nodes[i] != nullptr) + { + if (n == (byte)-1) + n = i; + else + return 16; + } + return n; +} + +TrieNode* TrieInfixNode::insert(fConstBytes _key, std::string const& _value) +{ + assert(_value.size()); + mark(); + if (contains(_key)) + { + m_next = m_next->insert(_key.cropped(m_ext.size()), _value); + return this; + } + else + { + int prefix = commonPrefix(_key, m_ext); + if (prefix) + { + // one infix becomes two infixes, then insert into the second + // instead of pop_front()... + trimFront(m_ext, prefix); + + return new TrieInfixNode(_key.cropped(0, prefix), insert(_key.cropped(prefix), _value)); + } + else + { + // split here. + auto f = m_ext[0]; + trimFront(m_ext, 1); + TrieNode* n = m_ext.empty() ? m_next : this; + if (n != this) + { + m_next = nullptr; + delete this; + } + TrieBranchNode* ret = new TrieBranchNode(f, n); + ret->insert(_key, _value); + return ret; + } + } +} + +TrieNode* TrieInfixNode::remove(fConstBytes _key) +{ + if (contains(_key)) + { + mark(); + m_next = m_next->remove(_key.cropped(m_ext.size())); + if (auto p = dynamic_cast(m_next)) + { + // merge with child... + m_ext.reserve(m_ext.size() + p->m_ext.size()); + for (auto i: p->m_ext) + m_ext.push_back(i); + p->m_ext = m_ext; + p->mark(); + m_next = nullptr; + delete this; + return p; + } + if (!m_next) + { + delete this; + return nullptr; + } + } + return this; +} + +TrieNode* TrieLeafNode::insert(fConstBytes _key, std::string const& _value) +{ + assert(_value.size()); + mark(); + if (contains(_key)) + { + m_value = _value; + return this; + } + else + { + // create new trie. + auto n = TrieNode::newBranch(_key, _value, fConstBytes(&m_ext), m_value); + delete this; + return n; + } +} + +TrieNode* TrieLeafNode::remove(fConstBytes _key) +{ + if (contains(_key)) + { + delete this; + return nullptr; + } + return this; +} + +Trie::~Trie() +{ + delete m_root; +} + +u256 Trie::sha256() const +{ + return m_root ? m_root->sha256() : eth::sha256(RLPNull); +} + +bytes Trie::rlp() const +{ + return m_root ? m_root->rlp() : RLPNull; +} + +void Trie::debugPrint() +{ +#if ENABLE_DEBUG_PRINT + if (m_root) + m_root->debugPrint(); +#endif +} + +std::string const& Trie::at(std::string const& _key) const +{ + if (!m_root) + return c_nullString; + auto h = toHex(_key); + return m_root->at(fConstBytes(&h)); +} + +void Trie::insert(std::string const& _key, std::string const& _value) +{ + if (_value.empty()) + remove(_key); + auto h = toHex(_key); + m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(fConstBytes(&h), _value); +} + +void Trie::remove(std::string const& _key) +{ + if (m_root) + { + auto h = toHex(_key); + m_root = m_root->remove(&h); + } +} + +} diff --git a/PatriciaTree.h b/PatriciaTree.h index 2c239c53b..c0864a0a3 100644 --- a/PatriciaTree.h +++ b/PatriciaTree.h @@ -4,513 +4,34 @@ #include "RLP.h" #include "sha256.h" -#define ENABLE_DEBUG_PRINT 1 - namespace eth { using StringMap = std::map; using HexMap = std::map; -extern bool g_hashDebug; - -/* - * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1 - * [0,0,1,2,3,4,5] 0x10012345 - * [0,1,2,3,4,5] 0x00012345 - * [1,2,3,4,5] 0x112345 - * [0,0,1,2,3,4] 0x00001234 - * [0,1,2,3,4] 0x101234 - * [1,2,3,4] 0x001234 - * [0,0,1,2,3,4,5,T] 0x30012345 - * [0,0,1,2,3,4,T] 0x20001234 - * [0,1,2,3,4,5,T] 0x20012345 - * [1,2,3,4,5,T] 0x312345 - * [1,2,3,4,T] 0x201234 - */ - -inline std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated = false, int _begin = 0, int _end = -1) -{ - uint begin = _begin; - uint end = _end < 0 ? _hexVector.size() + 1 + _end : _end; - bool termed = _terminated; - bool odd = (end - begin) % 2; - - std::string ret(1, ((termed ? 2 : 0) | (odd ? 1 : 0)) * 16); - if (odd) - { - ret[0] |= _hexVector[begin]; - ++begin; - } - for (uint i = begin; i < end; i += 2) - ret += _hexVector[i] * 16 + _hexVector[i + 1]; - return ret; -} - -inline bytes toHex(std::string const& _s) -{ - std::vector ret; - ret.reserve(_s.size() * 2); - for (auto i: _s) - { - ret.push_back(i / 16); - ret.push_back(i % 16); - } - return ret; -} +u256 hash256(StringMap const& _s); +std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated = false, int _begin = 0, int _end = -1); -inline std::string toBigEndianString(u256 _val) -{ - std::string ret; - ret.resize(32); - for (int i = 0; i <32; ++i, _val >>= 8) - ret[31 - i] = (char)(uint8_t)_val; - return ret; -} - -inline u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_iterator _end, unsigned _preLen) -{ - static std::string s_indent; - if (_preLen) - s_indent += " "; - - RLPStream rlp; - if (_begin == _end) - { - rlp << ""; // NULL - } - else if (std::next(_begin) == _end) - { - // only one left - terminate with the pair. - rlp << RLPList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; - if (g_hashDebug) - std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha256(rlp.out()) << std::endl; - } - else - { - // find the number of common prefix nibbles shared - // i.e. the minimum number of nibbles shared at the beginning between the first hex string and each successive. - uint sharedPre = (uint)-1; - uint c = 0; - for (auto i = std::next(_begin); i != _end && sharedPre; ++i, ++c) - { - uint x = std::min(sharedPre, std::min(_begin->first.size(), i->first.size())); - uint shared = _preLen; - for (; shared < x && _begin->first[shared] == i->first[shared]; ++shared) {} - sharedPre = std::min(shared, sharedPre); - } - if (sharedPre > _preLen) - { - // if they all have the same next nibble, we also want a pair. - if (g_hashDebug) - std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; - rlp << RLPList(2) << hexPrefixEncode(_begin->first, false, _preLen, sharedPre) << toBigEndianString(hash256aux(_s, _begin, _end, sharedPre)); - if (g_hashDebug) - std::cerr << s_indent << "= " << sha256(rlp.out()) << std::endl; - } - else - { - // otherwise enumerate all 16+1 entries. - rlp << RLPList(17); - auto b = _begin; - if (_preLen == b->first.size()) - { - if (g_hashDebug) - std::cerr << s_indent << "@: " << b->second << std::endl; - ++b; - } - for (auto i = 0; i < 16; ++i) - { - auto n = b; - for (; n != _end && n->first[_preLen] == i; ++n) {} - if (b == n) - rlp << ""; - else - { - if (g_hashDebug) - std::cerr << s_indent << std::hex << i << ": " << std::endl; - rlp << toBigEndianString(hash256aux(_s, b, n, _preLen + 1)); - } - b = n; - } - if (_preLen == _begin->first.size()) - rlp << _begin->second; - else - rlp << ""; - - if (g_hashDebug) - std::cerr << s_indent << "= " << sha256(rlp.out()) << std::endl; - } - } -// if (g_hashDebug) -// std::cerr << std::hex << sha256(rlp.out()) << ": " << asHex(rlp.out()) << ": " << RLP(rlp.out()) << std::endl; - if (_preLen) - s_indent.resize(s_indent.size() - 2); - return sha256(rlp.out()); -} - -inline u256 hash256(StringMap const& _s) -{ - // build patricia tree. - if (_s.empty()) - return sha256(RLPNull); - HexMap hexMap; - for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(i->first)] = i->second; -// for (auto const& i: _s) -// hexMap[toHex(i.first)] = i.second; - return hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0); -} - -template uint commonPrefix(_T const& _t, _U const& _u) -{ - uint s = std::min(_t.size(), _u.size()); - for (uint i = 0;; ++i) - if (i == s || _t[i] != _u[i]) - return i; - return s; -} +class TrieNode; /** - * @brief Merkle Patricia Tree: a modifed base-16 Radix tree. + * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. */ -class TrieNode -{ -public: - TrieNode() {} - virtual ~TrieNode() {} - - virtual std::string const& at(fConstBytes _key) const = 0; - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) = 0; - virtual TrieNode* remove(fConstBytes _key) = 0; - virtual bytes rlp() const = 0; - -#if ENABLE_DEBUG_PRINT - void debugPrint(std::string const& _indent = "") const { std::cerr << std::hex << sha256() << ":" << std::endl; debugPrintBody(_indent); } -#endif - - u256 sha256() const { /*if (!m_sha256)*/ m_sha256 = eth::sha256(rlp()); return m_sha256; } - void mark() { m_sha256 = 0; } - -protected: -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent = "") const = 0; -#endif - - static TrieNode* newBranch(fConstBytes _k1, std::string const& _v1, fConstBytes _k2, std::string const& _v2); - -private: - mutable u256 m_sha256 = 0; -}; - -static const std::string c_nullString; - -class TrieExtNode: public TrieNode -{ -public: - TrieExtNode(fConstBytes _bytes): m_ext(_bytes.begin(), _bytes.end()) {} - - bytes m_ext; -}; - -class TrieBranchNode: public TrieNode -{ -public: - TrieBranchNode(std::string const& _value): m_value(_value) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - } - - TrieBranchNode(byte _i1, TrieNode* _n1, std::string const& _value = std::string()): m_value(_value) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - m_nodes[_i1] = _n1; - } - - TrieBranchNode(byte _i1, TrieNode* _n1, byte _i2, TrieNode* _n2) - { - memset(m_nodes.data(), 0, sizeof(TrieNode*) * 16); - m_nodes[_i1] = _n1; - m_nodes[_i2] = _n2; - } - - virtual ~TrieBranchNode() - { - for (auto i: m_nodes) - delete i; - } - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - - if (m_value.size()) - std::cerr << _indent << "@: " << m_value << std::endl; - for (auto i = 0; i < 16; ++i) - if (m_nodes[i]) - { - std::cerr << _indent << std::hex << i << ": "; - m_nodes[i]->debugPrint(_indent + " "); - } - } -#endif - - virtual std::string const& at(fConstBytes _key) const override - { - if (_key.empty()) - return m_value; - else if (m_nodes[_key[0]] != nullptr) - return m_nodes[_key[0]]->at(_key.cropped(1)); - return c_nullString; - } - - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; - - virtual TrieNode* remove(fConstBytes _key) override; - - virtual bytes rlp() const override - { - RLPStream s; - s << RLPList(17); - for (auto i: m_nodes) - s << (i ? toBigEndianString(i->sha256()) : ""); - s << m_value; - return s.out(); - } - -private: - /// @returns (byte)-1 when no active branches, 16 when multiple active and the index of the active branch otherwise. - byte activeBranch() const - { - byte n = (byte)-1; - for (int i = 0; i < 16; ++i) - if (m_nodes[i] != nullptr) - { - if (n == (byte)-1) - n = i; - else - return 16; - } - return n; - } - - TrieNode* rejig(); - - std::array m_nodes; - std::string m_value; -}; - -class TrieLeafNode: public TrieExtNode -{ -public: - TrieLeafNode(fConstBytes _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - assert(m_value.size()); - std::cerr << _indent; - if (m_ext.size()) - std::cerr << asHex(m_ext, 1) << ": "; - else - std::cerr << "@: "; - std::cerr << m_value << std::endl; - } -#endif - - virtual std::string const& at(fConstBytes _key) const override - { - return contains(_key) ? m_value : c_nullString; - } - - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override - { - assert(_value.size()); - mark(); - if (contains(_key)) - { - m_value = _value; - return this; - } - else - { - // create new trie. - auto n = TrieNode::newBranch(_key, _value, fConstBytes(&m_ext), m_value); - delete this; - return n; - } - } - - virtual TrieNode* remove(fConstBytes _key) override - { - if (contains(_key)) - { - delete this; - return nullptr; - } - return this; - } - - virtual bytes rlp() const override - { - RLPStream s; - s << RLPList(2) << hexPrefixEncode(m_ext, true) << m_value; - return s.out(); - } - -private: - bool contains(fConstBytes _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } - - std::string m_value; -}; - -template void trimFront(_T& _t, uint _elements) -{ - memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); - _t.resize(_t.size() - _elements); -} - -template void pushFront(_T& _t, _U _e) -{ - _t.push_back(_e); - memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); - _t[0] = _e; -} - -class TrieInfixNode: public TrieExtNode -{ -public: - TrieInfixNode(fConstBytes _key, TrieNode* _next): TrieExtNode(_key), m_next(_next) {} - virtual ~TrieInfixNode() { delete m_next; } - -#if ENABLE_DEBUG_PRINT - virtual void debugPrintBody(std::string const& _indent) const - { - std::cerr << _indent << asHex(m_ext, 1) << ": "; - m_next->debugPrint(_indent + " "); - } -#endif - - virtual std::string const& at(fConstBytes _key) const override - { - assert(m_next); - return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; - } - - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override - { - assert(_value.size()); - mark(); - if (contains(_key)) - { - m_next = m_next->insert(_key.cropped(m_ext.size()), _value); - return this; - } - else - { - int prefix = commonPrefix(_key, m_ext); - if (prefix) - { - // one infix becomes two infixes, then insert into the second - // instead of pop_front()... - trimFront(m_ext, prefix); - - return new TrieInfixNode(_key.cropped(0, prefix), insert(_key.cropped(prefix), _value)); - } - else - { - // split here. - auto f = m_ext[0]; - trimFront(m_ext, 1); - TrieNode* n = m_ext.empty() ? m_next : this; - if (n != this) - { - m_next = nullptr; - delete this; - } - TrieBranchNode* ret = new TrieBranchNode(f, n); - ret->insert(_key, _value); - return ret; - } - } - } - - virtual TrieNode* remove(fConstBytes _key) override - { - if (contains(_key)) - { - mark(); - m_next = m_next->remove(_key.cropped(m_ext.size())); - if (auto p = dynamic_cast(m_next)) - { - // merge with child... - m_ext.reserve(m_ext.size() + p->m_ext.size()); - for (auto i: p->m_ext) - m_ext.push_back(i); - p->m_ext = m_ext; - p->mark(); - m_next = nullptr; - delete this; - return p; - } - if (!m_next) - { - delete this; - return nullptr; - } - } - return this; - } - - virtual bytes rlp() const override - { - assert(m_next); - RLPStream s; - s << RLPList(2) << hexPrefixEncode(m_ext, false) << toBigEndianString(m_next->sha256()); - return s.out(); - } - -private: - bool contains(fConstBytes _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } - - TrieNode* m_next; -}; - class Trie { public: Trie(): m_root(nullptr) {} - ~Trie() { delete m_root; } - - u256 sha256() const { return m_root ? m_root->sha256() : eth::sha256(RLPNull); } - bytes rlp() const { return m_root ? m_root->rlp() : RLPNull; } - - void debugPrint() { if (m_root) m_root->debugPrint(); } + ~Trie(); - std::string const& at(std::string const& _key) const - { - if (!m_root) - return c_nullString; - auto h = toHex(_key); - return m_root->at(fConstBytes(&h)); - } + u256 sha256() const; + bytes rlp() const; - void insert(std::string const& _key, std::string const& _value) - { - if (_value.empty()) - remove(_key); - auto h = toHex(_key); - m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(fConstBytes(&h), _value); - } + void debugPrint(); - void remove(std::string const& _key) - { - if (m_root) - { - auto h = toHex(_key); - m_root = m_root->remove(&h); - } - } + std::string const& at(std::string const& _key) const; + void insert(std::string const& _key, std::string const& _value); + void remove(std::string const& _key); private: TrieNode* m_root;