-
Notifications
You must be signed in to change notification settings - Fork 0
/
decoders.go
207 lines (186 loc) · 6.01 KB
/
decoders.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package bstates
import (
"fmt"
"time"
"github.com/jaracil/ei"
)
// FieldDecoderType defines the type for different field decoder names.
type FieldDecoderType string
// Implemented decoders are: [BufferToStringDecoder], [NumberToUnixTsMsDecoder] and [IntMapDecoder].
const (
BufferToStringDecoderType FieldDecoderType = "BufferToString"
NumberToUnixTsMsDecoderType FieldDecoderType = "NumberToUnixTsMs"
IntMapDecoderType FieldDecoderType = "IntMap"
)
// Decoder is an interface that defines how to decode or transform state information. They
// provide a "virtual" field.
//
// While decoders usually use the "from" parameter to specify the name of a [StateField] to decode, it's not mandatory.
//
// Decoders can:
// - Use multiple input fields (similar to having multiple "from" parameters), allowing for operations with
// several operands.
// - Access the entire state, enabling more complex decoding logic that isn't limited to specific fields.
// - Define constant fields that do not depend on any state field, although this might have limited practical use.
type Decoder interface {
Name() FieldDecoderType // Decoder type
Decode(s *State) (interface{}, error) // function called
GetParams() map[string]interface{} // returns a MSI
}
// NewDecoder creates a new [Decoder] instance based on the provided
// decoder type and parameters.
//
// dtype: Should be one of [FieldDecoderType].
func NewDecoder(dtype string, params map[string]interface{}) (d Decoder, err error) {
switch FieldDecoderType(dtype) {
case BufferToStringDecoderType:
d, err = NewBufferToStringDecoder(params)
case IntMapDecoderType:
d, err = NewIntMapDecoder(params)
case NumberToUnixTsMsDecoderType:
d, err = NewNumberToUnixTsMsDecoder(params)
default:
err = fmt.Errorf("unknown decoder \"%s\"", dtype)
}
return
}
// BufferToString implements a [Decoder] which returns a string from a buffer.
// This means that the original buffer will be returned as a [string] object stopping at the first null character.
type BufferToStringDecoder struct {
From string // "from" parameter: name of the encoded field as defined in StateSchema.Fields
}
func (d *BufferToStringDecoder) GetParams() map[string]interface{} {
m := map[string]interface{}{}
m["from"] = d.From
return m
}
func NewBufferToStringDecoder(params map[string]interface{}) (d *BufferToStringDecoder, err error) {
d = &BufferToStringDecoder{}
d.From, err = ei.N(params).M("from").String()
if err != nil {
return nil, err
}
return
}
func (d *BufferToStringDecoder) Name() FieldDecoderType {
return BufferToStringDecoderType
}
func (d *BufferToStringDecoder) Decode(s *State) (interface{}, error) {
fromValueI, err := s.Get(d.From)
if err != nil {
return nil, err
}
fromValue, err := ei.N(fromValueI).Bytes()
if err != nil {
return nil, err
}
i := 0
for ; i < len(fromValue); i++ {
if fromValue[i] == 0 {
break
}
}
return string(fromValue[:i]), nil
}
// IntMapDecoder implements a [Decoder] which decodes an integer value into a string based on a mapping defined
// in the State object.
type IntMapDecoder struct {
From string // "from" parameter: name of the encoded field as defined in StateSchema.Fields
MapId string // "mapId" parameter: name of the map as defined in the StateSchema.DecoderIntMaps
}
func (d *IntMapDecoder) GetParams() map[string]interface{} {
m := map[string]interface{}{}
m["from"] = d.From
m["mapId"] = d.MapId
return m
}
func NewIntMapDecoder(params map[string]interface{}) (d *IntMapDecoder, err error) {
d = &IntMapDecoder{}
d.From, err = ei.N(params).M("from").String()
if err != nil {
return nil, err
}
d.MapId, err = ei.N(params).M("mapId").String()
if err != nil {
return nil, err
}
return
}
func (d *IntMapDecoder) Name() FieldDecoderType {
return IntMapDecoderType
}
func (d *IntMapDecoder) Decode(s *State) (interface{}, error) {
fromValueI, err := s.Get(d.From)
if err != nil {
return nil, err
}
fromValue, err := ei.N(fromValueI).Int64()
if err != nil {
return nil, err
}
intMap, ok := s.schema.decoderIntMaps[d.MapId]
if !ok {
return nil, fmt.Errorf("map \"%s\" not found", d.MapId)
}
toValue, ok := intMap[fromValue]
if !ok {
//return nil, fmt.Errorf("value \"%d\" not in map", fromValue)
return "UNKNOWN", nil
}
return toValue, nil
}
// NumberToUnixTsMsDecoder implements a [Decoder] which decodes a numeric value using the following formula:
//
// decodedValue = UnixMillis(year) + valueToDecode*factor
type NumberToUnixTsMsDecoder struct {
From string // "from" parameter: name of the encoded field as defined in StateSchema.Fields
Year uint // "year"
Factor float64 // "factor"
}
func (d *NumberToUnixTsMsDecoder) GetParams() map[string]interface{} {
m := map[string]interface{}{}
m["from"] = d.From
m["year"] = d.Year
m["factor"] = d.Factor
return m
}
func NewNumberToUnixTsMsDecoder(params map[string]interface{}) (d *NumberToUnixTsMsDecoder, err error) {
d = &NumberToUnixTsMsDecoder{}
d.From, err = ei.N(params).M("from").String()
if err != nil {
return nil, fmt.Errorf("\"from\" field error: %v", err)
}
d.Year, err = ei.N(params).M("year").Uint()
if err != nil {
return nil, fmt.Errorf("\"year\" field error: %v", err)
}
d.Factor, err = ei.N(params).M("factor").Float64()
if err != nil {
return nil, fmt.Errorf("\"factor\" field error: %v", err)
}
if d.Factor <= 0 {
return nil, fmt.Errorf("\"factor\" must be > 0")
}
if d.Year < 1970 {
return nil, fmt.Errorf("\"year\" must be >= 1970")
}
return
}
func (d *NumberToUnixTsMsDecoder) Name() FieldDecoderType {
return NumberToUnixTsMsDecoderType
}
func (d *NumberToUnixTsMsDecoder) Decode(s *State) (interface{}, error) {
fromValueI, err := s.Get(d.From)
if err != nil {
return nil, err
}
fromValue, err := ei.N(fromValueI).Float64()
if err != nil {
return nil, err
}
offsetDate := time.Date(int(d.Year), time.January, 1, 0, 0, 0, 0, time.UTC)
offsetDateUnixMs := offsetDate.UnixMilli()
// convert to millis using given factor
unixTsMs := uint64(offsetDateUnixMs + int64(fromValue*d.Factor))
return unixTsMs, nil
}