Skip to content

Commit

Permalink
perf: Reduce allocations (#12)
Browse files Browse the repository at this point in the history
* perf: write a few benchmarks

* perf: implement `extism_input_load_u64`, `extism_load_u64`, and `extism_store_u64` in the host

* perf: don't enable cancellation by default
  • Loading branch information
mhmd-azeez authored Aug 9, 2023
1 parent dca75b6 commit a2a20c6
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 23 deletions.
22 changes: 13 additions & 9 deletions extism.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Runtime struct {
// PluginConfig contains configuration options for the Extism plugin.
type PluginConfig struct {
ModuleConfig wazero.ModuleConfig
RuntimeConfig []wazero.RuntimeConfig
RuntimeConfig wazero.RuntimeConfig
EnableWasi bool
// TODO: couldn't find a better way for this, but I wonder if there is a better and more idomatic way for Option<T>
LogLevel *LogLevel
Expand Down Expand Up @@ -228,17 +228,17 @@ func NewPlugin(
config PluginConfig,
functions []HostFunction) (*Plugin, error) {
var rconfig wazero.RuntimeConfig
if len(config.RuntimeConfig) == 0 {
if config.RuntimeConfig == nil {
rconfig = wazero.NewRuntimeConfig()
} else {
rconfig = config.RuntimeConfig[0]
rconfig = config.RuntimeConfig
}

if manifest.Memory.MaxPages > 0 {
rconfig = rconfig.WithMemoryLimitPages(manifest.Memory.MaxPages)
}

rt := wazero.NewRuntimeWithConfig(ctx, rconfig.WithCloseOnContextDone(true))
rt := wazero.NewRuntimeWithConfig(ctx, rconfig)

extism, err := rt.InstantiateWithConfig(ctx, extismRuntimeWasm, wazero.NewModuleConfig().WithName("extism"))
if err != nil {
Expand Down Expand Up @@ -374,19 +374,20 @@ func NewPlugin(
}

// SetInput sets the input data for the plugin to be used in the next WebAssembly function call.
func (plugin *Plugin) SetInput(data []byte) error {
func (plugin *Plugin) SetInput(data []byte) (uint64, error) {
_, err := plugin.Runtime.Extism.ExportedFunction("extism_reset").Call(plugin.Runtime.ctx)
if err != nil {
fmt.Println(err)
return errors.New("reset")
return 0, errors.New("reset")
}

ptr, err := plugin.Runtime.Extism.ExportedFunction("extism_alloc").Call(plugin.Runtime.ctx, uint64(len(data)))
if err != nil {
return err
return 0, err
}
plugin.Memory().Write(uint32(ptr[0]), data)
plugin.Runtime.Extism.ExportedFunction("extism_input_set").Call(plugin.Runtime.ctx, ptr[0], uint64(len(data)))
return nil
return ptr[0], nil
}

// GetOutput retrieves the output data from the last WebAssembly function call.
Expand Down Expand Up @@ -453,10 +454,13 @@ func (plugin *Plugin) Call(name string, data []byte) (uint32, []byte, error) {

ctx = context.WithValue(ctx, "plugin", plugin)

if err := plugin.SetInput(data); err != nil {
intputOffset, err := plugin.SetInput(data)
if err != nil {
return 1, []byte{}, err
}

ctx = context.WithValue(ctx, "inputOffset", intputOffset)

var f = plugin.Main.ExportedFunction(name)

if f == nil {
Expand Down
152 changes: 145 additions & 7 deletions extism_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"log"
"math/rand"
"os"
"testing"
"time"
Expand Down Expand Up @@ -494,25 +495,39 @@ func TestLog_custom(t *testing.T) {

func TestTimeout(t *testing.T) {
manifest := manifest("sleep.wasm")
manifest.Config["duration"] = "3" // sleep for 3 seconds
manifest.Timeout = 100 // 100ms
manifest.Config["duration"] = "3" // sleep for 3 seconds

if plugin, ok := plugin(t, manifest); ok {
defer plugin.Close()
config := PluginConfig{
ModuleConfig: wazero.NewModuleConfig().WithSysWalltime(),
EnableWasi: true,
RuntimeConfig: wazero.NewRuntimeConfig().WithCloseOnContextDone(true),
}

exit, _, err := plugin.Call("run_test", []byte{})
plugin, err := NewPlugin(context.Background(), manifest, config, []HostFunction{})

assert.Equal(t, sys.ExitCodeDeadlineExceeded, exit, "Exit code must be `sys.ExitCodeDeadlineExceeded`")
assert.Equal(t, "module closed with context deadline exceeded", err.Error())
if err != nil {
t.Errorf("Could not create plugin: %v", err)
}

defer plugin.Close()

exit, _, err := plugin.Call("run_test", []byte{})

assert.Equal(t, sys.ExitCodeDeadlineExceeded, exit, "Exit code must be `sys.ExitCodeDeadlineExceeded`")
assert.Equal(t, "module closed with context deadline exceeded", err.Error())
}

func TestCancel(t *testing.T) {
manifest := manifest("sleep.wasm")
manifest.Config["duration"] = "3" // sleep for 3 seconds

ctx, cancel := context.WithCancel(context.Background())
config := wasiPluginConfig()
config := PluginConfig{
ModuleConfig: wazero.NewModuleConfig().WithSysWalltime(),
EnableWasi: true,
RuntimeConfig: wazero.NewRuntimeConfig().WithCloseOnContextDone(true),
}

plugin, err := NewPlugin(ctx, manifest, config, []HostFunction{})

Expand Down Expand Up @@ -645,6 +660,129 @@ func TestHelloHaskell(t *testing.T) {
}
}

func BenchmarkNoop(b *testing.B) {
ctx := context.Background()
cache := wazero.NewCompilationCache()
defer cache.Close(ctx)

manifest := Manifest{Wasm: []Wasm{WasmFile{Path: "wasm/noop.wasm"}}}

config := PluginConfig{
EnableWasi: true,
ModuleConfig: wazero.NewModuleConfig(),
RuntimeConfig: wazero.NewRuntimeConfig().WithCompilationCache(cache),
}

plugin, err := NewPlugin(ctx, manifest, config, []HostFunction{})
if err != nil {
panic(err)
}

b.ResetTimer()

b.Run("noop", func(b *testing.B) {
b.ReportAllocs()

for i := 0; i < b.N; i++ {
_, _, err := plugin.Call("run_test", []byte{})
if err != nil {
panic(err)
}
}
})
}

var (
Regex2048 = "BYwnOjWhprPmDncp8qpQ5CY4r1RGZuqKLBowmtMCd test ETjLOG685YC4RIjXB0HadNpqYS4M7GPGUVAKRZRC1ibqQqGnuzqX2Hjosm6MKNCp5QifX7Up2phqkFqkjpSu3k59oi6M5YbTMiy4JukVFx2402IlrHU1McK7US0skB1cF0W2ZDpsypNmGJRXRMY0pPsYbw7G2a0xJnhTITXcuF5xJWR1rz5zdGZQbbjZoHZcEnveDFq5kOmCVc test DsJVHTsAlypLI9sVtbTLwmE1DG2C6AgUo3GO1DpCx3jV43oXUxaTVJqZO13AYqvNPbxizYZ5BckZFBbJybY3Vnm20Sm7nXbwZs5N2ugz3EpUQvXwqHdHWzc1T8uKPD5LTDM8UBpVoF test 9G3mWarrp43SvoidITriFhzHmyVWNd6n2LIVocr3pOai4DOlkAn7QDup6z6spMAf8UcI4wbfoSzG0k5Qy1rGBhPaJKJRW2 MC9ma3U3rnjAOBtEUHZ2qfOUpfMNgPlGpvzr4IGNNFf9RFlF7yRUBvRnYxyonIWPPiR1x1wWgxc20o5cW4GU7kytAOuGlpzpykcAxCJLLP6wJegaMhAeb8xBLpuBetNEbfcyyOcJBun5BhmFOmv8 test IvICWx2wlYZ61YDBpPcIpqnMb9MHwT8GroC1YITZBlNGBHMpAe4d2sNZe9d0Wvfbv5mMo30Bm1Pa5S3x38jgu6y0BaqZl9GhlukE9CqPJGUsJZ5suDH19WiOrvz7mXwXhi4lWm1YdwNi0xhVnXITtmKq5rikIS6dul1USgDf3TwyLYpyCG46Xj92PssJmnhPdH1WAnvXY sbs8RaemyqmPggtGNwU2JjuPjdmQRakIusv2WimN7zG8R8Pf1225IAJ2j8aiZBrxnjmrucaYOQCrLm7e2Q5q8 test HOkCEJJGHVLYJtGgHKa1PRQ5qCcsIAUdkW3yRfdulutteLe3We9z9XQvWuTYMLDPpOJqMzDNTGpTYts7AL8pFog1k82XVuMZ6ItccxOBpuzDcahH4wDqCGjak8qPVxmnrGmSsrdUHVz6SrScElMo0nOF8RIpYAVdJr5NxWIK1uzc1iIiZnbUD6uDNmBkmfec6IgK6aqnEZaGLDJXDHSYfzWUOi7y3KNPl0CghL9BId8v4040mCKMfmdthWWLJ2tpWIo1482ghiU5 2qtrzgFgYKfyfr4X6FXzN3hM3bLnuwItQrTCEp3BYz79bCAaQGhicZzqE83Mh2 test IIVID622qlEyVEGuEmNJ5JteEzbpklhTKnVMflzzWyWbZe6kIgeUr9mxWjkJGisvRbZKwfnojeC82M1nHgUa4k46x7Dw7mL3rChORjBxBMYjFeOvCsT6kEo3vPeachLUKdkExJbr9Yei0fKyOFSDlxpFhlRKuwGxXu4jGo4CzKDsVsahqzC9iGw53bHiw0V4Pwmdhzv482s3zU9XLTgQr6GuL1I0kSfh9BkVoK5fFvg1hm7ECrt6p8q3kLVjxte EK9W9q2q9etMaPLymcCRZ0XauMDzJY08JeVvovnT2g5hxE7UGW1 test YRotQUivrrXQnhEw55faznZZBU1ULVs4BfYkIkEfS91NetBhona6zrzDwMsXi0FJjdaiJ25lvetPDaMzUs0l6nfkGkVyU376mFPfPkpBKZR2z2Xwzxndi0SkUnqm8jCa7iq2oSJstTdUXtCK2xTXMIh7tiuPVftit GFYQXXI3vY QFe1xShWJgFAqYguQ8gcxMPSzMlyDaPmMuTPgFZDM0cd test NS3fTggxBa4p5jgS4S0nhae05RkYkXGzuNMXeu6IoR9PFqVFnXcBYD0Ld9otrAiqUuIGYGmAjm3Wx29va2UtIFaRhL02ckRfycz3BGfwqYl3TGtjWdKjmxn1WreRIIq5gkbWJws5VQsov0V2U8pGedj N2RDqWgh2tFiJA9fmytgRgqSnqxIwyBMgY5RnE6CZ0 test Iv4QPiWMu0oG70e4nSNtG13O test "
Match2048 = "BYwnOjWhprPmDncp8qpQ5CY4r1RGZuqKLBowmtMCd wasm ETjLOG685YC4RIjXB0HadNpqYS4M7GPGUVAKRZRC1ibqQqGnuzqX2Hjosm6MKNCp5QifX7Up2phqkFqkjpSu3k59oi6M5YbTMiy4JukVFx2402IlrHU1McK7US0skB1cF0W2ZDpsypNmGJRXRMY0pPsYbw7G2a0xJnhTITXcuF5xJWR1rz5zdGZQbbjZoHZcEnveDFq5kOmCVc wasm DsJVHTsAlypLI9sVtbTLwmE1DG2C6AgUo3GO1DpCx3jV43oXUxaTVJqZO13AYqvNPbxizYZ5BckZFBbJybY3Vnm20Sm7nXbwZs5N2ugz3EpUQvXwqHdHWzc1T8uKPD5LTDM8UBpVoF wasm 9G3mWarrp43SvoidITriFhzHmyVWNd6n2LIVocr3pOai4DOlkAn7QDup6z6spMAf8UcI4wbfoSzG0k5Qy1rGBhPaJKJRW2 MC9ma3U3rnjAOBtEUHZ2qfOUpfMNgPlGpvzr4IGNNFf9RFlF7yRUBvRnYxyonIWPPiR1x1wWgxc20o5cW4GU7kytAOuGlpzpykcAxCJLLP6wJegaMhAeb8xBLpuBetNEbfcyyOcJBun5BhmFOmv8 wasm IvICWx2wlYZ61YDBpPcIpqnMb9MHwT8GroC1YITZBlNGBHMpAe4d2sNZe9d0Wvfbv5mMo30Bm1Pa5S3x38jgu6y0BaqZl9GhlukE9CqPJGUsJZ5suDH19WiOrvz7mXwXhi4lWm1YdwNi0xhVnXITtmKq5rikIS6dul1USgDf3TwyLYpyCG46Xj92PssJmnhPdH1WAnvXY sbs8RaemyqmPggtGNwU2JjuPjdmQRakIusv2WimN7zG8R8Pf1225IAJ2j8aiZBrxnjmrucaYOQCrLm7e2Q5q8 wasm HOkCEJJGHVLYJtGgHKa1PRQ5qCcsIAUdkW3yRfdulutteLe3We9z9XQvWuTYMLDPpOJqMzDNTGpTYts7AL8pFog1k82XVuMZ6ItccxOBpuzDcahH4wDqCGjak8qPVxmnrGmSsrdUHVz6SrScElMo0nOF8RIpYAVdJr5NxWIK1uzc1iIiZnbUD6uDNmBkmfec6IgK6aqnEZaGLDJXDHSYfzWUOi7y3KNPl0CghL9BId8v4040mCKMfmdthWWLJ2tpWIo1482ghiU5 2qtrzgFgYKfyfr4X6FXzN3hM3bLnuwItQrTCEp3BYz79bCAaQGhicZzqE83Mh2 wasm IIVID622qlEyVEGuEmNJ5JteEzbpklhTKnVMflzzWyWbZe6kIgeUr9mxWjkJGisvRbZKwfnojeC82M1nHgUa4k46x7Dw7mL3rChORjBxBMYjFeOvCsT6kEo3vPeachLUKdkExJbr9Yei0fKyOFSDlxpFhlRKuwGxXu4jGo4CzKDsVsahqzC9iGw53bHiw0V4Pwmdhzv482s3zU9XLTgQr6GuL1I0kSfh9BkVoK5fFvg1hm7ECrt6p8q3kLVjxte EK9W9q2q9etMaPLymcCRZ0XauMDzJY08JeVvovnT2g5hxE7UGW1 wasm YRotQUivrrXQnhEw55faznZZBU1ULVs4BfYkIkEfS91NetBhona6zrzDwMsXi0FJjdaiJ25lvetPDaMzUs0l6nfkGkVyU376mFPfPkpBKZR2z2Xwzxndi0SkUnqm8jCa7iq2oSJstTdUXtCK2xTXMIh7tiuPVftit GFYQXXI3vY QFe1xShWJgFAqYguQ8gcxMPSzMlyDaPmMuTPgFZDM0cd wasm NS3fTggxBa4p5jgS4S0nhae05RkYkXGzuNMXeu6IoR9PFqVFnXcBYD0Ld9otrAiqUuIGYGmAjm3Wx29va2UtIFaRhL02ckRfycz3BGfwqYl3TGtjWdKjmxn1WreRIIq5gkbWJws5VQsov0V2U8pGedj N2RDqWgh2tFiJA9fmytgRgqSnqxIwyBMgY5RnE6CZ0 wasm Iv4QPiWMu0oG70e4nSNtG13O wasm "

Regex4096 = Regex2048 + Regex2048
Match4096 = Match2048 + Match2048

Regex8192 = Regex4096 + Regex4096
Match8192 = Match4096 + Match4096

Regex16384 = Regex8192 + Regex8192
Match16384 = Match8192 + Match8192

Regex32768 = Regex16384 + Regex16384
Match32768 = Match16384 + Match16384

Regex65536 = Regex32768 + Regex32768
Match65536 = Match32768 + Match32768
)

func BenchmarkReplace(b *testing.B) {
ctx := context.Background()
cache := wazero.NewCompilationCache()
defer cache.Close(ctx)

manifest := Manifest{Wasm: []Wasm{WasmFile{Path: "wasm/replace.wasm"}}}

config := PluginConfig{
EnableWasi: true,
ModuleConfig: wazero.NewModuleConfig(),
RuntimeConfig: wazero.NewRuntimeConfig().WithCompilationCache(cache),
}

plugin, err := NewPlugin(ctx, manifest, config, []HostFunction{})
if err != nil {
panic(err)
}

b.ResetTimer()

inputs := map[string][]byte{
"empty": {},
"2048": []byte(Regex2048),
"4096": []byte(Regex4096),
"8192": []byte(Regex8192),
"16383": []byte(Regex16384),
"32768": []byte(Regex32768),
}

expected := map[string][]byte{
"empty": {},
"2048": []byte(Match2048),
"4096": []byte(Match4096),
"8192": []byte(Match8192),
"16383": []byte(Match16384),
"32768": []byte(Match32768),
}

for k, v := range inputs {
expected := expected[k]
b.Run(k, func(b *testing.B) {
input := v
b.SetBytes(int64(len(input)))
b.ReportAllocs()

for i := 0; i < b.N; i++ {
_, out, err := plugin.Call("run_test", input)
if err != nil {
fmt.Println("SOMETHING BAD HAPPENED: ", err)
panic(err)
}

if !equal(out, expected) {
fmt.Println(string(out))
panic("invalid regex match")
}
}
})
}
}

func generateRandomString(length int, seed int64) string {
rand.Seed(seed)
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
result := make([]byte, length)
for i := range result {
result[i] = charset[rand.Intn(len(charset))]
}
return string(result)
}

func wasiPluginConfig() PluginConfig {
level := Warn
config := PluginConfig{
Expand Down
61 changes: 54 additions & 7 deletions host.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,27 +187,28 @@ func defineCustomHostFunctions(builder wazero.HostModuleBuilder, funcs []HostFun
// a separate variable (closure) and assigning the value of f to it, you might run into unexpected behavior.
// All the closures created in the loop would end up referencing the same f, which could lead to incorrect or unintended results.
// See: https://github.com/extism/go-sdk/issues/5#issuecomment-1666774486
closure := f
closure := f.Callback
userData := f.UserData

builder.NewFunctionBuilder().WithGoFunction(api.GoFunc(func(ctx context.Context, stack []uint64) {
if plugin, ok := ctx.Value("plugin").(*Plugin); ok {
closure.Callback(ctx, &CurrentPlugin{plugin}, closure.UserData, stack)
closure(ctx, &CurrentPlugin{plugin}, userData, stack)
return
}

panic("Invalid context, `plugin` key not found")
}), closure.Params, closure.Results).Export(closure.Name)
}), f.Params, f.Results).Export(f.Name)
}
}

func buildEnvModule(ctx context.Context, rt wazero.Runtime, extism api.Module, funcs []HostFunction) (api.Module, error) {
builder := rt.NewHostModuleBuilder("env")

wrap := func(name string, params []ValType, results []ValType) {
f := extism.ExportedFunction(name)
builder.
NewFunctionBuilder().
WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) {
f := extism.ExportedFunction(name)
err := f.CallWithStack(ctx, stack)
if err != nil {
panic(err)
Expand All @@ -218,16 +219,13 @@ func buildEnvModule(ctx context.Context, rt wazero.Runtime, extism api.Module, f

wrap("extism_alloc", []ValType{I64}, []ValType{I64})
wrap("extism_free", []ValType{I64}, []ValType{})
wrap("extism_load_u64", []ValType{I64}, []ValType{I64})
wrap("extism_load_u8", []ValType{I64}, []ValType{I32})
wrap("extism_input_load_u64", []ValType{I64}, []ValType{I64})
wrap("extism_input_load_u8", []ValType{I64}, []ValType{I32})
wrap("extism_store_u64", []ValType{I64, I64}, []ValType{})
wrap("extism_store_u8", []ValType{I64, I32}, []ValType{})
wrap("extism_input_set", []ValType{I64, I64}, []ValType{})
wrap("extism_output_set", []ValType{I64, I64}, []ValType{})
wrap("extism_input_length", []ValType{}, []ValType{I64})
wrap("extism_input_offset", []ValType{}, []ValType{I64})
wrap("extism_output_length", []ValType{}, []ValType{I64})
wrap("extism_output_offset", []ValType{}, []ValType{I64})
wrap("extism_length", []ValType{I64}, []ValType{I64})
Expand All @@ -236,6 +234,55 @@ func buildEnvModule(ctx context.Context, rt wazero.Runtime, extism api.Module, f
wrap("extism_error_get", []ValType{}, []ValType{I64})
wrap("extism_memory_bytes", []ValType{}, []ValType{I64})

builder.NewFunctionBuilder().
WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) {
p, ok := ctx.Value("plugin").(*Plugin)
if !ok {
panic("Invalid context")
}

offset, ok := ctx.Value("inputOffset").(uint64)
if !ok {
panic("Invalid context")
}

stack[0], ok = p.Memory().ReadUint64Le(uint32(stack[0] + offset))
if !ok {
panic(fmt.Sprintf("could not read value at offset: %v", stack[0]))
}
}), []ValType{I64}, []ValType{I64}).
Export("extism_input_load_u64")

builder.NewFunctionBuilder().
WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) {
p, ok := ctx.Value("plugin").(*Plugin)
if !ok {
panic("Invalid context")
}

stack[0], ok = p.Memory().ReadUint64Le(uint32(stack[0]))
if !ok {
panic(fmt.Sprintf("could not read value at offset: %v", stack[0]))
}
}), []ValType{I64}, []ValType{I64}).
Export("extism_load_u64")

builder.NewFunctionBuilder().
WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) {
p, ok := ctx.Value("plugin").(*Plugin)
if !ok {
panic("Invalid context")
}

offset := stack[0]
value := stack[1]
ok = p.Memory().WriteUint64Le(uint32(offset), value)
if !ok {
panic(fmt.Sprintf("could not write value '%v' at offset: %v", value, offset))
}
}), []ValType{I64, I64}, []ValType{}).
Export("extism_store_u64")

hostFunc := func(name string, f interface{}) {
builder.NewFunctionBuilder().WithFunc(f).Export(name)
}
Expand Down
8 changes: 8 additions & 0 deletions plugins/noop/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/extism/extism-sdk-plugins-noop

go 1.20

require (
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64 // indirect
github.com/valyala/fastjson v1.6.3 // indirect
)
4 changes: 4 additions & 0 deletions plugins/noop/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64 h1:IfR1k741q+yQLvv5sLShCkvt3FgKU4wQVJfp7hhb/iY=
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64/go.mod h1:1wdiAoG8306g4WK+6laBrS+75089/0V4XRVTllt8b5U=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
8 changes: 8 additions & 0 deletions plugins/noop/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

//export run_test
func run_test() int32 {
return 0
}

func main() {}
8 changes: 8 additions & 0 deletions plugins/replace/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/extism/extism-sdk-plugins-replace

go 1.20

require (
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64 // indirect
github.com/valyala/fastjson v1.6.3 // indirect
)
4 changes: 4 additions & 0 deletions plugins/replace/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64 h1:IfR1k741q+yQLvv5sLShCkvt3FgKU4wQVJfp7hhb/iY=
github.com/extism/go-pdk v0.0.0-20230119214914-65bffbeb3e64/go.mod h1:1wdiAoG8306g4WK+6laBrS+75089/0V4XRVTllt8b5U=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
Loading

0 comments on commit a2a20c6

Please sign in to comment.