globeglitter 1.0.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.
@@ -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: []