forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 4
/
codec.go
130 lines (110 loc) · 3.68 KB
/
codec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package linearcodec
import (
"fmt"
"reflect"
"sync"
"github.com/MetalBlockchain/metalgo/codec"
"github.com/MetalBlockchain/metalgo/codec/reflectcodec"
"github.com/MetalBlockchain/metalgo/utils/wrappers"
)
const (
// default max length of a slice being marshalled by Marshal(). Should be <= math.MaxUint32.
DefaultMaxSliceLength = 256 * 1024
)
var (
_ Codec = (*linearCodec)(nil)
_ codec.Codec = (*linearCodec)(nil)
_ codec.Registry = (*linearCodec)(nil)
_ codec.GeneralCodec = (*linearCodec)(nil)
)
// Codec marshals and unmarshals
type Codec interface {
codec.Registry
codec.Codec
SkipRegistrations(int)
}
// Codec handles marshaling and unmarshaling of structs
type linearCodec struct {
codec.Codec
lock sync.RWMutex
nextTypeID uint32
typeIDToType map[uint32]reflect.Type
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 {
hCodec := &linearCodec{
nextTypeID: 0,
typeIDToType: map[uint32]reflect.Type{},
typeToTypeID: map[reflect.Type]uint32{},
}
hCodec.Codec = reflectcodec.New(hCodec, tagNames, 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)
}
// Skip some number of type IDs
func (c *linearCodec) SkipRegistrations(num int) {
c.lock.Lock()
c.nextTypeID += uint32(num)
c.lock.Unlock()
}
// 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 {
c.lock.Lock()
defer c.lock.Unlock()
valType := reflect.TypeOf(val)
if _, exists := c.typeToTypeID[valType]; exists {
return fmt.Errorf("%w: %v", codec.ErrDuplicateType, valType)
}
c.typeIDToType[c.nextTypeID] = valType
c.typeToTypeID[valType] = c.nextTypeID
c.nextTypeID++
return nil
}
func (*linearCodec) PrefixSize(reflect.Type) int {
// see PackPrefix implementation
return wrappers.IntLen
}
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
if !ok {
return fmt.Errorf("can't marshal unregistered type %q", valueType)
}
p.PackInt(typeID) // Pack type ID so we know what to unmarshal this into
return p.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
implementingType, ok := c.typeIDToType[typeID]
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",
implementingType,
codec.ErrDoesNotImplementInterface,
valueType,
)
}
return reflect.New(implementingType).Elem(), nil // instance of the proper type
}