blockstore.go 2.75 KB
Newer Older
1 2
// package blockstore implements a thin wrapper over a datastore, giving a
// clean interface for Getting and Putting block objects.
3 4 5 6 7
package blockstore

import (
	"errors"

8
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10
	dsns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace"
	dsq "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query"
11
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
12

13 14 15 16
	blocks "github.com/jbenet/go-ipfs/blocks"
	u "github.com/jbenet/go-ipfs/util"
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
17 18 19
// BlockPrefix namespaces blockstore datastores
var BlockPrefix = ds.NewKey("blocks")

20 21
var ValueTypeMismatch = errors.New("The retrieved value is not a Block")

22 23
var ErrNotFound = errors.New("blockstore: block not found")

Brian Tiger Chow's avatar
Brian Tiger Chow committed
24
// Blockstore wraps a ThreadSafeDatastore
25
type Blockstore interface {
26 27
	DeleteBlock(u.Key) error
	Has(u.Key) (bool, error)
28
	Get(u.Key) (*blocks.Block, error)
29
	Put(*blocks.Block) error
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30
	AllKeys(offset int, limit int) ([]u.Key, error)
31 32
}

33
func NewBlockstore(d ds.ThreadSafeDatastore) Blockstore {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34
	dd := dsns.Wrap(d, BlockPrefix)
35
	return &blockstore{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
36
		datastore: dd,
37 38 39 40
	}
}

type blockstore struct {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42 43
	datastore ds.Datastore
	// cant be ThreadSafeDatastore cause namespace.Datastore doesnt support it.
	// we do check it on `NewBlockstore` though.
44 45 46
}

func (bs *blockstore) Get(k u.Key) (*blocks.Block, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
47
	maybeData, err := bs.datastore.Get(k.DsKey())
48 49 50
	if err == ds.ErrNotFound {
		return nil, ErrNotFound
	}
51 52 53 54 55 56 57
	if err != nil {
		return nil, err
	}
	bdata, ok := maybeData.([]byte)
	if !ok {
		return nil, ValueTypeMismatch
	}
Jeromy's avatar
Jeromy committed
58 59

	return blocks.NewBlockWithHash(bdata, mh.Multihash(k))
60 61
}

62
func (bs *blockstore) Put(block *blocks.Block) error {
63 64 65 66 67 68 69
	// Has is cheaper than
	k := block.Key().DsKey()
	exists, err := bs.datastore.Has(k)
	if err != nil && exists {
		return nil // already stored.
	}
	return bs.datastore.Put(k, block.Data)
70
}
71 72 73 74 75 76 77 78

func (bs *blockstore) Has(k u.Key) (bool, error) {
	return bs.datastore.Has(k.DsKey())
}

func (s *blockstore) DeleteBlock(k u.Key) error {
	return s.datastore.Delete(k.DsKey())
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

// AllKeys runs a query for keys from the blockstore.
// this is very simplistic, in the future, take dsq.Query as a param?
// if offset and limit are 0, they are ignored.
func (bs *blockstore) AllKeys(offset int, limit int) ([]u.Key, error) {
	var keys []u.Key

	// TODO make async inside ds/leveldb.Query
	// KeysOnly, because that would be _a lot_ of data.
	q := dsq.Query{KeysOnly: true, Offset: offset, Limit: limit}
	res, err := bs.datastore.Query(q)
	if err != nil {
		return nil, err
	}

	for e := range res.Entries() {
		// need to convert to u.Key using u.KeyFromDsKey.
		k := u.KeyFromDsKey(ds.NewKey(e.Key))
		keys = append(keys, k)
	}
	return keys, nil
}