extism 0.0.1.rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Rakefile +3 -0
- data/example.rb +16 -0
- data/lib/extism/version.rb +5 -0
- data/lib/extism.rb +183 -0
- data/sig/extism.rbs +4 -0
- data/user_code.wasm +0 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9baa968de02938c07de67d56f468c482392d15c3b0d4856679ffa65ac54bcf8e
|
4
|
+
data.tar.gz: 18577dac8681ec65162505ea3664ec85d39be3c7b97df203a12f89a0eeff1617
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ffc8f845fc8d42e6f55dc90bc56ab6e4b2ad35f1a1af88c4a5caf1c8f0353314be92e2e33157eb9c50c9a7d168d477d78c7af883f7d30f5879f27e53e4b34f0f
|
7
|
+
data.tar.gz: cf4175f208e06380f761ff80dec20ac38a416e7c866cee1ccb3844a37e24369464eeb4a60bcd547b7e348fc00744d06de8cceda5cfcd87dc9675d45bdbb407f4
|
data/Gemfile
ADDED
data/Rakefile
ADDED
data/example.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require './lib/extism'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# a Context provides a scope for plugins to be managed within. creating multiple contexts
|
5
|
+
# is expected and groups plugins based on source/tenant/lifetime etc.
|
6
|
+
ctx = Extism::Context.new
|
7
|
+
Extism::with_context {|ctx|
|
8
|
+
manifest = {
|
9
|
+
:wasm => [{:path => "../wasm/code.wasm"}]
|
10
|
+
}
|
11
|
+
|
12
|
+
plugin = ctx.plugin(manifest)
|
13
|
+
res = JSON.parse(plugin.call("count_vowels", ARGV[0] || "this is a test"))
|
14
|
+
|
15
|
+
puts res['count']
|
16
|
+
}
|
data/lib/extism.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Extism
|
5
|
+
module C
|
6
|
+
extend FFI::Library
|
7
|
+
ffi_lib "extism"
|
8
|
+
attach_function :extism_context_new, [], :pointer
|
9
|
+
attach_function :extism_context_free, [:pointer], :void
|
10
|
+
attach_function :extism_plugin_new, [:pointer, :pointer, :uint64, :bool], :int32
|
11
|
+
attach_function :extism_plugin_update, [:pointer, :int32, :pointer, :uint64, :bool], :bool
|
12
|
+
attach_function :extism_error, [:pointer, :int32], :string
|
13
|
+
attach_function :extism_plugin_call, [:pointer, :int32, :string, :pointer, :uint64], :int32
|
14
|
+
attach_function :extism_plugin_function_exists, [:pointer, :int32, :string], :bool
|
15
|
+
attach_function :extism_plugin_output_length, [:pointer, :int32], :uint64
|
16
|
+
attach_function :extism_plugin_output_data, [:pointer, :int32], :pointer
|
17
|
+
attach_function :extism_log_file, [:string, :pointer], :void
|
18
|
+
attach_function :extism_plugin_free, [:pointer, :int32], :void
|
19
|
+
attach_function :extism_context_reset, [:pointer], :void
|
20
|
+
attach_function :extism_version, [], :string
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class Error < StandardError
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the version of Extism
|
28
|
+
def self.extism_version
|
29
|
+
C.extism_version
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set log file and level, this is a global configuration
|
33
|
+
def self.set_log_file(name, level=nil)
|
34
|
+
if level then
|
35
|
+
level = FFI::MemoryPointer::from_string(level)
|
36
|
+
end
|
37
|
+
C.extism_log_file(name, level)
|
38
|
+
end
|
39
|
+
|
40
|
+
$PLUGINS = {}
|
41
|
+
$FREE_PLUGIN = proc { |id|
|
42
|
+
x = $PLUGINS[id]
|
43
|
+
if !x.nil? then
|
44
|
+
C.extism_plugin_free(x[:context].pointer, x[:plugin])
|
45
|
+
$PLUGINS.delete(id)
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
$CONTEXTS = {}
|
50
|
+
$FREE_CONTEXT = proc { |id|
|
51
|
+
x = $CONTEXTS[id]
|
52
|
+
if !x.nil? then
|
53
|
+
C.extism_context_free($CONTEXTS[id])
|
54
|
+
$CONTEXTS.delete(id)
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
# Context is used to manage plugins
|
59
|
+
class Context
|
60
|
+
attr_accessor :pointer
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@pointer = C.extism_context_new()
|
64
|
+
$CONTEXTS[self.object_id] = @pointer
|
65
|
+
ObjectSpace.define_finalizer(self, $FREE_CONTEXT)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Remove all registered plugins
|
69
|
+
def reset
|
70
|
+
C.extism_context_reset(@pointer)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Free the context, this should be called when it is no longer needed
|
74
|
+
def free
|
75
|
+
if @pointer.nil? then
|
76
|
+
return
|
77
|
+
end
|
78
|
+
$CONTEXTS.delete(self.object_id)
|
79
|
+
C.extism_context_free(@pointer)
|
80
|
+
@pointer = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create a new plugin from a WASM module or JSON encoded manifest
|
84
|
+
def plugin(wasm, wasi=false, config=nil)
|
85
|
+
return Plugin.new(self, wasm, wasi, config)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def self.with_context(&block)
|
91
|
+
ctx = Context.new
|
92
|
+
begin
|
93
|
+
x = block.call(ctx)
|
94
|
+
return x
|
95
|
+
ensure
|
96
|
+
ctx.free
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Plugin
|
101
|
+
def initialize(context, wasm, wasi=false, config=nil)
|
102
|
+
if wasm.class == Hash then
|
103
|
+
wasm = JSON.generate(wasm)
|
104
|
+
end
|
105
|
+
code = FFI::MemoryPointer.new(:char, wasm.bytesize)
|
106
|
+
code.put_bytes(0, wasm)
|
107
|
+
@plugin = C.extism_plugin_new(context.pointer, code, wasm.bytesize, wasi)
|
108
|
+
if @plugin < 0 then
|
109
|
+
err = C.extism_error(-1)
|
110
|
+
if err&.empty? then
|
111
|
+
raise Error.new "extism_plugin_new failed"
|
112
|
+
else raise Error.new err
|
113
|
+
end
|
114
|
+
end
|
115
|
+
@context = context
|
116
|
+
$PLUGINS[self.object_id] = {:plugin => @plugin, :context => context}
|
117
|
+
ObjectSpace.define_finalizer(self, $FREE_PLUGIN)
|
118
|
+
if config != nil and @plugin >= 0 then
|
119
|
+
s = JSON.generate(config)
|
120
|
+
ptr = FFI::MemoryPointer::from_string(s)
|
121
|
+
C.extism_plugin_config(@context.pointer, @plugin, ptr, s.bytesize)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Update a plugin with new WASM module or manifest
|
126
|
+
def update(wasm, wasi=false, config=nil)
|
127
|
+
if wasm.class == Hash then
|
128
|
+
wasm = JSON.generate(wasm)
|
129
|
+
end
|
130
|
+
code = FFI::MemoryPointer.new(:char, wasm.bytesize)
|
131
|
+
code.put_bytes(0, wasm)
|
132
|
+
ok = C.extism_plugin_update(@context.pointer, @plugin, code, wasm.bytesize, wasi)
|
133
|
+
if !ok then
|
134
|
+
err = C.extism_error(-1)
|
135
|
+
if err&.empty? then
|
136
|
+
raise Error.new "extism_plugin_update failed"
|
137
|
+
else raise Error.new err
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if config != nil then
|
142
|
+
s = JSON.generate(config)
|
143
|
+
ptr = FFI::MemoryPointer::from_string(s)
|
144
|
+
C.extism_plugin_config(@context.pointer, @plugin, ptr, s.bytesize)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Check if a function exists
|
149
|
+
def function_exists(name)
|
150
|
+
return C.extism_function_exists(@context.pointer, @plugin, name)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Call a function by name
|
154
|
+
def call(name, data, &block)
|
155
|
+
# If no block was passed then use Pointer::read_string
|
156
|
+
block ||= ->(buf, len){ buf.read_string(len) }
|
157
|
+
input = FFI::MemoryPointer::from_string(data)
|
158
|
+
rc = C.extism_plugin_call(@context.pointer, @plugin, name, input, data.bytesize)
|
159
|
+
if rc != 0 then
|
160
|
+
err = C.extism_error(@context.pointer, @plugin)
|
161
|
+
if err&.empty? then
|
162
|
+
raise Error.new "extism_call failed"
|
163
|
+
else raise Error.new err
|
164
|
+
end
|
165
|
+
end
|
166
|
+
out_len = C.extism_plugin_output_length(@context.pointer, @plugin)
|
167
|
+
buf = C.extism_plugin_output_data(@context.pointer, @plugin)
|
168
|
+
return block.call(buf, out_len)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Free a plugin, this should be called when the plugin is no longer needed
|
172
|
+
def free
|
173
|
+
if @context.pointer.nil? then
|
174
|
+
return
|
175
|
+
end
|
176
|
+
|
177
|
+
$PLUGINS.delete(self.object_id)
|
178
|
+
C.extism_plugin_free(@context.pointer, @plugin)
|
179
|
+
@plugin = -1
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
data/sig/extism.rbs
ADDED
data/user_code.wasm
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: extism
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.rc4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- zach
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A library for loading and executing WASM plugins
|
14
|
+
email:
|
15
|
+
- zachshipko@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- Rakefile
|
22
|
+
- example.rb
|
23
|
+
- lib/extism.rb
|
24
|
+
- lib/extism/version.rb
|
25
|
+
- sig/extism.rbs
|
26
|
+
- user_code.wasm
|
27
|
+
homepage: https://github.com/extism/extism
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata:
|
31
|
+
homepage_uri: https://github.com/extism/extism
|
32
|
+
source_code_uri: https://github.com/extism/extism
|
33
|
+
changelog_uri: https://github.com/extism/extism
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 2.6.0
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.3.1
|
48
|
+
requirements: []
|
49
|
+
rubygems_version: 3.1.6
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Extism WASM SDK
|
53
|
+
test_files: []
|