uuidx 0.9.0

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: d2e41b677c8a6cb523605c6f10620796d1b5831ad511a32b49e8d98415629677
4
+ data.tar.gz: 8e8935a0b62e6538a5de355ec22119a0cba5242a3c76767bb720d5d13192431d
5
+ SHA512:
6
+ metadata.gz: ff313efefe5c2ccca58479e2e208cd98d636b74e5311f5e29de20faaf5207f47baf708c304c2e15275fa2a75b26d0f050f7c38174ddeaadcbf8e9449e9dd8a1c
7
+ data.tar.gz: 4def103fd92484499eef8474454eed8e7f6bc2a0e975e7f22deabe986d02f12d957b7bf2457b8d6c6101e66c4789cffaf3844812b6b0e973c95950e7b7abfbf5
data/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Unreleased
2
+
3
+ # Stable Releases
4
+
5
+ ## 1.0.0 – ???
6
+
7
+ # Alpha Releases
8
+
9
+ ## 0.9.0 – 2023-02-11
10
+ - Swap gem name to shorter uuidx
11
+ - Main documentation written
12
+ - Add type signatures
13
+ - Add monotonic batch support to simple API.
14
+
15
+ ## 0.8.0 – 2023-02-10
16
+ - Add simple Uuid generation API
17
+ - Generator classes are now lock-free
18
+ - Remove UUID value type
19
+
20
+ ## 0.7.0 – 2023-02-10
21
+ - Generator APIs now return opaque string values for UUIDs
22
+ - Add faster UUID v4 implementation
23
+
24
+ ## 0.6.0 – 2023-02-04
25
+ - Convert generator modules to classes
26
+ - Fix clock ID references in UUID v7
27
+ - Pass tests on all supported versions of Ruby
28
+
29
+ ## 0.5.0 – 2023-01-31
30
+ - Threading safety for clock sequence in UUID v6
31
+ - Clock drift detection in UUID v6
32
+
33
+ ## 0.4.0 – 2023-01-26
34
+ - Add clock resolution verification methods
35
+ - UUID v6 and v7 are now thread-safe
36
+
37
+ ## 0.3.0 – 2023-01-25
38
+ - New, faster design of UUID generator modules
39
+ - Removal of old implementations
40
+
41
+ ## 0.2.0 – 2023-01-22
42
+ - UUID v6, v7, and v8 implemented quickly
43
+ - Some performance optimization done
44
+
45
+ ## 0.1.0 – 2023-01-18
46
+ - Initial release without design considerations
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Stephan Tarulli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,195 @@
1
+ <h1 align="center">uuidx</h1>
2
+ <p align="center">Fast Ruby implementations of UUID versions 4, 6, 7, and 8 🪪</p>
3
+
4
+ ---
5
+
6
+ ## What's in the box?
7
+
8
+ ✅ Simple usage documentation written to get started fast. [30 seconds and you're off.](#usage)
9
+
10
+ 📚 API documentation for the library. [Read the docs.](https://tinychameleon.github.io/uuidx/)
11
+
12
+ ⚡ A reasonably fast, zero dependency implementation of the new UUID standards.
13
+
14
+ 🤖 RBS types for your type checking wants. [Typed goodness.](./sig)
15
+
16
+ 💎 Tests against Ruby 2.7, 3.0, 3.1, and 3.2.
17
+
18
+ 🔒 MFA protection on gem owners.
19
+
20
+ ---
21
+
22
+ ## Installation
23
+
24
+ ### Using Bundler
25
+
26
+ Run `bundle add uuidx` to update your `Gemfile` and install the gem. You may
27
+ also add the gem to your `Gemfile` manually and then run `bundle install`.
28
+
29
+ ### Manual
30
+
31
+ Use `gem install uuidx` to install the gem from [RubyGems](https://rubygems.org).
32
+
33
+ ## Usage
34
+
35
+ To get started using the default generators for UUID v4, v6, or v7 `require`
36
+ the library and call the associated method.
37
+
38
+ ```ruby
39
+ require "uuidx"
40
+
41
+ Uuid.v4 # => "2b54639d-e43e-489f-9c64-30ecdcac3c95"
42
+ Uuid.v6 # => "1eda9761-9f6f-6414-8c5f-fd61f1239907"
43
+ Uuid.v7 # => "01863d24-6d1e-78ba-92ee-6e80c79c4e28"
44
+ ```
45
+
46
+ These methods all use default generators and are thread-safe. However, if you
47
+ are using child processes (like Puma's clustered mode) you should also reset
48
+ the generators you are using when each child process starts.
49
+
50
+ ```ruby
51
+ # Puma example that resets all default generators.
52
+ on_worker_boot do
53
+ Uuid.reset_v4!
54
+ Uuid.reset_v6!
55
+ Uuid.reset_v7!
56
+ end
57
+ ```
58
+
59
+ This way you will get thread-safe state access per process without requiring
60
+ IPC.
61
+
62
+ ### Monotonicity & Batching
63
+ The simple API provided by `Uuid` also supports monotonic batches. Provide the
64
+ batch size and you will receive an array of UUID values back.
65
+
66
+ ```ruby
67
+ Uuid.batch_v4(10) # => ["2b54639d-e43e-489f-9c64-30ecdcac3c95", ...]
68
+ Uuid.batch_v6(10) # => ["1eda9761-9f6f-6414-8c5f-fd61f1239907", ...]
69
+ Uuid.batch_v7(10) # => ["01863d24-6d1e-78ba-92ee-6e80c79c4e28", ...]
70
+ ```
71
+
72
+ ### Advanced Usage
73
+ If you require multiple generators you can drop below the simple API presented
74
+ above to create generators directly.
75
+
76
+ ```ruby
77
+ v6 = Uuid::Version6.new
78
+ v6.generate # => "1eda9adc-2ed9-629e-9a02-4d2ccc87c569"
79
+ ```
80
+
81
+ These generators are lock-free, so if you use them to obtain higher throughput
82
+ keep in mind that you must ensure they are never shared between threads.
83
+
84
+ For typical MRI Ruby workloads using multi-process worker strategies state will
85
+ be cloned and modified using copy-on-write and the thread safety provided by
86
+ the simple API is adequate.
87
+
88
+ #### UUID v8
89
+ UUID v8 is a special case of advanced usage that always requires you to build
90
+ a generator directly. It takes a single parameter to its constructor which must
91
+ be the class name of your UUID v8 definition.
92
+
93
+ ```ruby
94
+ v8 = Uuid::Version8.new(MyV8Definition)
95
+ v8.generate # => "..."
96
+ ```
97
+
98
+ The definition class should implement the methods `custom_a`, `custom_b`, and
99
+ `custom_c` in order to fill out the UUID data [according to the draft](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-uuid-version-8).
100
+
101
+ See the [documentation for Version8](https://tinychameleon.github.io/uuidx/Uuid/Version8.html) for precise details.
102
+
103
+ #### Batching
104
+ Any custom UUID v8 generators can also participate in thread-safe batching by using
105
+ the `batch` method.
106
+
107
+ ```ruby
108
+ Uuid.batch(v8, 10) # => ["<a v8 uuid>", ...]
109
+ ```
110
+
111
+ ### Clock Resolution
112
+ If you have need to verify the clock resolution for UUID v6 or v7 you can call
113
+ the `verify_clock_resolution!` method on either class. A `ClockResolutionError`
114
+ is raised if the system has insufficient precision.
115
+
116
+ ```ruby
117
+ begin
118
+ Uuid::Version6.verify_clock_resolution! # or Uuid::Version7
119
+ rescue Uuid::ClockResolutionError
120
+ # ...
121
+ end
122
+ ```
123
+
124
+ The API documentation has details about what the clock resolution must be for
125
+ each of the UUID versions. See the
126
+ [Version 6](https://tinychameleon.github.io/uuidx/Uuid/Version6.html) and
127
+ [Version 7](https://tinychameleon.github.io/uuidx/Uuid/Version7.html)
128
+ documentation for details.
129
+
130
+ ### A Note on Clock Timings
131
+ The API documentation contains specific details around how the implementations
132
+ deal with clock drift. See the
133
+ [Uuid](https://tinychameleon.github.io/uuidx/Uuid.html),
134
+ [Version 6](https://tinychameleon.github.io/uuidx/Uuid/Version6.html), and
135
+ [Version 7](https://tinychameleon.github.io/uuidx/Uuid/Version7.html)
136
+ documentation for more information.
137
+
138
+ ## Performance
139
+ This performance data was captured using `benchmark/ips`. It uses the following
140
+ Ruby version
141
+
142
+ ruby 3.2.0 (2022-12-25 revision a528908271) [arm64-darwin22]
143
+
144
+ and is run on a Macbook Pro with an M1 Max CPU.
145
+
146
+ ❯ bundle exec ruby test/benchmarks/simple_api.rb
147
+ Warming up --------------------------------------
148
+ stdlib 60.617k i/100ms
149
+ uuid4 106.927k i/100ms
150
+ uuid6 111.417k i/100ms
151
+ uuid7 103.551k i/100ms
152
+ Calculating -------------------------------------
153
+ stdlib 626.957k (± 0.3%) i/s - 3.152M in 5.027633s
154
+ uuid4 1.077M (± 1.2%) i/s - 5.453M in 5.065726s
155
+ uuid6 1.121M (± 0.2%) i/s - 5.682M in 5.067392s
156
+ uuid7 1.027M (± 1.3%) i/s - 5.178M in 5.041666s
157
+
158
+ Comparison:
159
+ uuid6: 1121344.6 i/s
160
+ uuid4: 1076662.4 i/s - 1.04x (± 0.00) slower
161
+ uuid7: 1027127.8 i/s - 1.09x (± 0.00) slower
162
+ stdlib: 626957.1 i/s - 1.79x (± 0.00) slower
163
+
164
+
165
+ As reported, the `stdlib` version of `SecureRandom.uuid` is at least 1.70x
166
+ slower than the simple API implementation of UUID v4.
167
+
168
+ These timings are good enough that they shouldn't get in the way of any web
169
+ frameworks.
170
+
171
+ ## Development
172
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
173
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
174
+ prompt that will allow you to experiment.
175
+
176
+ To run tests across all supported Ruby versions on Debian and Alpine Linux run
177
+ `make test-all`. To run a specific version use `make test-[version]`;
178
+ for example, `make test-3.2.0`.
179
+
180
+ To install this gem onto your local machine, run `bundle exec rake install`.
181
+
182
+ To release a new version:
183
+
184
+ 1. ensure the documentation and changelog are ready
185
+ 2. run `bundle exec rake rdoc` to generate the new documentation and commit it
186
+ 3. update the version number in `lib/uuid/gem_version.rb`
187
+ 4. run `bundle install` to update the `Gemfile.lock`
188
+ 5. create a release commit with the version updates
189
+ 6. run `bundle exec rake release` to tag and push the version to RubyGems
190
+
191
+ ## Contributing
192
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tinychameleon/uuidx.
193
+
194
+ ## License
195
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uuid
4
+ # Top-level error for all Uuid errors.
5
+ class Error < StandardError
6
+ end
7
+
8
+ # Raised when the clock resolution is insufficient for a UUID Version.
9
+ class ClockResolutionError < Error
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uuid
4
+ # The gem version.
5
+ VERSION = "0.9.0"
6
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Uuid
6
+ # UUID Version 7 defined by the
7
+ # {RFC 4122 BIS-01 Draft}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-uuid-version-4].
8
+ #
9
+ # To construct a new UUID v4 value create a generator, then use #generate.
10
+ # g = Uuid::Version4.new
11
+ # g.generate # => "2b54639d-e43e-489f-9c64-30ecdcac3c95"
12
+ #
13
+ # The implementation will cache 1024 bytes of random data from +SecureRandom+ to facilitate faster construction.
14
+ class Version4
15
+ VERSION_VARIANT = (0x4 << 76) | (0x2 << 62) # :nodoc:
16
+ BUFFER_SIZE = 64 # :nodoc:
17
+ NEEDED_BYTES = BUFFER_SIZE * 16 # :nodoc:
18
+ UNPACK_FORMAT = "QQ" * BUFFER_SIZE # :nodoc:
19
+ RANDOM_AB_MASK = 0xffffffff_ffff0fff # :nodoc:
20
+ AB_SHIFT = 64 # :nodoc:
21
+ RANDOM_C_MASK = 0x3fffffff_ffffffff # :nodoc:
22
+
23
+ # Construct a UUID v4 generator.
24
+ def initialize
25
+ @pool = []
26
+ end
27
+
28
+ # Construct a UUID v4 value.
29
+ def generate
30
+ @pool = SecureRandom.bytes(NEEDED_BYTES).unpack(UNPACK_FORMAT) if @pool.empty?
31
+ ab, c = @pool.pop(2)
32
+
33
+ Uuid.format(VERSION_VARIANT | ((ab & RANDOM_AB_MASK) << AB_SHIFT) | (c & RANDOM_C_MASK))
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Uuid
6
+ # UUID Version 6 defined by the
7
+ # {RFC 4122 BIS-01 Draft}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-uuid-version-6].
8
+ #
9
+ # To construct a new UUID v6 value create a generator, then use #generate.
10
+ # g = Uuid::Version6.new
11
+ # g.generate # => "1eda9761-9f6f-6414-8c5f-fd61f1239907"
12
+ #
13
+ # The implementation will use +SecureRandom+ to populate the Node and Clock Sequence bits with a random value
14
+ # at module load time.
15
+ #
16
+ # Generation is thread-safe, but if you are using multi-process clusters you should call #reset! at the start
17
+ # of each process to reduce the chance of two processes generating the same value.
18
+ #
19
+ # If you have need to make sure that the clock resolution is sufficient for the v6 specification
20
+ # you can call ::verify_clock_resolution! and handle the ClockResolutionError as you see fit.
21
+ #
22
+ # begin
23
+ # Uuid::Version6.verify_clock_resolution!
24
+ # rescue Uuid::ClockResolutionError
25
+ # # ...
26
+ # end
27
+ #
28
+ # The necessary clock resolution for v6 is 100ns.
29
+ #
30
+ # ===== A Note on Clock Timings
31
+ # To combat clock drift, leap-second smearing, and other clock value changes that can appear without
32
+ # requiring additional compute cost this implementation always increments the clock sequence number.
33
+ class Version6
34
+ VERSION_VARIANT = (0x6 << 76) + (0x2 << 62) # :nodoc:
35
+ GREGORIAN_MICROSECOND_TENTHS = 122_192_928_000_000_000 # :nodoc:
36
+ CLOCK_SEQ_SHIFT = 48 # :nodoc:
37
+ CLOCK_SEQ_INCREMENT = 1 << CLOCK_SEQ_SHIFT # :nodoc:
38
+ CLOCK_SEQ_MASK = 0x3fff << CLOCK_SEQ_SHIFT # :nodoc:
39
+ TS_NS_FACTOR = 100 # :nodoc:
40
+ TS_LOW_MASK = 0xfff # :nodoc:
41
+ TS_HIGH_MID_MASK = 0xffffffff_ffff0000 # :nodoc:
42
+ TS_MASK_SHIFT = 4 # :nodoc:
43
+ TS_POSITIONAL_SHIFT = 64 # :nodoc:
44
+ NODE_ID_MASK = 0xffff_ffffffff # :nodoc:
45
+ NODE_ID_MC_BIT = 0x0100_00000000 # :nodoc:
46
+
47
+ # Construct a new UUID v6 generator.
48
+ def initialize
49
+ reset!
50
+ end
51
+
52
+ # Construct a UUID v6 value.
53
+ def generate
54
+ @clock_sequence = (@clock_sequence + CLOCK_SEQ_INCREMENT) & CLOCK_SEQ_MASK
55
+
56
+ ts = GREGORIAN_MICROSECOND_TENTHS + (Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) / TS_NS_FACTOR)
57
+ ts = ((ts << TS_MASK_SHIFT) & TS_HIGH_MID_MASK) | (ts & TS_LOW_MASK)
58
+
59
+ Uuid.format(VERSION_VARIANT | (ts << TS_POSITIONAL_SHIFT) | @clock_sequence | @node_id)
60
+ end
61
+
62
+ # Reset the generator with a new random node ID and clock sequence.
63
+ #
64
+ # This method is not thread-safe and should only be called at application or child process start.
65
+ def reset!
66
+ @node_id = (SecureRandom.bytes(8).unpack1("Q") & NODE_ID_MASK) | NODE_ID_MC_BIT
67
+ @clock_sequence = SecureRandom.bytes(4).unpack1("L") << CLOCK_SEQ_SHIFT
68
+ end
69
+
70
+ # Verify that the clock resolution is capable of 100ns resolution.
71
+ #
72
+ # Raises ClockResolutionError when the clock resolution is insufficient.
73
+ def self.verify_clock_resolution!
74
+ ns_res = Process.clock_getres(Process::CLOCK_REALTIME, :nanosecond)
75
+ raise ClockResolutionError, "Detected #{ns_res}ns resolution, need <= 100ns" if ns_res > 100
76
+
77
+ true
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Uuid
6
+ # UUID Version 7 defined by the
7
+ # {RFC 4122 BIS-01 Draft}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-uuid-version-7].
8
+ #
9
+ # To construct a new UUID v7 value create a generator, then use #generate.
10
+ # g = Uuid::Version7.new
11
+ # g.generate # => "01863d24-6d1e-78ba-92ee-6e80c79c4e28"
12
+ #
13
+ # The implementation will cache 640 bytes of random data from +SecureRandom+ to facilitate faster construction.
14
+ #
15
+ # If you have need to make sure that the clock resolution is sufficient for the v7 specification
16
+ # you can call ::verify_clock_resolution! and handle the ClockResolutionError as you see fit.
17
+ #
18
+ # begin
19
+ # Uuid::Version7.verify_clock_resolution!
20
+ # rescue Uuid::ClockResolutionError
21
+ # # ...
22
+ # end
23
+ #
24
+ # The necessary clock resolution for v6 is 1ms.
25
+ #
26
+ # ===== A Note on Clock Timings
27
+ # To combat clock drift, leap-second smearing, and other clock value changes that can appear without
28
+ # requiring additional compute cost this implementation relies on secure random data to have sufficient
29
+ # variation such that all generated UUIDs within 1ms have a low probability of collision.
30
+ #
31
+ # For a 0.001% chance of collision, the 74 bits of random data will require between 6.1×10^6 and 4.0×10^11
32
+ # UUIDs to be generated in 1ms.
33
+ class Version7
34
+ VERSION_VARIANT = (0x7 << 76) | (0x2 << 62) # :nodoc:
35
+ BUFFER_SIZE = 64 # :nodoc:
36
+ NEEDED_BYTES = BUFFER_SIZE * 10 # :nodoc:
37
+ UNPACK_FORMAT = "SQ" * BUFFER_SIZE # :nodoc:
38
+ TS_MASK = 0xffff_ffffffff # :nodoc:
39
+ RAND_A_MASK = 0xfff # :nodoc:
40
+ RAND_B_MASK = 0x3fffffff_ffffffff # :nodoc:
41
+ TS_SHIFT = 16 # :nodoc:
42
+ HIGH_SHIFT = 64 # :nodoc:
43
+
44
+ # Construct a UUID v7 generator.
45
+ def initialize
46
+ @pool = []
47
+ end
48
+
49
+ # Construct a UUID v7 value.
50
+ def generate
51
+ @pool = SecureRandom.bytes(NEEDED_BYTES).unpack(UNPACK_FORMAT) if @pool.empty?
52
+ a, b = @pool.pop(2)
53
+ ts = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond) & TS_MASK
54
+ high = (ts << TS_SHIFT) | (a & RAND_A_MASK)
55
+
56
+ Uuid.format(VERSION_VARIANT | (high << HIGH_SHIFT) | (b & RAND_B_MASK))
57
+ end
58
+
59
+ # Verify that the clock resolution is capable of 1ms resolution.
60
+ #
61
+ # Raises ClockResolutionError when the clock resolution is insufficient.
62
+ def self.verify_clock_resolution!
63
+ ms_res = Process.clock_getres(Process::CLOCK_REALTIME, :millisecond)
64
+ raise ClockResolutionError, "Detected #{ms_res}ms resolution, need <= 1ms" if ms_res > 1
65
+
66
+ true
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uuid
4
+ # UUID Version 8 defined by the
5
+ # {RFC 4122 BIS-01 Draft}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-uuid-version-8].
6
+ #
7
+ # Since UUID v8 is entirely custom to your application, first create a generator definition class.
8
+ # class MyGeneratorDefinition
9
+ # def custom_a
10
+ # 1
11
+ # end
12
+ #
13
+ # def custom_b
14
+ # 2
15
+ # end
16
+ #
17
+ # def custom_c
18
+ # 3
19
+ # end
20
+ # end
21
+ #
22
+ # The requirements for each method are:
23
+ #
24
+ # - +custom_a+ should generate a 48-bit value which will be the most significant 6 octets.
25
+ # - +custom_b+ should generate a 12-bit value which is placed between the version and variant bits.
26
+ # - +custom_c+ should generate a 62-bit value which acts as the remaining least significant octets.
27
+ #
28
+ # Then create a UUID v8 generator by passing in the class, and call #generate.
29
+ # g = Uuid::Version8.new(MyGeneratorDefinition)
30
+ # g.generate # => "00000000-0001-8002-8000-000000000003"
31
+ #
32
+ # The implementation will truncate the results of each generator module method so that they abide by the bit lengths
33
+ # of the UUID specification. The thread safety of UUID v8 depends entirely on your implementation.
34
+ #
35
+ # There is no default implementation of UUID v8.
36
+ class Version8
37
+ VERSION_VARIANT = (0x8 << 76) | (0x2 << 62) # :nodoc:
38
+ CUSTOM_A_MASK = 0xffff_ffffffff # :nodoc:
39
+ CUSTOM_B_MASK = 0xfff # :nodoc:
40
+ CUSTOM_C_MASK = 0x3fffffff_ffffffff # :nodoc:
41
+ A_SHIFT = 16 # :nodoc:
42
+ HIGH_SHIFT = 64 # :nodoc:
43
+
44
+ # Construct a UUID v8 generator.
45
+ def initialize(definition_class)
46
+ @definition = definition_class.new
47
+ end
48
+
49
+ # Construct a UUID v8 value.
50
+ def generate
51
+ a = @definition.custom_a & CUSTOM_A_MASK
52
+ b = @definition.custom_b & CUSTOM_B_MASK
53
+ high = (a << A_SHIFT) | b
54
+ c = @definition.custom_c & CUSTOM_C_MASK
55
+
56
+ Uuid.format(VERSION_VARIANT | (high << HIGH_SHIFT) | c)
57
+ end
58
+ end
59
+ end
data/lib/uuid.rb ADDED
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require_relative "uuid/gem_version"
5
+ require_relative "uuid/errors"
6
+ require_relative "uuid/version4"
7
+ require_relative "uuid/version6"
8
+ require_relative "uuid/version7"
9
+ require_relative "uuid/version8"
10
+
11
+ # The Uuid module contains a simple API to generate v4, v6, and v7 UUIDs
12
+ # without needing to create generators manually.
13
+ #
14
+ # The simple API is exposed as a set of methods ::v4, ::v6, and ::v7 which
15
+ # handle thread-safety and generator creation.
16
+ #
17
+ # Uuid.v4 # => "2b54639d-e43e-489f-9c64-30ecdcac3c95"
18
+ # Uuid.v6 # => "1eda9761-9f6f-6414-8c5f-fd61f1239907"
19
+ # Uuid.v7 # => "01863d24-6d1e-78ba-92ee-6e80c79c4e28"
20
+ #
21
+ # See the Version4, Version6, and Version7 classes for details on how to create
22
+ # generators manually.
23
+ #
24
+ # ===== Monotonic Batching
25
+ # The simple API also provides thread-safe monotonic batch methods which expect
26
+ # an amount.
27
+ #
28
+ # Uuid.batch_v4(10) # => ["2b54639d-e43e-489f-9c64-30ecdcac3c95", ...]
29
+ # Uuid.batch_v6(10) # => ["1eda9761-9f6f-6414-8c5f-fd61f1239907", ...]
30
+ # Uuid.batch_v7(10) # => ["01863d24-6d1e-78ba-92ee-6e80c79c4e28", ...]
31
+ #
32
+ # Monotonicity has little meaning with UUID v4, but the batches are ordered for
33
+ # consistency.
34
+ #
35
+ # ===== A Note on Clock Timings
36
+ # This library uses the +Process::CLOCK_REALTIME+ clock ID to obtain the current
37
+ # time. While the specification allows for implementations to manipulate time
38
+ # values, this library does not. Any system-based smearing or drift will appear
39
+ # within the timestamp values.
40
+ #
41
+ # See the Version6 and Version7 documentation for manifestation details.
42
+ module Uuid
43
+ # The nil UUID as defined by
44
+ # {§5.10 of RFC 4122 BIS-01}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-nil-uuid].
45
+ #
46
+ # This UUID is written as +00000000-0000-0000-0000-000000000000+ and is less than all other UUIDs in comparisons.
47
+ # It does not act as +nil+.
48
+ def self.nil_uuid
49
+ "00000000-0000-0000-0000-000000000000"
50
+ end
51
+
52
+ # The maximum UUID as defined by
53
+ # {§5.10 of RFC 4122 BIS-01}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-01.html#name-max-uuid].
54
+ #
55
+ # This UUID is written as +ffffffff-ffff-ffff-ffff-ffffffffffff+ and is greater than all other UUIDs in comparisons.
56
+ def self.max_uuid
57
+ "ffffffff-ffff-ffff-ffff-ffffffffffff"
58
+ end
59
+
60
+ # Generate a new UUID v4 value using the default generator.
61
+ def self.v4
62
+ @lock4.synchronize { @uuid4.generate }
63
+ end
64
+
65
+ # Generate a new UUID v6 value using the default generator.
66
+ def self.v6
67
+ @lock6.synchronize { @uuid6.generate }
68
+ end
69
+
70
+ # Generate a new UUID v7 value using the default generator.
71
+ def self.v7
72
+ @lock7.synchronize { @uuid7.generate }
73
+ end
74
+
75
+ # Create a batch of UUID v4 values using the default generator.
76
+ def self.batch_v4(amount)
77
+ @lock4.synchronize { batch(@uuid4, amount) }
78
+ end
79
+
80
+ # Create a batch of UUID v6 values using the default generator.
81
+ def self.batch_v6(amount)
82
+ @lock6.synchronize { batch(@uuid6, amount) }
83
+ end
84
+
85
+ # Create a batch of UUID v7 values using the default generator.
86
+ def self.batch_v7(amount)
87
+ @lock7.synchronize { batch(@uuid7, amount) }
88
+ end
89
+
90
+ # Reset the UUID v4 default generator.
91
+ def self.reset_v4!
92
+ @lock4 = Mutex.new
93
+ @uuid4 = Version4.new
94
+ end
95
+
96
+ # Reset the UUID v6 default generator.
97
+ def self.reset_v6!
98
+ @lock6 = Mutex.new
99
+ @uuid6 = Version6.new
100
+ end
101
+
102
+ # Reset the UUID v7 default generator.
103
+ def self.reset_v7!
104
+ @lock7 = Mutex.new
105
+ @uuid7 = Version7.new
106
+ end
107
+
108
+ # Create a batch of UUIDs from a generator.
109
+ #
110
+ # This can be useful for custom Version8 generators.
111
+ def self.batch(generator, amount)
112
+ s = Set.new
113
+ s << generator.generate while s.length < amount
114
+ s.sort
115
+ end
116
+
117
+ reset_v4!
118
+ reset_v6!
119
+ reset_v7!
120
+
121
+ # :nodoc:
122
+ def self.format(value)
123
+ b = +value.to_s(16).rjust(32, "0")
124
+ b.insert(8, "-")
125
+ b.insert(13, "-")
126
+ b.insert(18, "-")
127
+ b.insert(23, "-")
128
+ b.freeze
129
+ end
130
+ end
@@ -0,0 +1,7 @@
1
+ module Uuid
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ClockResolutionError < Error
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Uuid
2
+ class Version4
3
+ def initialize: () -> void
4
+ def generate: () -> uuid
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Uuid
2
+ class Version6
3
+ public
4
+
5
+ def initialize: () -> void
6
+ def generate: () -> uuid
7
+ def reset!:() -> void
8
+
9
+ def self.verify_clock_resolution!: () -> true
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Uuid
2
+ class Version7
3
+ public
4
+
5
+ def initialize: () -> void
6
+ def generate: () -> uuid
7
+
8
+ def self.verify_clock_resolution!: () -> true
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Uuid
2
+ class Version8
3
+ public
4
+
5
+ interface _GeneratorDefinition
6
+ def custom_a: () -> Integer
7
+ def custom_b: () -> Integer
8
+ def custom_c: () -> Integer
9
+ end
10
+
11
+ def initialize: [Definition < _GeneratorDefinition] (Definition) -> void
12
+ def generate: () -> uuid
13
+ end
14
+ end
data/sig/uuid.rbs ADDED
@@ -0,0 +1,30 @@
1
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
2
+
3
+ module Uuid
4
+ VERSION: String
5
+
6
+ type uuid = String
7
+
8
+ public
9
+
10
+ interface _Generator
11
+ def generate: () -> uuid
12
+ end
13
+
14
+ def self.nil_uuid: () -> uuid
15
+ def self.max_uuid: () -> uuid
16
+ def self.v4: () -> uuid
17
+ def self.v6: () -> uuid
18
+ def self.v7: () -> uuid
19
+ def self.batch_v4: (Integer) -> Array[uuid]
20
+ def self.batch_v6: (Integer) -> Array[uuid]
21
+ def self.batch_v7: (Integer) -> Array[uuid]
22
+ def self.reset_v4!: () -> void
23
+ def self.reset_v6!: () -> void
24
+ def self.reset_v7!: () -> void
25
+ def self.batch: (_Generator, Integer) -> Array[uuid]
26
+
27
+ private
28
+
29
+ def self.format: (Integer) -> uuid
30
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uuidx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephan Tarulli
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-02-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: "A fast Ruby implementation of UUID versions 4, 6, 7, and 8 \U0001FAAA"
14
+ email:
15
+ - srt@tinychameleon.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - LICENSE.txt
22
+ - README.md
23
+ - lib/uuid.rb
24
+ - lib/uuid/errors.rb
25
+ - lib/uuid/gem_version.rb
26
+ - lib/uuid/version4.rb
27
+ - lib/uuid/version6.rb
28
+ - lib/uuid/version7.rb
29
+ - lib/uuid/version8.rb
30
+ - sig/uuid.rbs
31
+ - sig/uuid/errors.rbs
32
+ - sig/uuid/version4.rbs
33
+ - sig/uuid/version6.rbs
34
+ - sig/uuid/version7.rbs
35
+ - sig/uuid/version8.rbs
36
+ homepage: https://github.com/tinychameleon/uuidx
37
+ licenses:
38
+ - MIT
39
+ metadata:
40
+ rubygems_mfa_required: 'true'
41
+ homepage_uri: https://github.com/tinychameleon/uuidx
42
+ source_code_uri: https://github.com/tinychameleon/uuidx
43
+ changelog_uri: https://github.com/tinychameleon/uuidx/blob/main/CHANGELOG.md
44
+ bug_tracker_uri: https://github.com/tinychameleon/uuidx/issues
45
+ documentation_uri: https://tinychameleon.github.io/uuidx/
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.7.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.4.3
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: "A fast Ruby implementation of UUID versions 4, 6, 7, and 8 \U0001FAAA"
65
+ test_files: []