diff --git a/api/keystore/codec.go b/api/keystore/codec.go index df6c18ae9521..55346cb09da1 100644 --- a/api/keystore/codec.go +++ b/api/keystore/codec.go @@ -19,7 +19,7 @@ const ( var c codec.Manager func init() { - lc := linearcodec.NewCustomMaxLength(maxSliceLength) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(maxSliceLength)) c = codec.NewManager(maxPackerSize) if err := c.RegisterCodec(codecVersion, lc); err != nil { panic(err) diff --git a/codec/linearcodec/codec.go b/codec/linearcodec/codec.go index d488b3a1d4c0..f0899d9a54e8 100644 --- a/codec/linearcodec/codec.go +++ b/codec/linearcodec/codec.go @@ -42,35 +42,71 @@ type linearCodec struct { typeToTypeID map[reflect.Type]uint32 } -// New returns a new, concurrency-safe codec; it allow to specify -// both tagNames and maxSlicelenght -func New(tagNames []string, maxSliceLen uint32) Codec { +// New returns a new, concurrency-safe codec. +// tagNames and maxSlicelength must be specified. +func New(opts ...Option) Codec { + o := &Options{} + o.applyOptions(opts) + hCodec := &linearCodec{ - nextTypeID: 0, + nextTypeID: o.nextTypeID, typeIDToType: map[uint32]reflect.Type{}, typeToTypeID: map[reflect.Type]uint32{}, } - hCodec.Codec = reflectcodec.New(hCodec, tagNames, maxSliceLen) + hCodec.Codec = reflectcodec.New(hCodec, o.tagNames, o.maxSliceLen) return hCodec } -// NewDefault is a convenience constructor; it returns a new codec with reasonable default values -func NewDefault() Codec { - return New([]string{reflectcodec.DefaultTagName}, defaultMaxSliceLength) -} - -// NewCustomMaxLength is a convenience constructor; it returns a new codec with custom max length and default tags -func NewCustomMaxLength(maxSliceLen uint32) Codec { - return New([]string{reflectcodec.DefaultTagName}, maxSliceLen) +// NewDefault is a convenience constructor; it returns a new codec with reasonable default values. +func NewDefault(opts ...Option) Codec { + return New(append([]Option{WithTagName(reflectcodec.DefaultTagName), WithMaxSliceLen(defaultMaxSliceLength)}, opts...)...) } -// Skip some number of type IDs +// DEPRECATED Skip some number of type IDs func (c *linearCodec) SkipRegistrations(num int) { c.lock.Lock() c.nextTypeID += uint32(num) c.lock.Unlock() } +type Option func(*Options) + +type Options struct { + tagNames []string + maxSliceLen uint32 + nextTypeID uint32 +} + +func (o *Options) applyOptions(ops []Option) { + for _, op := range ops { + op(o) + } +} + +func WithTagName(tagName string) Option { + return func(o *Options) { + o.tagNames = append(o.tagNames, tagName) + } +} + +func WithTagNames(tagNames []string) Option { + return func(o *Options) { + o.tagNames = tagNames + } +} + +func WithMaxSliceLen(maxSliceLen uint32) Option { + return func(o *Options) { + o.maxSliceLen = maxSliceLen + } +} + +func WithNextTypeID(nextTypeID uint32) Option { + return func(o *Options) { + o.nextTypeID = nextTypeID + } +} + // RegisterType is used to register types that may be unmarshaled into an interface // [val] is a value of the type being registered func (c *linearCodec) RegisterType(val interface{}) error { @@ -95,9 +131,8 @@ func (*linearCodec) PrefixSize(reflect.Type) int { func (c *linearCodec) PackPrefix(p *wrappers.Packer, valueType reflect.Type) error { c.lock.RLock() - defer c.lock.RUnlock() - typeID, ok := c.typeToTypeID[valueType] // Get the type ID of the value being marshaled + c.lock.RUnlock() if !ok { return fmt.Errorf("can't marshal unregistered type %q", valueType) } @@ -106,18 +141,19 @@ func (c *linearCodec) PackPrefix(p *wrappers.Packer, valueType reflect.Type) err } func (c *linearCodec) UnpackPrefix(p *wrappers.Packer, valueType reflect.Type) (reflect.Value, error) { - c.lock.RLock() - defer c.lock.RUnlock() - typeID := p.UnpackInt() // Get the type ID if p.Err != nil { return reflect.Value{}, fmt.Errorf("couldn't unmarshal interface: %w", p.Err) } + // Get a type that implements the interface + c.lock.RLock() implementingType, ok := c.typeIDToType[typeID] + c.lock.RUnlock() if !ok { return reflect.Value{}, fmt.Errorf("couldn't unmarshal interface: unknown type ID %d", typeID) } + // Ensure type actually does implement the interface if !implementingType.Implements(valueType) { return reflect.Value{}, fmt.Errorf("couldn't unmarshal interface: %s %w %s", diff --git a/codec/linearcodec/codec_test.go b/codec/linearcodec/codec_test.go index 1e6b836abd37..7708fb049f33 100644 --- a/codec/linearcodec/codec_test.go +++ b/codec/linearcodec/codec_test.go @@ -18,7 +18,7 @@ func TestVectors(t *testing.T) { func TestMultipleTags(t *testing.T) { for _, test := range codec.MultipleTagsTests { - c := New([]string{"tag1", "tag2"}, defaultMaxSliceLength) + c := New(WithTagName("tag1"), WithTagName("tag2"), WithMaxSliceLen(defaultMaxSliceLength)) test(c, t) } } diff --git a/database/linkeddb/codec.go b/database/linkeddb/codec.go index 7780690b2e92..c56b2ee0d38a 100644 --- a/database/linkeddb/codec.go +++ b/database/linkeddb/codec.go @@ -20,7 +20,7 @@ var ( ) func init() { - lc := linearcodec.NewCustomMaxLength(math.MaxUint32) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)) c = codec.NewManager(math.MaxInt32) if err := c.RegisterCodec(codecVersion, lc); err != nil { diff --git a/indexer/indexer.go b/indexer/indexer.go index 8936e57ff1e2..b7c5dbd08e57 100644 --- a/indexer/indexer.go +++ b/indexer/indexer.go @@ -98,7 +98,7 @@ func NewIndexer(config Config) (Indexer, error) { if err := indexer.codec.RegisterCodec( codecVersion, - linearcodec.NewCustomMaxLength(math.MaxUint32), + linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)), ); err != nil { return nil, fmt.Errorf("couldn't register codec: %w", err) } diff --git a/snow/engine/avalanche/vertex/codec.go b/snow/engine/avalanche/vertex/codec.go index 564d699a25e9..6ee421b299d4 100644 --- a/snow/engine/avalanche/vertex/codec.go +++ b/snow/engine/avalanche/vertex/codec.go @@ -21,8 +21,8 @@ const ( var c codec.Manager func init() { - lc := linearcodec.New([]string{reflectcodec.DefaultTagName + "V0"}, maxSize) - lc2 := linearcodec.New([]string{reflectcodec.DefaultTagName + "V1"}, maxSize) + lc := linearcodec.New(linearcodec.WithTagName(reflectcodec.DefaultTagName+"V0"), linearcodec.WithMaxSliceLen(maxSize)) + lc2 := linearcodec.New(linearcodec.WithTagName(reflectcodec.DefaultTagName+"V1"), linearcodec.WithMaxSliceLen(maxSize)) c = codec.NewManager(maxSize) // for backward compatibility, still register the initial codec version diff --git a/vms/avm/txs/parser.go b/vms/avm/txs/parser.go index f5f16d59432e..471e34da05b2 100644 --- a/vms/avm/txs/parser.go +++ b/vms/avm/txs/parser.go @@ -58,7 +58,7 @@ func NewCustomParser( log logging.Logger, fxs []fxs.Fx, ) (Parser, error) { - gc := linearcodec.New([]string{reflectcodec.DefaultTagName}, 1<<20) + gc := linearcodec.New(linearcodec.WithTagName(reflectcodec.DefaultTagName), linearcodec.WithMaxSliceLen(1<<20)) c := linearcodec.NewDefault() gcm := codec.NewManager(math.MaxInt32) diff --git a/vms/components/keystore/codec.go b/vms/components/keystore/codec.go index 6e547c9e4f86..cf5ee0b51e6c 100644 --- a/vms/components/keystore/codec.go +++ b/vms/components/keystore/codec.go @@ -25,7 +25,7 @@ var ( func init() { c := linearcodec.NewDefault() Codec = codec.NewDefaultManager() - lc := linearcodec.NewCustomMaxLength(math.MaxUint32) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)) LegacyCodec = codec.NewManager(math.MaxInt32) errs := wrappers.Errs{} diff --git a/vms/components/message/codec.go b/vms/components/message/codec.go index d41de9b2dd71..b10c067f9d6d 100644 --- a/vms/components/message/codec.go +++ b/vms/components/message/codec.go @@ -21,7 +21,7 @@ var c codec.Manager func init() { c = codec.NewManager(maxMessageSize) - lc := linearcodec.NewCustomMaxLength(maxSliceLen) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(maxSliceLen)) errs := wrappers.Errs{} errs.Add( diff --git a/vms/platformvm/blocks/codec.go b/vms/platformvm/blocks/codec.go index ac0f42da83f2..723e6eb7d859 100644 --- a/vms/platformvm/blocks/codec.go +++ b/vms/platformvm/blocks/codec.go @@ -27,7 +27,7 @@ var ( func init() { c := linearcodec.NewDefault() Codec = codec.NewDefaultManager() - gc := linearcodec.NewCustomMaxLength(math.MaxInt32) + gc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxInt32)) GenesisCodec = codec.NewManager(math.MaxInt32) errs := wrappers.Errs{} diff --git a/vms/platformvm/txs/codec.go b/vms/platformvm/txs/codec.go index d3667b9406db..ac2ceecfad0e 100644 --- a/vms/platformvm/txs/codec.go +++ b/vms/platformvm/txs/codec.go @@ -28,18 +28,16 @@ var ( ) func init() { - c := linearcodec.NewDefault() + // Order in which type are registered affect the byte representation + // generated by marshalling ops. To maintain codec type ordering, + // we skip positions for the blocks. + c := linearcodec.NewDefault(linearcodec.WithNextTypeID(5)) Codec = codec.NewDefaultManager() - gc := linearcodec.NewCustomMaxLength(math.MaxInt32) + gc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxInt32), linearcodec.WithNextTypeID(5)) GenesisCodec = codec.NewManager(math.MaxInt32) errs := wrappers.Errs{} for _, c := range []linearcodec.Codec{c, gc} { - // Order in which type are registered affect the byte representation - // generated by marshalling ops. To maintain codec type ordering, - // we skip positions for the blocks. - c.SkipRegistrations(5) - errs.Add(RegisterUnsignedTxsTypes(c)) } errs.Add( diff --git a/vms/platformvm/warp/codec.go b/vms/platformvm/warp/codec.go index 0213a6701c6e..67a2cd457302 100644 --- a/vms/platformvm/warp/codec.go +++ b/vms/platformvm/warp/codec.go @@ -18,7 +18,7 @@ var c codec.Manager func init() { c = codec.NewManager(math.MaxInt) - lc := linearcodec.NewCustomMaxLength(math.MaxInt32) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxInt32)) errs := wrappers.Errs{} errs.Add( diff --git a/vms/proposervm/block/codec.go b/vms/proposervm/block/codec.go index bf8089dbeb5a..fcf395c16c7f 100644 --- a/vms/proposervm/block/codec.go +++ b/vms/proposervm/block/codec.go @@ -21,7 +21,7 @@ const codecVersion = 0 var c codec.Manager func init() { - linearCodec := linearcodec.NewCustomMaxLength(math.MaxUint32) + linearCodec := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)) c = codec.NewManager(math.MaxInt) errs := wrappers.Errs{} diff --git a/vms/proposervm/state/codec.go b/vms/proposervm/state/codec.go index f73523806e53..0d9176982498 100644 --- a/vms/proposervm/state/codec.go +++ b/vms/proposervm/state/codec.go @@ -15,7 +15,7 @@ const version = 0 var c codec.Manager func init() { - lc := linearcodec.NewCustomMaxLength(math.MaxUint32) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)) c = codec.NewManager(math.MaxInt32) err := c.RegisterCodec(version, lc) diff --git a/vms/proposervm/summary/codec.go b/vms/proposervm/summary/codec.go index a71350f37d0f..9c2f59ae336a 100644 --- a/vms/proposervm/summary/codec.go +++ b/vms/proposervm/summary/codec.go @@ -20,7 +20,7 @@ var ( ) func init() { - lc := linearcodec.NewCustomMaxLength(math.MaxUint32) + lc := linearcodec.NewDefault(linearcodec.WithMaxSliceLen(math.MaxUint32)) c = codec.NewManager(math.MaxInt32) if err := c.RegisterCodec(codecVersion, lc); err != nil { panic(err)