Skip to content

Commit 723e44d

Browse files
authored
Merge pull request #172 from ipld/multicodec-registry-type
Make a multicodec.Registry type available.
2 parents aff91f7 + 575054d commit 723e44d

File tree

4 files changed

+186
-112
lines changed

4 files changed

+186
-112
lines changed

linking/cid/linksystem.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,28 @@ import (
1010
"github.com/ipld/go-ipld-prime/multicodec"
1111
)
1212

13+
// DefaultLinkSystem returns an ipld.LinkSystem which uses cidlink.Link for ipld.Link.
14+
// During selection of encoders, decoders, and hashers, it examines the multicodec indicator numbers and multihash indicator numbers from the CID,
15+
// and uses the default global multicodec registry (see the go-ipld-prime/multicodec package) for resolving codec implementations,
16+
// and the default global multihash registry (see the go-multihash/core package) for resolving multihash implementations.
17+
//
18+
// No storage functions are present in the returned LinkSystem.
19+
// The caller can assign those themselves as desired.
1320
func DefaultLinkSystem() ipld.LinkSystem {
21+
return LinkSystemUsingMulticodecRegistry(multicodec.DefaultRegistry)
22+
}
23+
24+
// LinkSystemUsingMulticodecRegistry is similar to DefaultLinkSystem, but accepts a multicodec.Registry as a parameter.
25+
//
26+
// This can help create a LinkSystem which uses different multicodec implementations than the global registry.
27+
// (Sometimes this can be desired if you want some parts of a program to support a more limited suite of codecs than other parts of the program,
28+
// or needed to use a different multicodec registry than the global one for synchronization purposes, or etc.)
29+
func LinkSystemUsingMulticodecRegistry(mcReg multicodec.Registry) ipld.LinkSystem {
1430
return ipld.LinkSystem{
1531
EncoderChooser: func(lp ipld.LinkPrototype) (ipld.Encoder, error) {
1632
switch lp2 := lp.(type) {
1733
case LinkPrototype:
18-
fn, err := multicodec.LookupEncoder(lp2.GetCodec())
34+
fn, err := mcReg.LookupEncoder(lp2.GetCodec())
1935
if err != nil {
2036
return nil, err
2137
}
@@ -28,7 +44,7 @@ func DefaultLinkSystem() ipld.LinkSystem {
2844
lp := lnk.Prototype()
2945
switch lp2 := lp.(type) {
3046
case LinkPrototype:
31-
fn, err := multicodec.LookupDecoder(lp2.GetCodec())
47+
fn, err := mcReg.LookupDecoder(lp2.GetCodec())
3248
if err != nil {
3349
return nil, err
3450
}

multicodec/defaultRegistry.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package multicodec
2+
3+
import (
4+
"github.com/ipld/go-ipld-prime"
5+
)
6+
7+
// DefaultRegistry is a multicodec.Registry instance which is global to the program,
8+
// and is used as a default set of codecs.
9+
//
10+
// Some systems (for example, cidlink.DefaultLinkSystem) will use this default registry,
11+
// which makes it easier to write programs that pass fewer explicit arguments around.
12+
// However, these are *only* for default behaviors;
13+
// variations of functions which allow explicit non-default options should always be available
14+
// (for example, cidlink also has other LinkSystem constructor functions which accept an explicit multicodec.Registry,
15+
// and the LookupEncoder and LookupDecoder functions in any LinkSystem can be replaced).
16+
//
17+
// Since this registry is global, mind that there are also some necessary tradeoffs and limitations:
18+
// It can be difficult to control exactly what's present in this global registry
19+
// (Libraries may register codecs in this registry as a side-effect of importing, so even transitive dependencies can affect its content!).
20+
// Also, this registry is only considered safe to modify at package init time.
21+
// If these are concerns for your program, you can create your own multicodec.Registry values,
22+
// and eschew using the global default.
23+
var DefaultRegistry = Registry{}
24+
25+
// RegisterEncoder updates the global DefaultRegistry to map a multicodec indicator number to the given ipld.Encoder function.
26+
// The encoder functions registered can be subsequently looked up using LookupEncoder.
27+
// It is a shortcut to the RegisterEncoder method on the global DefaultRegistry.
28+
//
29+
// Packages which implement an IPLD codec and have a multicodec number associated with them
30+
// are encouraged to register themselves at package init time using this function.
31+
// (Doing this at package init time ensures the default global registry is populated
32+
// without causing race conditions for application code.)
33+
//
34+
// No effort is made to detect conflicting registrations in this map.
35+
// If your dependency tree is such that this becomes a problem,
36+
// there are two ways to address this:
37+
// If RegisterEncoder is called with the same indicator code more than once, the last call wins.
38+
// In practice, this means that if an application has a strong opinion about what implementation for a certain codec,
39+
// then this can be done by making a Register call with that effect at init time in the application's main package.
40+
// This should have the desired effect because the root of the import tree has its init time effect last.
41+
// Alternatively, one can just avoid use of this registry entirely:
42+
// do this by making a LinkSystem that uses a custom EncoderChooser function.
43+
func RegisterEncoder(indicator uint64, encodeFunc ipld.Encoder) {
44+
DefaultRegistry.RegisterEncoder(indicator, encodeFunc)
45+
}
46+
47+
// LookupEncoder yields an ipld.Encoder function matching a multicodec indicator code number.
48+
// It is a shortcut to the LookupEncoder method on the global DefaultRegistry.
49+
//
50+
// To be available from this lookup function, an encoder must have been registered
51+
// for this indicator number by an earlier call to the RegisterEncoder function.
52+
func LookupEncoder(indicator uint64) (ipld.Encoder, error) {
53+
return DefaultRegistry.LookupEncoder(indicator)
54+
}
55+
56+
// RegisterDecoder updates the global DefaultRegistry a map a multicodec indicator number to the given ipld.Decoder function.
57+
// The decoder functions registered can be subsequently looked up using LookupDecoder.
58+
// It is a shortcut to the RegisterDecoder method on the global DefaultRegistry.
59+
//
60+
// Packages which implement an IPLD codec and have a multicodec number associated with them
61+
// are encouraged to register themselves in this map at package init time.
62+
// (Doing this at package init time ensures the default global registry is populated
63+
// without causing race conditions for application code.)
64+
//
65+
// No effort is made to detect conflicting registrations in this map.
66+
// If your dependency tree is such that this becomes a problem,
67+
// there are two ways to address this:
68+
// If RegisterDecoder is called with the same indicator code more than once, the last call wins.
69+
// In practice, this means that if an application has a strong opinion about what implementation for a certain codec,
70+
// then this can be done by making a Register call with that effect at init time in the application's main package.
71+
// This should have the desired effect because the root of the import tree has its init time effect last.
72+
// Alternatively, one can just avoid use of this registry entirely:
73+
// do this by making a LinkSystem that uses a custom DecoderChooser function.
74+
func RegisterDecoder(indicator uint64, decodeFunc ipld.Decoder) {
75+
DefaultRegistry.RegisterDecoder(indicator, decodeFunc)
76+
}
77+
78+
// LookupDecoder yields an ipld.Decoder function matching a multicodec indicator code number.
79+
// It is a shortcut to the LookupDecoder method on the global DefaultRegistry.
80+
//
81+
// To be available from this lookup function, an decoder must have been registered
82+
// for this indicator number by an earlier call to the RegisterDecoder function.
83+
func LookupDecoder(indicator uint64) (ipld.Decoder, error) {
84+
return DefaultRegistry.LookupDecoder(indicator)
85+
}

multicodec/multicodec.go

-110
This file was deleted.

multicodec/registry.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package multicodec
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ipld/go-ipld-prime"
7+
)
8+
9+
// Registry is a structure for storing mappings of multicodec indicator numbers to ipld.Encoder and ipld.Decoder functions.
10+
//
11+
// The most typical usage of this structure is in combination with an ipld.LinkSystem.
12+
// For example, a linksystem using CIDs and a custom multicodec registry can be constructed
13+
// using cidlink.LinkSystemUsingMulticodecRegistry.
14+
//
15+
// Registry includes no mutexing. If using Registry in a concurrent context, you must handle synchronization yourself.
16+
// (Typically, it is recommended to do initialization earlier in a program, before fanning out goroutines;
17+
// this avoids the need for mutexing overhead.)
18+
//
19+
// go-ipld-prime also has a default registry, which has the same methods as this structure, but are at package scope.
20+
// Some systems, like cidlink.DefaultLinkSystem, will use this default registry.
21+
// However, this default registry is global to the entire program.
22+
// This Registry type is for helping if you wish to make your own registry which does not share that global state.
23+
//
24+
// Multicodec indicator numbers are specified in
25+
// https://github.com/multiformats/multicodec/blob/master/table.csv .
26+
// You should not use indicator numbers which are not specified in that table
27+
// (however, there is nothing in this implementation that will attempt to stop you, either; please behave).
28+
type Registry struct {
29+
encoders map[uint64]ipld.Encoder
30+
decoders map[uint64]ipld.Decoder
31+
}
32+
33+
func (r *Registry) ensureInit() {
34+
if r.encoders != nil {
35+
return
36+
}
37+
r.encoders = make(map[uint64]ipld.Encoder)
38+
r.decoders = make(map[uint64]ipld.Decoder)
39+
}
40+
41+
// RegisterEncoder updates a simple map of multicodec indicator number to ipld.Encoder function.
42+
// The encoder functions registered can be subsequently looked up using LookupEncoder.
43+
func (r *Registry) RegisterEncoder(indicator uint64, encodeFunc ipld.Encoder) {
44+
r.ensureInit()
45+
if encodeFunc == nil {
46+
panic("not sensible to attempt to register a nil function")
47+
}
48+
r.encoders[indicator] = encodeFunc
49+
}
50+
51+
// LookupEncoder yields an ipld.Encoder function matching a multicodec indicator code number.
52+
//
53+
// To be available from this lookup function, an encoder must have been registered
54+
// for this indicator number by an earlier call to the RegisterEncoder function.
55+
func (r *Registry) LookupEncoder(indicator uint64) (ipld.Encoder, error) {
56+
encodeFunc, exists := r.encoders[indicator]
57+
if !exists {
58+
return nil, fmt.Errorf("no encoder registered for multicodec code %d (0x%x)", indicator, indicator)
59+
}
60+
return encodeFunc, nil
61+
}
62+
63+
// RegisterDecoder updates a simple map of multicodec indicator number to ipld.Decoder function.
64+
// The decoder functions registered can be subsequently looked up using LookupDecoder.
65+
func (r *Registry) RegisterDecoder(indicator uint64, decodeFunc ipld.Decoder) {
66+
r.ensureInit()
67+
if decodeFunc == nil {
68+
panic("not sensible to attempt to register a nil function")
69+
}
70+
r.decoders[indicator] = decodeFunc
71+
}
72+
73+
// LookupDecoder yields an ipld.Decoder function matching a multicodec indicator code number.
74+
//
75+
// To be available from this lookup function, an decoder must have been registered
76+
// for this indicator number by an earlier call to the RegisterDecoder function.
77+
func (r *Registry) LookupDecoder(indicator uint64) (ipld.Decoder, error) {
78+
decodeFunc, exists := r.decoders[indicator]
79+
if !exists {
80+
return nil, fmt.Errorf("no decoder registered for multicodec code %d (0x%x)", indicator, indicator)
81+
}
82+
return decodeFunc, nil
83+
}

0 commit comments

Comments
 (0)