uuidx 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: []