...

Source file src/cuelang.org/go/cue/interpreter/wasm/layout.go

Documentation: cuelang.org/go/cue/interpreter/wasm

     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  }
   344  

View as plain text