From 385f4d5ac63ebaf4b5107ca0a4dc48f7dfcbb3df Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Wed, 19 May 2021 15:28:47 -0700 Subject: [PATCH] Make a multicodec.Registry type available. 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. --- linking/cid/linksystem.go | 8 +- .../{multicodec.go => defaultRegistry.go} | 37 ++------- multicodec/registry.go | 78 +++++++++++++++++++ 3 files changed, 89 insertions(+), 34 deletions(-) rename multicodec/{multicodec.go => defaultRegistry.go} (75%) create mode 100644 multicodec/registry.go diff --git a/linking/cid/linksystem.go b/linking/cid/linksystem.go index 2c5a996a..8375595d 100644 --- a/linking/cid/linksystem.go +++ b/linking/cid/linksystem.go @@ -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 } @@ -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 } diff --git a/multicodec/multicodec.go b/multicodec/defaultRegistry.go similarity index 75% rename from multicodec/multicodec.go rename to multicodec/defaultRegistry.go index 2898834d..bb1c1167 100644 --- a/multicodec/multicodec.go +++ b/multicodec/defaultRegistry.go @@ -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. @@ -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. @@ -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. @@ -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. @@ -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) } diff --git a/multicodec/registry.go b/multicodec/registry.go new file mode 100644 index 00000000..5bc7c0aa --- /dev/null +++ b/multicodec/registry.go @@ -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 +}