github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/iextengine/wazero/impl.go (about)

     1  /*
     2    - Copyright (c) 2023-present unTill Software Development Group B.V.
     3      @author Michael Saigachenko
     4  */
     5  
     6  package iextenginewazero
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"math"
    13  	"os"
    14  	"runtime"
    15  	"strings"
    16  
    17  	"github.com/tetratelabs/wazero"
    18  	"github.com/tetratelabs/wazero/api"
    19  	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
    20  
    21  	"github.com/voedger/voedger/pkg/appdef"
    22  	"github.com/voedger/voedger/pkg/iextengine"
    23  	"github.com/voedger/voedger/pkg/istructs"
    24  	safe "github.com/voedger/voedger/pkg/state/isafestateapi"
    25  	"github.com/voedger/voedger/pkg/state/safestate"
    26  )
    27  
    28  type limitedWriter struct {
    29  	limit int
    30  	buf   []byte
    31  }
    32  
    33  type wazeroExtPkg struct {
    34  	moduleCfg wazero.ModuleConfig
    35  	compiled  wazero.CompiledModule
    36  	module    api.Module
    37  	exts      map[string]api.Function
    38  	wasmData  []byte
    39  
    40  	funcMalloc api.Function
    41  	funcFree   api.Function
    42  
    43  	funcVer          api.Function
    44  	funcGetHeapInuse api.Function
    45  	funcGetHeapSys   api.Function
    46  	funcGetMallocs   api.Function
    47  	funcGetFrees     api.Function
    48  	funcGc           api.Function
    49  	funcOnReadValue  api.Function
    50  
    51  	allocatedBufs []*allocatedBuf
    52  	stdout        limitedWriter
    53  	//	memBackup     *api.MemoryBackup
    54  	//	recoverMem    []byte
    55  }
    56  
    57  type wazeroExtEngine struct {
    58  	app istructs.AppQName
    59  
    60  	compile bool
    61  	config  *iextengine.ExtEngineConfig
    62  	modules map[string]*wazeroExtPkg
    63  	host    api.Module
    64  	rtm     wazero.Runtime
    65  
    66  	wasiCloser api.Closer
    67  
    68  	// Invoke-related!
    69  	safeApi safe.IStateSafeAPI
    70  
    71  	ctx             context.Context
    72  	pkg             *wazeroExtPkg
    73  	autoRecover     bool
    74  	numAutoRecovers int
    75  }
    76  
    77  type allocatedBuf struct {
    78  	addr uint32
    79  	offs uint32
    80  	cap  uint32
    81  }
    82  
    83  type extensionEngineFactory struct {
    84  	compile bool
    85  }
    86  
    87  func newLimitedWriter(limit int) limitedWriter {
    88  	return limitedWriter{limit: limit}
    89  }
    90  
    91  func (w *limitedWriter) Write(p []byte) (n int, err error) {
    92  	if len(w.buf)+len(p) > w.limit {
    93  		w.buf = append(w.buf, p[:w.limit-len(w.buf)]...)
    94  	} else {
    95  		w.buf = append(w.buf, p...)
    96  	}
    97  	return len(p), nil
    98  }
    99  
   100  func (f extensionEngineFactory) New(ctx context.Context, app istructs.AppQName, packages []iextengine.ExtensionPackage, config *iextengine.ExtEngineConfig, numEngines int) (engines []iextengine.IExtensionEngine, err error) {
   101  	for i := 0; i < numEngines; i++ {
   102  		engine := &wazeroExtEngine{
   103  			app:         app,
   104  			modules:     make(map[string]*wazeroExtPkg),
   105  			config:      config,
   106  			compile:     f.compile,
   107  			autoRecover: true,
   108  		}
   109  		err = engine.init(ctx)
   110  		if err != nil {
   111  			return engines, err
   112  		}
   113  		engines = append(engines, engine)
   114  	}
   115  
   116  	for _, pkg := range packages {
   117  		if pkg.ModuleUrl.Scheme == "file" && (pkg.ModuleUrl.Host == "" || strings.EqualFold("localhost", pkg.ModuleUrl.Scheme)) {
   118  			path := pkg.ModuleUrl.Path
   119  			if runtime.GOOS == "windows" {
   120  				path = strings.TrimPrefix(path, "/")
   121  			}
   122  
   123  			wasmdata, err := os.ReadFile(path)
   124  
   125  			if err != nil {
   126  				return nil, err
   127  			}
   128  
   129  			for _, eng := range engines {
   130  				err = eng.(*wazeroExtEngine).initModule(ctx, pkg.QualifiedName, wasmdata, pkg.ExtensionNames)
   131  				if err != nil {
   132  					return nil, err
   133  				}
   134  			}
   135  		} else {
   136  			return nil, fmt.Errorf("unsupported URL: " + pkg.ModuleUrl.String())
   137  		}
   138  	}
   139  	return engines, nil
   140  }
   141  
   142  func (f *wazeroExtEngine) SetLimits(limits iextengine.ExtensionLimits) {
   143  	// f.cep.Duration = limits.ExecutionInterval
   144  }
   145  
   146  func (f *wazeroExtPkg) importFuncs(funcs map[string]*api.Function) error {
   147  
   148  	for k, v := range funcs {
   149  		*v = f.module.ExportedFunction(k)
   150  		if *v == nil {
   151  			return fmt.Errorf("missing exported function: %s", k)
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  func (f *wazeroExtEngine) init(ctx context.Context) error {
   158  	var err error
   159  	var memPages = f.config.MemoryLimitPages
   160  	if memPages == 0 {
   161  		memPages = iextengine.DefaultMemoryLimitPages
   162  	}
   163  	if memPages > maxMemoryPages {
   164  		return errors.New("maximum allowed MemoryLimitPages is 0xffff")
   165  	}
   166  	// Total amount of memory must be at least 170% of WasmPreallocatedBufferSize
   167  	const memoryLimitCoef = 1.7
   168  	memoryLimit := memPages * iextengine.MemoryPageSize
   169  	limit := math.Trunc(float64(WasmPreallocatedBufferSize) * float64(memoryLimitCoef))
   170  	if uint32(memoryLimit) <= uint32(limit) {
   171  		return fmt.Errorf("the minimum limit of memory is: %.1f bytes, requested limit is: %.1f", limit, float32(memoryLimit))
   172  	}
   173  
   174  	var rtConf wazero.RuntimeConfig
   175  
   176  	if f.compile {
   177  		rtConf = wazero.NewRuntimeConfigCompiler()
   178  	} else {
   179  		rtConf = wazero.NewRuntimeConfigInterpreter()
   180  	}
   181  	rtConf = rtConf.
   182  		WithCoreFeatures(api.CoreFeatureBulkMemoryOperations).
   183  		WithCloseOnContextDone(true).
   184  		WithMemoryCapacityFromMax(true).
   185  		WithMemoryLimitPages(uint32(memPages))
   186  
   187  	f.rtm = wazero.NewRuntimeWithConfig(ctx, rtConf)
   188  	f.wasiCloser, err = wasi_snapshot_preview1.Instantiate(ctx, f.rtm)
   189  
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	f.host, err = f.rtm.NewHostModuleBuilder("env").
   195  		NewFunctionBuilder().WithFunc(f.hostGetKey).Export("hostGetKey").
   196  		NewFunctionBuilder().WithFunc(f.hostMustExist).Export("hostGetValue").
   197  		NewFunctionBuilder().WithFunc(f.hostCanExist).Export("hostQueryValue").
   198  		NewFunctionBuilder().WithFunc(f.hostReadValues).Export("hostReadValues").
   199  		// IKey
   200  		NewFunctionBuilder().WithFunc(f.hostKeyAsString).Export("hostKeyAsString").
   201  		NewFunctionBuilder().WithFunc(f.hostKeyAsBytes).Export("hostKeyAsBytes").
   202  		NewFunctionBuilder().WithFunc(f.hostKeyAsInt32).Export("hostKeyAsInt32").
   203  		NewFunctionBuilder().WithFunc(f.hostKeyAsInt64).Export("hostKeyAsInt64").
   204  		NewFunctionBuilder().WithFunc(f.hostKeyAsFloat32).Export("hostKeyAsFloat32").
   205  		NewFunctionBuilder().WithFunc(f.hostKeyAsFloat64).Export("hostKeyAsFloat64").
   206  		NewFunctionBuilder().WithFunc(f.hostKeyAsBool).Export("hostKeyAsBool").
   207  		NewFunctionBuilder().WithFunc(f.hostKeyAsQNamePkg).Export("hostKeyAsQNamePkg").
   208  		NewFunctionBuilder().WithFunc(f.hostKeyAsQNameEntity).Export("hostKeyAsQNameEntity").
   209  		// IValue
   210  		NewFunctionBuilder().WithFunc(f.hostValueLength).Export("hostValueLength").
   211  		NewFunctionBuilder().WithFunc(f.hostValueAsValue).Export("hostValueAsValue").
   212  		NewFunctionBuilder().WithFunc(f.hostValueAsString).Export("hostValueAsString").
   213  		NewFunctionBuilder().WithFunc(f.hostValueAsBytes).Export("hostValueAsBytes").
   214  		NewFunctionBuilder().WithFunc(f.hostValueAsInt32).Export("hostValueAsInt32").
   215  		NewFunctionBuilder().WithFunc(f.hostValueAsInt64).Export("hostValueAsInt64").
   216  		NewFunctionBuilder().WithFunc(f.hostValueAsFloat32).Export("hostValueAsFloat32").
   217  		NewFunctionBuilder().WithFunc(f.hostValueAsFloat64).Export("hostValueAsFloat64").
   218  		NewFunctionBuilder().WithFunc(f.hostValueAsQNamePkg).Export("hostValueAsQNamePkg").
   219  		NewFunctionBuilder().WithFunc(f.hostValueAsQNameEntity).Export("hostValueAsQNameEntity").
   220  		NewFunctionBuilder().WithFunc(f.hostValueAsBool).Export("hostValueAsBool").
   221  		NewFunctionBuilder().WithFunc(f.hostValueGetAsBytes).Export("hostValueGetAsBytes").
   222  		NewFunctionBuilder().WithFunc(f.hostValueGetAsString).Export("hostValueGetAsString").
   223  		NewFunctionBuilder().WithFunc(f.hostValueGetAsInt32).Export("hostValueGetAsInt32").
   224  		NewFunctionBuilder().WithFunc(f.hostValueGetAsInt64).Export("hostValueGetAsInt64").
   225  		NewFunctionBuilder().WithFunc(f.hostValueGetAsFloat32).Export("hostValueGetAsFloat32").
   226  		NewFunctionBuilder().WithFunc(f.hostValueGetAsFloat64).Export("hostValueGetAsFloat64").
   227  		NewFunctionBuilder().WithFunc(f.hostValueGetAsValue).Export("hostValueGetAsValue").
   228  		NewFunctionBuilder().WithFunc(f.hostValueGetAsQNamePkg).Export("hostValueGetAsQNamePkg").
   229  		NewFunctionBuilder().WithFunc(f.hostValueGetAsQNameEntity).Export("hostValueGetAsQNameEntity").
   230  		NewFunctionBuilder().WithFunc(f.hostValueGetAsBool).Export("hostValueGetAsBool").
   231  		// Intents
   232  		NewFunctionBuilder().WithFunc(f.hostNewValue).Export("hostNewValue").
   233  		NewFunctionBuilder().WithFunc(f.hostUpdateValue).Export("hostUpdateValue").
   234  		// RowWriters
   235  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutString).Export("hostRowWriterPutString").
   236  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutBytes).Export("hostRowWriterPutBytes").
   237  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutInt32).Export("hostRowWriterPutInt32").
   238  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutInt64).Export("hostRowWriterPutInt64").
   239  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutFloat32).Export("hostRowWriterPutFloat32").
   240  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutFloat64).Export("hostRowWriterPutFloat64").
   241  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutBool).Export("hostRowWriterPutBool").
   242  		NewFunctionBuilder().WithFunc(f.hostRowWriterPutQName).Export("hostRowWriterPutQName").
   243  		//ExportFunction("printstr", f.printStr).
   244  
   245  		Instantiate(ctx)
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	return nil
   251  
   252  }
   253  
   254  func (f *wazeroExtEngine) resetModule(ctx context.Context, ePkg *wazeroExtPkg) (err error) {
   255  	if ePkg.module != nil {
   256  		err = ePkg.module.Close(ctx)
   257  		if err != nil {
   258  			return err
   259  		}
   260  		ePkg.stdout.buf = ePkg.stdout.buf[:0]
   261  	}
   262  	if f.compile {
   263  		ePkg.module, err = f.rtm.InstantiateModule(ctx, ePkg.compiled, ePkg.moduleCfg)
   264  	} else {
   265  		ePkg.module, err = f.rtm.InstantiateWithConfig(ctx, ePkg.wasmData, ePkg.moduleCfg)
   266  	}
   267  
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	err = ePkg.importFuncs(map[string]*api.Function{
   273  		"malloc":               &ePkg.funcMalloc,
   274  		"free":                 &ePkg.funcFree,
   275  		"WasmAbiVersion_0_0_1": &ePkg.funcVer,
   276  		"WasmGetHeapInuse":     &ePkg.funcGetHeapInuse,
   277  		"WasmGetHeapSys":       &ePkg.funcGetHeapSys,
   278  		"WasmGetMallocs":       &ePkg.funcGetMallocs,
   279  		"WasmGetFrees":         &ePkg.funcGetFrees,
   280  		"WasmGC":               &ePkg.funcGc,
   281  		"WasmOnReadValue":      &ePkg.funcOnReadValue,
   282  	})
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	res, err := ePkg.funcMalloc.Call(ctx, uint64(WasmPreallocatedBufferSize))
   288  	if err != nil {
   289  		return err
   290  	}
   291  	ePkg.allocatedBufs = append(ePkg.allocatedBufs, &allocatedBuf{
   292  		addr: uint32(res[0]),
   293  		offs: 0,
   294  		cap:  WasmPreallocatedBufferSize,
   295  	})
   296  
   297  	for name := range ePkg.exts {
   298  		expFunc := ePkg.module.ExportedFunction(name)
   299  		if expFunc != nil {
   300  			ePkg.exts[name] = expFunc
   301  		} else {
   302  			return missingExportedFunction(name)
   303  		}
   304  	}
   305  
   306  	return nil
   307  }
   308  
   309  func (f *wazeroExtEngine) initModule(ctx context.Context, pkgName string, wasmdata []byte, extNames []string) (err error) {
   310  	ePkg := &wazeroExtPkg{}
   311  
   312  	ePkg.stdout = newLimitedWriter(maxStdErrSize)
   313  	ePkg.moduleCfg = wazero.NewModuleConfig().WithName("wasm").WithStdout(&ePkg.stdout)
   314  
   315  	if f.compile {
   316  		ePkg.compiled, err = f.rtm.CompileModule(ctx, wasmdata)
   317  		if err != nil {
   318  			return err
   319  		}
   320  	} else {
   321  		ePkg.wasmData = wasmdata
   322  	}
   323  
   324  	ePkg.exts = make(map[string]api.Function)
   325  
   326  	for _, name := range extNames {
   327  		if !strings.HasPrefix(name, "Wasm") && name != "alloc" && name != "free" &&
   328  			name != "calloc" && name != "realloc" && name != "malloc" && name != "_start" && name != "memory" {
   329  			ePkg.exts[name] = nil // put to map to init later
   330  		} else {
   331  			return incorrectExtensionName(name)
   332  		}
   333  	}
   334  
   335  	if err = f.resetModule(ctx, ePkg); err != nil {
   336  		return err
   337  	}
   338  
   339  	// Check WASM SDK version
   340  	_, err = ePkg.funcVer.Call(ctx)
   341  	if err != nil {
   342  		return errors.New("unsupported WASM version")
   343  	}
   344  
   345  	f.modules[pkgName] = ePkg
   346  
   347  	return nil
   348  }
   349  
   350  func (f *wazeroExtEngine) Close(ctx context.Context) {
   351  	for _, m := range f.modules {
   352  		if m.module != nil {
   353  			m.module.Close(ctx)
   354  		}
   355  	}
   356  	if f.host != nil {
   357  		f.host.Close(ctx)
   358  	}
   359  	if f.wasiCloser != nil {
   360  		f.wasiCloser.Close(ctx)
   361  	}
   362  }
   363  
   364  func (f *wazeroExtEngine) recover(ctx context.Context) {
   365  	if err := f.resetModule(ctx, f.pkg); err != nil {
   366  		panic(err)
   367  	}
   368  }
   369  
   370  func (f *wazeroExtEngine) selectModule(pkgPath string) error {
   371  	pkg, ok := f.modules[pkgPath]
   372  	if !ok {
   373  		return errUndefinedPackage(pkgPath)
   374  	}
   375  	f.pkg = pkg
   376  	return nil
   377  }
   378  
   379  func (f *wazeroExtEngine) isMemoryOverflow(err error) bool {
   380  	return strings.Contains(err.Error(), "runtime.alloc")
   381  }
   382  
   383  func (f *wazeroExtEngine) isPanic(err error) bool {
   384  	return strings.Contains(err.Error(), "wasm error: unreachable")
   385  }
   386  
   387  func (f *wazeroExtEngine) invoke(ctx context.Context, extension appdef.FullQName, io iextengine.IExtensionIO) (err error) {
   388  	var ok bool
   389  	f.pkg, ok = f.modules[extension.PkgPath()]
   390  	if !ok {
   391  		return errUndefinedPackage(extension.PkgPath())
   392  	}
   393  
   394  	funct := f.pkg.exts[extension.Entity()]
   395  	if funct == nil {
   396  		return invalidExtensionName(extension.Entity())
   397  	}
   398  
   399  	f.safeApi = safestate.Provide(io, f.safeApi)
   400  	f.ctx = ctx
   401  
   402  	for i := range f.pkg.allocatedBufs {
   403  		f.pkg.allocatedBufs[i].offs = 0 // reuse pre-allocated memory
   404  	}
   405  
   406  	// fmt.Print(funct)
   407  	_, err = funct.Call(ctx)
   408  
   409  	return err
   410  }
   411  
   412  func (f *wazeroExtEngine) Invoke(ctx context.Context, extension appdef.FullQName, io iextengine.IExtensionIO) (err error) {
   413  	err = f.invoke(ctx, extension, io)
   414  	if err != nil && f.isMemoryOverflow(err) && f.autoRecover {
   415  		f.numAutoRecovers++
   416  		f.recover(ctx)
   417  		err = f.invoke(ctx, extension, io)
   418  	}
   419  	if err != nil && f.isPanic(err) && len(f.pkg.stdout.buf) > 0 {
   420  		stdout := string(f.pkg.stdout.buf)
   421  		if strings.HasPrefix(stdout, "panic: ") {
   422  			return errors.New(strings.TrimSpace(stdout))
   423  		}
   424  	}
   425  	return err
   426  }
   427  
   428  func (f *wazeroExtEngine) decodeStr(ptr, size uint32) string {
   429  	if bytes, ok := f.pkg.module.Memory().Read(ptr, size); ok {
   430  		return string(bytes)
   431  	}
   432  	panic(ErrUnableToReadMemory)
   433  }
   434  
   435  func (f *wazeroExtEngine) hostGetKey(storagePtr, storageSize, entityPtr, entitySize uint32) (res uint64) {
   436  	storageFull := f.decodeStr(storagePtr, storageSize)
   437  	entitystr := f.decodeStr(entityPtr, entitySize)
   438  	return uint64(f.safeApi.KeyBuilder(storageFull, entitystr))
   439  }
   440  
   441  func (f *wazeroExtEngine) hostReadValues(keyId uint64) {
   442  	f.safeApi.ReadValues(safe.TKeyBuilder(keyId), func(key safe.TKey, value safe.TValue) {
   443  		_, err := f.pkg.funcOnReadValue.Call(f.ctx, uint64(key), uint64(value))
   444  		if err != nil {
   445  			panic(err.Error())
   446  		}
   447  	})
   448  }
   449  
   450  func (f *wazeroExtEngine) hostMustExist(keyId uint64) (result uint64) {
   451  	return uint64(f.safeApi.MustGetValue(safe.TKeyBuilder(keyId)))
   452  }
   453  
   454  const maxUint64 = ^uint64(0)
   455  
   456  func (f *wazeroExtEngine) hostCanExist(keyId uint64) (result uint64) {
   457  	v, ok := f.safeApi.QueryValue(safe.TKeyBuilder(keyId))
   458  	if !ok {
   459  		return maxUint64
   460  	}
   461  	return uint64(v)
   462  }
   463  
   464  func (f *wazeroExtEngine) allocAndSend(buf []byte) (result uint64) {
   465  	addrPkg, e := f.allocBuf(uint32(len(buf)))
   466  	if e != nil {
   467  		panic(e)
   468  	}
   469  	if !f.pkg.module.Memory().Write(addrPkg, buf) {
   470  		panic(errMemoryOutOfRange)
   471  	}
   472  	return (uint64(addrPkg) << uint64(bitsInFourBytes)) | uint64(len(buf))
   473  }
   474  
   475  func (f *wazeroExtEngine) hostKeyAsString(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   476  	v := f.safeApi.KeyAsString(safe.TKey(id), f.decodeStr(namePtr, nameSize))
   477  	return f.allocAndSend([]byte(v))
   478  }
   479  
   480  func (f *wazeroExtEngine) hostKeyAsBytes(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   481  	v := f.safeApi.KeyAsBytes(safe.TKey(id), f.decodeStr(namePtr, nameSize))
   482  	return f.allocAndSend(v)
   483  }
   484  
   485  func (f *wazeroExtEngine) hostKeyAsInt32(id uint64, namePtr uint32, nameSize uint32) (result uint32) {
   486  	return uint32(f.safeApi.KeyAsInt32(safe.TKey(id), f.decodeStr(namePtr, nameSize)))
   487  }
   488  
   489  func (f *wazeroExtEngine) hostKeyAsInt64(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   490  	return uint64(f.safeApi.KeyAsInt64(safe.TKey(id), f.decodeStr(namePtr, nameSize)))
   491  }
   492  
   493  func (f *wazeroExtEngine) hostKeyAsBool(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   494  	b := f.safeApi.KeyAsBool(safe.TKey(id), f.decodeStr(namePtr, nameSize))
   495  	if b {
   496  		return uint64(1)
   497  	}
   498  	return uint64(0)
   499  }
   500  
   501  func (f *wazeroExtEngine) hostKeyAsQNamePkg(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   502  	qname := f.safeApi.KeyAsQName(safe.TKey(id), f.decodeStr(namePtr, nameSize))
   503  	return f.allocAndSend([]byte(qname.FullPkgName))
   504  }
   505  
   506  func (f *wazeroExtEngine) hostKeyAsQNameEntity(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   507  	qname := f.safeApi.KeyAsQName(safe.TKey(id), f.decodeStr(namePtr, nameSize))
   508  	return f.allocAndSend([]byte(qname.Entity))
   509  }
   510  
   511  func (f *wazeroExtEngine) hostKeyAsFloat32(key uint64, namePtr uint32, nameSize uint32) (result float32) {
   512  	return f.safeApi.KeyAsFloat32(safe.TKey(key), f.decodeStr(namePtr, nameSize))
   513  }
   514  
   515  func (f *wazeroExtEngine) hostKeyAsFloat64(key uint64, namePtr uint32, nameSize uint32) (result float64) {
   516  	return f.safeApi.KeyAsFloat64(safe.TKey(key), f.decodeStr(namePtr, nameSize))
   517  }
   518  
   519  func (f *wazeroExtEngine) hostValueGetAsString(value uint64, index uint32) (result uint64) {
   520  	v := f.safeApi.ValueGetAsString(safe.TValue(value), int(index))
   521  	return f.allocAndSend([]byte(v))
   522  }
   523  
   524  func (f *wazeroExtEngine) hostValueGetAsQNameEntity(value uint64, index uint32) (result uint64) {
   525  	qname := f.safeApi.ValueGetAsQName(safe.TValue(value), int(index))
   526  	return f.allocAndSend([]byte(qname.Entity))
   527  }
   528  
   529  func (f *wazeroExtEngine) hostValueGetAsQNamePkg(value uint64, index uint32) (result uint64) {
   530  	qname := f.safeApi.ValueGetAsQName(safe.TValue(value), int(index))
   531  	return f.allocAndSend([]byte(qname.FullPkgName))
   532  }
   533  
   534  func (f *wazeroExtEngine) hostValueGetAsBytes(value uint64, index uint32) (result uint64) {
   535  	return f.allocAndSend(f.safeApi.ValueGetAsBytes(safe.TValue(value), int(index)))
   536  }
   537  
   538  func (f *wazeroExtEngine) hostValueGetAsBool(value uint64, index uint32) (result uint64) {
   539  	b := f.safeApi.ValueGetAsBool(safe.TValue(value), int(index))
   540  	if b {
   541  		return 1
   542  	}
   543  	return 0
   544  }
   545  
   546  func (f *wazeroExtEngine) hostValueGetAsInt32(value uint64, index uint32) (result int32) {
   547  	return f.safeApi.ValueGetAsInt32(safe.TValue(value), int(index))
   548  }
   549  
   550  func (f *wazeroExtEngine) hostValueGetAsInt64(value uint64, index uint32) (result uint64) {
   551  	return uint64(f.safeApi.ValueGetAsInt64(safe.TValue(value), int(index)))
   552  }
   553  
   554  func (f *wazeroExtEngine) hostValueGetAsFloat32(id uint64, index uint32) float32 {
   555  	return f.safeApi.ValueGetAsFloat32(safe.TValue(id), int(index))
   556  }
   557  
   558  func (f *wazeroExtEngine) hostValueGetAsFloat64(id uint64, index uint32) float64 {
   559  	return f.safeApi.ValueGetAsFloat64(safe.TValue(id), int(index))
   560  }
   561  
   562  func (f *wazeroExtEngine) hostValueGetAsValue(val uint64, index uint32) (result uint64) {
   563  	return uint64(f.safeApi.ValueGetAsValue(safe.TValue(val), int(index)))
   564  }
   565  
   566  func (f *wazeroExtEngine) hostValueAsString(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   567  	s := f.safeApi.ValueAsString(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   568  	return f.allocAndSend([]byte(s))
   569  }
   570  
   571  func (f *wazeroExtEngine) hostValueAsBytes(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   572  	b := f.safeApi.ValueAsBytes(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   573  	return f.allocAndSend(b)
   574  }
   575  
   576  func (f *wazeroExtEngine) hostValueAsInt32(id uint64, namePtr uint32, nameSize uint32) (result int32) {
   577  	return f.safeApi.ValueAsInt32(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   578  }
   579  
   580  func (f *wazeroExtEngine) hostValueAsInt64(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   581  	return uint64(f.safeApi.ValueAsInt64(safe.TValue(id), f.decodeStr(namePtr, nameSize)))
   582  }
   583  
   584  func (f *wazeroExtEngine) hostValueAsBool(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   585  	b := f.safeApi.ValueAsBool(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   586  	if b {
   587  		return 1
   588  	}
   589  	return 0
   590  }
   591  
   592  func (f *wazeroExtEngine) hostValueAsFloat32(id uint64, namePtr, nameSize uint32) float32 {
   593  	return f.safeApi.ValueAsFloat32(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   594  }
   595  
   596  func (f *wazeroExtEngine) hostValueAsFloat64(id uint64, namePtr, nameSize uint32) float64 {
   597  	return f.safeApi.ValueAsFloat64(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   598  }
   599  
   600  func (f *wazeroExtEngine) hostValueAsQNameEntity(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   601  	qname := f.safeApi.ValueAsQName(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   602  	return f.allocAndSend([]byte(qname.Entity))
   603  }
   604  
   605  func (f *wazeroExtEngine) hostValueAsQNamePkg(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   606  	qname := f.safeApi.ValueAsQName(safe.TValue(id), f.decodeStr(namePtr, nameSize))
   607  	return f.allocAndSend([]byte(qname.FullPkgName))
   608  }
   609  
   610  func (f *wazeroExtEngine) hostValueAsValue(id uint64, namePtr uint32, nameSize uint32) (result uint64) {
   611  	return uint64(f.safeApi.ValueAsValue(safe.TValue(id), f.decodeStr(namePtr, nameSize)))
   612  }
   613  
   614  func (f *wazeroExtEngine) hostValueLength(id uint64) (result uint32) {
   615  	return uint32(f.safeApi.ValueLen(safe.TValue(id)))
   616  }
   617  
   618  func (f *wazeroExtEngine) allocBuf(size uint32) (addr uint32, err error) {
   619  	for i := range f.pkg.allocatedBufs {
   620  		if f.pkg.allocatedBufs[i].cap-f.pkg.allocatedBufs[i].offs >= size {
   621  			addr = f.pkg.allocatedBufs[i].addr + f.pkg.allocatedBufs[i].offs
   622  			f.pkg.allocatedBufs[i].offs += size
   623  			return
   624  		}
   625  	}
   626  	// no space in the allocated buffers
   627  
   628  	var newBufferSize uint32 = WasmPreallocatedBufferIncrease
   629  	if size > newBufferSize {
   630  		newBufferSize = size
   631  	}
   632  
   633  	var res []uint64
   634  	res, err = f.pkg.funcMalloc.Call(f.ctx, uint64(newBufferSize))
   635  	if err != nil {
   636  		return 0, err
   637  	}
   638  	addr = uint32(res[0])
   639  	f.pkg.allocatedBufs = append(f.pkg.allocatedBufs, &allocatedBuf{
   640  		addr: addr,
   641  		offs: 0,
   642  		cap:  newBufferSize,
   643  	})
   644  	return addr, nil
   645  }
   646  
   647  func (f *wazeroExtEngine) getFrees(packagePath string, ctx context.Context) (uint64, error) {
   648  	pkg, ok := f.modules[packagePath]
   649  	if !ok {
   650  		return 0, errUndefinedPackage(packagePath)
   651  	}
   652  	res, err := pkg.funcGetFrees.Call(ctx)
   653  	if err != nil {
   654  		return 0, err
   655  	}
   656  	return res[0], nil
   657  }
   658  
   659  func (f *wazeroExtEngine) gc(packagePath string, ctx context.Context) error {
   660  	pkg, ok := f.modules[packagePath]
   661  	if !ok {
   662  		return errUndefinedPackage(packagePath)
   663  	}
   664  	_, err := pkg.funcGc.Call(ctx)
   665  	if err != nil {
   666  		return err
   667  	}
   668  	return nil
   669  }
   670  
   671  func (f *wazeroExtEngine) getHeapinuse(packagePath string, ctx context.Context) (uint64, error) {
   672  	pkg, ok := f.modules[packagePath]
   673  	if !ok {
   674  		return 0, errUndefinedPackage(packagePath)
   675  	}
   676  	res, err := pkg.funcGetHeapInuse.Call(ctx)
   677  	if err != nil {
   678  		return 0, err
   679  	}
   680  	return res[0], nil
   681  }
   682  
   683  func (f *wazeroExtEngine) getHeapSys(packagePath string, ctx context.Context) (uint64, error) {
   684  	pkg, ok := f.modules[packagePath]
   685  	if !ok {
   686  		return 0, errUndefinedPackage(packagePath)
   687  	}
   688  	res, err := pkg.funcGetHeapSys.Call(ctx)
   689  	if err != nil {
   690  		return 0, err
   691  	}
   692  	return res[0], nil
   693  }
   694  
   695  func (f *wazeroExtEngine) getMallocs(packagePath string, ctx context.Context) (uint64, error) {
   696  	pkg, ok := f.modules[packagePath]
   697  	if !ok {
   698  		return 0, errUndefinedPackage(packagePath)
   699  	}
   700  	res, err := pkg.funcGetMallocs.Call(ctx)
   701  	if err != nil {
   702  		return 0, err
   703  	}
   704  	return res[0], nil
   705  }
   706  
   707  func (f *wazeroExtEngine) hostNewValue(keyId uint64) uint64 {
   708  	return uint64(f.safeApi.NewValue(safe.TKeyBuilder(keyId)))
   709  }
   710  
   711  func (f *wazeroExtEngine) hostUpdateValue(keyId, existingValueId uint64) (result uint64) {
   712  	return uint64(f.safeApi.UpdateValue(safe.TKeyBuilder(keyId), safe.TValue(existingValueId)))
   713  }
   714  
   715  func (f *wazeroExtEngine) hostRowWriterPutString(id uint64, typ uint32, namePtr uint32, nameSize, valuePtr, valueSize uint32) {
   716  	if typ == 0 {
   717  		f.safeApi.KeyBuilderPutString(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), f.decodeStr(valuePtr, valueSize))
   718  	} else {
   719  		f.safeApi.IntentPutString(safe.TIntent(id), f.decodeStr(namePtr, nameSize), f.decodeStr(valuePtr, valueSize))
   720  	}
   721  }
   722  
   723  func (f *wazeroExtEngine) hostRowWriterPutBytes(id uint64, typ uint32, namePtr uint32, nameSize, valuePtr, valueSize uint32) {
   724  	var bytes []byte
   725  	var ok bool
   726  	bytes, ok = f.pkg.module.Memory().Read(valuePtr, valueSize)
   727  	if !ok {
   728  		panic(ErrUnableToReadMemory)
   729  	}
   730  	if typ == 0 {
   731  		f.safeApi.KeyBuilderPutBytes(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), bytes)
   732  	} else {
   733  		f.safeApi.IntentPutBytes(safe.TIntent(id), f.decodeStr(namePtr, nameSize), bytes)
   734  	}
   735  }
   736  
   737  func (f *wazeroExtEngine) hostRowWriterPutInt32(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int32) {
   738  	if typ == 0 {
   739  		f.safeApi.KeyBuilderPutInt32(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value)
   740  	} else {
   741  		f.safeApi.IntentPutInt32(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value)
   742  	}
   743  }
   744  
   745  func (f *wazeroExtEngine) hostRowWriterPutInt64(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int64) {
   746  	if typ == 0 {
   747  		f.safeApi.KeyBuilderPutInt64(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value)
   748  	} else {
   749  		f.safeApi.IntentPutInt64(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value)
   750  	}
   751  }
   752  
   753  func (f *wazeroExtEngine) hostRowWriterPutQName(id uint64, typ uint32, namePtr uint32, nameSize uint32, pkgPtr, pkgSize, entityPtr, entitySize uint32) {
   754  	qname := safe.QName{
   755  		FullPkgName: f.decodeStr(pkgPtr, pkgSize),
   756  		Entity:      f.decodeStr(entityPtr, entitySize),
   757  	}
   758  	if typ == 0 {
   759  		f.safeApi.KeyBuilderPutQName(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), qname)
   760  	} else {
   761  		f.safeApi.IntentPutQName(safe.TIntent(id), f.decodeStr(namePtr, nameSize), qname)
   762  	}
   763  }
   764  
   765  func (f *wazeroExtEngine) hostRowWriterPutBool(id uint64, typ uint32, namePtr uint32, nameSize uint32, value int32) {
   766  	if typ == 0 {
   767  		f.safeApi.KeyBuilderPutBool(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value > 0)
   768  	} else {
   769  		f.safeApi.IntentPutBool(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value > 0)
   770  	}
   771  }
   772  
   773  func (f *wazeroExtEngine) hostRowWriterPutFloat32(id uint64, typ uint32, namePtr uint32, nameSize uint32, value float32) {
   774  	if typ == 0 {
   775  		f.safeApi.KeyBuilderPutFloat32(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value)
   776  	} else {
   777  		f.safeApi.IntentPutFloat32(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value)
   778  	}
   779  }
   780  
   781  func (f *wazeroExtEngine) hostRowWriterPutFloat64(id uint64, typ uint32, namePtr, nameSize uint32, value float64) {
   782  	if typ == 0 {
   783  		f.safeApi.KeyBuilderPutFloat64(safe.TKeyBuilder(id), f.decodeStr(namePtr, nameSize), value)
   784  	} else {
   785  		f.safeApi.IntentPutFloat64(safe.TIntent(id), f.decodeStr(namePtr, nameSize), value)
   786  	}
   787  }