mirror of
https://github.com/ipfs/kubo.git
synced 2025-07-01 19:24:14 +08:00
add blockset and bloomfilter and beginnings of pinning service
This commit is contained in:
70
blocks/bloom/filter.go
Normal file
70
blocks/bloom/filter.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package bloom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"hash/adler32"
|
||||||
|
"hash/crc32"
|
||||||
|
"hash/fnv"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Filter interface {
|
||||||
|
Add([]byte)
|
||||||
|
Find([]byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func BasicFilter() Filter {
|
||||||
|
// Non crypto hashes, because speed
|
||||||
|
return NewFilter(2048, adler32.New(), fnv.New32(), crc32.NewIEEE())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFilter(size int, hashes ...hash.Hash) Filter {
|
||||||
|
return &filter{
|
||||||
|
filter: make([]byte, size),
|
||||||
|
hashes: hashes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type filter struct {
|
||||||
|
filter []byte
|
||||||
|
hashes []hash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) Add(k []byte) {
|
||||||
|
for _, h := range f.hashes {
|
||||||
|
i := bytesMod(h.Sum(k), int64(len(f.filter)*8))
|
||||||
|
f.setBit(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) Find(k []byte) bool {
|
||||||
|
for _, h := range f.hashes {
|
||||||
|
i := bytesMod(h.Sum(k), int64(len(f.filter)*8))
|
||||||
|
if !f.getBit(i) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) setBit(i int64) {
|
||||||
|
fmt.Printf("setting bit %d\n", i)
|
||||||
|
f.filter[i/8] |= (1 << byte(i%8))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) getBit(i int64) bool {
|
||||||
|
fmt.Printf("getting bit %d\n", i)
|
||||||
|
return f.filter[i/8]&(1<<byte(i%8)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func bytesMod(b []byte, modulo int64) int64 {
|
||||||
|
i := big.NewInt(0)
|
||||||
|
i = i.SetBytes(b)
|
||||||
|
|
||||||
|
bigmod := big.NewInt(int64(modulo))
|
||||||
|
result := big.NewInt(0)
|
||||||
|
result.Mod(i, bigmod)
|
||||||
|
|
||||||
|
return result.Int64()
|
||||||
|
}
|
10
blocks/bloom/filter.proto
Normal file
10
blocks/bloom/filter.proto
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package bloom;
|
||||||
|
|
||||||
|
message PackedFilter {
|
||||||
|
enum HashType {
|
||||||
|
|
||||||
|
}
|
||||||
|
optional bool compressed;
|
||||||
|
optional bytes data;
|
||||||
|
repeated HashType hashes;
|
||||||
|
}
|
30
blocks/bloom/filter_test.go
Normal file
30
blocks/bloom/filter_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package bloom
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
f := BasicFilter()
|
||||||
|
keys := [][]byte{
|
||||||
|
[]byte("hello"),
|
||||||
|
[]byte("fish"),
|
||||||
|
[]byte("ipfsrocks"),
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Add(keys[0])
|
||||||
|
if !f.Find(keys[0]) {
|
||||||
|
t.Fatal("Failed to find single inserted key!")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Add(keys[1])
|
||||||
|
if !f.Find(keys[1]) {
|
||||||
|
t.Fatal("Failed to find key!")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Add(keys[2])
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
if !f.Find(k) {
|
||||||
|
t.Fatal("Couldnt find one of three keys")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
blocks/set/set.go
Normal file
42
blocks/set/set.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jbenet/go-ipfs/blocks/bloom"
|
||||||
|
"github.com/jbenet/go-ipfs/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockSet interface {
|
||||||
|
AddBlock(util.Key)
|
||||||
|
RemoveBlock(util.Key)
|
||||||
|
HasKey(util.Key) bool
|
||||||
|
GetBloomFilter() bloom.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimpleBlockSet() BlockSet {
|
||||||
|
return &simpleBlockSet{blocks: make(map[util.Key]struct{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleBlockSet struct {
|
||||||
|
blocks map[util.Key]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBlockSet) AddBlock(k util.Key) {
|
||||||
|
b.blocks[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBlockSet) RemoveBlock(k util.Key) {
|
||||||
|
delete(b.blocks, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBlockSet) HasKey(k util.Key) bool {
|
||||||
|
_, has := b.blocks[k]
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBlockSet) GetBloomFilter() bloom.Filter {
|
||||||
|
f := bloom.BasicFilter()
|
||||||
|
for k, _ := range b.blocks {
|
||||||
|
f.Add([]byte(k))
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
@ -59,6 +59,14 @@ func MakeLink(n *Node) (*Link, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Link) GetNode(serv *DAGService) (*Node, error) {
|
||||||
|
if l.Node != nil {
|
||||||
|
return l.Node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return serv.Get(u.Key(l.Hash))
|
||||||
|
}
|
||||||
|
|
||||||
// AddNodeLink adds a link to another node.
|
// AddNodeLink adds a link to another node.
|
||||||
func (n *Node) AddNodeLink(name string, that *Node) error {
|
func (n *Node) AddNodeLink(name string, that *Node) error {
|
||||||
lnk, err := MakeLink(that)
|
lnk, err := MakeLink(that)
|
||||||
|
88
pin/pin.go
Normal file
88
pin/pin.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package pin
|
||||||
|
|
||||||
|
import (
|
||||||
|
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
|
||||||
|
"github.com/jbenet/go-ipfs/blocks/set"
|
||||||
|
mdag "github.com/jbenet/go-ipfs/merkledag"
|
||||||
|
"github.com/jbenet/go-ipfs/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pinner interface {
|
||||||
|
Pin(*mdag.Node, bool) error
|
||||||
|
Unpin(util.Key, bool) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type pinner struct {
|
||||||
|
recursePin set.BlockSet
|
||||||
|
directPin set.BlockSet
|
||||||
|
indirPin set.BlockSet
|
||||||
|
dserv *mdag.DAGService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPinner(dstore ds.Datastore, serv *mdag.DAGService) Pinner {
|
||||||
|
return &pinner{
|
||||||
|
recursePin: set.NewSimpleBlockSet(),
|
||||||
|
directPin: set.NewSimpleBlockSet(),
|
||||||
|
indirPin: NewRefCountBlockSet(),
|
||||||
|
dserv: serv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pinner) Pin(node *mdag.Node, recurse bool) error {
|
||||||
|
k, err := node.Key()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if recurse {
|
||||||
|
if p.recursePin.HasKey(k) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.recursePin.AddBlock(k)
|
||||||
|
|
||||||
|
err := p.pinLinks(node)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.directPin.AddBlock(k)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pinner) Unpin(k util.Key, recurse bool) error {
|
||||||
|
panic("not yet implemented!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pinner) pinIndirectRecurse(node *mdag.Node) error {
|
||||||
|
k, err := node.Key()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.indirPin.AddBlock(k)
|
||||||
|
return p.pinLinks(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pinner) pinLinks(node *mdag.Node) error {
|
||||||
|
for _, l := range node.Links {
|
||||||
|
subnode, err := l.GetNode(p.dserv)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Maybe just log and continue?
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = p.pinIndirectRecurse(subnode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pinner) IsPinned(key util.Key) bool {
|
||||||
|
return p.recursePin.HasKey(key) ||
|
||||||
|
p.directPin.HasKey(key) ||
|
||||||
|
p.indirPin.HasKey(key)
|
||||||
|
}
|
40
pin/refset.go
Normal file
40
pin/refset.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package pin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jbenet/go-ipfs/blocks/bloom"
|
||||||
|
"github.com/jbenet/go-ipfs/blocks/set"
|
||||||
|
"github.com/jbenet/go-ipfs/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type refCntBlockSet struct {
|
||||||
|
blocks map[util.Key]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRefCountBlockSet() set.BlockSet {
|
||||||
|
return &refCntBlockSet{blocks: make(map[util.Key]int)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refCntBlockSet) AddBlock(k util.Key) {
|
||||||
|
r.blocks[k]++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refCntBlockSet) RemoveBlock(k util.Key) {
|
||||||
|
v, ok := r.blocks[k]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v <= 1 {
|
||||||
|
delete(r.blocks, k)
|
||||||
|
} else {
|
||||||
|
r.blocks[k] = v - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refCntBlockSet) HasKey(k util.Key) bool {
|
||||||
|
_, ok := r.blocks[k]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *refCntBlockSet) GetBloomFilter() bloom.Filter {
|
||||||
|
return nil
|
||||||
|
}
|
Reference in New Issue
Block a user