hyperuuid 0.2.1-aarch64-linux → 0.3.0-aarch64-linux

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b849ff1768c8be95682aca3a919293c35e41544021e32cabb16af7eaea47a75f
4
- data.tar.gz: d146d988512e41065a80fa628cfd64a270add25b7ecbdba731f3bf3b9da091c1
3
+ metadata.gz: f236489589d106e3922b7ea6afc388da14ee34b7eeae2398d13a26e6de842ba8
4
+ data.tar.gz: ebadf7adf1ff518f468fcb00d28abe9004e26e29013c9f393063752ea07fdd31
5
5
  SHA512:
6
- metadata.gz: 4b1aee8bf3925edfaf5c62fd812abc81ca945d01bfffab1354a16fdca414581f0e5349a29aee2ec36a01d84d10a36d16c0231c5ed87f1405141eed53706785ac
7
- data.tar.gz: 66d93d8cfc927384f81694200dbbd6f048c3df9d3088f6e4f5b821cdbd729fba2b5b238ab6d54d161bb68e922e40713ee0ceb9af65721b684590ac940332e2dd
6
+ metadata.gz: c83e2025e8f493d5762607b0ef5acc1238252dd7b7998a9afe4462f4a2bb382af52298713abce4b79830e274b6da317e51ff14db21146d6ae7f8cfc1e1e74d72
7
+ data.tar.gz: b57eca92e941b4ec157457d5610d3db7ed36af4057f0f91e053c461681f36228714344e8a177985659ed5af0545a7f3f93a7de6eb91bcc743e2a4c5d859da8b8
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  **Ruby's own stdlib stops at `SecureRandom.uuid` — random v4, full stop. No v5, no v6, no v7. This gem is the whole RFC, with zero gem dependency beyond `Fiddle` (which ships with every Ruby install) — and it's faster than `SecureRandom.uuid` too.**
7
7
 
8
8
  RFC 9562 UUID v4 (random), v5 (deterministic), v6 and v7 (time-sortable) generation, with
9
- two backends sharing one public surface. The fast path is a native extension built with
9
+ three backends sharing one public surface. The fast path is a native extension built with
10
10
  [Magnus](https://github.com/matsadler/magnus) — the Rust core linked directly into the Ruby
11
11
  VM, auto-selected when loadable — which redefines the low-level `Runtime` methods in place
12
12
  on require; everything above them (`Uuid`, the module doors, batch slicing) is shared
@@ -15,7 +15,10 @@ shared library via [`Fiddle`](https://docs.ruby-lang.org/en/master/Fiddle.html)
15
15
  dlopen/dlsym plus a raw C-ABI call, no runtime bridge, nothing to compile on
16
16
  `bundle install`. Set `HYPERUUID_PURE=1` to force the Fiddle backend;
17
17
  `HyperUuid::BACKEND` reports which one is live. Bundles a native build for every supported
18
- platform (linux/darwin/windows × x64/arm64) and picks the right one at runtime.
18
+ platform (linux/darwin/windows × x64/arm64) and picks the right one at runtime. A third
19
+ backend runs the same core as a WebAssembly module inside the
20
+ [`wasmtime`](https://rubygems.org/gems/wasmtime) gem, for any platform with no native build
21
+ at all — see [WebAssembly (wasmtime)](#webassembly-wasmtime).
19
22
 
20
23
  ```ruby
21
24
  require "hyperuuid"
@@ -109,6 +112,77 @@ Batch generation still amortizes per-call cost on both backends — one native c
109
112
 
110
113
  The batch multiplier shrank from 11x to ~3.8x for the best reason available: the individual calls got 3x faster, so there's less waste left to amortize. If you need v5/v6/v7, need many at once, or need this Ruby service's IDs to agree byte-for-byte with a Go or Python service's, that's what this gem is for — and now it's the fast option too, not just the capable one.
111
114
 
115
+ ## WebAssembly (wasmtime)
116
+
117
+ The Rust core also ships inside this gem as a `wasm32-wasip1` module
118
+ (`lib/hyperuuid/native/wasm32-wasip1/hyperuuid.wasm`), and the
119
+ [`wasmtime`](https://rubygems.org/gems/wasmtime) gem can run it in-process. This is the
120
+ inverse of ruby.wasm — not Ruby inside a wasm sandbox, but a wasm module inside Ruby — and
121
+ it is the one backend that needs no shared library for the platform it runs on: no
122
+ `dlopen`, no Magnus extension, nothing compiled against this Ruby's ABI. Everything above
123
+ `Runtime` (`Uuid`, the module doors, batch slicing) is the same code the other two backends
124
+ run, and `spec/wasm_backend_spec.rb` pins that the outputs agree with the Fiddle backend
125
+ byte for byte.
126
+
127
+ `wasmtime` is deliberately **not** a dependency of this gem; a consumer who wants this path
128
+ installs it:
129
+
130
+ ```sh
131
+ gem install wasmtime
132
+ HYPERUUID_WASM=1 ruby -rhyperuuid -e 'p HyperUuid::BACKEND' # => :wasm
133
+ ```
134
+
135
+ `HYPERUUID_WASM=1` forces the backend (and raises a `LoadError` naming the gem if it is
136
+ missing). Without it, the wasm backend is only ever chosen automatically when there is no
137
+ native library for this platform at all — no Magnus extension and no `libhyperuuid` for the
138
+ RID — and `wasmtime` happens to be installed. No supported platform's behavior changes just
139
+ because this backend exists.
140
+
141
+ Two things are different under the sandbox, both by necessity. A wasm guest only sees its own
142
+ linear memory, so every buffer the core fills comes from the module's own exported `malloc`
143
+ (the same wasi-libc allocator Rust's std uses on that target) and is read back with
144
+ `Memory#read` — using the guest's allocator rather than a host-picked offset is what keeps a
145
+ batch from being clobbered by the guest's next allocation. And a `Wasmtime::Store` is
146
+ single-threaded, so every call is serialized under one Mutex around one shared instance,
147
+ which is also what keeps the core's v7 counter (it lives inside the instance) monotonic
148
+ across threads and batches, exactly as the one dlopen'd library does natively.
149
+
150
+ Measured, same box as the benchmarks above (Ruby 4.0.6, linux-arm64, wasmtime 47.0.3):
151
+
152
+ | Call | wasmtime | native (Magnus) |
153
+ |---|---:|---:|
154
+ | `uuid_new_v7`, single, per call | 867 ns | ~450 ns |
155
+ | `uuid_new_v7_batch(1000)`, per call | 40.6 µs (50.6 µs with the 16 KB read back into Ruby) | 24 µs |
156
+
157
+ So roughly 2x the native cost per call, and the batch doors amortize it the same way they do
158
+ for Fiddle. The guest's own work is not where the time goes — the identical module runs at
159
+ 14 µs per thousand under a JIT-compiled host — it is the crossing, and Ruby's is one of the
160
+ cheaper ones.
161
+
162
+ ## Verifying provenance
163
+
164
+ Every gem RubyGems.org serves — the universal fallback and each of the six precompiled
165
+ platform gems — carries its own GitHub build-provenance attestation, signed directly by
166
+ this repo's own `release.yml` (the `rubygems-publish` job attests `ruby/pkg/*.gem` right
167
+ before the push), so plain `--repo` verifies any of them:
168
+
169
+ ```sh
170
+ gem fetch hyperuuid -v X.Y.Z --platform <platform> # or omit --platform for the universal gem
171
+ gh attestation verify hyperuuid-X.Y.Z-<platform>.gem --repo SkunkWerkx/HyperUuid
172
+ ```
173
+
174
+ That's the release's second layer of checking, not the only one: before any gem gets built,
175
+ the same job verifies all ten native binaries it packs (six FFI libs, four Magnus
176
+ extensions) against *their own* attestations — those are signed from `SkunkWerkx/.github`
177
+ by `hyper-build-native.yml`, so that check needs `--signer-repo SkunkWerkx/.github` added —
178
+ and refuses to proceed on an unverified one. RubyGems.org has no unpublish and no
179
+ duplicate-version overwrite, so this all happens while a bad artifact is still reversible.
180
+ The release run's job summary then re-fetches every gem from the CDN and records
181
+ attested-vs-served digests, turning "rubygems.org stores an upload verbatim" into a
182
+ per-release measurement rather than an assumption — see
183
+ [csharp/README.md's provenance section](../csharp/README.md#native-binary-provenance) for
184
+ more on why `--signer-repo` is needed for some artifacts here and not others.
185
+
112
186
  ## Install
113
187
 
114
188
  ```sh
Binary file
Binary file
@@ -1,6 +1,9 @@
1
1
  # native/
2
2
 
3
3
  Populated per-RID with the platform's native `libhyperuuid` build (`native/{rid}/{lib}`) by CI
4
- and by `cargo build --release` for local dev — see `../../../.gitignore`. This file exists so
4
+ and by `cargo build --release` for local dev — see `../../../.gitignore` plus
5
+ `wasm32-wasip1/hyperuuid.wasm`, the same core as a WebAssembly module for the `wasmtime`
6
+ backend (`../wasm_runtime.rb`), staged the same way from
7
+ `cargo build --release --target wasm32-wasip1` run inside `rust/`. This file exists so
5
8
  the directory has at least one tracked file on a fresh checkout, matching the Go/Swift
6
9
  bindings' `native/`/`NativeLibs/` placeholder convention.
@@ -104,6 +104,16 @@ module HyperUuid
104
104
  rewrite(:v6_to_rfc_order, bytes)
105
105
  end
106
106
 
107
+ # Whether this platform has a shared library for Fiddle to dlopen at all: a known RID
108
+ # and the file actually present in this install. Backend selection (hyperuuid.rb)
109
+ # asks this before falling back to the WebAssembly backend, which needs neither.
110
+ def fiddle_library_available?
111
+ rid, lib_name = NativePlatform.rid_and_library_name
112
+ File.exist?(File.join(NATIVE_DIR, rid, lib_name))
113
+ rescue NativePlatform::UnsupportedPlatformError
114
+ false
115
+ end
116
+
107
117
  private
108
118
 
109
119
  # One 16-byte scratch allocation per thread, reused by every single-item call —
@@ -0,0 +1,241 @@
1
+ require "wasmtime"
2
+
3
+ module HyperUuid
4
+ # The WebAssembly backend: the same Rust core, compiled once to a `wasm32-wasip1` module
5
+ # (lib/hyperuuid/native/wasm32-wasip1/hyperuuid.wasm) and run inside this process by the
6
+ # `wasmtime` gem — no per-platform shared library needed, and nothing dlopen'd. This is the
7
+ # inverse of ruby.wasm: not Ruby running inside a wasm sandbox, but a wasm module running
8
+ # inside Ruby.
9
+ #
10
+ # Same integration shape as the Magnus extension: on require (after runtime.rb has defined
11
+ # the Fiddle backend) this redefines the `HyperUuid::Runtime` singleton methods **in place**
12
+ # — no delegation layer, no second surface. Everything above Runtime (the `Uuid` class, the
13
+ # module doors, batch slicing) stays shared byte-for-byte across all three backends, and the
14
+ # same `Runtime::RandomSourceError` / `Runtime::TimestampOutOfRangeError` classes carry the
15
+ # same messages.
16
+ #
17
+ # Two things differ from the native backends, both forced by the sandbox:
18
+ #
19
+ # * A wasm guest only sees its own linear memory, so no Fiddle::Pointer can cross. Every
20
+ # buffer the core writes into is obtained from the module's own exported `malloc` (the
21
+ # wasi-libc allocator Rust's std already uses on this target) and read back with
22
+ # `Memory#read`. Using the guest's allocator rather than a host-picked offset is
23
+ # load-bearing: dlmalloc claims the tail of the initial memory on first use, and a batch
24
+ # written at a guessed offset was observed corrupted by the guest's next allocation.
25
+ # * A `Wasmtime::Store` is single-threaded, so every call is serialized under one Mutex.
26
+ # One shared instance per process is also what keeps the core's v7 counter — which lives
27
+ # inside the instance — monotonic across threads and batches, exactly as the one dlopen'd
28
+ # library does natively.
29
+ module Runtime
30
+ WASM_MODULE_PATH = File.join(NATIVE_DIR, "wasm32-wasip1", "hyperuuid.wasm")
31
+
32
+ # One instantiated module: the exported functions, the exported memory, and the two
33
+ # 16-byte scratch buffers (one in, one out) the single-item doors reuse for the life of
34
+ # the process. Batches and v5 names get a `malloc`/`free` pair per call instead — one
35
+ # allocation amortized over `count` IDs, the same shape as the Fiddle backend's per-batch
36
+ # Pointer.malloc.
37
+ class WasmInstance
38
+ attr_reader :memory, :in, :out
39
+
40
+ EXPORTS = %i[
41
+ malloc free
42
+ uuid_new_v4 uuid_new_v5 uuid_new_v6 uuid_v6_unix_millis uuid_new_v6_batch
43
+ uuid_new_v7 uuid_v7_unix_millis uuid_new_v7_batch
44
+ uuid_v7_to_sql_order uuid_v7_to_rfc_order uuid_v6_to_sql_order uuid_v6_to_rfc_order
45
+ ].freeze
46
+
47
+ def initialize(path)
48
+ unless File.exist?(path)
49
+ raise LoadError,
50
+ "hyperuuid: #{path} not found (this gem was built without its WebAssembly module)"
51
+ end
52
+
53
+ engine = Wasmtime::Engine.new
54
+ mod = Wasmtime::Module.from_file(engine, path)
55
+ linker = Wasmtime::Linker.new(engine)
56
+ # The module imports five WASI preview1 functions (random_get is the one that
57
+ # matters: it is the core's only entropy source; the rest are wasi-libc's startup
58
+ # plumbing). A default WasiConfig — no stdio, no filesystem, no environment — is all
59
+ # any of them need.
60
+ Wasmtime::WASI::P1.add_to_linker_sync(linker)
61
+ store = Wasmtime::Store.new(engine, wasi_p1_config: Wasmtime::WasiConfig.new)
62
+ instance = linker.instantiate(store, mod)
63
+
64
+ @memory = instance.export("memory").to_memory
65
+ @fn = EXPORTS.to_h { |name| [name, instance.export(name.to_s).to_func] }
66
+ @in = malloc(16)
67
+ @out = malloc(16)
68
+ end
69
+
70
+ def fn(name)
71
+ @fn.fetch(name)
72
+ end
73
+
74
+ def malloc(size)
75
+ ptr = @fn[:malloc].call(size)
76
+ raise NoMemoryError, "hyperuuid: wasm malloc(#{size}) failed" if ptr.zero?
77
+ ptr
78
+ end
79
+
80
+ def free(ptr)
81
+ @fn[:free].call(ptr)
82
+ end
83
+
84
+ # Runs `block` with a `size`-byte guest buffer, freeing it afterwards even on error.
85
+ def with_buffer(size)
86
+ ptr = malloc(size)
87
+ begin
88
+ yield ptr
89
+ ensure
90
+ free(ptr)
91
+ end
92
+ end
93
+ end
94
+
95
+ @wasm_mutex = Mutex.new
96
+ @wasm = nil
97
+
98
+ class << self
99
+ def new_v4
100
+ wasm do |w|
101
+ rc = w.fn(:uuid_new_v4).call(w.out)
102
+ raise RandomSourceError, "uuid_new_v4 failed with code #{rc}" unless rc.zero?
103
+ w.memory.read(w.out, 16)
104
+ end
105
+ end
106
+
107
+ def new_v5(namespace_bytes, name_bytes)
108
+ wasm do |w|
109
+ w.memory.write(w.in, namespace_bytes)
110
+ # A zero-length name still needs a valid (non-null) pointer inside the guest —
111
+ # the `in` scratch, already holding the namespace, serves; nothing reads past
112
+ # a zero length.
113
+ rc =
114
+ if name_bytes.empty?
115
+ w.fn(:uuid_new_v5).call(w.in, w.in, 0, w.out)
116
+ else
117
+ w.with_buffer(name_bytes.bytesize) do |name|
118
+ w.memory.write(name, name_bytes)
119
+ w.fn(:uuid_new_v5).call(w.in, name, name_bytes.bytesize, w.out)
120
+ end
121
+ end
122
+ raise RandomSourceError, "uuid_new_v5 failed with code #{rc}" unless rc.zero?
123
+ w.memory.read(w.out, 16)
124
+ end
125
+ end
126
+
127
+ def new_v6(unix_millis)
128
+ wasm do |w|
129
+ rc = w.fn(:uuid_new_v6).call(i64(unix_millis), w.out)
130
+ case rc
131
+ when 0 then w.memory.read(w.out, 16)
132
+ when 2 then raise TimestampOutOfRangeError, "unix_millis does not fit the 60-bit v6 timestamp field"
133
+ else raise RandomSourceError, "uuid_new_v6 failed with code #{rc}"
134
+ end
135
+ end
136
+ end
137
+
138
+ def v6_unix_millis(bytes)
139
+ wasm do |w|
140
+ w.memory.write(w.in, bytes)
141
+ u64(w.fn(:uuid_v6_unix_millis).call(w.in))
142
+ end
143
+ end
144
+
145
+ def new_v6_batch(count, unix_millis)
146
+ return "" if count.zero?
147
+ wasm do |w|
148
+ w.with_buffer(count * 16) do |out|
149
+ rc = w.fn(:uuid_new_v6_batch).call(i64(unix_millis), count, out)
150
+ case rc
151
+ when 0 then w.memory.read(out, count * 16)
152
+ when 2 then raise TimestampOutOfRangeError, "unix_millis does not fit the 60-bit v6 timestamp field"
153
+ else raise RandomSourceError, "uuid_new_v6_batch failed with code #{rc}"
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ def new_v7(unix_millis)
160
+ wasm do |w|
161
+ rc = w.fn(:uuid_new_v7).call(i64(unix_millis), w.out)
162
+ case rc
163
+ when 0 then w.memory.read(w.out, 16)
164
+ when 2 then raise TimestampOutOfRangeError, "unix_millis must fit within the RFC 9562 48-bit field"
165
+ else raise RandomSourceError, "uuid_new_v7 failed with code #{rc}"
166
+ end
167
+ end
168
+ end
169
+
170
+ def v7_unix_millis(bytes)
171
+ wasm do |w|
172
+ w.memory.write(w.in, bytes)
173
+ u64(w.fn(:uuid_v7_unix_millis).call(w.in))
174
+ end
175
+ end
176
+
177
+ def new_v7_batch(count, unix_millis)
178
+ return "" if count.zero?
179
+ wasm do |w|
180
+ w.with_buffer(count * 16) do |out|
181
+ rc = w.fn(:uuid_new_v7_batch).call(i64(unix_millis), count, out)
182
+ case rc
183
+ when 0 then w.memory.read(out, count * 16)
184
+ when 2 then raise TimestampOutOfRangeError, "unix_millis must fit within the RFC 9562 48-bit field"
185
+ else raise RandomSourceError, "uuid_new_v7_batch failed with code #{rc}"
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def v7_to_sql_order(bytes)
192
+ rewrite(:uuid_v7_to_sql_order, bytes)
193
+ end
194
+
195
+ def v7_to_rfc_order(bytes)
196
+ rewrite(:uuid_v7_to_rfc_order, bytes)
197
+ end
198
+
199
+ def v6_to_sql_order(bytes)
200
+ rewrite(:uuid_v6_to_sql_order, bytes)
201
+ end
202
+
203
+ def v6_to_rfc_order(bytes)
204
+ rewrite(:uuid_v6_to_rfc_order, bytes)
205
+ end
206
+
207
+ private
208
+
209
+ # The in-place byte-order rewrites: copy the caller's frozen bytes into the guest,
210
+ # let the core rewrite them there, read the result back.
211
+ def rewrite(export, bytes)
212
+ wasm do |w|
213
+ w.memory.write(w.in, bytes)
214
+ w.fn(export).call(w.in)
215
+ w.memory.read(w.in, 16)
216
+ end
217
+ end
218
+
219
+ # Every call holds the one lock: the Store is single-threaded, and the lazily created
220
+ # instance is shared by every thread for the life of the process (never torn down,
221
+ # same as the dlopen'd library natively).
222
+ def wasm
223
+ @wasm_mutex.synchronize do
224
+ @wasm ||= WasmInstance.new(WASM_MODULE_PATH)
225
+ yield @wasm
226
+ end
227
+ end
228
+
229
+ # wasm has no unsigned integers: a u64 crosses as an i64. The core's own range checks
230
+ # (48-bit v7, 60-bit v6) still see the exact value the caller passed, they just see
231
+ # it through a two's-complement reinterpretation on the way in and back.
232
+ def i64(value)
233
+ value >= 2**63 ? value - 2**64 : value
234
+ end
235
+
236
+ def u64(value)
237
+ value.negative? ? value + 2**64 : value
238
+ end
239
+ end
240
+ end
241
+ end
data/lib/hyperuuid.rb CHANGED
@@ -13,7 +13,7 @@ require_relative "hyperuuid/runtime"
13
13
  module HyperUuid
14
14
  # This gem's own version — distinct from the RFC 9562 UUID *versions* (v4/v5/v6/v7) the
15
15
  # rest of this module generates.
16
- VERSION = "0.2.1"
16
+ VERSION = "0.3.0"
17
17
 
18
18
  # Creates a random UUID version 4 (RFC 9562 §5.4).
19
19
  def self.new_v4
@@ -115,8 +115,24 @@ end
115
115
  # remain the universal zero-compile fallback; precompiled platform gems are how the
116
116
  # extension ships without ever making a consumer compile anything. Set HYPERUUID_PURE=1 to
117
117
  # force Fiddle.
118
+ #
119
+ # The third backend is WebAssembly (lib/hyperuuid/wasm_runtime.rb): the same core as a
120
+ # wasm32-wasip1 module, run in-process by the `wasmtime` gem, which is deliberately not a
121
+ # runtime dependency of this gem — a consumer who wants it installs it. HYPERUUID_WASM=1
122
+ # forces it (and fails loudly if wasmtime is missing); otherwise it is only ever chosen when
123
+ # there is no native library for this platform at all and wasmtime happens to be available,
124
+ # so no supported platform's behavior changes by its existence.
118
125
  HyperUuid::BACKEND =
119
- if ENV["HYPERUUID_PURE"]
126
+ if ENV["HYPERUUID_WASM"]
127
+ begin
128
+ require "wasmtime"
129
+ rescue LoadError
130
+ raise LoadError,
131
+ "hyperuuid: HYPERUUID_WASM=1 needs the wasmtime gem — `gem install wasmtime` (or add it to your Gemfile)"
132
+ end
133
+ require_relative "hyperuuid/wasm_runtime"
134
+ :wasm
135
+ elsif ENV["HYPERUUID_PURE"]
120
136
  :fiddle
121
137
  else
122
138
  # Two layouts, and both have to work. A released platform gem is a "fat" gem carrying one
@@ -136,7 +152,21 @@ HyperUuid::BACKEND =
136
152
  require "hyperuuid_native"
137
153
  :native
138
154
  rescue LoadError
139
- :fiddle
155
+ if HyperUuid::Runtime.fiddle_library_available?
156
+ :fiddle
157
+ else
158
+ # No shared library for this platform either. wasmtime, if the consumer has it,
159
+ # is the only backend left that can run here; without it, stay on Fiddle so the
160
+ # first call raises its own precise "not found" LoadError rather than a vaguer one
161
+ # from here.
162
+ begin
163
+ require "wasmtime"
164
+ require_relative "hyperuuid/wasm_runtime"
165
+ :wasm
166
+ rescue LoadError
167
+ :fiddle
168
+ end
169
+ end
140
170
  end
141
171
  end
142
172
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperuuid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Brian Buvinghausen
@@ -84,11 +84,13 @@ files:
84
84
  - lib/hyperuuid/native/linux-x64/libhyperuuid.so
85
85
  - lib/hyperuuid/native/osx-arm64/libhyperuuid.dylib
86
86
  - lib/hyperuuid/native/osx-x64/libhyperuuid.dylib
87
+ - lib/hyperuuid/native/wasm32-wasip1/hyperuuid.wasm
87
88
  - lib/hyperuuid/native/win-arm64/hyperuuid.dll
88
89
  - lib/hyperuuid/native/win-x64/hyperuuid.dll
89
90
  - lib/hyperuuid/native_platform.rb
90
91
  - lib/hyperuuid/runtime.rb
91
92
  - lib/hyperuuid/uuid.rb
93
+ - lib/hyperuuid/wasm_runtime.rb
92
94
  homepage: https://github.com/SkunkWerkx/HyperUuid
93
95
  licenses:
94
96
  - MIT