mirror of https://github.com/ethereum/go-ethereum
parent
863d166c7b
commit
cd791bd855
@ -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