forked from mirror/go-ethereum
Merge pull request #3055 from karalabe/release/1.4
Geth 1.4.14: What else should we rewrite?release/1.4 v1.4.14
commit
f88bca7ba9
@ -1,206 +0,0 @@ |
|||||||
// Copyright (c) 2015 Hans Alexander Gugel <alexander.gugel@gmail.com>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
// This file contains a modified version of package arc from
|
|
||||||
// https://github.com/alexanderGugel/arc
|
|
||||||
//
|
|
||||||
// It implements the ARC (Adaptive Replacement Cache) algorithm as detailed in
|
|
||||||
// https://www.usenix.org/legacy/event/fast03/tech/full_papers/megiddo/megiddo.pdf
|
|
||||||
|
|
||||||
package trie |
|
||||||
|
|
||||||
import ( |
|
||||||
"container/list" |
|
||||||
"sync" |
|
||||||
) |
|
||||||
|
|
||||||
type arc struct { |
|
||||||
p int |
|
||||||
c int |
|
||||||
t1 *list.List |
|
||||||
b1 *list.List |
|
||||||
t2 *list.List |
|
||||||
b2 *list.List |
|
||||||
cache map[string]*entry |
|
||||||
mutex sync.Mutex |
|
||||||
} |
|
||||||
|
|
||||||
type entry struct { |
|
||||||
key hashNode |
|
||||||
value node |
|
||||||
ll *list.List |
|
||||||
el *list.Element |
|
||||||
} |
|
||||||
|
|
||||||
// newARC returns a new Adaptive Replacement Cache with the
|
|
||||||
// given capacity.
|
|
||||||
func newARC(c int) *arc { |
|
||||||
return &arc{ |
|
||||||
c: c, |
|
||||||
t1: list.New(), |
|
||||||
b1: list.New(), |
|
||||||
t2: list.New(), |
|
||||||
b2: list.New(), |
|
||||||
cache: make(map[string]*entry, c), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Clear clears the cache
|
|
||||||
func (a *arc) Clear() { |
|
||||||
a.mutex.Lock() |
|
||||||
defer a.mutex.Unlock() |
|
||||||
a.p = 0 |
|
||||||
a.t1 = list.New() |
|
||||||
a.b1 = list.New() |
|
||||||
a.t2 = list.New() |
|
||||||
a.b2 = list.New() |
|
||||||
a.cache = make(map[string]*entry, a.c) |
|
||||||
} |
|
||||||
|
|
||||||
// Put inserts a new key-value pair into the cache.
|
|
||||||
// This optimizes future access to this entry (side effect).
|
|
||||||
func (a *arc) Put(key hashNode, value node) bool { |
|
||||||
a.mutex.Lock() |
|
||||||
defer a.mutex.Unlock() |
|
||||||
ent, ok := a.cache[string(key)] |
|
||||||
if ok != true { |
|
||||||
ent = &entry{key: key, value: value} |
|
||||||
a.req(ent) |
|
||||||
a.cache[string(key)] = ent |
|
||||||
} else { |
|
||||||
ent.value = value |
|
||||||
a.req(ent) |
|
||||||
} |
|
||||||
return ok |
|
||||||
} |
|
||||||
|
|
||||||
// Get retrieves a previously via Set inserted entry.
|
|
||||||
// This optimizes future access to this entry (side effect).
|
|
||||||
func (a *arc) Get(key hashNode) (value node, ok bool) { |
|
||||||
a.mutex.Lock() |
|
||||||
defer a.mutex.Unlock() |
|
||||||
ent, ok := a.cache[string(key)] |
|
||||||
if ok { |
|
||||||
a.req(ent) |
|
||||||
return ent.value, ent.value != nil |
|
||||||
} |
|
||||||
return nil, false |
|
||||||
} |
|
||||||
|
|
||||||
func (a *arc) req(ent *entry) { |
|
||||||
if ent.ll == a.t1 || ent.ll == a.t2 { |
|
||||||
// Case I
|
|
||||||
ent.setMRU(a.t2) |
|
||||||
} else if ent.ll == a.b1 { |
|
||||||
// Case II
|
|
||||||
// Cache Miss in t1 and t2
|
|
||||||
|
|
||||||
// Adaptation
|
|
||||||
var d int |
|
||||||
if a.b1.Len() >= a.b2.Len() { |
|
||||||
d = 1 |
|
||||||
} else { |
|
||||||
d = a.b2.Len() / a.b1.Len() |
|
||||||
} |
|
||||||
a.p = a.p + d |
|
||||||
if a.p > a.c { |
|
||||||
a.p = a.c |
|
||||||
} |
|
||||||
|
|
||||||
a.replace(ent) |
|
||||||
ent.setMRU(a.t2) |
|
||||||
} else if ent.ll == a.b2 { |
|
||||||
// Case III
|
|
||||||
// Cache Miss in t1 and t2
|
|
||||||
|
|
||||||
// Adaptation
|
|
||||||
var d int |
|
||||||
if a.b2.Len() >= a.b1.Len() { |
|
||||||
d = 1 |
|
||||||
} else { |
|
||||||
d = a.b1.Len() / a.b2.Len() |
|
||||||
} |
|
||||||
a.p = a.p - d |
|
||||||
if a.p < 0 { |
|
||||||
a.p = 0 |
|
||||||
} |
|
||||||
|
|
||||||
a.replace(ent) |
|
||||||
ent.setMRU(a.t2) |
|
||||||
} else if ent.ll == nil { |
|
||||||
// Case IV
|
|
||||||
|
|
||||||
if a.t1.Len()+a.b1.Len() == a.c { |
|
||||||
// Case A
|
|
||||||
if a.t1.Len() < a.c { |
|
||||||
a.delLRU(a.b1) |
|
||||||
a.replace(ent) |
|
||||||
} else { |
|
||||||
a.delLRU(a.t1) |
|
||||||
} |
|
||||||
} else if a.t1.Len()+a.b1.Len() < a.c { |
|
||||||
// Case B
|
|
||||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c { |
|
||||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c { |
|
||||||
a.delLRU(a.b2) |
|
||||||
} |
|
||||||
a.replace(ent) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ent.setMRU(a.t1) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (a *arc) delLRU(list *list.List) { |
|
||||||
lru := list.Back() |
|
||||||
list.Remove(lru) |
|
||||||
delete(a.cache, string(lru.Value.(*entry).key)) |
|
||||||
} |
|
||||||
|
|
||||||
func (a *arc) replace(ent *entry) { |
|
||||||
if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) { |
|
||||||
lru := a.t1.Back().Value.(*entry) |
|
||||||
lru.value = nil |
|
||||||
lru.setMRU(a.b1) |
|
||||||
} else { |
|
||||||
lru := a.t2.Back().Value.(*entry) |
|
||||||
lru.value = nil |
|
||||||
lru.setMRU(a.b2) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (e *entry) setLRU(list *list.List) { |
|
||||||
e.detach() |
|
||||||
e.ll = list |
|
||||||
e.el = e.ll.PushBack(e) |
|
||||||
} |
|
||||||
|
|
||||||
func (e *entry) setMRU(list *list.List) { |
|
||||||
e.detach() |
|
||||||
e.ll = list |
|
||||||
e.el = e.ll.PushFront(e) |
|
||||||
} |
|
||||||
|
|
||||||
func (e *entry) detach() { |
|
||||||
if e.ll != nil { |
|
||||||
e.ll.Remove(e.el) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,157 @@ |
|||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package trie |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"hash" |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto/sha3" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
type hasher struct { |
||||||
|
tmp *bytes.Buffer |
||||||
|
sha hash.Hash |
||||||
|
} |
||||||
|
|
||||||
|
// hashers live in a global pool.
|
||||||
|
var hasherPool = sync.Pool{ |
||||||
|
New: func() interface{} { |
||||||
|
return &hasher{tmp: new(bytes.Buffer), sha: sha3.NewKeccak256()} |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func newHasher() *hasher { |
||||||
|
return hasherPool.Get().(*hasher) |
||||||
|
} |
||||||
|
|
||||||
|
func returnHasherToPool(h *hasher) { |
||||||
|
hasherPool.Put(h) |
||||||
|
} |
||||||
|
|
||||||
|
// hash collapses a node down into a hash node, also returning a copy of the
|
||||||
|
// original node initialzied with the computed hash to replace the original one.
|
||||||
|
func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { |
||||||
|
// If we're not storing the node, just hashing, use avaialble cached data
|
||||||
|
if hash, dirty := n.cache(); hash != nil && (db == nil || !dirty) { |
||||||
|
return hash, n, nil |
||||||
|
} |
||||||
|
// Trie not processed yet or needs storage, walk the children
|
||||||
|
collapsed, cached, err := h.hashChildren(n, db) |
||||||
|
if err != nil { |
||||||
|
return hashNode{}, n, err |
||||||
|
} |
||||||
|
hashed, err := h.store(collapsed, db, force) |
||||||
|
if err != nil { |
||||||
|
return hashNode{}, n, err |
||||||
|
} |
||||||
|
// Cache the hash and RLP blob of the ndoe for later reuse
|
||||||
|
if hash, ok := hashed.(hashNode); ok && !force { |
||||||
|
switch cached := cached.(type) { |
||||||
|
case shortNode: |
||||||
|
cached.hash = hash |
||||||
|
if db != nil { |
||||||
|
cached.dirty = false |
||||||
|
} |
||||||
|
return hashed, cached, nil |
||||||
|
case fullNode: |
||||||
|
cached.hash = hash |
||||||
|
if db != nil { |
||||||
|
cached.dirty = false |
||||||
|
} |
||||||
|
return hashed, cached, nil |
||||||
|
} |
||||||
|
} |
||||||
|
return hashed, cached, nil |
||||||
|
} |
||||||
|
|
||||||
|
// hashChildren replaces the children of a node with their hashes if the encoded
|
||||||
|
// size of the child is larger than a hash, returning the collapsed node as well
|
||||||
|
// as a replacement for the original node with the child hashes cached in.
|
||||||
|
func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { |
||||||
|
var err error |
||||||
|
|
||||||
|
switch n := original.(type) { |
||||||
|
case shortNode: |
||||||
|
// Hash the short node's child, caching the newly hashed subtree
|
||||||
|
cached := n |
||||||
|
cached.Key = common.CopyBytes(cached.Key) |
||||||
|
|
||||||
|
n.Key = compactEncode(n.Key) |
||||||
|
if _, ok := n.Val.(valueNode); !ok { |
||||||
|
if n.Val, cached.Val, err = h.hash(n.Val, db, false); err != nil { |
||||||
|
return n, original, err |
||||||
|
} |
||||||
|
} |
||||||
|
if n.Val == nil { |
||||||
|
n.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings.
|
||||||
|
} |
||||||
|
return n, cached, nil |
||||||
|
|
||||||
|
case fullNode: |
||||||
|
// Hash the full node's children, caching the newly hashed subtrees
|
||||||
|
cached := fullNode{dirty: n.dirty} |
||||||
|
|
||||||
|
for i := 0; i < 16; i++ { |
||||||
|
if n.Children[i] != nil { |
||||||
|
if n.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false); err != nil { |
||||||
|
return n, original, err |
||||||
|
} |
||||||
|
} else { |
||||||
|
n.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings.
|
||||||
|
} |
||||||
|
} |
||||||
|
cached.Children[16] = n.Children[16] |
||||||
|
if n.Children[16] == nil { |
||||||
|
n.Children[16] = valueNode(nil) |
||||||
|
} |
||||||
|
return n, cached, nil |
||||||
|
|
||||||
|
default: |
||||||
|
// Value and hash nodes don't have children so they're left as were
|
||||||
|
return n, original, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (h *hasher) store(n node, db DatabaseWriter, force bool) (node, error) { |
||||||
|
// Don't store hashes or empty nodes.
|
||||||
|
if _, isHash := n.(hashNode); n == nil || isHash { |
||||||
|
return n, nil |
||||||
|
} |
||||||
|
// Generate the RLP encoding of the node
|
||||||
|
h.tmp.Reset() |
||||||
|
if err := rlp.Encode(h.tmp, n); err != nil { |
||||||
|
panic("encode error: " + err.Error()) |
||||||
|
} |
||||||
|
if h.tmp.Len() < 32 && !force { |
||||||
|
return n, nil // Nodes smaller than 32 bytes are stored inside their parent
|
||||||
|
} |
||||||
|
// Larger nodes are replaced by their hash and stored in the database.
|
||||||
|
hash, _ := n.cache() |
||||||
|
if hash == nil { |
||||||
|
h.sha.Reset() |
||||||
|
h.sha.Write(h.tmp.Bytes()) |
||||||
|
hash = hashNode(h.sha.Sum(nil)) |
||||||
|
} |
||||||
|
if db != nil { |
||||||
|
return hash, db.Put(hash, h.tmp.Bytes()) |
||||||
|
} |
||||||
|
return hash, nil |
||||||
|
} |
Loading…
Reference in new issue