mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
initial commit for swarm/bzz - to include blockhash pkg https://github.com/nagydani/go-dpa
This commit is contained in:
parent
75f0412f9d
commit
e32cdc4244
5 changed files with 982 additions and 0 deletions
377
bzz/blockhash.go
Normal file
377
bzz/blockhash.go
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
The blockhash package implements a hash tree based fixed block size distributed
|
||||
data storage
|
||||
The block hash of a byte array is defined as follows:
|
||||
|
||||
- if size is no more than BlockSize, it is stored in a single block
|
||||
blockhash = sha256(int64(size) + data)
|
||||
|
||||
- if size is more than BlockSize*BlockHashCount^l, but no more than BlockSize*
|
||||
BlockHashCount^(l+1), the data vector is split into slices of BlockSize*
|
||||
BlockHashCount^l length (except the last one).
|
||||
blockhash = sha256(int64(size) + blockhash(slice0) + blockhash(slice1) + ...)
|
||||
*/
|
||||
|
||||
package blockhash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const HashSize = 32
|
||||
const BlockSize = 4096
|
||||
const BlockHashCount = BlockSize / HashSize
|
||||
|
||||
type HashType []byte
|
||||
|
||||
/*
|
||||
The layered (memory, disk, distributed) storage model provides two channels, one
|
||||
for storing and one for retrieving blocks. The layers are chained so that every
|
||||
layer can store blocks and try to retrieve them if the previous layer did not
|
||||
succeed.
|
||||
*/
|
||||
|
||||
type dpaStorage struct {
|
||||
store_chn chan *dpaStoreReq
|
||||
retrieve_chn chan *dpaRetrieveReq
|
||||
chain *dpaStorage
|
||||
}
|
||||
|
||||
type dpaReaderAt struct {
|
||||
hash HashType
|
||||
store *dpaStorage
|
||||
size int64
|
||||
}
|
||||
|
||||
type dpaNode struct {
|
||||
data []byte
|
||||
size int64 // denotes the size of data represented by the whole subtree
|
||||
}
|
||||
|
||||
type dpaStoreReq struct {
|
||||
dpaNode
|
||||
hash HashType
|
||||
}
|
||||
|
||||
type dpaRetrieveRes struct {
|
||||
dpaNode
|
||||
req_id int
|
||||
}
|
||||
|
||||
type dpaRetrieveReq struct {
|
||||
hash HashType
|
||||
req_id int
|
||||
result_chn chan *dpaRetrieveRes
|
||||
}
|
||||
|
||||
func (h HashType) bits(i, j uint) uint {
|
||||
|
||||
ii := i >> 3
|
||||
jj := i & 7
|
||||
if ii >= HashSize {
|
||||
return 0
|
||||
}
|
||||
|
||||
if jj+j <= 8 {
|
||||
return uint((h[ii] >> jj) & ((1 << j) - 1))
|
||||
}
|
||||
|
||||
res := uint(h[ii] >> jj)
|
||||
jj = 8 - jj
|
||||
j -= jj
|
||||
for j != 0 {
|
||||
ii++
|
||||
if j < 8 {
|
||||
res += uint(h[ii]&((1<<j)-1)) << jj
|
||||
return res
|
||||
}
|
||||
res += uint(h[ii]) << jj
|
||||
jj += 8
|
||||
j -= 8
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (h HashType) isEqual(h2 HashType) bool {
|
||||
|
||||
for i := range h {
|
||||
if h[i] != h2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaStorage) Init() {
|
||||
|
||||
s.store_chn = make(chan *dpaStoreReq, 1000)
|
||||
s.retrieve_chn = make(chan *dpaRetrieveReq, 1000)
|
||||
|
||||
}
|
||||
|
||||
// get the root hash of any data vector and store the blocks of the tree if store != nil
|
||||
|
||||
func GetDPAroot(data []byte, store *dpaStorage) HashType {
|
||||
|
||||
return GetDPAhash(io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))), store)
|
||||
|
||||
}
|
||||
|
||||
func goGetDPAhash(reader *io.SectionReader, store *dpaStorage, hash HashType, done chan<- bool) {
|
||||
|
||||
hh := GetDPAhash(reader, store)
|
||||
if hh == nil {
|
||||
done <- false
|
||||
} else {
|
||||
copy(hash[:], hh[:])
|
||||
done <- true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetDPAhash(reader *io.SectionReader, store *dpaStorage) HashType {
|
||||
|
||||
size := reader.Size()
|
||||
var block []byte
|
||||
|
||||
if size <= BlockSize {
|
||||
block = make([]byte, size)
|
||||
br, _ := reader.Read(block)
|
||||
if br < int(size) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
stc := (size + BlockSize - 1) / BlockSize
|
||||
SubtreeSize := int64(BlockSize)
|
||||
|
||||
for stc > BlockHashCount {
|
||||
stc = (stc-1)/BlockHashCount + 1
|
||||
SubtreeSize *= BlockHashCount
|
||||
}
|
||||
SubtreeCount := int(stc)
|
||||
|
||||
block = make([]byte, SubtreeCount*HashSize)
|
||||
|
||||
hdone := make(chan bool, SubtreeCount)
|
||||
|
||||
ptr := int64(0)
|
||||
hptr := 0
|
||||
for i := 0; i < SubtreeCount; i++ {
|
||||
ptr2 := ptr + SubtreeSize
|
||||
if ptr2 > size {
|
||||
ptr2 = size
|
||||
}
|
||||
go goGetDPAhash(io.NewSectionReader(reader, ptr, ptr2-ptr), store, HashType(block[hptr:hptr+HashSize]), hdone)
|
||||
ptr = ptr2
|
||||
hptr += HashSize
|
||||
}
|
||||
|
||||
for i := 0; i < SubtreeCount; i++ {
|
||||
if !<-hdone {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hashfn := sha256.New()
|
||||
//binary.LittleEndian.PutUint16(b, uint16(i))
|
||||
//fmt.Printf("%d\n", size)
|
||||
binary.Write(hashfn, binary.LittleEndian, int64(size))
|
||||
hashfn.Write(block)
|
||||
hash := hashfn.Sum(nil)
|
||||
|
||||
if store != nil {
|
||||
req := new(dpaStoreReq)
|
||||
req.data = block
|
||||
req.size = int64(size)
|
||||
req.hash = hash
|
||||
store.store_chn <- req
|
||||
}
|
||||
|
||||
return hash
|
||||
|
||||
}
|
||||
|
||||
// recursive function to retrieve a section of a subtree
|
||||
// len(data) == stop-start
|
||||
|
||||
func getDPAblock(res *dpaRetrieveRes, data []byte, start int64, stop int64, bsize int64, retrv chan<- *dpaRetrieveReq, done chan<- bool) bool {
|
||||
|
||||
for bsize >= res.size {
|
||||
if bsize == BlockSize {
|
||||
bsize = 0
|
||||
} else {
|
||||
bsize /= BlockHashCount
|
||||
}
|
||||
}
|
||||
|
||||
if bsize < BlockSize {
|
||||
if res.size < stop {
|
||||
if done != nil {
|
||||
done <- false
|
||||
}
|
||||
return false
|
||||
}
|
||||
copy(data[:], res.data[start:stop])
|
||||
if done != nil {
|
||||
done <- true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
bstart := int(start / bsize)
|
||||
bstop := int((stop + bsize - 1) / bsize)
|
||||
|
||||
if len(res.data) < bstop*HashSize {
|
||||
if done != nil {
|
||||
done <- false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
chn := make(chan *dpaRetrieveRes, bstop-bstart)
|
||||
sdone := make(chan bool, bstop-bstart)
|
||||
|
||||
for i := bstart; i < bstop; i++ {
|
||||
|
||||
hash := HashType(res.data[i*HashSize : (i+1)*HashSize])
|
||||
req := new(dpaRetrieveReq)
|
||||
req.hash = hash
|
||||
req.req_id = i
|
||||
req.result_chn = chn
|
||||
retrv <- req
|
||||
|
||||
}
|
||||
|
||||
for j := bstart; j < bstop; j++ {
|
||||
|
||||
res := <-chn
|
||||
|
||||
i := int64(res.req_id)
|
||||
a := i * bsize
|
||||
aa := a
|
||||
b := a + bsize
|
||||
|
||||
if a < start {
|
||||
a = start
|
||||
}
|
||||
if b > stop {
|
||||
b = stop
|
||||
}
|
||||
|
||||
if res.size < b-aa {
|
||||
if done != nil {
|
||||
done <- false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if bsize == BlockSize {
|
||||
getDPAblock(res, data[a-start:b-start], a-aa, b-aa, 0, retrv, sdone)
|
||||
} else {
|
||||
go getDPAblock(res, data[a-start:b-start], a-aa, b-aa, bsize/BlockHashCount, retrv, sdone)
|
||||
}
|
||||
}
|
||||
|
||||
dd := true
|
||||
for j := bstart; j < bstop; j++ {
|
||||
if !<-sdone {
|
||||
dd = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if done != nil {
|
||||
done <- dd
|
||||
}
|
||||
return dd
|
||||
|
||||
}
|
||||
|
||||
func (r *dpaReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
|
||||
chn := make(chan *dpaRetrieveRes)
|
||||
|
||||
req := new(dpaRetrieveReq)
|
||||
req.hash = r.hash
|
||||
req.req_id = 0
|
||||
req.result_chn = chn
|
||||
|
||||
r.store.retrieve_chn <- req
|
||||
res := <-chn
|
||||
|
||||
if res.size == 0 {
|
||||
return 0, fmt.Errorf("Block hash %064x not found", r.hash)
|
||||
}
|
||||
|
||||
r.size = res.size
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
bsize := int64(0)
|
||||
if res.size > BlockSize {
|
||||
bsize = int64(BlockSize)
|
||||
for bsize*BlockHashCount < res.size {
|
||||
bsize *= BlockHashCount
|
||||
}
|
||||
}
|
||||
|
||||
err = error(nil)
|
||||
|
||||
eoff := off + int64(len(p))
|
||||
if eoff > res.size {
|
||||
eoff = res.size
|
||||
err = io.EOF
|
||||
}
|
||||
|
||||
if !getDPAblock(res, p, off, eoff, bsize, r.store.retrieve_chn, nil) {
|
||||
return 0, fmt.Errorf("Can't load section [%d:%d] of block hash %064x", off, eoff, r.hash)
|
||||
}
|
||||
|
||||
return int(eoff - off), err
|
||||
|
||||
}
|
||||
|
||||
func GetDPAreader(hash HashType, st *dpaStorage) *io.SectionReader {
|
||||
|
||||
rd := new(dpaReaderAt)
|
||||
rd.hash = hash
|
||||
rd.store = st
|
||||
rd.size = -1
|
||||
|
||||
rd.ReadAt(nil, 0)
|
||||
|
||||
if rd.size >= 0 {
|
||||
return io.NewSectionReader(rd, 0, rd.size)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// retrieve a data vector of a given block hash from the given storage
|
||||
|
||||
func GetDPAdata(hash HashType, st *dpaStorage) []byte {
|
||||
|
||||
sr := GetDPAreader(hash, st)
|
||||
if sr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
size := sr.Size()
|
||||
|
||||
data := make([]byte, int(size))
|
||||
br, _ := sr.Read(data)
|
||||
if int64(br) == size {
|
||||
return data
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
97
bzz/blockhash_test.go
Normal file
97
bzz/blockhash_test.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// test bench for the package blockhash
|
||||
|
||||
package blockhash
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func maketest(l int) []byte {
|
||||
|
||||
r := rand.New(rand.NewSource(int64(l)))
|
||||
|
||||
test := make([]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
test[i] = byte(r.Intn(256))
|
||||
}
|
||||
|
||||
return test
|
||||
}
|
||||
|
||||
func cmptest(a, b []byte) bool {
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const testcnt = 10
|
||||
|
||||
func testlen(i int) int {
|
||||
|
||||
/* if i == 79 {
|
||||
return 16777217
|
||||
}*/
|
||||
|
||||
return int(0.5 + math.Exp2(3.0+float64(i)/5))
|
||||
}
|
||||
|
||||
func TestBlockHashStorage(t *testing.T) {
|
||||
t.Logf("Creating DBStorage...")
|
||||
|
||||
dbstore := new(dpaDBStorage)
|
||||
dbstore.Init(nil)
|
||||
go dbstore.Run()
|
||||
|
||||
t.Logf("Creating MemStorage...")
|
||||
|
||||
memstore := new(dpaMemStorage)
|
||||
memstore.Init(&dbstore.dpaStorage)
|
||||
go memstore.Run()
|
||||
|
||||
t.Logf("Storing test vectors...")
|
||||
|
||||
test := make([][]byte, testcnt)
|
||||
hash := make([]HashType, testcnt)
|
||||
for i := 0; i < testcnt; i++ {
|
||||
test[i] = maketest(testlen(i))
|
||||
//t.Logf("Test[%d] = %x", i, test[i])
|
||||
hash[i] = GetDPAroot(test[i], &memstore.dpaStorage)
|
||||
//t.Logf("Hash[%d] = %x", i, hash[i])
|
||||
}
|
||||
|
||||
t.Logf("Retrieving test vectors...")
|
||||
|
||||
rnd := rand.New(rand.NewSource(0))
|
||||
|
||||
for i := 0; i < testcnt; i++ {
|
||||
|
||||
tt := GetDPAdata(hash[i], &memstore.dpaStorage) // get the whole vector with byte array wrapper
|
||||
|
||||
sr := GetDPAreader(hash[i], &memstore.dpaStorage)
|
||||
size := int(sr.Size())
|
||||
pos := rnd.Intn(size - 1)
|
||||
slen := rnd.Intn(size-1-pos) + 1
|
||||
sr.Seek(int64(pos), 0)
|
||||
br, _ := sr.Read(tt[pos : pos+slen]) // re-read a random section
|
||||
|
||||
if (br == slen) && cmptest(test[i], tt) {
|
||||
t.Logf("Test case %d passed (test vector length %d)", i, len(tt))
|
||||
} else {
|
||||
t.Errorf("Test case %d failed", i)
|
||||
if size < 20 {
|
||||
t.Errorf("pos = %d slen = %d br = %d vector = %x instead of %x", pos, slen, br, tt, test[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
89
bzz/database.go
Normal file
89
bzz/database.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package blockhash
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/ethereum/go-ethereum/compression/rle"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
)
|
||||
|
||||
type LDBDatabase struct {
|
||||
db *leveldb.DB
|
||||
comp bool
|
||||
}
|
||||
|
||||
func NewLDBDatabase(name string) (*LDBDatabase, error) {
|
||||
dbPath := path.Join(ethutil.Config.ExecPath, name)
|
||||
|
||||
// Open the db
|
||||
db, err := leveldb.OpenFile(dbPath, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
database := &LDBDatabase{db: db, comp: true}
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Put(key []byte, value []byte) {
|
||||
if self.comp {
|
||||
value = rle.Compress(value)
|
||||
}
|
||||
|
||||
err := self.db.Put(key, value, nil)
|
||||
if err != nil {
|
||||
fmt.Println("Error put", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
|
||||
dat, err := self.db.Get(key, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.comp {
|
||||
return rle.Decompress(dat)
|
||||
}
|
||||
|
||||
return dat, nil
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Delete(key []byte) error {
|
||||
return self.db.Delete(key, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) LastKnownTD() []byte {
|
||||
data, _ := self.Get([]byte("LTD"))
|
||||
|
||||
if len(data) == 0 {
|
||||
data = []byte{0x0}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) NewIterator() iterator.Iterator {
|
||||
return self.db.NewIterator(nil, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Close() {
|
||||
// Close the leveldb database
|
||||
self.db.Close()
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Print() {
|
||||
iter := self.db.NewIterator(nil, nil)
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
value := iter.Value()
|
||||
|
||||
fmt.Printf("%x(%d): ", key, len(key))
|
||||
node := ethutil.NewValueFromBytes(value)
|
||||
fmt.Printf("%v\n", node)
|
||||
}
|
||||
}
|
||||
117
bzz/dbstore.go
Normal file
117
bzz/dbstore.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// disk storage layer for the package blockhash
|
||||
// inefficient work-in-progress version
|
||||
|
||||
package blockhash
|
||||
|
||||
import (
|
||||
// "crypto/sha256"
|
||||
// "encoding/binary"
|
||||
"bytes"
|
||||
"fmt"
|
||||
// "github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"path"
|
||||
)
|
||||
|
||||
type dpaDBStorage struct {
|
||||
dpaStorage
|
||||
// db *ethdb.LDBDatabase
|
||||
db *LDBDatabase
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) add(entry *dpaStoreReq) {
|
||||
|
||||
data := ethutil.Encode([]interface{}{entry.data, entry.size})
|
||||
|
||||
s.db.Put(entry.hash, data)
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) find(hash HashType) (entry dpaNode) {
|
||||
|
||||
fmt.Printf("mao")
|
||||
|
||||
data, err := s.db.Get(hash)
|
||||
if err != nil {
|
||||
panic("hi")
|
||||
}
|
||||
fmt.Printf("mao")
|
||||
|
||||
dec := rlp.NewListStream(bytes.NewReader(data), uint64(len(data)))
|
||||
dec.Decode(&entry)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) process_store(req *dpaStoreReq) {
|
||||
|
||||
s.add(req)
|
||||
|
||||
if s.chain != nil {
|
||||
s.chain.store_chn <- req
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) process_retrieve(req *dpaRetrieveReq) {
|
||||
|
||||
fmt.Printf("mao")
|
||||
entry := s.find(req.hash)
|
||||
|
||||
fmt.Printf("%v", entry.size)
|
||||
if entry.data == nil {
|
||||
if s.chain != nil {
|
||||
s.chain.retrieve_chn <- req
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res := new(dpaRetrieveRes)
|
||||
if entry.data != nil {
|
||||
res.dpaNode = entry
|
||||
}
|
||||
res.req_id = req.req_id
|
||||
req.result_chn <- res
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) Init(ch *dpaStorage) {
|
||||
|
||||
// dbPath := path.Join(ethutil.Config.ExecPath, "bzz")
|
||||
dbPath := path.Join(".", "bzz")
|
||||
|
||||
// Open the db
|
||||
db, err := leveldb.OpenFile(dbPath, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// s.db = ðdb.LDBDatabase{db: db, comp: false}
|
||||
s.db = &LDBDatabase{db: db, comp: false}
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaDBStorage) Run() {
|
||||
|
||||
for {
|
||||
bb := true
|
||||
for bb {
|
||||
select {
|
||||
case store := <-s.store_chn:
|
||||
s.process_store(store)
|
||||
default:
|
||||
bb = false
|
||||
}
|
||||
}
|
||||
select {
|
||||
case store := <-s.store_chn:
|
||||
s.process_store(store)
|
||||
case retrv := <-s.retrieve_chn:
|
||||
s.process_retrieve(retrv)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
302
bzz/memstore.go
Normal file
302
bzz/memstore.go
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// memory storage layer for the package blockhash
|
||||
|
||||
package blockhash
|
||||
|
||||
const MaxEntries = 500 // max number of stored (cached) blocks
|
||||
const MemTreeLW = 2 // log2(subtree count) of the subtrees
|
||||
const MemTreeFLW = 14 // log2(subtree count) of the root layer
|
||||
|
||||
type dpaMemStorage struct {
|
||||
dpaStorage
|
||||
memtree *dpaMemTree
|
||||
entry_cnt uint // stored entries
|
||||
access_cnt uint64 // access counter; oldest is thrown away when full
|
||||
}
|
||||
|
||||
/*
|
||||
a hash prefix subtree containing subtrees or one storage entry (but never both)
|
||||
|
||||
- access[0] stores the smallest (oldest) access count value in this subtree
|
||||
- if it contains more subtrees and its subtree count is at least 4, access[1:2]
|
||||
stores the smallest access count in the first and second halves of subtrees
|
||||
(so that access[0] = min(access[1], access[2])
|
||||
- likewise, if subtree count is at least 8,
|
||||
access[1] = min(access[3], access[4])
|
||||
access[2] = min(access[5], access[6])
|
||||
(access[] is a binary tree inside the multi-bit leveled hash tree)
|
||||
*/
|
||||
|
||||
type dpaMemTree struct {
|
||||
subtree []*dpaMemTree
|
||||
parent *dpaMemTree
|
||||
parent_idx uint
|
||||
|
||||
bits uint // log2(subtree count)
|
||||
width uint // subtree count
|
||||
|
||||
entry *dpaStoreReq // if subtrees are present, entry should be nil
|
||||
access []uint64
|
||||
}
|
||||
|
||||
func newTreeNode(b uint, parent *dpaMemTree, pidx uint) (node *dpaMemTree) {
|
||||
|
||||
node = new(dpaMemTree)
|
||||
node.bits = b
|
||||
node.width = 1 << uint(b)
|
||||
node.subtree = make([]*dpaMemTree, node.width)
|
||||
node.access = make([]uint64, node.width-1)
|
||||
node.parent = parent
|
||||
node.parent_idx = pidx
|
||||
if parent != nil {
|
||||
parent.subtree[pidx] = node
|
||||
}
|
||||
|
||||
return node
|
||||
|
||||
}
|
||||
|
||||
func (node *dpaMemTree) update_access(a uint64) {
|
||||
|
||||
aidx := uint(0)
|
||||
var aa uint64
|
||||
oa := node.access[0]
|
||||
for node.access[aidx] == oa {
|
||||
node.access[aidx] = a
|
||||
if aidx > 0 {
|
||||
aa = node.access[((aidx-1)^1)+1]
|
||||
aidx = (aidx - 1) >> 1
|
||||
} else {
|
||||
pidx := node.parent_idx
|
||||
node = node.parent
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
nn := node.subtree[pidx^1]
|
||||
if nn != nil {
|
||||
aa = nn.access[0]
|
||||
} else {
|
||||
aa = 0
|
||||
}
|
||||
aidx = (node.width + pidx - 2) >> 1
|
||||
}
|
||||
|
||||
if (aa != 0) && (aa < a) {
|
||||
a = aa
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaMemStorage) add(entry *dpaStoreReq) {
|
||||
|
||||
s.access_cnt++
|
||||
|
||||
node := s.memtree
|
||||
bitpos := uint(0)
|
||||
for node.entry == nil {
|
||||
l := entry.hash.bits(bitpos, node.bits)
|
||||
st := node.subtree[l]
|
||||
if st == nil {
|
||||
st = newTreeNode(MemTreeLW, node, l)
|
||||
bitpos += node.bits
|
||||
node = st
|
||||
break
|
||||
}
|
||||
bitpos += node.bits
|
||||
node = st
|
||||
}
|
||||
|
||||
if node.entry != nil {
|
||||
|
||||
if node.entry.hash.isEqual(entry.hash) {
|
||||
node.update_access(s.access_cnt)
|
||||
return
|
||||
}
|
||||
|
||||
for node.entry != nil {
|
||||
|
||||
l := node.entry.hash.bits(bitpos, node.bits)
|
||||
st := node.subtree[l]
|
||||
if st == nil {
|
||||
st = newTreeNode(MemTreeLW, node, l)
|
||||
}
|
||||
st.entry = node.entry
|
||||
node.entry = nil
|
||||
st.update_access(node.access[0])
|
||||
|
||||
l = entry.hash.bits(bitpos, node.bits)
|
||||
st = node.subtree[l]
|
||||
if st == nil {
|
||||
st = newTreeNode(MemTreeLW, node, l)
|
||||
}
|
||||
bitpos += node.bits
|
||||
node = st
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
node.entry = entry
|
||||
node.update_access(s.access_cnt)
|
||||
s.entry_cnt++
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaMemStorage) find(hash HashType) (entry *dpaStoreReq) {
|
||||
|
||||
node := s.memtree
|
||||
bitpos := uint(0)
|
||||
for node.entry == nil {
|
||||
l := hash.bits(bitpos, node.bits)
|
||||
st := node.subtree[l]
|
||||
if st == nil {
|
||||
return nil
|
||||
}
|
||||
bitpos += node.bits
|
||||
node = st
|
||||
}
|
||||
|
||||
if node.entry.hash.isEqual(hash) {
|
||||
s.access_cnt++
|
||||
node.update_access(s.access_cnt)
|
||||
return node.entry
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dpaMemStorage) remove_oldest() {
|
||||
|
||||
node := s.memtree
|
||||
|
||||
for node.entry == nil {
|
||||
|
||||
aidx := uint(0)
|
||||
av := node.access[aidx]
|
||||
|
||||
for aidx < node.width/2-1 {
|
||||
if av == node.access[aidx*2+1] {
|
||||
node.access[aidx] = node.access[aidx*2+2]
|
||||
aidx = aidx*2 + 1
|
||||
} else if av == node.access[aidx*2+2] {
|
||||
node.access[aidx] = node.access[aidx*2+1]
|
||||
aidx = aidx*2 + 2
|
||||
} else {
|
||||
panic(nil)
|
||||
}
|
||||
}
|
||||
pidx := aidx*2 + 2 - node.width
|
||||
if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) {
|
||||
if node.subtree[pidx+1] != nil {
|
||||
node.access[aidx] = node.subtree[pidx+1].access[0]
|
||||
} else {
|
||||
node.access[aidx] = 0
|
||||
}
|
||||
} else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) {
|
||||
if node.subtree[pidx] != nil {
|
||||
node.access[aidx] = node.subtree[pidx].access[0]
|
||||
} else {
|
||||
node.access[aidx] = 0
|
||||
}
|
||||
pidx++
|
||||
} else {
|
||||
panic(nil)
|
||||
}
|
||||
|
||||
//fmt.Println(pidx)
|
||||
node = node.subtree[pidx]
|
||||
|
||||
}
|
||||
|
||||
node.entry = nil
|
||||
s.entry_cnt--
|
||||
node.access[0] = 0
|
||||
|
||||
//---
|
||||
|
||||
aidx := uint(0)
|
||||
for {
|
||||
aa := node.access[aidx]
|
||||
if aidx > 0 {
|
||||
aidx = (aidx - 1) >> 1
|
||||
} else {
|
||||
pidx := node.parent_idx
|
||||
node = node.parent
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
aidx = (node.width + pidx - 2) >> 1
|
||||
}
|
||||
if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) {
|
||||
node.access[aidx] = aa
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// process store channel requests
|
||||
|
||||
func (s *dpaMemStorage) process_store(req *dpaStoreReq) {
|
||||
|
||||
if s.entry_cnt >= MaxEntries {
|
||||
s.remove_oldest()
|
||||
}
|
||||
s.add(req)
|
||||
|
||||
if s.chain != nil {
|
||||
s.chain.store_chn <- req
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// process retrieve channel requests
|
||||
|
||||
func (s *dpaMemStorage) process_retrieve(req *dpaRetrieveReq) {
|
||||
|
||||
entry := s.find(req.hash)
|
||||
if entry == nil {
|
||||
if s.chain != nil {
|
||||
s.chain.retrieve_chn <- req
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res := new(dpaRetrieveRes)
|
||||
if entry != nil {
|
||||
res.dpaNode = entry.dpaNode
|
||||
}
|
||||
res.req_id = req.req_id
|
||||
req.result_chn <- res
|
||||
|
||||
}
|
||||
|
||||
func (s *dpaMemStorage) Init(ch *dpaStorage) {
|
||||
|
||||
s.dpaStorage.Init()
|
||||
s.memtree = newTreeNode(MemTreeFLW, nil, 0)
|
||||
s.chain = ch
|
||||
|
||||
}
|
||||
|
||||
// storage main goroutine; always processes store messages first
|
||||
|
||||
func (s *dpaMemStorage) Run() {
|
||||
|
||||
for {
|
||||
bb := true
|
||||
for bb {
|
||||
select {
|
||||
case store := <-s.store_chn:
|
||||
s.process_store(store)
|
||||
default:
|
||||
bb = false
|
||||
}
|
||||
}
|
||||
select {
|
||||
case store := <-s.store_chn:
|
||||
s.process_store(store)
|
||||
case retrv := <-s.retrieve_chn:
|
||||
s.process_retrieve(retrv)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue