Module: HyperUuid::Runtime

Defined in:
lib/hyperuuid/runtime.rb,
lib/hyperuuid/wasm_runtime.rb

Overview

The WebAssembly backend: the same Rust core, compiled once to a wasm32-wasip1 module (lib/hyperuuid/native/wasm32-wasip1/hyperuuid.wasm) and run inside this process by the wasmtime gem — no per-platform shared library needed, and nothing dlopen'd. This is the inverse of ruby.wasm: not Ruby running inside a wasm sandbox, but a wasm module running inside Ruby.

Same integration shape as the Magnus extension: on require (after runtime.rb has defined the Fiddle backend) this redefines the HyperUuid::Runtime singleton methods in place — no delegation layer, no second surface. Everything above Runtime (the Uuid class, the module doors, batch slicing) stays shared byte-for-byte across all three backends, and the same Runtime::RandomSourceError / Runtime::TimestampOutOfRangeError classes carry the same messages.

Two things differ from the native backends, both forced by the sandbox:

  • A wasm guest only sees its own linear memory, so no Fiddle::Pointer can cross. Every buffer the core writes into is obtained from the module's own exported malloc (the wasi-libc allocator Rust's std already uses on this target) and read back with Memory#read. Using the guest's allocator rather than a host-picked offset is load-bearing: dlmalloc claims the tail of the initial memory on first use, and a batch written at a guessed offset was observed corrupted by the guest's next allocation.
  • A Wasmtime::Store is single-threaded, so every call is serialized under one Mutex. One shared instance per process is also what keeps the core's v7 counter — which lives inside the instance — monotonic across threads and batches, exactly as the one dlopen'd library does natively.

Defined Under Namespace

Classes: RandomSourceError, TimestampOutOfRangeError, WasmInstance

Constant Summary collapse

NATIVE_DIR =
File.join(__dir__, "native")
WASM_MODULE_PATH =
File.join(NATIVE_DIR, "wasm32-wasip1", "hyperuuid.wasm")

Class Method Summary collapse

Class Method Details

.fiddle_library_available? ⇒ Boolean

Whether this platform has a shared library for Fiddle to dlopen at all: a known RID and the file actually present in this install. Backend selection (hyperuuid.rb) asks this before falling back to the WebAssembly backend, which needs neither.

Returns:

  • (Boolean)


110
111
112
113
114
115
# File 'lib/hyperuuid/runtime.rb', line 110

def fiddle_library_available?
  rid, lib_name = NativePlatform.rid_and_library_name
  File.exist?(File.join(NATIVE_DIR, rid, lib_name))
rescue NativePlatform::UnsupportedPlatformError
  false
end

.new_v4 ⇒ Object

Raises:



24
25
26
27
28
29
# File 'lib/hyperuuid/runtime.rb', line 24

def new_v4
  out = scratch
  rc = functions[:new_v4].call(out)
  raise RandomSourceError, "uuid_new_v4 failed with code #{rc}" unless rc.zero?
  out[0, 16]
end

.new_v5(namespace_bytes, name_bytes) ⇒ Object

Raises:



31
32
33
34
35
36
37
38
39
# File 'lib/hyperuuid/runtime.rb', line 31

def new_v5(namespace_bytes, name_bytes)
  out = scratch
  # Fiddle passes a String's bytes for void* directly (read-only) — no Pointer
  # wrapper, no copy — the same zero-copy crossing every other input here uses.
  name = name_bytes.empty? ? nil : name_bytes
  rc = functions[:new_v5].call(namespace_bytes, name, name_bytes.bytesize, out)
  raise RandomSourceError, "uuid_new_v5 failed with code #{rc}" unless rc.zero?
  out[0, 16]
end

.new_v6(unix_millis) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/hyperuuid/runtime.rb', line 41

def new_v6(unix_millis)
  out = scratch
  rc = functions[:new_v6].call(unix_millis, out)
  case rc
  when 0 then out[0, 16]
  when 2 then raise TimestampOutOfRangeError, "unix_millis does not fit the 60-bit v6 timestamp field"
  else raise RandomSourceError, "uuid_new_v6 failed with code #{rc}"
  end
end

.new_v6_batch(count, unix_millis) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/hyperuuid/runtime.rb', line 55

def new_v6_batch(count, unix_millis)
  return "" if count.zero?
  out = Fiddle::Pointer.malloc(count * 16, Fiddle::RUBY_FREE)
  rc = functions[:new_v6_batch].call(unix_millis, count, out)
  case rc
  when 0 then out[0, count * 16]
  when 2 then raise TimestampOutOfRangeError, "unix_millis does not fit the 60-bit v6 timestamp field"
  else raise RandomSourceError, "uuid_new_v6_batch failed with code #{rc}"
  end
end

.new_v7(unix_millis) ⇒ Object



66
67
68
69
70
71
72
73
74
# File 'lib/hyperuuid/runtime.rb', line 66

def new_v7(unix_millis)
  out = scratch
  rc = functions[:new_v7].call(unix_millis, out)
  case rc
  when 0 then out[0, 16]
  when 2 then raise TimestampOutOfRangeError, "unix_millis must fit within the RFC 9562 48-bit field"
  else raise RandomSourceError, "uuid_new_v7 failed with code #{rc}"
  end
end

.new_v7_batch(count, unix_millis) ⇒ Object



80
81
82
83
84
85
86
87
88
89
# File 'lib/hyperuuid/runtime.rb', line 80

def new_v7_batch(count, unix_millis)
  return "" if count.zero?
  out = Fiddle::Pointer.malloc(count * 16, Fiddle::RUBY_FREE)
  rc = functions[:new_v7_batch].call(unix_millis, count, out)
  case rc
  when 0 then out[0, count * 16]
  when 2 then raise TimestampOutOfRangeError, "unix_millis must fit within the RFC 9562 48-bit field"
  else raise RandomSourceError, "uuid_new_v7_batch failed with code #{rc}"
  end
end

.v6_to_rfc_order(bytes) ⇒ Object



103
104
105
# File 'lib/hyperuuid/runtime.rb', line 103

def v6_to_rfc_order(bytes)
  rewrite(:v6_to_rfc_order, bytes)
end

.v6_to_sql_order(bytes) ⇒ Object



99
100
101
# File 'lib/hyperuuid/runtime.rb', line 99

def v6_to_sql_order(bytes)
  rewrite(:v6_to_sql_order, bytes)
end

.v6_unix_millis(bytes) ⇒ Object



51
52
53
# File 'lib/hyperuuid/runtime.rb', line 51

def v6_unix_millis(bytes)
  functions[:v6_unix_millis].call(bytes)
end

.v7_to_rfc_order(bytes) ⇒ Object



95
96
97
# File 'lib/hyperuuid/runtime.rb', line 95

def v7_to_rfc_order(bytes)
  rewrite(:v7_to_rfc_order, bytes)
end

.v7_to_sql_order(bytes) ⇒ Object



91
92
93
# File 'lib/hyperuuid/runtime.rb', line 91

def v7_to_sql_order(bytes)
  rewrite(:v7_to_sql_order, bytes)
end

.v7_unix_millis(bytes) ⇒ Object



76
77
78
# File 'lib/hyperuuid/runtime.rb', line 76

def v7_unix_millis(bytes)
  functions[:v7_unix_millis].call(bytes)
end