Skip to content

Commit

Permalink
Make a multicodec.Registry type available.
Browse files Browse the repository at this point in the history
The hope is that this might be helpful if you want to build a
multicodec registry other than the global one, but are still buying
into the idea of the registry being keyed by indicator numbers.

The current interfaces are functionally unchanged.

This type can now be used when constructing a cidlink.LinkSystem.

I considered a mechanism like:
`func (r *Registry) InstallOn(lsys *ipld.LinkSystem)` ...
and probably would've found that a bit cleaner.
However, it doesn't jive with the way we've isolated the CID types
into a package with a LinkSystem just for them (sigh; that really is
the gift of complexity that just keeps giving); you can see how the
EncoderChooser and DecoderChooser funcs need a tiny bit of type
assertions in order to figure out how to extract the multicodec
indicator from the Link/LinkPrototype types?  That bit is a bit
that we still want to keep cordoned off the rest of the import tree.

I probably would've preferred to keep the DefaultRegistry variable
unexported, because I can't imagine any good coming of touching it,
but again, the above paragraph forced my hand.
  • Loading branch information
warpfork committed May 19, 2021
1 parent aff91f7 commit 385f4d5
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 34 deletions.
8 changes: 6 additions & 2 deletions linking/cid/linksystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import (
)

func DefaultLinkSystem() ipld.LinkSystem {
return LinkSystemUsingMulticodecRegistry(multicodec.DefaultRegistry)
}

func LinkSystemUsingMulticodecRegistry(mcReg multicodec.Registry) ipld.LinkSystem {
return ipld.LinkSystem{
EncoderChooser: func(lp ipld.LinkPrototype) (ipld.Encoder, error) {
switch lp2 := lp.(type) {
case LinkPrototype:
fn, err := multicodec.LookupEncoder(lp2.GetCodec())
fn, err := mcReg.LookupEncoder(lp2.GetCodec())
if err != nil {
return nil, err
}
Expand All @@ -28,7 +32,7 @@ func DefaultLinkSystem() ipld.LinkSystem {
lp := lnk.Prototype()
switch lp2 := lp.(type) {
case LinkPrototype:
fn, err := multicodec.LookupDecoder(lp2.GetCodec())
fn, err := mcReg.LookupDecoder(lp2.GetCodec())
if err != nil {
return nil, err
}
Expand Down
37 changes: 5 additions & 32 deletions multicodec/multicodec.go → multicodec/defaultRegistry.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package multicodec

import (
"fmt"

"github.com/ipld/go-ipld-prime"
)

var encoderRegistry = make(map[uint64]ipld.Encoder)
var decoderRegistry = make(map[uint64]ipld.Decoder)
var DefaultRegistry = Registry{}

// RegisterEncoder updates a simple map of multicodec indicator number to ipld.Encoder function.
// The encoder functions registered can be subsequently looked up using LookupEncoder.
Expand All @@ -33,15 +30,7 @@ var decoderRegistry = make(map[uint64]ipld.Decoder)
// Alternatively, one can just avoid use of this registry entirely:
// do this by making a LinkSystem that uses a custom EncoderChooser function.
func RegisterEncoder(indicator uint64, encodeFunc ipld.Encoder) {
// This function could arguably be just a bare map access.
// We introduced a function primarily for the interest of potential future changes.
// E.g. one could introduce logging here to help detect unintended conflicting registrations.
// (We probably won't do this, but you can do it yourself as a printf debug hack. :))

if encodeFunc == nil {
panic("not sensible to attempt to register a nil function")
}
encoderRegistry[indicator] = encodeFunc
DefaultRegistry.RegisterEncoder(indicator, encodeFunc)
}

// LookupEncoder yields an ipld.Encoder function matching a multicodec indicator code number.
Expand All @@ -52,11 +41,7 @@ func RegisterEncoder(indicator uint64, encodeFunc ipld.Encoder) {
// To be available from this lookup function, an encoder must have been registered
// for this indicator number by an earlier call to the RegisterEncoder function.
func LookupEncoder(indicator uint64) (ipld.Encoder, error) {
encodeFunc, exists := encoderRegistry[indicator]
if !exists {
return nil, fmt.Errorf("no encoder registered for multicodec code %d (0x%x)", indicator, indicator)
}
return encodeFunc, nil
return DefaultRegistry.LookupEncoder(indicator)
}

// RegisterDecoder updates a simple map of multicodec indicator number to ipld.Decoder function.
Expand All @@ -83,15 +68,7 @@ func LookupEncoder(indicator uint64) (ipld.Encoder, error) {
// Alternatively, one can just avoid use of this registry entirely:
// do this by making a LinkSystem that uses a custom DecoderChooser function.
func RegisterDecoder(indicator uint64, decodeFunc ipld.Decoder) {
// This function could arguably be just a bare map access.
// We introduced a function primarily for the interest of potential future changes.
// E.g. one could introduce logging here to help detect unintended conflicting registrations.
// (We probably won't do this, but you can do it yourself as a printf debug hack. :))

if decodeFunc == nil {
panic("not sensible to attempt to register a nil function")
}
decoderRegistry[indicator] = decodeFunc
DefaultRegistry.RegisterDecoder(indicator, decodeFunc)
}

// LookupDecoder yields an ipld.Decoder function matching a multicodec indicator code number.
Expand All @@ -102,9 +79,5 @@ func RegisterDecoder(indicator uint64, decodeFunc ipld.Decoder) {
// To be available from this lookup function, an decoder must have been registered
// for this indicator number by an earlier call to the RegisterDecoder function.
func LookupDecoder(indicator uint64) (ipld.Decoder, error) {
decodeFunc, exists := decoderRegistry[indicator]
if !exists {
return nil, fmt.Errorf("no decoder registered for multicodec code %d (0x%x)", indicator, indicator)
}
return decodeFunc, nil
return DefaultRegistry.LookupDecoder(indicator)
}
78 changes: 78 additions & 0 deletions multicodec/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package multicodec

import (
"fmt"

"github.com/ipld/go-ipld-prime"
)

// Registry is a structure for storing mappings of multicodec indiactor numbers to ipld.Encoder and ipld.Decoder functions.
//
// The most typical usage of this structure is in combination with an ipld.LinkSystem.
// For example, a linksystem using CIDs and a custom multicodec registry can be constructed
// using cidlink.LinkSystemUsingMulticodecRegistry.
//
// go-ipld-prime also has a default registry, which has the same methods as this structure, but are at package scope.
// Some systems, like cidlink.DefaultLinkSystem, will use this default registry.
// This Registry type is for helping if you wish to make your own registry which does not share that global state.
//
// Multicodec indicator numbers are specified in
// https://github.com/multiformats/multicodec/blob/master/table.csv .
// You should not use indicator numbers which are not specified in that table
// (however, there is nothing in this implementation that will attempt to stop you, either; please behave).
type Registry struct {
encoders map[uint64]ipld.Encoder
decoders map[uint64]ipld.Decoder
}

func (r *Registry) ensureInit() {
if r.encoders != nil {
return
}
r.encoders = make(map[uint64]ipld.Encoder)
r.decoders = make(map[uint64]ipld.Decoder)
}

// RegisterEncoder updates a simple map of multicodec indicator number to ipld.Encoder function.
// The encoder functions registered can be subsequently looked up using LookupEncoder.
func (r *Registry) RegisterEncoder(indicator uint64, encodeFunc ipld.Encoder) {
r.ensureInit()
if encodeFunc == nil {
panic("not sensible to attempt to register a nil function")
}
r.encoders[indicator] = encodeFunc
}

// LookupEncoder yields an ipld.Encoder function matching a multicodec indicator code number.
//
// To be available from this lookup function, an encoder must have been registered
// for this indicator number by an earlier call to the RegisterEncoder function.
func (r *Registry) LookupEncoder(indicator uint64) (ipld.Encoder, error) {
encodeFunc, exists := r.encoders[indicator]
if !exists {
return nil, fmt.Errorf("no encoder registered for multicodec code %d (0x%x)", indicator, indicator)
}
return encodeFunc, nil
}

// RegisterDecoder updates a simple map of multicodec indicator number to ipld.Decoder function.
// The decoder functions registered can be subsequently looked up using LookupDecoder.
func (r *Registry) RegisterDecoder(indicator uint64, decodeFunc ipld.Decoder) {
r.ensureInit()
if decodeFunc == nil {
panic("not sensible to attempt to register a nil function")
}
r.decoders[indicator] = decodeFunc
}

// LookupDecoder yields an ipld.Decoder function matching a multicodec indicator code number.
//
// To be available from this lookup function, an decoder must have been registered
// for this indicator number by an earlier call to the RegisterDecoder function.
func (r *Registry) LookupDecoder(indicator uint64) (ipld.Decoder, error) {
decodeFunc, exists := r.decoders[indicator]
if !exists {
return nil, fmt.Errorf("no decoder registered for multicodec code %d (0x%x)", indicator, indicator)
}
return decodeFunc, nil
}

0 comments on commit 385f4d5

Please sign in to comment.