1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package wasm
16
17 import (
18 "encoding/binary"
19 "fmt"
20 "math"
21
22 "cuelang.org/go/cue"
23 "cuelang.org/go/internal/core/adt"
24 )
25
26
27 type typ int8
28
29 const (
30 typErr typ = iota
31 typBool
32 typUint8
33 typUint16
34 typUint32
35 typUint64
36 typInt8
37 typInt16
38 typInt32
39 typInt64
40 typFloat32
41 typFloat64
42 typStruct
43 )
44
45
46 type field struct {
47 typ
48 from string
49 }
50
51
52 type positionedField struct {
53 field
54 offset int
55
56 inner *structLayout
57 }
58
59
60 type structLayout struct {
61 fields []positionedField
62 size int
63 align int
64 }
65
66 func sizeof(t typ) int {
67 switch t {
68 case typBool, typUint8, typInt8:
69 return 1
70 case typUint16, typInt16:
71 return 2
72 case typUint32, typInt32, typFloat32:
73 return 4
74 case typUint64, typInt64, typFloat64:
75 return 8
76 }
77 panic("unreachable")
78 }
79
80 func encodeStruct(i *instance, v cue.Value, l *structLayout) []*memory {
81 buf := make([]byte, l.size)
82 ms := make([]*memory, 1, 2)
83
84 buf, ms = encode(i, v, l, buf, ms)
85 ms[0] = encBytes(i, buf)
86 return ms
87 }
88
89
90 func encode(i *instance, v cue.Value, l *structLayout, buf []byte, ms []*memory) ([]byte, []*memory) {
91 for _, f := range l.fields {
92 arg := v.LookupPath(cue.ParsePath(f.from))
93
94 switch f.typ {
95 case typBool:
96 b, _ := arg.Bool()
97 if b {
98 buf[f.offset] = 1
99 } else {
100 buf[f.offset] = 0
101 }
102
103 case typUint8:
104 u, _ := arg.Uint64()
105 buf[f.offset] = byte(u)
106 case typUint16:
107 u, _ := arg.Uint64()
108 binary.LittleEndian.PutUint16(buf[f.offset:], uint16(u))
109 case typUint32:
110 u, _ := arg.Uint64()
111 binary.LittleEndian.PutUint32(buf[f.offset:], uint32(u))
112 case typUint64:
113 u, _ := arg.Uint64()
114 binary.LittleEndian.PutUint64(buf[f.offset:], u)
115
116 case typInt8:
117 u, _ := arg.Int64()
118 buf[f.offset] = byte(u)
119 case typInt16:
120 u, _ := arg.Int64()
121 binary.LittleEndian.PutUint16(buf[f.offset:], uint16(u))
122 case typInt32:
123 u, _ := arg.Int64()
124 binary.LittleEndian.PutUint32(buf[f.offset:], uint32(u))
125 case typInt64:
126 u, _ := arg.Int64()
127 binary.LittleEndian.PutUint64(buf[f.offset:], uint64(u))
128
129 case typFloat32:
130 x, _ := arg.Float64()
131 binary.LittleEndian.PutUint32(buf[f.offset:], math.Float32bits(float32(x)))
132 case typFloat64:
133 x, _ := arg.Float64()
134 binary.LittleEndian.PutUint64(buf[f.offset:], math.Float64bits(x))
135
136 case typStruct:
137 encode(i, arg, f.inner, buf[f.offset:], ms)
138
139 default:
140 panic(fmt.Sprintf("unsupported argument %v (kind %v)", v, v.IncompleteKind()))
141 }
142 }
143 return buf, ms
144 }
145
146
147
148 func decodeStruct(buf []byte, l *structLayout) map[string]any {
149 m := make(map[string]any)
150
151 for _, f := range l.fields {
152 switch f.typ {
153 case typBool:
154 u := buf[f.offset]
155 if u == 1 {
156 m[f.from] = true
157 } else {
158 m[f.from] = false
159 }
160
161 case typUint8:
162 u := buf[f.offset]
163 m[f.from] = u
164 case typUint16:
165 u := binary.LittleEndian.Uint16(buf[f.offset:])
166 m[f.from] = u
167 case typUint32:
168 u := binary.LittleEndian.Uint32(buf[f.offset:])
169 m[f.from] = u
170 case typUint64:
171 u := binary.LittleEndian.Uint64(buf[f.offset:])
172 m[f.from] = u
173
174 case typInt8:
175 u := buf[f.offset]
176 m[f.from] = int8(u)
177 case typInt16:
178 u := binary.LittleEndian.Uint16(buf[f.offset:])
179 m[f.from] = int16(u)
180 case typInt32:
181 u := binary.LittleEndian.Uint32(buf[f.offset:])
182 m[f.from] = int32(u)
183 case typInt64:
184 u := binary.LittleEndian.Uint64(buf[f.offset:])
185 m[f.from] = int64(u)
186
187 case typFloat32:
188 u := binary.LittleEndian.Uint32(buf[f.offset:])
189 m[f.from] = math.Float32frombits(u)
190 case typFloat64:
191 u := binary.LittleEndian.Uint64(buf[f.offset:])
192 m[f.from] = math.Float64frombits(u)
193
194 case typStruct:
195 to := f.offset + f.inner.size
196 m[f.from] = decodeStruct(buf[f.offset:to], f.inner)
197
198 default:
199 panic(fmt.Sprintf("unsupported argument type: %v", f.typ))
200 }
201 }
202 return m
203 }
204
205 func align(x, n int) int {
206 return (x + n - 1) & ^(n - 1)
207 }
208
209
210
211 func structLayoutVal(t cue.Value) *structLayout {
212 if t.IncompleteKind() != adt.StructKind {
213 panic("expected CUE struct")
214 }
215
216 var sl structLayout
217 off, size := 0, 0
218 for i, _ := t.Fields(cue.Attributes(true)); i.Next(); {
219 f := i.Value()
220 path := i.Selector().String()
221
222 switch f.IncompleteKind() {
223 case adt.StructKind:
224 inner := structLayoutVal(f)
225 off = align(off, inner.align)
226
227 lval := positionedField{
228 field: field{
229 typ: typStruct,
230 from: path,
231 },
232 offset: off,
233 inner: inner,
234 }
235 sl.fields = append(sl.fields, lval)
236
237 off += inner.size
238 case cue.BoolKind, cue.IntKind, cue.FloatKind, cue.NumberKind:
239 typ := typVal(f)
240 size = sizeof(typ)
241 off = align(off, size)
242
243 lval := positionedField{
244 field: field{
245 typ: typ,
246 from: path,
247 },
248 offset: off,
249 }
250 sl.fields = append(sl.fields, lval)
251
252 off += size
253 default:
254 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", f, f.IncompleteKind()))
255 }
256 }
257
258
259
260 maxalign := 0
261 for _, f := range sl.fields {
262 if f.typ == typStruct {
263 if f.inner.align > maxalign {
264 maxalign = f.inner.align
265 }
266 continue
267 }
268 if sizeof(f.typ) > maxalign {
269 maxalign = sizeof(f.typ)
270 }
271 }
272 sl.size = align(off, maxalign)
273 sl.align = maxalign
274
275 return &sl
276 }
277
278 func typVal(v cue.Value) typ {
279 switch v.IncompleteKind() {
280 case cue.BoolKind:
281 return typBool
282 case cue.IntKind, cue.FloatKind, cue.NumberKind:
283 return typNum(v)
284 default:
285 panic(fmt.Sprintf("unsupported argument type %v (kind %v)", v, v.IncompleteKind()))
286 }
287 }
288
289 func typNum(t cue.Value) typ {
290 ctx := t.Context()
291
292 _int8 := ctx.CompileString("int8")
293 if _int8.Subsume(t) == nil {
294 return typInt8
295 }
296
297 _uint8 := ctx.CompileString("uint8")
298 if _uint8.Subsume(t) == nil {
299 return typUint8
300 }
301
302 _int16 := ctx.CompileString("int16")
303 if _int16.Subsume(t) == nil {
304 return typInt16
305 }
306
307 _uint16 := ctx.CompileString("uint16")
308 if _uint16.Subsume(t) == nil {
309 return typUint16
310 }
311
312 _int32 := ctx.CompileString("int32")
313 if _int32.Subsume(t) == nil {
314 return typInt32
315 }
316
317 _uint32 := ctx.CompileString("uint32")
318 if _uint32.Subsume(t) == nil {
319 return typUint32
320 }
321
322 _int64 := ctx.CompileString("int64")
323 if _int64.Subsume(t) == nil {
324 return typInt64
325 }
326
327 _uint64 := ctx.CompileString("uint64")
328 if _uint64.Subsume(t) == nil {
329 return typUint64
330 }
331
332 _float32 := ctx.CompileString("float32")
333 if _float32.Subsume(t) == nil {
334 return typFloat32
335 }
336
337 _float64 := ctx.CompileString("float64")
338 if _float64.Subsume(t) == nil {
339 return typFloat64
340 }
341
342 panic("unreachable")
343 }
344
View as plain text