mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			483 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 	This file is part of cpp-ethereum.
 | |
| 
 | |
| 	cpp-ethereum is free software: you can redistribute it and/or modify
 | |
| 	it under the terms of the GNU General Public License as published by
 | |
| 	the Free Software Foundation, either version 3 of the License, or
 | |
| 	(at your option) any later version.
 | |
| 
 | |
| 	cpp-ethereum is distributed in the hope that it will be useful,
 | |
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| 	GNU General Public License for more details.
 | |
| 
 | |
| 	You should have received a copy of the GNU General Public License
 | |
| 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| /** @file MemTrie.cpp
 | |
|  * @author Gav Wood <i@gavwood.com>
 | |
|  * @date 2014
 | |
|  */
 | |
| 
 | |
| #include "MemTrie.h"
 | |
| 
 | |
| #include <libdevcrypto/TrieCommon.h>
 | |
| #include <libdevcrypto/SHA3.h>
 | |
| #include <libethcore/CommonEth.h>
 | |
| using namespace std;
 | |
| using namespace dev;
 | |
| using namespace dev::eth;
 | |
| 
 | |
| namespace dev
 | |
| {
 | |
| 
 | |
| #define ENABLE_DEBUG_PRINT 0
 | |
| 
 | |
| /*/
 | |
| #define APPEND_CHILD appendData
 | |
| /*/
 | |
| #define APPEND_CHILD appendRaw
 | |
| /**/
 | |
| 
 | |
| class MemTrieNode
 | |
| {
 | |
| public:
 | |
| 	MemTrieNode() {}
 | |
| 	virtual ~MemTrieNode() {}
 | |
| 
 | |
| 	virtual std::string const& at(bytesConstRef _key) const = 0;
 | |
| 	virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) = 0;
 | |
| 	virtual MemTrieNode* remove(bytesConstRef _key) = 0;
 | |
| 	void putRLP(RLPStream& _parentStream) const;
 | |
| 
 | |
| #if ENABLE_DEBUG_PRINT
 | |
| 	void debugPrint(std::string const& _indent = "") const { std::cerr << std::hex << hash256() << ":" << std::dec << std::endl; debugPrintBody(_indent); }
 | |
| #endif
 | |
| 
 | |
| 	/// 256-bit hash of the node - this is a SHA-3/256 hash of the RLP of the node.
 | |
| 	h256 hash256() const { RLPStream s; makeRLP(s); return dev::sha3(s.out()); }
 | |
| 	bytes rlp() const { RLPStream s; makeRLP(s); return s.out(); }
 | |
| 	void mark() { m_hash256 = h256(); }
 | |
| 
 | |
| protected:
 | |
| 	virtual void makeRLP(RLPStream& _intoStream) const = 0;
 | |
| 
 | |
| #if ENABLE_DEBUG_PRINT
 | |
| 	virtual void debugPrintBody(std::string const& _indent = "") const = 0;
 | |
| #endif
 | |
| 
 | |
| 	static MemTrieNode* newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2);
 | |
| 
 | |
| private:
 | |
| 	mutable h256 m_hash256;
 | |
| };
 | |
| 
 | |
| static const std::string c_nullString;
 | |
| 
 | |
| class TrieExtNode: public MemTrieNode
 | |
| {
 | |
| public:
 | |
| 	TrieExtNode(bytesConstRef _bytes): m_ext(_bytes.begin(), _bytes.end()) {}
 | |
| 
 | |
| 	bytes m_ext;
 | |
| };
 | |
| 
 | |
| class TrieBranchNode: public MemTrieNode
 | |
| {
 | |
| public:
 | |
| 	TrieBranchNode(std::string const& _value): m_value(_value)
 | |
| 	{
 | |
| 		memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 16);
 | |
| 	}
 | |
| 
 | |
| 	TrieBranchNode(byte _i1, MemTrieNode* _n1, std::string const& _value = std::string()): m_value(_value)
 | |
| 	{
 | |
| 		memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 16);
 | |
| 		m_nodes[_i1] = _n1;
 | |
| 	}
 | |
| 
 | |
| 	TrieBranchNode(byte _i1, MemTrieNode* _n1, byte _i2, MemTrieNode* _n2)
 | |
| 	{
 | |
| 		memset(m_nodes.data(), 0, sizeof(MemTrieNode*) * 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 << ": " << std::dec;
 | |
| 				m_nodes[i]->debugPrint(_indent + "  ");
 | |
| 			}
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	virtual std::string const& at(bytesConstRef _key) const override;
 | |
| 	virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override;
 | |
| 	virtual MemTrieNode* remove(bytesConstRef _key) override;
 | |
| 	virtual void makeRLP(RLPStream& _parentStream) 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;
 | |
| 
 | |
| 	MemTrieNode* rejig();
 | |
| 
 | |
| 	std::array<MemTrieNode*, 16> m_nodes;
 | |
| 	std::string m_value;
 | |
| };
 | |
| 
 | |
| class TrieLeafNode: public TrieExtNode
 | |
| {
 | |
| public:
 | |
| 	TrieLeafNode(bytesConstRef _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 << toHex(m_ext, 1) << ": ";
 | |
| 		else
 | |
| 			std::cerr << "@: ";
 | |
| 		std::cerr << m_value << std::endl;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	virtual std::string const& at(bytesConstRef _key) const override { return contains(_key) ? m_value : c_nullString; }
 | |
| 	virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override;
 | |
| 	virtual MemTrieNode* remove(bytesConstRef _key) override;
 | |
| 	virtual void makeRLP(RLPStream& _parentStream) const override;
 | |
| 
 | |
| private:
 | |
| 	bool contains(bytesConstRef _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(bytesConstRef _key, MemTrieNode* _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 << toHex(m_ext, 1) << ": ";
 | |
| 		m_next->debugPrint(_indent + "  ");
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	virtual std::string const& at(bytesConstRef _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; }
 | |
| 	virtual MemTrieNode* insert(bytesConstRef _key, std::string const& _value) override;
 | |
| 	virtual MemTrieNode* remove(bytesConstRef _key) override;
 | |
| 	virtual void makeRLP(RLPStream& _parentStream) const override;
 | |
| 
 | |
| private:
 | |
| 	bool contains(bytesConstRef _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); }
 | |
| 
 | |
| 	MemTrieNode* m_next;
 | |
| };
 | |
| 
 | |
| void MemTrieNode::putRLP(RLPStream& _parentStream) const
 | |
| {
 | |
| 	RLPStream s;
 | |
| 	makeRLP(s);
 | |
| 	if (s.out().size() < 32)
 | |
| 		_parentStream.APPEND_CHILD(s.out());
 | |
| 	else
 | |
| 		_parentStream << dev::sha3(s.out());
 | |
| }
 | |
| 
 | |
| void TrieBranchNode::makeRLP(RLPStream& _intoStream) const
 | |
| {
 | |
| 	_intoStream.appendList(17);
 | |
| 	for (auto i: m_nodes)
 | |
| 		if (i)
 | |
| 			i->putRLP(_intoStream);
 | |
| 		else
 | |
| 			_intoStream << "";
 | |
| 	_intoStream << m_value;
 | |
| }
 | |
| 
 | |
| void TrieLeafNode::makeRLP(RLPStream& _intoStream) const
 | |
| {
 | |
| 	_intoStream.appendList(2) << hexPrefixEncode(m_ext, true) << m_value;
 | |
| }
 | |
| 
 | |
| void TrieInfixNode::makeRLP(RLPStream& _intoStream) const
 | |
| {
 | |
| 	assert(m_next);
 | |
| 	_intoStream.appendList(2);
 | |
| 	_intoStream << hexPrefixEncode(m_ext, false);
 | |
| 	m_next->putRLP(_intoStream);
 | |
| }
 | |
| 
 | |
| MemTrieNode* MemTrieNode::newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2)
 | |
| {
 | |
| 	unsigned prefix = commonPrefix(_k1, _k2);
 | |
| 
 | |
| 	MemTrieNode* ret;
 | |
| 	if (_k1.size() == prefix)
 | |
| 		ret = new TrieBranchNode(_k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2), _v1);
 | |
| 	else if (_k2.size() == prefix)
 | |
| 		ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _v2);
 | |
| 	else // both continue after split
 | |
| 		ret = new TrieBranchNode(_k1[prefix], new TrieLeafNode(_k1.cropped(prefix + 1), _v1), _k2[prefix], new TrieLeafNode(_k2.cropped(prefix + 1), _v2));
 | |
| 
 | |
| 	if (prefix)
 | |
| 		// have shared prefix - split.
 | |
| 		ret = new TrieInfixNode(_k1.cropped(0, prefix), ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| std::string const& TrieBranchNode::at(bytesConstRef _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;
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieBranchNode::insert(bytesConstRef _key, std::string const& _value)
 | |
| {
 | |
| 	assert(_value.size());
 | |
| 	mark();
 | |
| 	if (_key.empty())
 | |
| 		m_value = _value;
 | |
| 	else
 | |
| 		if (!m_nodes[_key[0]])
 | |
| 			m_nodes[_key[0]] = new TrieLeafNode(_key.cropped(1), _value);
 | |
| 		else
 | |
| 			m_nodes[_key[0]] = m_nodes[_key[0]]->insert(_key.cropped(1), _value);
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieBranchNode::remove(bytesConstRef _key)
 | |
| {
 | |
| 	if (_key.empty())
 | |
| 		if (m_value.size())
 | |
| 		{
 | |
| 			m_value.clear();
 | |
| 			return rejig();
 | |
| 		}
 | |
| 		else {}
 | |
| 	else if (m_nodes[_key[0]] != nullptr)
 | |
| 	{
 | |
| 		m_nodes[_key[0]] = m_nodes[_key[0]]->remove(_key.cropped(1));
 | |
| 		return rejig();
 | |
| 	}
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieBranchNode::rejig()
 | |
| {
 | |
| 	mark();
 | |
| 	byte n = activeBranch();
 | |
| 
 | |
| 	if (n == (byte)-1 && m_value.size())
 | |
| 	{
 | |
| 		// switch to leaf
 | |
| 		auto r = new TrieLeafNode(bytesConstRef(), m_value);
 | |
| 		delete this;
 | |
| 		return r;
 | |
| 	}
 | |
| 	else if (n < 16 && m_value.empty())
 | |
| 	{
 | |
| 		// only branching to n...
 | |
| 		if (auto b = dynamic_cast<TrieBranchNode*>(m_nodes[n]))
 | |
| 		{
 | |
| 			// switch to infix
 | |
| 			m_nodes[n] = nullptr;
 | |
| 			delete this;
 | |
| 			return new TrieInfixNode(bytesConstRef(&n, 1), b);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			auto x = dynamic_cast<TrieExtNode*>(m_nodes[n]);
 | |
| 			assert(x);
 | |
| 			// include in child
 | |
| 			pushFront(x->m_ext, n);
 | |
| 			m_nodes[n] = nullptr;
 | |
| 			delete this;
 | |
| 			return x;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| 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 = (byte)i;
 | |
| 			else
 | |
| 				return 16;
 | |
| 		}
 | |
| 	return n;
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieInfixNode::insert(bytesConstRef _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
 | |
| 	{
 | |
| 		unsigned 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);
 | |
| 			MemTrieNode* 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;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieInfixNode::remove(bytesConstRef _key)
 | |
| {
 | |
| 	if (contains(_key))
 | |
| 	{
 | |
| 		mark();
 | |
| 		m_next = m_next->remove(_key.cropped(m_ext.size()));
 | |
| 		if (auto p = dynamic_cast<TrieExtNode*>(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;
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieLeafNode::insert(bytesConstRef _key, std::string const& _value)
 | |
| {
 | |
| 	assert(_value.size());
 | |
| 	mark();
 | |
| 	if (contains(_key))
 | |
| 	{
 | |
| 		m_value = _value;
 | |
| 		return this;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// create new trie.
 | |
| 		auto n = MemTrieNode::newBranch(_key, _value, bytesConstRef(&m_ext), m_value);
 | |
| 		delete this;
 | |
| 		return n;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| MemTrieNode* TrieLeafNode::remove(bytesConstRef _key)
 | |
| {
 | |
| 	if (contains(_key))
 | |
| 	{
 | |
| 		delete this;
 | |
| 		return nullptr;
 | |
| 	}
 | |
| 	return this;
 | |
| }
 | |
| 
 | |
| MemTrie::~MemTrie()
 | |
| {
 | |
| 	delete m_root;
 | |
| }
 | |
| 
 | |
| h256 MemTrie::hash256() const
 | |
| {
 | |
| 	return m_root ? m_root->hash256() : sha3(dev::rlp(bytesConstRef()));
 | |
| }
 | |
| 
 | |
| bytes MemTrie::rlp() const
 | |
| {
 | |
| 	return m_root ? m_root->rlp() : dev::rlp(bytesConstRef());
 | |
| }
 | |
| 
 | |
| void MemTrie::debugPrint()
 | |
| {
 | |
| #if ENABLE_DEBUG_PRINT
 | |
| 	if (m_root)
 | |
| 		m_root->debugPrint();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| std::string const& MemTrie::at(std::string const& _key) const
 | |
| {
 | |
| 	if (!m_root)
 | |
| 		return c_nullString;
 | |
| 	auto h = asNibbles(_key);
 | |
| 	return m_root->at(bytesConstRef(&h));
 | |
| }
 | |
| 
 | |
| void MemTrie::insert(std::string const& _key, std::string const& _value)
 | |
| {
 | |
| 	if (_value.empty())
 | |
| 		remove(_key);
 | |
| 	auto h = asNibbles(_key);
 | |
| 	m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(bytesConstRef(&h), _value);
 | |
| }
 | |
| 
 | |
| void MemTrie::remove(std::string const& _key)
 | |
| {
 | |
| 	if (m_root)
 | |
| 	{
 | |
| 		auto h = asNibbles(_key);
 | |
| 		m_root = m_root->remove(&h);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 |