globeglitter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,277 @@
1
+ require('securerandom') unless defined?(::SecureRandom)
2
+
3
+ # Silence warning for `::IO::Buffer` as of Ruby 3.2.
4
+ # TODO: Remove this once it is "stable".
5
+ ::Warning[:experimental] = false
6
+
7
+
8
+ # Apollo AEGIS UID:
9
+ # - https://dl.acm.org/doi/pdf/10.1145/800220.806679 (1982)
10
+ #
11
+ # Apollo NCS:
12
+ # - https://jim.rees.org/apollo-archive/papers/ncs.pdf.gz
13
+ # - https://bitsavers.org/pdf/apollo/014962-A00_Domain_OS_Design_Principles_Jan89.pdf
14
+ # - https://stuff.mit.edu/afs/athena/astaff/project/opssrc/quotasrc/src/ncs/nck/uuid.c
15
+ #
16
+ # Version 1/3/4/5, variant 1 UUID:
17
+ # - https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.667-201210-I!!PDF-E&type=items
18
+ # - https://www.ietf.org/rfc/rfc4122.txt
19
+ #
20
+ # Version 2, variant 1 UUID:
21
+ # - https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
22
+ #
23
+ #
24
+ # Other implementations for reference:
25
+ # - FreeBSD: https://github.com/freebsd/freebsd-src/blob/main/sys/kern/kern_uuid.c
26
+ # - Lunix: https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib/uuid/gen_uuid.c
27
+ # - Winders: https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
28
+ # - Boost: https://www.boost.org/doc/libs/1_81_0/libs/uuid/doc/uuid.html
29
+ # - Apple: https://developer.apple.com/documentation/foundation/uuid
30
+ # - Apple: https://opensource.apple.com/source/CF/CF-299.35/Base.subproj/uuid.c.auto.html
31
+ # - Java: https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/UUID.html
32
+ # - .NET: https://learn.microsoft.com/en-us/dotnet/api/system.guid
33
+ # - PHP: https://uuid.ramsey.dev/en/stable/index.html
34
+ # - TianoCore EDKⅡ: https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/35_guids
35
+ # - GUID Partition Table: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
36
+ # - LAS: https://github.com/ASPRSorg/LAS/wiki/LAS-ProjectID-Encoding-and-Representation
37
+ # - Python: https://docs.python.org/3/library/uuid.html
38
+ # - ReactOS: https://doxygen.reactos.org/d9/d36/psdk_2guiddef_8h_source.html
39
+ ::GlobeGlitter = ::Data::define(:inner_spirit, :rules, :structure) do
40
+
41
+ # Terminology NOTE:
42
+ #
43
+ # `::GlobeGlitter`'s `structure` and `rules` are broadly equivalent to ITU-T Rec. X.667's / RFC 4122's
44
+ # `variant` and `version`, respectively. I am renaming them for two reasons:
45
+ #
46
+ # - because `variant` and `version` are terrible names,
47
+ # telling you only that something varies and not *how* or *why* it varies.
48
+ # - because `::GlobeGlitter` implements more than just the one specification,
49
+ # and I prefer to avoid defining one spec in terms of another.
50
+ #
51
+ # TL;DR:
52
+ # - `structure` describes the boundaries and endianness of the chunks making up a single `::GlobeGlitter`.
53
+ # - `rules` describe the meaning of chunks within a single `::GlobeGlitter` as well as how it should
54
+ # relate to other `GG` instances.
55
+
56
+ # Default constructor arguments.
57
+ self::STRUCTURE_UNSET = -1
58
+ self::RULES_UNSET = -1
59
+
60
+ #
61
+ self::STRUCTURE_NCS = 0
62
+
63
+ # ITU-T Rec. X.667, ISO/IEC 9834-8, and RFC 4122 are all the same standard,
64
+ # via either the telecom world or the Internet world.
65
+ # Many people [who?] refer to this standard by the names of the RFC draft authors, P. Leach & R. Salz.
66
+ # - Draft: http://upnp.org/resources/draft-leach-uuids-guids-00.txt
67
+ # - ITU: https://www.itu.int/rec/T-REC-X.667
68
+ # - ISO: https://www.iso.org/standard/62795.html
69
+ # - IETF: https://www.ietf.org/rfc/rfc4122.txt
70
+ self::STRUCTURE_LEACH_SALZ = 1
71
+ self::STRUCTURE_ITU_T_REC_X_667 = 1
72
+ self::STRUCTURE_RFC_4122 = 1
73
+ self::STRUCTURE_ISO_9834_8 = 1
74
+ self::STRUCTURE_IEC_9834_8 = 1
75
+
76
+ # These two values correspond to the equivalent ITU-T Rec. X.667 / RFC 4122 `variant` for MS and future-reservation.
77
+ # The `microsoft` type is awkwardly mixed-endian, and future is afaik still unused.
78
+ self::STRUCTURE_MICROSOFT = 2
79
+ self::STRUCTURE_FUTURE = 3
80
+
81
+ #
82
+ self::RULES_TIME_GREGORIAN = 1
83
+ self::RULES_RANDOM = 4
84
+ # TODO: Versions 2–8 (WIP)
85
+
86
+ # NOTE: I am adopting two conventions here which are ***not*** explicitly part of any specification
87
+ # as a compromise between two mostly-compatible widely-used conventions!
88
+ #
89
+ # Within this library, usage of the term "GUID" (versus "UUID") and usage of upper-case hexadecimal `A-F`
90
+ # indicate Microsoft-style mixed-endianness, i.e. the `time` bits are little-endian while the `clock_seq`
91
+ # and `node` bits are big/network-endian, represented in Windows-land as an `Array` of bytes, e.g.:
92
+ # https://learn.microsoft.com/en-us/windows/win32/wic/-wic-guids-clsids
93
+ #
94
+ # My support of the GUID/UUID differentiation convention is based on the fact that ITU-T Rec X.667 does not
95
+ # contain a single instance of the term "GUID". RFC 4122, on the other hand, sez:
96
+ # “Uniform Resource Name namespace for UUIDs (Universally Unique IDentifier),
97
+ # also known as GUIDs (Globally Unique IDentifier).”
98
+ #
99
+ # GG will store GUIDs internally in the network byte order, and our `#<=>` method will handle the opposite
100
+ # sortability implications, RE: https://devblogs.microsoft.com/oldnewthing/20190426-00/?p=102450
101
+ #
102
+ # The upper-case-hex GUID convention is an old-Microsoft thing, not part of the ITU/RFC UUID spec:
103
+ # https://learn.microsoft.com/en-us/windows/win32/msi/guid “All GUIDs must be authored in uppercase.”
104
+ #
105
+ # https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid sez —
106
+ # “A GUID is a 128-bit value consisting of one group of 8 hexadecimal digits, followed by three groups
107
+ # of 4 hexadecimal digits each, followed by one group of 12 hexadecimal digits. The following example GUID
108
+ # shows the groupings of hexadecimal digits in a GUID: `6B29FC40-CA47-1067-B31D-00DD010662DA`.”
109
+ # `typedef struct _GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8]; } GUID;`
110
+ # “The first 2 bytes [of `Data4`] contain the third group of 4 hexadecimal digits.
111
+ # The remaining 6 bytes contain the final 12 hexadecimal digits.”
112
+ #
113
+ # https://www.mandiant.com/resources/blog/hunting-com-objects sez —
114
+ # “Every COM object is identified by a unique binary identifier. These 128 bit (16 byte) globally
115
+ # unique identifiers are generically referred to as GUIDs. When a GUID is used to identify a COM object,
116
+ # it is a CLSID (class identifier), and when it is used to identify an Interface it is an IID (interface identifier).
117
+ # Some CLSIDs also have human-readable text equivalents called a ProgID.”
118
+ #
119
+ # Laser file format describes Microsoft-style endianness: https://github.com/ASPRSorg/LAS/wiki/LAS-ProjectID-Encoding-and-Representation
120
+ # “LAS GUIDs as encoded in the four ProjectID fields of the LAS header are intended to follow Microsoft-style 4-2-2-2-6 UUIDs
121
+ # with one 4-byte unsigned integer, followed by three 2-byte unsigned integers, followed by one 6-byte unsigned integer.
122
+ # The first three integers are customarily encoded in Little-Endian (LE) format,
123
+ # while the last two integers are encoded in Big-Endian (BE) format.”
124
+ #
125
+ # SMBIOS spec https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.6.0.pdf#page=21 sez —
126
+ # “Although RFC 4122 recommends network byte order for all fields, the PC industry (including the ACPI, UEFI,
127
+ # and Microsoft specifications) has consistently used little-endian byte encoding for the first three fields:
128
+ # `time_low`, `time_mid`, `time_hi_and_version`. The same encoding, also known as wire format, should also
129
+ # be used for the SMBIOS representation of the UUID. The UUID `{00112233-4455-6677-8899-AABBCCDDEEFF}`
130
+ # would thus be represented as `33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF`.
131
+ # If the value is all `FFh`, the ID is not currently present in the system, but can be set.
132
+ # If the value is all `00h`, the ID is not present in the system.”
133
+ #
134
+ # These posts show examples of poor endian handling SMBIOS tools:
135
+ # https://ocdnix.wordpress.com/2013/02/26/fuckin-uuids/
136
+ # http://howtowriteaprogram.blogspot.com/2009/03/uuid-and-byte-order.html
137
+ # http://howtowriteaprogram.blogspot.com/2012/06/smbios-uuid-fail.html
138
+ #
139
+ # New-Microsoft seem to use lower-case hex indiscriminately despite still calling them GUIDs,
140
+ # e.g. https://learn.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-partition_information_gpt
141
+ #
142
+ # Correspondingly, ITU-T Rec. X.667 sez,
143
+ # “Software generating the hexadecimal representation of a UUID shall not use upper case letters.”
144
+ #
145
+ # EFI specification https://stuff.mit.edu/afs/sipb/contrib/doc/EFI/EFISpec_v102.pdf#page=35
146
+ # and UEFI specification https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf#page=105 say —
147
+ # “`EFI_GUID` — 128-bit buffer containing a unique identifier value. Unless otherwise specified, aligned on a 64-bit boundary.”
148
+ # `typedef struct { UINT32 Data1; UINT16 Data2; UINT16 Data3; UINT8 Data4[8]; } EFI_GUID;`
149
+ #
150
+ # National Security Agency UEFI Secure Boot Customization Cybersecurity Technical Report
151
+ # https://media.defense.gov/2020/Sep/15/2002497594/-1/-1/0/CTR-UEFI-Secure-Boot-Customization-UOO168873-20.PDF#page=18 sez —
152
+ # “Note that GUIDs and UUIDs are similar. However, EFI GUID structures observe an 8-4-4-16
153
+ # format in source code. UUID structures, in contrast, observe an 8-4-4-4-12 format.”
154
+ #
155
+ # UEFI 2.0 errata https://uefi.org/sites/default/files/resources/UEFI_Spec_Errata_Only.pdf sez —
156
+ # “Add clarification to the spec so that we avoid references to GUIDs that do not comply to the
157
+ # <32bit><16bit><16bit><byte><byte><byte><byte><byte><byte><byte><byte> format.”,
158
+ # indicating use of Microsoft-style endianness.
159
+ #
160
+ # Our third match condition allows mixed-case, because we have to contend with nonconforming software,
161
+ # with hand-authored input by people unfamiliar with those conventions, etc. We will treat these as UUID.
162
+ #
163
+ # When asked to emit a "GUID", GlobeGlitter will produce upper-case.
164
+ # By default (e.g. `#to_s`) — and when asked to emit a "UUID", GlobeGlitter will produce lower-case.
165
+ #
166
+ #
167
+ # MAYBE: Should we assume any bracketed string is a GUID regardless of hex case?
168
+ # Search for counter-examples in the form of bracketed-UUIDs to decide.
169
+ self::MATCH_GUID = /\A\{?([0-9A-F]{8})-?([0-9A-F]{4})-?([0-9A-F]{4})-?([0-9A-F]{4})-?([0-9A-F]{12})\}?\Z/
170
+ self::MATCH_UUID = /\A\{?([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})\}?\Z/
171
+ self::MATCH_UUID_OR_GUID = /\A\{?(\h{8})-?(\h{4})-?(\h{4})-?(\h{4})-?(\h{12})\}?\Z/
172
+
173
+ # https://zverok.space/blog/2023-01-03-data-initialize.html
174
+ def self.new(*parts, structure: self::STRUCTURE_UNSET, rules: self::RULES_UNSET) = self::allocate.tap { |gg|
175
+ gg.send(:initialize,
176
+ inner_spirit: ::IO::Buffer::new(
177
+ size=16, # “UUIDs are an octet string of 16 octets (128 bits).”
178
+ flags=::IO::Buffer::INTERNAL,
179
+ ).tap { |buffer|
180
+ case parts
181
+ in [::String => probably_guid] if (
182
+ probably_guid.match(self::MATCH_GUID) or
183
+ (probably_guid.match(self::MATCH_UUID) and structure.eql?(self::STRUCTURE_MICROSOFT))
184
+ ) then
185
+ ::Regexp::last_match.captures.map!(&:hex).tap {
186
+ buffer.set_value(:u32, 0, _1[0])
187
+ buffer.set_value(:u16, 4, _1[1])
188
+ buffer.set_value(:u16, 6, _1[2])
189
+ buffer.set_value(:U16, 8, _1[3])
190
+ buffer.set_value(:U16, 10, (_1[4] >> 32))
191
+ buffer.set_value(:U32, 12, (_1[4] & 0xFFFFFFFF))
192
+ }
193
+ in [::Integer => data1, ::Integer => data2, ::Integer => data3, ::Array => data4] if (
194
+ data1.bit_length.<=(32) and data2.bit_length.<=(16) and data3.bit_length.<=(16) and (
195
+ data4.all?(&::Integer::method(:===)) and data4.max.bit_length.<=(8)
196
+ )
197
+ ) then
198
+ # https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
199
+ structure = self::STRUCTURE_MICROSOFT
200
+ buffer.set_value(:u32, 0, data1)
201
+ buffer.set_value(:u16, 4, data2)
202
+ buffer.set_value(:u16, 6, data3)
203
+ data4.each_with_index { buffer.set_value(:U8, (8 + _2), _1) }
204
+ in [::String => either_or] if (
205
+ either_or.match(self::MATCH_UUID) or either_or.match(self::MATCH_UUID_OR_GUID)
206
+ ) then
207
+ ::Regexp::last_match.captures.map!(&:hex).tap {
208
+ buffer.set_value(:U32, 0, _1[0])
209
+ buffer.set_value(:U16, 4, _1[1])
210
+ buffer.set_value(:U16, 6, _1[2])
211
+ buffer.set_value(:U16, 8, _1[3])
212
+ buffer.set_value(:U16, 10, (_1[4] >> 32))
213
+ buffer.set_value(:U32, 12, (_1[4] & 0xFFFFFFFF))
214
+ }
215
+ in [::Integer => spirit] if spirit.bit_length.<=(128) then
216
+ buffer.set_value(:U64, 0, (spirit >> 64))
217
+ buffer.set_value(:U64, 8, (spirit & 0xFFFFFFFF_FFFFFFFF))
218
+ in [::Integer => msb, ::Integer => lsb] if (
219
+ msb.bit_length.<=(64) and lsb.bit_length.<=(64)
220
+ ) then
221
+ buffer.set_value(:U64, 0, msb)
222
+ buffer.set_value(:U64, 8, lsb)
223
+ in [::Integer => time, ::Integer => seq, ::Integer => node] if (
224
+ time.bit_length.<=(64) and seq.bit_length.<=(16) and node.bit_length.<=(48)
225
+ ) then
226
+ buffer.set_value(:U64, 0, time)
227
+ buffer.set_value(:U16, 8, seq)
228
+ buffer.set_value(:U16, 10, (node >> 32))
229
+ buffer.set_value(:U32, 12, (node & 0xFFFFFFFF))
230
+ else raise ::ArgumentError::new("invalid number or rules of arguments") #TOD0: "given/expected"?
231
+ end
232
+ },
233
+ structure: (structure.respond_to?(:>=) and structure&.>=(0)) ? structure : self::STRUCTURE_UNSET,
234
+ rules: (rules.respond_to?(:>=) and rules&.>=(1)) ? rules : self::RULES_UNSET
235
+ ) # send
236
+ } # def self.new
237
+
238
+ # Our custom `::new` handles most of this functionality already but `raise`s on mismatch.
239
+ def self.try_convert(...) = begin; self.new(...); rescue ::ArgumentError; nil; end
240
+
241
+ # ITU-T Rec. X.667 sez —
242
+ # “The nil UUID is special form of UUID that is specified to have all 128 bits set to zero.”
243
+ def self.nil = self::new(0)
244
+
245
+ # New UUID Formats draft
246
+ # https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-max-uuid sez —
247
+ # “The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
248
+ # This UUID can be thought of as the inverse of Nil UUID defined in [RFC4122], Section 4.1.7”
249
+ def self.max = self::new(0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF)
250
+
251
+ # Generate version 4 random UUID.
252
+ # `::SecureRandom::uuid` does this already and is built-in since Ruby 1.9, but it only provides a `::String`.
253
+ # Our implementation with `random_number` is much faster than converting that `::String` to `::Integer`.
254
+ def self.random = self::new(
255
+ ::SecureRandom::random_number(0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF), # "Maximum" 128-bit UUID
256
+ structure: self::STRUCTURE_ITU_T_REC_X_667,
257
+ rules: self::RULES_RANDOM,
258
+ )
259
+
260
+ end # ::GlobeGlitter
261
+
262
+ # Bit-twiddling and bit-chunking components.
263
+ require_relative('globeglitter/inner_spirit') unless defined?(::GlobeGlitter::INNER_SPIRIT)
264
+ ::GlobeGlitter::include(::GlobeGlitter::INNER_SPIRIT)
265
+
266
+ # `::String`-printing components.
267
+ require_relative('globeglitter/say_yeeeahh') unless defined?(::GlobeGlitter::SAY_YEEEAHH)
268
+ ::GlobeGlitter::include(::GlobeGlitter::SAY_YEEEAHH)
269
+
270
+ # Sorting components.
271
+ require_relative('globeglitter/first_resolution') unless defined?(::GlobeGlitter::FIRST_RESOLUTION)
272
+ ::GlobeGlitter::include(::GlobeGlitter::FIRST_RESOLUTION)
273
+
274
+ # Time-based components for UUIDv1, UUIDv6, UUIDv7, etc.
275
+ require_relative('globeglitter/chrono_diver') unless defined?(::GlobeGlitter::CHRONO_DIVER)
276
+ ::GlobeGlitter::extend(::GlobeGlitter::CHRONO_DIVER::PENDULUMS)
277
+ ::GlobeGlitter::include(::GlobeGlitter::CHRONO_DIVER::FRAGMENT)
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: globeglitter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - okeeblow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: test-unit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-prof
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: memory_profiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: benchmark-ips
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ description: DistorteD UUID/GUID library
70
+ email:
71
+ - root@cooltrainer.org
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - bin/are-we-unallocated-yet
79
+ - bin/benchmark
80
+ - bin/profile
81
+ - bin/repl
82
+ - bin/test-my-best
83
+ - lib/globeglitter.rb
84
+ - lib/globeglitter/chrono_diver.rb
85
+ - lib/globeglitter/first_resolution.rb
86
+ - lib/globeglitter/inner_spirit.rb
87
+ - lib/globeglitter/rottel_da_sun.rb
88
+ - lib/globeglitter/say_yeeeahh.rb
89
+ homepage: https://cooltrainer.org
90
+ licenses:
91
+ - AGPL-3.0
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 3.2.0
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.4.10
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Surrogate keys
112
+ test_files: []