hyperuuid 0.1.1-x86_64-linux → 0.2.0-x86_64-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: 0e5b3395faa479b95098822f2daabae04fe58fc217b73ccf31091c1524463cb3
4
- data.tar.gz: c5ff1e192de7c70e18ccd9490d0f1b6b30392727f9387ec6dc0e3f1a2cbf4d79
3
+ metadata.gz: f3bdd878a601c1d3fc1fe11cb201fa9afb5716a0da4e37af12489887566559a8
4
+ data.tar.gz: 51af5963b4b41c2210cf6cbbd00e0a09c735ee9a7ce69b062e8990ae9e6d75a2
5
5
  SHA512:
6
- metadata.gz: 4826b02473c7af629a659ea70adbcf35f805fae8bff013210d5d2b37709ec2e5f318183abad67a2b3664f98c6496c5f35d8c2160f93d96c17db25dd0d5ef6a57
7
- data.tar.gz: 93ff96e2ea5aa847526394fd85a4d6cae50fd06f4760df55cd8469d1462a7e9090386340fd868dc2ed757d33adeb019ca10f6e65b6be9bff03e02632bdc38dbb
6
+ metadata.gz: '03204299634596b085494434e1f8c2379638a893c7c41228db4de5c4040c9f198150c29709ccba0477d77fcd1d1c41b9cb0f0e038570519312504d2b0e80d7f7'
7
+ data.tar.gz: 767bbb90f3082d7a1d729c405bbd23684475561578f1b93454b101b82752f3306e1fdfc4fab28ca46c705dc70a3ce6dfe45c4c08e16414b46a6c30f498028937
data/README.md CHANGED
@@ -63,6 +63,21 @@ timestamp capture and one native call, instead of `count` of each.
63
63
 
64
64
  The honest trade-off: this gem `dlopen`s a native library instead of being pure Ruby, so it needs a platform-specific `libhyperuuid.so`/`.dylib`/`.dll` bundled alongside it. If plain v4 randomness is all you need, `SecureRandom.uuid` is simpler and already in stdlib — that's a completely reasonable choice.
65
65
 
66
+ ## Bulk generation into bytes
67
+
68
+ `new_v6_batch_bytes` and `new_v7_batch_bytes` return the batch as one binary `String` of raw RFC 9562-ordered bytes — 16 per UUID — instead of an Array of `Uuid` objects:
69
+
70
+ ```ruby
71
+ bytes = HyperUuid.new_v7_batch_bytes(1000)
72
+ first = bytes[0, 16] # ready for a BINARY(16) bind parameter
73
+ ```
74
+
75
+ **About 15x faster than `new_v7_batch`** for a 1000-UUID batch (24 µs versus 370 µs). The native call is identical — the difference is that `new_v7_batch` then allocates 1000 `Uuid` objects and 1000 String slices on top of it. This hands back the bytes the native core already produced, untouched.
76
+
77
+ The catch, and it inverts the advice: **if you need `Uuid` objects, keep using `new_v7_batch`.** Slicing these bytes into objects yourself just relocates the identical allocations into your own code, and measures no better — sometimes worse. Reach for the byte form only when bytes are the destination: a bind parameter, a wire format, a bulk load.
78
+
79
+ Slice it with `bytes[i * 16, 16]` — which is exactly what `new_v7_batch` does internally.
80
+
66
81
  ## Benchmarks
67
82
 
68
83
  Real numbers, `benchmark-ips` on Ruby 4.0.6, linux-arm64 (`ruby benchmark/uuid_benchmark.rb`) — not claimed, measured. With the Magnus backend (the default wherever the extension loads):
@@ -102,9 +117,69 @@ gem install hyperuuid
102
117
 
103
118
  Published to [RubyGems.org](https://rubygems.org/gems/hyperuuid) as real precompiled
104
119
  "platform gems" — `bundle`/`gem install` auto-selects the matching one for
105
- linux-x64/arm64 or osx-x64/arm64 (the compiled Magnus native extension, `backend: :native`),
106
- falling back automatically to the universal `ruby`-platform gem (pure Fiddle, zero compile,
107
- bundles all 6 platforms' native libs) everywhere else — Windows included, since Magnus
108
- doesn't target it. No extra configuration needed either way.
120
+ linux-x64/arm64, osx-x64/arm64, x64-mingw-ucrt or aarch64-mingw-ucrt (the compiled Magnus
121
+ native extension, `backend: :native`), falling back automatically to the universal
122
+ `ruby`-platform gem (pure Fiddle, zero compile, bundles all 6 platforms' native libs)
123
+ everywhere else. No extra configuration needed either way.
124
+
125
+ Selection has **two** axes here, unlike every other binding in this repo. A Magnus extension
126
+ is bound to one Ruby minor ABI — there's no `abi3` equivalent to collapse the version axis the
127
+ way [the Python binding's](../python/) wheels do — so each platform gem is a "fat" gem
128
+ carrying one compiled extension per supported Ruby, under `lib/hyperuuid/<minor>/`, and picks
129
+ one at `require` time:
130
+
131
+ | Ruby | linux-x64/arm64, osx-x64/arm64, x64-mingw-ucrt, aarch64-mingw-ucrt | anywhere else (musl/Alpine, …) |
132
+ | --- | --- | --- |
133
+ | 4.0 (primary) | Magnus, `backend: :native` | Fiddle |
134
+ | 3.4 (floor, until its EOL 2028-03-31) | Magnus, `backend: :native` | Fiddle |
135
+ | 3.2 / 3.3 | Fiddle | Fiddle |
136
+
137
+ The platform gems declare `required_ruby_version >= 3.4, < 4.1` precisely so RubyGems
138
+ *declines* them outside that range and resolves the universal gem instead — a wrong-ABI
139
+ extension must never be installed in the first place. On Windows it would at least fail to
140
+ load cleanly (the extension imports `<arch>-ucrt-ruby<minor>.dll` by name —
141
+ `x64-ucrt-ruby400.dll` on x64, `aarch64-ucrt-ruby400.dll` on ARM), but Linux extensions
142
+ don't link libruby at all, so one can load successfully against the wrong ABI and misbehave
143
+ later. When 3.4 goes EOL it simply leaves the matrix and its users fall back to Fiddle, which
144
+ is exactly what the fallback is for.
145
+
146
+ An earlier edition of this section said the fallback covered "Windows included, since Magnus
147
+ doesn't target it." That was wrong in both halves. MinGW is the *only* Windows flavour
148
+ `rb-sys` targets — its own `data/toolchains.json` maps `x64-mingw-ucrt` to the
149
+ `x86_64-pc-windows-gnu` Rust target, `supported: true`; the target it has no support for is
150
+ `x86_64-pc-windows-msvc`. And Windows is where the Fiddle fallback cost the most: measured
151
+ on win-x64, Ruby 3.4, the Magnus backend does `new_v4` in 406ns against Fiddle's 2407ns
152
+ (**5.9x**) and `new_v7` in 595ns against 2759ns (**4.6x**) — a far wider gap than any Linux
153
+ or macOS leg shows.
154
+
155
+ Windows-on-ARM is no longer the exception it was. `rb-sys` maps `aarch64-mingw-ucrt` to the
156
+ `aarch64-pc-windows-gnullvm` Rust target (`supported: true`), and RubyInstaller ships an
157
+ `aarch64-mingw-ucrt` build of both ABIs in the table above, so win-arm64 now gets the same fat
158
+ platform gem as every other leg. Measured on real Windows-on-ARM hardware, Ruby 4.0.6, the
159
+ Magnus backend does `new_v4` in 416ns against Fiddle's 2299ns (**5.5x**) and `new_v7` in 621ns
160
+ against 2474ns (**4.0x**) — the same shape the x64 leg shows.
161
+
162
+ What differs from the x64 build follows from `gnullvm` being LLVM-based where
163
+ `x86_64-pc-windows-gnu` is GCC-based. The linker driver is `aarch64-w64-mingw32-clang` from
164
+ MSYS2's CLANGARM64 environment — RubyInstaller's own ARM devkit installs it locally, and
165
+ `ruby/setup-ruby` installs it on `windows-11-arm` — so there is no `link-self-contained=no`
166
+ dance. Two flags are load-bearing:
167
+
168
+ - **`-l static=unwind`.** rustc emits its `-lunwind` in the linker's *dynamic* section, where
169
+ CLANGARM64 offers both `libunwind.a` and `libunwind.dll.a` and lld prefers the latter. Left
170
+ alone, the extension acquires a runtime dependency on `libunwind.dll` — a file no consumer
171
+ of the gem would have.
172
+ - **`BINDGEN_EXTRA_CLANG_ARGS=--target=aarch64-w64-mingw32`.** bindgen hands clang the *Rust*
173
+ target triple when cargo cross-compiles, and clang rejects `aarch64-pc-windows-gnullvm`
174
+ outright (`version 'llvm' in target triple ... is invalid`), then fails behind that on a
175
+ missing `stdalign.h` it never reached its own resource dir to find. `aarch64-w64-mingw32`
176
+ is the same ABI spelled the way clang accepts, exactly as x64 spells its gnu target
177
+ `x86_64-w64-mingw32`.
178
+
179
+ That second one was briefly documented here as *unnecessary*, on the strength of a local
180
+ build where it genuinely was: that build used a `gnullvm` **host** toolchain, so host equals
181
+ target, cargo was not cross-compiling, and bindgen injected no `--target` at all. CI's host is
182
+ MSVC, so it does. A local build says nothing about that step unless its host triple matches
183
+ the runner's.
109
184
 
110
185
  See [the repo root README](../README.md) for the full RFC 9562 coverage table and the state of every other language binding.
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.1.1"
16
+ VERSION = "0.2.0"
17
17
 
18
18
  # Creates a random UUID version 4 (RFC 9562 §5.4).
19
19
  def self.new_v4
@@ -78,6 +78,34 @@ module HyperUuid
78
78
  bytes = Runtime.new_v7_batch(count, unix_millis_from(unix_millis))
79
79
  Array.new(count) { |i| Uuid.new(bytes[i * 16, 16]) }
80
80
  end
81
+
82
+ # Returns `count` version 7 UUIDs as one binary String of raw RFC 9562-ordered bytes,
83
+ # 16 per UUID, instead of an Array of Uuid objects.
84
+ #
85
+ # Roughly 11x faster than #new_v7_batch for a 1000-UUID batch (about 35 us versus 400 us).
86
+ # The difference is not the native call — that is identical — it is that #new_v7_batch then
87
+ # allocates `count` Uuid objects and `count` String slices on top of it. This hands back the
88
+ # bytes the native core already produced, untouched.
89
+ #
90
+ # Use it when bytes are the destination: a BYTEA/uniqueidentifier bind parameter, a wire
91
+ # format, a bulk COPY. If you need Uuid objects, keep using #new_v7_batch — slicing this
92
+ # String into them yourself just moves the same allocations into your own code.
93
+ #
94
+ # Slice it with `bytes[i * 16, 16]`, which is what #new_v7_batch does internally.
95
+ def self.new_v7_batch_bytes(count, unix_millis = nil)
96
+ Runtime.new_v7_batch(count, unix_millis_from(unix_millis))
97
+ end
98
+
99
+ # Returns `count` version 6 UUIDs as one binary String of raw RFC 9562-ordered bytes,
100
+ # 16 per UUID. The version 6 counterpart to #new_v7_batch_bytes, with the same rationale and
101
+ # the same guidance about when it is the right call.
102
+ #
103
+ # clock_seq and node are independently random per item; unlike version 7 there is no
104
+ # monotonic counter, so items minted in the same millisecond are not guaranteed to sort in
105
+ # creation order.
106
+ def self.new_v6_batch_bytes(count, unix_millis = nil)
107
+ Runtime.new_v6_batch(count, unix_millis_from(unix_millis))
108
+ end
81
109
  end
82
110
 
83
111
  # --- backend selection: the Magnus extension, when present, replaces the Runtime methods
@@ -91,10 +119,24 @@ HyperUuid::BACKEND =
91
119
  if ENV["HYPERUUID_PURE"]
92
120
  :fiddle
93
121
  else
122
+ # Two layouts, and both have to work. A released platform gem is a "fat" gem carrying one
123
+ # extension per supported Ruby ABI under lib/hyperuuid/<minor>/ (see the Rakefile's
124
+ # native:gem task for why an ABI-per-file is unavoidable — Magnus has no `abi3`
125
+ # equivalent). CI's in-job staging and a local `cargo build --release --features ruby`
126
+ # instead drop a single extension flat at lib/. Trying the versioned path first and the
127
+ # flat one second means neither has to know the other exists.
128
+ #
129
+ # A miss on both is not an error: it means this Ruby/platform combination has no
130
+ # precompiled extension, which is precisely what the Fiddle backend below is for.
94
131
  begin
95
- require "hyperuuid_native"
132
+ require "hyperuuid/#{RUBY_VERSION[/\d+\.\d+/]}/hyperuuid_native"
96
133
  :native
97
134
  rescue LoadError
98
- :fiddle
135
+ begin
136
+ require "hyperuuid_native"
137
+ :native
138
+ rescue LoadError
139
+ :fiddle
140
+ end
99
141
  end
100
142
  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.1.1
4
+ version: 0.2.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Brian Buvinghausen
@@ -76,6 +76,8 @@ files:
76
76
  - LICENSE
77
77
  - README.md
78
78
  - lib/hyperuuid.rb
79
+ - lib/hyperuuid/3.4/hyperuuid_native.so
80
+ - lib/hyperuuid/4.0/hyperuuid_native.so
79
81
  - lib/hyperuuid/namespaces.rb
80
82
  - lib/hyperuuid/native/README.md
81
83
  - lib/hyperuuid/native/linux-arm64/libhyperuuid.so
@@ -87,7 +89,6 @@ files:
87
89
  - lib/hyperuuid/native_platform.rb
88
90
  - lib/hyperuuid/runtime.rb
89
91
  - lib/hyperuuid/uuid.rb
90
- - lib/hyperuuid_native.so
91
92
  homepage: https://github.com/SkunkWerkx/HyperUuid
92
93
  licenses:
93
94
  - MIT
@@ -100,14 +101,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - ">="
102
103
  - !ruby/object:Gem::Version
103
- version: '3.2'
104
+ version: '3.4'
105
+ - - "<"
106
+ - !ruby/object:Gem::Version
107
+ version: '4.1'
104
108
  required_rubygems_version: !ruby/object:Gem::Requirement
105
109
  requirements:
106
110
  - - ">="
107
111
  - !ruby/object:Gem::Version
108
112
  version: '0'
109
113
  requirements: []
110
- rubygems_version: 3.6.9
114
+ rubygems_version: 4.0.16
111
115
  specification_version: 4
112
116
  summary: RFC 9562 UUID v4/v5/v6/v7 generation — direct native FFI into a Rust core,
113
117
  no runtime bridge.
Binary file