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 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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in extism.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "ffi"
10
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
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
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Extism
4
+ VERSION = "0.0.1.rc4"
5
+ end
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
@@ -0,0 +1,4 @@
1
+ module Extism
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []