Skip to content

Commit feb0ba7

Browse files
committed
feat: remove block decoding global registry
1 parent e379dec commit feb0ba7

File tree

2 files changed

+84
-32
lines changed

2 files changed

+84
-32
lines changed

coding.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,53 @@ package format
22

33
import (
44
"fmt"
5-
"sync"
6-
75
blocks "github.com/ipfs/go-block-format"
86
)
97

108
// DecodeBlockFunc functions decode blocks into nodes.
119
type DecodeBlockFunc func(block blocks.Block) (Node, error)
1210

13-
type BlockDecoder interface {
14-
Register(codec uint64, decoder DecodeBlockFunc)
15-
Decode(blocks.Block) (Node, error)
16-
}
17-
type safeBlockDecoder struct {
18-
// Can be replaced with an RCU if necessary.
19-
lock sync.RWMutex
11+
// Registry is a structure for storing mappings of multicodec IPLD codec numbers to DecodeBlockFunc functions.
12+
//
13+
// Registry includes no mutexing. If using Registry in a concurrent context, you must handle synchronization yourself.
14+
// (Typically, it is recommended to do initialization earlier in a program, before fanning out goroutines;
15+
// this avoids the need for mutexing overhead.)
16+
//
17+
// Multicodec indicator numbers are specified in
18+
// https://github.com/multiformats/multicodec/blob/master/table.csv .
19+
// You should not use indicator numbers which are not specified in that table
20+
// (however, there is nothing in this implementation that will attempt to stop you, either).
21+
type Registry struct {
2022
decoders map[uint64]DecodeBlockFunc
2123
}
2224

25+
func (r *Registry) ensureInit() {
26+
if r.decoders != nil {
27+
return
28+
}
29+
r.decoders = make(map[uint64]DecodeBlockFunc)
30+
}
31+
2332
// Register registers decoder for all blocks with the passed codec.
2433
//
2534
// This will silently replace any existing registered block decoders.
26-
func (d *safeBlockDecoder) Register(codec uint64, decoder DecodeBlockFunc) {
27-
d.lock.Lock()
28-
defer d.lock.Unlock()
29-
d.decoders[codec] = decoder
35+
func (r *Registry) Register(codec uint64, decoder DecodeBlockFunc) {
36+
r.ensureInit()
37+
if decoder == nil {
38+
panic("not sensible to attempt to register a nil function")
39+
}
40+
r.decoders[codec] = decoder
3041
}
3142

32-
func (d *safeBlockDecoder) Decode(block blocks.Block) (Node, error) {
43+
func (r *Registry) Decode(block blocks.Block) (Node, error) {
3344
// Short-circuit by cast if we already have a Node.
3445
if node, ok := block.(Node); ok {
3546
return node, nil
3647
}
3748

3849
ty := block.Cid().Type()
39-
40-
d.lock.RLock()
41-
decoder, ok := d.decoders[ty]
42-
d.lock.RUnlock()
50+
r.ensureInit()
51+
decoder, ok := r.decoders[ty]
4352

4453
if ok {
4554
return decoder(block)
@@ -49,14 +58,13 @@ func (d *safeBlockDecoder) Decode(block blocks.Block) (Node, error) {
4958
}
5059
}
5160

52-
var DefaultBlockDecoder BlockDecoder = &safeBlockDecoder{decoders: make(map[uint64]DecodeBlockFunc)}
53-
54-
// Decode decodes the given block using the default BlockDecoder.
55-
func Decode(block blocks.Block) (Node, error) {
56-
return DefaultBlockDecoder.Decode(block)
57-
}
61+
// Decode decodes the given block using passed DecodeBlockFunc.
62+
// Note: this is just a helper function, consider using the DecodeBlockFunc itself rather than this helper
63+
func Decode(block blocks.Block, decoder DecodeBlockFunc) (Node, error) {
64+
// Short-circuit by cast if we already have a Node.
65+
if node, ok := block.(Node); ok {
66+
return node, nil
67+
}
5868

59-
// Register registers block decoders with the default BlockDecoder.
60-
func Register(codec uint64, decoder DecodeBlockFunc) {
61-
DefaultBlockDecoder.Register(codec, decoder)
69+
return decoder(block)
6270
}

coding_test.go

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,53 @@ import (
99
mh "github.com/multiformats/go-multihash"
1010
)
1111

12-
func init() {
13-
Register(cid.Raw, func(b blocks.Block) (Node, error) {
12+
func TestDecode(t *testing.T) {
13+
decoder := func(b blocks.Block) (Node, error) {
1414
node := &EmptyNode{}
1515
if b.RawData() != nil || !b.Cid().Equals(node.Cid()) {
1616
return nil, errors.New("can only decode empty blocks")
1717
}
1818
return node, nil
19-
})
19+
}
20+
21+
id, err := cid.Prefix{
22+
Version: 1,
23+
Codec: cid.Raw,
24+
MhType: mh.ID,
25+
MhLength: 0,
26+
}.Sum(nil)
27+
28+
if err != nil {
29+
t.Fatalf("failed to create cid: %s", err)
30+
}
31+
32+
block, err := blocks.NewBlockWithCid(nil, id)
33+
if err != nil {
34+
t.Fatalf("failed to create empty block: %s", err)
35+
}
36+
node, err := Decode(block, decoder)
37+
if err != nil {
38+
t.Fatalf("failed to decode empty node: %s", err)
39+
}
40+
if !node.Cid().Equals(id) {
41+
t.Fatalf("empty node doesn't have the right cid")
42+
}
43+
44+
if _, ok := node.(*EmptyNode); !ok {
45+
t.Fatalf("empty node doesn't have the right type")
46+
}
47+
2048
}
2149

22-
func TestDecode(t *testing.T) {
50+
func TestRegistryDecode(t *testing.T) {
51+
decoder := func(b blocks.Block) (Node, error) {
52+
node := &EmptyNode{}
53+
if b.RawData() != nil || !b.Cid().Equals(node.Cid()) {
54+
return nil, errors.New("can only decode empty blocks")
55+
}
56+
return node, nil
57+
}
58+
2359
id, err := cid.Prefix{
2460
Version: 1,
2561
Codec: cid.Raw,
@@ -35,10 +71,18 @@ func TestDecode(t *testing.T) {
3571
if err != nil {
3672
t.Fatalf("failed to create empty block: %s", err)
3773
}
38-
node, err := Decode(block)
74+
75+
reg := Registry{}
76+
_, err = reg.Decode(block)
77+
if err == nil || err.Error() != "unrecognized object type: 85" {
78+
t.Fatalf("expected error, got %v", err)
79+
}
80+
reg.Register(cid.Raw, decoder)
81+
node, err := reg.Decode(block)
3982
if err != nil {
4083
t.Fatalf("failed to decode empty node: %s", err)
4184
}
85+
4286
if !node.Cid().Equals(id) {
4387
t.Fatalf("empty node doesn't have the right cid")
4488
}

0 commit comments

Comments
 (0)