cuelang.org/go@v0.10.1/cue/interpreter/wasm/layout.go (about) 1 // Copyright 2023 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 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 // typ is the type (or kind) of an external type. 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 // field represents a name struct field. 46 type field struct { 47 typ 48 from string // the field name 49 } 50 51 // positionedField represents a struct field with a known location. 52 type positionedField struct { 53 field 54 offset int // memory offset in the parent struct. 55 56 inner *structLayout // IFF typ==typStruct 57 } 58 59 // structLayout describes the memory layout of a struct. 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) // cap is 2 for strings and bytes. 83 84 buf, ms = encode(i, v, l, buf, ms) 85 ms[0] = encBytes(i, buf) 86 return ms 87 } 88 89 // encodeStruct serializes v into buf according to the layout. 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 // decodeStruct takes the binary representation of a struct described 147 // by the layout and returns its Go representation as a map. 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 // structLayoutVal returns the System V (C ABI) memory layout of the 210 // struct expressed by t. 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 // The alignment of a struct is the maximum alignment of its 259 // constituent fields. 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 }