Repository: danlentz/clj-uuid Branch: master Commit: 157f161ecf73 Files: 51 Total size: 791.3 KB Directory structure: gitextract_dgp_2w_6/ ├── .github/ │ └── workflows/ │ └── clojure.yml ├── .gitignore ├── CHANGES.md ├── LICENSE ├── README.md ├── deps.edn ├── doc/ │ ├── api/ │ │ ├── clj-uuid.bitmop.html │ │ ├── clj-uuid.clock.html │ │ ├── clj-uuid.constants.html │ │ ├── clj-uuid.core.html │ │ ├── clj-uuid.html │ │ ├── clj-uuid.node.html │ │ ├── clj-uuid.random.html │ │ ├── clj-uuid.util.html │ │ ├── css/ │ │ │ ├── default.css │ │ │ └── highlight.css │ │ ├── index.html │ │ └── js/ │ │ └── page_effects.js │ ├── apples.md │ ├── draft-peabody-dispatch-new-uuid-format-04.html │ ├── java-and-unsigned-numbers.html │ ├── perf-analysis.md │ ├── rfc4122.txt │ ├── rfc9562.txt │ └── uuid-generation-benchmarks.md ├── project.clj ├── src/ │ ├── clj_uuid/ │ │ ├── bitmop.clj │ │ ├── clock.clj │ │ ├── constants.clj │ │ ├── core.clj │ │ ├── node.clj │ │ ├── random.clj │ │ └── util.clj │ └── clj_uuid.clj └── test/ └── clj_uuid/ ├── api_test.clj ├── bench.clj ├── bitmop_test.clj ├── clock_test.clj ├── compare_bench.clj ├── core_test.clj ├── node_test.clj ├── random_test.clj ├── util_test.clj ├── v1_test.clj ├── v3_test.clj ├── v4_test.clj ├── v5_test.clj ├── v6_test.clj ├── v7_test.clj ├── v7nc_test.clj └── v8_test.clj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/clojure.yml ================================================ name: Clojure CI on: push: branches: - "*" pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Prepare java uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' - name: Install clojure tools uses: DeLaGuardo/setup-clojure@13.4 with: # Install just one or all simultaneously # The value must indicate a particular version of the tool, or use 'latest' # to always provision the latest version # cli: 1.10.1.693 # Clojure CLI based on tools.deps lein: 2.11.2 # Leiningen - name: Run tests run: lein test ================================================ FILE: .gitignore ================================================ /target /classes /checkouts pom.xml pom.xml.asc *.jar *.class /.lein-* .nrepl-port lpr.sh test/new.clj /clj-uuid.iml /.idea /.claude /.lein-failures /.lein-repl-history .DS_Store ================================================ FILE: CHANGES.md ================================================ # Changes ## 0.2.5 ### Performance The bitmop layer has been rewritten around ByteBuffer-based primitives and JVM intrinsics, delivering substantial performance gains across the board with no changes to the public API. `mask-offset`, `mask-width`, and `bit-count` now compile to single hardware instructions (`TZCNT`, `POPCNT`) via `Long/numberOfTrailingZeros` and `Long/bitCount`, replacing the previous O(n) loops. A new ByteBuffer abstraction (`uuid->buf`, `buf->uuid`) provides direct typed access at byte offsets via native `getLong`/`putLong` operations, eliminating manual shift/mask loops for serialization. Representative speedups over 0.2.0: | Category | Speedup | |---------------------------------|-------------| | `to-byte-array` | **57x** | | `to-hex-string` | **29x** | | v3 (MD5) generation | **9.0x** | | v5 (SHA1) generation | **6.0x** | | v8 (custom) generation | **4.2x** | | v1 (time-based) generation | **1.5x** | | v6 (time-based) generation | **1.4x** | Combined generate + serialize operations see 3-19x end-to-end improvement depending on UUID version and serialization format. v3/v5 generation now uses a fused digest pipeline with ThreadLocal ByteBuffer reuse, `ByteBuffer/wrap` on digest output, and inlined version/variant bit stamping -- eliminating all intermediate allocations and var lookups. v5 is now at parity with JUG 5.2 (~260 ns vs ~254 ns). ### Correctness - `get-clk-seq` now uses `bitmop/ldb` to extract the 14-bit clock sequence directly, fixing incorrect results that occurred when Java's `.clockSequence()` method threw on non-v1 UUIDs. ### Clock improvements - The Gregorian monotonic clock now uses `AtomicLong` with `compareAndSet` on a packed long (50-bit millis + 14-bit seqid), replacing `atom` + `swap!` + per-call `State` allocation. - v1 and v6 constructors inline the CAS loop and bit-field packing, eliminating var lookup, function dispatch, and `ldb`/`dpb` overhead on the hot path. - The monotonic counter initial state is now seeded with a cryptographically random 10-bit value instead of zero, avoiding predictable first values after library load. - Per-tick counter reseed uses 10-bit entropy (was 8-bit). ### New UUID versions - v6 (reordered time-based, lexically sortable) - v7 (unix time-based, cryptographically secure, lexically sortable) - v7nc (unix time-based, non-cryptographic, maximum throughput) - v8 (custom / user-defined) - Max UUID sentinel (`+max+`, `max`, `max?`) `v7nc` uses `ThreadLocalRandom` and a per-thread monotonic counter instead of `SecureRandom` and a global `AtomicLong`. At ~39 ns/op, it is **1.26x faster** than JUG 5.2's `TimeBasedEpochGenerator` (~50 ns/op). ### New protocol members - `get-instant` -- returns a `java.util.Date` for time-based UUIDs - `get-unix-time` -- returns POSIX millis for v1, v6, and v7 - `get-clk-seq` -- returns the 14-bit clock sequence for v1 and v6 - `to-hex-string` -- 32-character hex encoding (no dashes) - `to-uri` -- returns `java.net.URI` in URN format - `max?` -- predicate for the max UUID - `UUIDRfc9562` protocol (with `UUIDRfc4122` as a backward-compatible alias) ### New constructors - `v0` / `null` -- null UUID constructor - `max` -- max UUID constructor - `v4` two-arity form `(v4 msb lsb)` -- stamps version and variant bits onto caller-supplied words - Multi-arity `=`, `<`, `>` comparison operators ### Extended polymorphism - `as-uuid` now accepts `java.net.URI`, byte arrays, and URN strings in addition to canonical UUID strings - `uuidable?` predicate for testing coercibility ### Build and packaging - Added `deps.edn` for tools.deps / CLI users - Removed `.travis.yml`; CI now uses GitHub Actions - Updated to Clojure 1.12.0 and primitive-math 1.0.1 - Comprehensive test suite: 138 tests, 1.4M+ assertions, 0 failures - Test coverage: 94% forms, 93% lines (via cloverage) ### Documentation - README rewritten to cover v6, v7, v8, max UUID, and the new performance characteristics - Typo corrections throughout (`get-timestamp`, `approximately`, `associated`, `requirements`, `consciousness`, `alphabetically`, `elapsed`, `duplicate`, `construction`) ================================================ FILE: LICENSE ================================================ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor tocontrol, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of Washington and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. ================================================ FILE: README.md ================================================ clj-uuid ======== > _"The intent of the UUID is to enable distributed systems to uniquely_ > _identify information without significant central coordination."_ > -- [_Wikipedia/UUID_](http://en.wikipedia.org/wiki/Universally_unique_identifier) * * * * * * **clj-uuid** is a Clojure library for generation and utilization of UUIDs (Universally Unique Identifiers) as described by [**IETF RFC-9562**](http://www.ietf.org/rfc/rfc9562.txt). This library extends the standard Java UUID class to provide true v1, v6, v7 (time based), v3/v5 (namespace based), and v8 (user customizable) identifier generation. Additionally, a number of useful utilities are provided to support serialization and manipulation of these UUIDs in a simple, efficient manner. The essential nature of the value RFC-9562 UUIDs provide is that of an enormous namespace and a deterministic mathematical model by means of which one navigates it. UUIDs represent an extremely powerful and versatile computation technique that is often overlooked, and underutilized. In my opinion, this, in part, is due to the generally poor quality, performance, and capability of available libraries and, in part, due to a general misunderstanding in the popular consciousness of their proper use and benefit. It is my hope that this library will serve to expand awareness, make available, and simplify the use of standards compliant UUIDs to a wider audience. ### The Most Recent Release With Leiningen: [![Clojars Project](https://img.shields.io/clojars/v/danlentz/clj-uuid.svg)](https://clojars.org/danlentz/clj-uuid) ### What's new? The latest release focuses on performance. There are no external changes to the API. clj-uuid 0.2.5 now uses ByteBuffer-based primitives and JVM intrinsics to deliver significant performance gains over traditional shift/mask loop approach. | Category | Speedup | |-------------------------------|-------------| | `to-byte-array` | **57x** | | `to-hex-string` | **29x** | | v3 (MD5) generation | **9.0x** | | v5 (SHA1) generation | **6.0x** | | v8 (custom) generation | **4.2x** | | v1 (time-based) generation | **1.5x** | | v6 (time-based) generation | **1.4x** | v5 generation is now at parity with JUG 5.2 (~260 ns vs ~254 ns) thanks to a fused digest pipeline with ThreadLocal ByteBuffer reuse. A new `v7nc` constructor provides non-cryptographic v7 UUIDs at **~39 ns** -- **1.26x faster** than JUG's `TimeBasedEpochGenerator` (~50 ns). Combined generate + serialize operations see **3-19x** end-to-end improvement depending on UUID version and serialization format. For detailed benchmarks and further analysis, see: * [Performance Analysis](doc/perf-analysis.md) -- architectural analysis of bitmop (old) vs bitmop2 (new) primitives * [Benchmarks](doc/uuid-generation-benchmarks.md) -- per-version timings, throughput, and combined operation benchmarks * [Competitive Benchmarks](doc/apples.md) -- head-to-head comparison with JUG, uuid-creator, and JDK ### Why is this library useful?? The JVM version only provides an automatic generator for random (v4) and (non-namespaced) pseudo-v3 UUID's. Where appropriate, this library does use the internal JVM UUID implementation. The benefit with this library is that clj-uuid provides an easy way to get fast time-based (v1, v6), true namespaced (v3, v5), and high quality cryptographcically secure time-based (v7) UUIDs. ### But wait, why so many choices? Each version of UUID offers advantages in particular situations. Please read on to learn more, but, to help put you at ease, your decision on which is appropriate to use will usually be clear. v1, v6, and v7nc time-encoded UUIDs are useful because they can be generated much more quickly than other forms of UUID, as there is no need to call a cryptographic random number generator. v7nc is the fastest at ~39 ns. v3/v5 deternibistic UUID's are necessary because many of the interesting things that you can do with UUID's require stable, reproducable, namespaced identifiers. v7's combine time encoding, secure cryptogrsphy, lexical ordering, and index-friendliness to provide a premium UUID experience, but at some additional cost to produce. ### How Big? The provided namespace represents an _inexhaustable_ resource and as such can be used in a variety of ways not feasible using traditional techniques rooted in the notions imposed by finite resources. When I say "inexhaustable" this of course is slight hyperbolie, but not by much. The upper bound on the representation implemented by this library limits the number of unique identifiers to a mere... *three hundred forty undecillion two hundred eighty-two decillion three* *hundred sixty-six nonillion nine hundred twenty octillion nine hundred* *thirty-eight septillion four hundred sixty-three sextillion four hundred* *sixty-three quintillion three hundred seventy-four quadrillion six hundred* *seven trillion four hundred thirty-one billion seven hundred sixty-eight* *million two hundred eleven thousand four hundred and fifty-five.* If you think you might be starting to run low, let me know when you get down to your last few undecillion or so and I'll see what I can do to help out. ### Usage Using clj-uuid is really easy. Docstrings are provided, but sometimes examples help, too. The following cases demonstrate about 90% of the functionality that you are likely to ever need. In order to refer to the symbols in this library, it is recommended to *require* it in a given namespace: ```clojure (require '[clj-uuid.core :as uuid]) ``` Or include in namespace declaration: ```clojure (ns foo (:require [clj-uuid.core :as uuid]) ... ) ``` The legacy single-segment namespace `clj-uuid` is still supported for backward compatibility: ```clojure (require '[clj-uuid :as uuid]) ;; also works ``` #### Literal Syntax UUID's have a convenient literal syntax supported by the clojure reader. The tag `#uuid` denotes that the following string literal will be read as a UUID. UUID's evaluate to themselves, similarly to Clojure keywords. ```clojure user> #uuid "e6ff478d-9492-48dd-886d-23ec4c6385ee" ;; => #uuid "e6ff478d-9492-48dd-886d-23ec4c6385ee" ``` #### The NULL Identifier The special UUID, `#uuid "00000000-0000-0000-0000-000000000000"` is known as the _**null** UUID_ or _version 0 UUID_ and can be useful for representing special values such as _nil_ or _null-context_. One may reference the null UUID declaratively or functionally, although it is best to pick one convention and remain consistant. When comparing UUID's the NULL UUID is considered the MININUM VALUE. ```clojure user> (uuid/null) ;; => #uuid "00000000-0000-0000-0000-000000000000" user> uuid/+null+ ;; => #uuid "00000000-0000-0000-0000-000000000000" ``` #### The MAX Identifier The special UUID, `#uuid "ffffffff-ffff-ffff-ffff-ffffffffffff"` is known as the _**max** UUID_ and is used similarly to the _**null** UUID_. When comparing UUID's the NULL UUID is considered the MAXIMUM VALUE. ```clojure user> (uuid/max) ;; => #uuid "ffffffff-ffff-ffff-ffff-ffffffffffff" user> uuid/+max+ ;; => #uuid "ffffffff-ffff-ffff-ffff-ffffffffffff" ``` #### v6/v1: Fast, Time Encoded Identifiers You can make your own v1 and v6 UUID's at home with the functions `uuid/v1` and `uuid/v6`. These are fast to produce (~100 ns) and guarantee to be unique and thread-safe regardless of clock precision or degree of concurrency, but each with slightly different characteristics: A v6 UUID encodes both the time and a random node identifier that is reset each time the library is loaded. They are fast, lexically (alphabetically) ordered, and index-friendly. A v1 UUID is similar, but may reveal both the identity of the computer that generated the UUID and the time at which it did so. Its uniqueness across computers is guaranteed as long as node/MAC addresses are not duplicated. In general, other than for legacy compatibility, the use case for this would be for situations where it is useful to know the provenance of any given UUID. It does not provide lexical ordering or index-friendliness. ```clojure (uuid/v6) ;; => #uuid "1ef7b36c-4ca7-6df0-91a1-233a797d04c0" ;; => #uuid "1ef7b36c-9c4c-60e0-91a1-233a797d04c0" ;; => #uuid "1ef7b373-1c84-6180-91a1-233a797d04c0" (uuid/v1) ;; => #uuid "ffa803f0-b3d3-11e4-a03e-3af93c3de9ae" ;; => #uuid "005b7570-b3d4-11e4-a03e-3af93c3de9ae" ;; => #uuid "018a0a60-b3d4-11e4-a03e-3af93c3de9ae" ``` v7nc is the fastest UUID generator at ~39 ns, followed by v1 and v6 at ~100 ns, all significantly faster than the JVM's `java.util.UUID/randomUUID` (~345 ns): ``` user> (criterium.core/bench (uuid/v7nc)) ;; Execution time mean : 39.412000 ns user> (criterium.core/bench (uuid/v6)) ;; Execution time mean : 100.764073 ns user> (criterium/bench (java.util.UUID/randomUUID)) ;; Execution time mean : 344.654110 ns ``` ##### Sequential (Temporal) Namespace v6 and v1 UUID's retrievably encode the time of their creation. The native representation of this timestamp is as a 60 bit value indicating the number of 100 nanosecond intervals since the Gregorian epoch (for the younger readers, this was at 12am Friday October 15, 1582 UTC). ```clojure user> (uuid/get-timestamp (uuid/v6)) ;; => 136459064897650000 user> (map uuid/get-timestamp (repeatedly 10 uuid/v1)) ;; => (136459065592300000 ;; 136459065592310000 ;; 136459065592320000 ;; 136459065592340000 ;; 136459065592340001 <-+ subcounter ensures unique timestamp ;; 136459065592350000 | even when the resolution of the ;; 136459065592350001 <-+ system clock is insufficiently ;; 136459065592370000 | granular to provide uniqueness. ;; 136459065592370001 <-+ ;; 136459065592380000) ``` Clearly, that is pretty useful. We can look at any two time-based UUID's and compare their timestamps relative to one another. We can also look at the absolute timestamp values of time-based UUID's using the ideomatic Clojure representation of timestamp values: ```clojure user> (uuid/get-instant (uuid/v1)) ;; => #inst "2015-03-17T17:51:15.970-00:00" user> (map uuid/get-instant (repeatedly 10 uuid/v1)) ;; => (#inst "2015-03-17T17:51:53.800-00:00" <-+ Note, however, ;; #inst "2015-03-17T17:51:53.800-00:00" <-+ insufficient clock precision ;; #inst "2015-03-17T17:51:53.802-00:00" | to distinguish betweem ;; #inst "2015-03-17T17:51:53.803-00:00" <-+ absolute timestamp values ;; #inst "2015-03-17T17:51:53.803-00:00" <-+ ;; #inst "2015-03-17T17:51:53.804-00:00" ;; #inst "2015-03-17T17:51:53.807-00:00" ;; #inst "2015-03-17T17:51:53.808-00:00" ;; #inst "2015-03-17T17:51:53.812-00:00" ;; #inst "2015-03-17T17:51:53.814-00:00") ``` #### v4: Random Identifiers V4 identifiers are generated by directly invoking the static method `java.util.UUID/randomUUID` and are, in typical situations, slower to generate in addition to being non-deterministically unique. It exists primarily because it is very simple to implement and because randomly generated UUID's are hard to guess. They can be useful in that case, for example to seed a UUID namespace as we will see in a later example. ```clojure user> (uuid/v4) ;; => #uuid "49c248c3-d232-4960-b2f4-fd5a3a72ea62" ``` #### v7: Time Encoded Cryptographically Random Identifiers Combining some of the best features of all of the above, v7 UUIDs provide time encoding, lexical ordering, and entropy-friendly randomness, at, of course, some additional cost to compute. ```clojure user> (uuid/v7) ;; => #uuid "0192292b-c52c-7058-bdf8-741af201c7d3" user> (uuid/get-timestamp (uuid/v7)) ;; => 1727267644205 (note -- POSIX time!) user> (uuid/get-instant (uuid/v7)) ;; => #inst "2024-09-25T12:34:57.981-00:00" user> (criterium.core/bench (uuid/v7)) ;; Execution time mean : 333.232000 ns ``` If cryptographic unguessability of the random portion is not required, `v7nc` provides the same v7 UUID structure using `ThreadLocalRandom` instead of `SecureRandom`: ```clojure user> (uuid/v7nc) ;; => #uuid "0194cba3-f2d1-7a4c-8b1e-6c3a9d8e4f21" user> (criterium.core/bench (uuid/v7nc)) ;; Execution time mean : 39.412000 ns ``` #### Lexical Comparability Ok, you've heard me mention "lexical ordering" a few times. What does this mean? v6 and v7 UUIDs offer identifiers that can be efficiently ordered alphabetically, requiring no decoding, based on the order of their creation. Let's take an example: ```clojure user> (def x (uuid/v7)) ;; => #uuid "0192293c-8640-7058-9106-b97bf1754d98" user> (def y (uuid/v7)) ;; => #uuid "0192293c-a931-709d-afba-5ad27082a4b6" user> (get-instant x) ;; => #inst "2024-09-25T12:51:25.376-00:00" user> (get-instant y) ;; => #inst "2024-09-25T12:51:34.321-00:00" ``` As you can see, it is always possible to order time encoded ids by parsing them, but v6 and v7 UUIDs make this easier, on any platform, even if you don't have your trusty clj-uuid library available. ```clojure user> (uuid/= x y) ;; => false user> (uuid/< x y) ;; => true user> (clojure.core/compare (str x) (str y)) ;; => -41 (negative -- ie, "less than") user> (clojure.core/compare (str y) (str x)) ;; => 41 (positive -- ie "greater than") ``` #### v3/v5: Namespaced Identifiers First of all, the only difference between v3 and v5 UUID's is that v3's are computed using an MD5 digest algorithm and v5's are computed using SHA1. It is generally considered that SHA1 is a superior hash, but MD5 is computationally less expensive and so v3 may be preferred in situations requiring slightly faster performance. As such, when we give examples of namespaced identifiers, we will typically just use `v5` with the understanding that `v3` could be used identically in each case. ##### Namespaces If you are familiar with Clojure _vars_, you already understand the idea of _namespaced_ identifiers. To resolve the value of a var, one needs to know not only the _name_ of a var, but also the _namespace_ it resides in. It is intuitively clear that vars `#'user/x` and `#'library/x` are distinct. Namespaced UUID's follow a similar concept, however namespaces are themselves represented as UUID's. Names are strings that encode a representation of a symbol or value in the namespace of that identifier. Given a namespace and a local-name, one can always (re)construct the unique identifier that represents it. We can demonstrate a few examples constructed using several of the canonical top level namespace UUIDs: ```clojure user> (uuid/v5 uuid/+namespace-url+ "http://example.com/") ;; => #uuid "0a300ee9-f9e4-5697-a51a-efc7fafaba67" user> (uuid/v5 uuid/+namespace-x500+ "http://example.com/") ;; => #uuid "0cb29677-4eaf-578f-ab9b-f9ac67c33cb9" user> (uuid/v3 uuid/+namespace-dns+ "www.clojure.org") ;; => #uuid "3bdca4f7-fc85-3a8b-9038-7626457527b0" user> (uuid/v5 uuid/+namespace-oid+ "0.1.22.13.8.236.1") ;; => #uuid "9989a7d2-b7fc-5b6a-84d6-556b0531a065" ``` You can see in each case that the local "name" string is given in some well-definted format specific to each namespace. This is a very common convention, but not enforced. It is perfectly valid to construct a namespaced UUID from any literal string. ```clojure user> (uuid/v5 uuid/+namespace-url+ "I am clearly not a URL") ;; => #uuid "a167a791-e550-57ae-b20f-666ee47ce7c1" ``` As a matter of fact, the requirements for a valid the local-part constituent are even more general than even just Strings. Any kind of object will do: ```clojure user> (uuid/v5 uuid/+namespace-url+ :keyword) ;; => #uuid "bc480d53-fba7-5e5f-8f33-6ad77880a007" user> (uuid/v5 uuid/+namespace-url+ :keyword) ;; => #uuid "bc480d53-fba7-5e5f-8f33-6ad77880a007" user> (uuid/v5 uuid/+namespace-oid+ :keyword) ;; => #uuid "9b3d8a3d-fadf-55b5-811f-12a0c50c3e86" user> (uuid/v5 uuid/+null+ 'this-symbol) ;; => #uuid "8b2941d5-e40b-5364-afcf-0008833715a2" user> (uuid/v5 uuid/+null+ 'this-symbol) ;; => #uuid "8b2941d5-e40b-5364-afcf-0008833715a2" ``` This will be most efficient for classes of object that have been extended with the `UUIDNameBytes` protocol. If one intends to do such a thing fequently, it is a simple matter to specialize an `as-byte-array` method which can extract a byte serialization that represents the 'name' of an object, typically unique within some given namespace. Here is a simple example where one adds specialized support for URLs to be quickly digested as the bytes of their string representation. ```clojure (extend-protocol UUIDNameBytes java.net.URL (as-byte-array [this] (.getBytes (.toString this) StandardCharsets/UTF_8))) (uuid/v5 uuid/+namespace-url+ "http://example.com/") ;; => #uuid "0a300ee9-f9e4-5697-a51a-efc7fafaba67" (uuid/v5 uuid/+namespace-url+ (java.net.URL. "http://example.com/")) ;; => #uuid "0a300ee9-f9e4-5697-a51a-efc7fafaba67" ``` ##### Hierarchical Namespace Because each UUID denotes its own namespace, it is easy to compose v5 identifiers in order to represent hierarchical sub-namespaces. This, for example, can be used to assign unique identifiers based not only on the content of a string but the unique identity representing its source or provenance: ```clojure user> (uuid/v5 (uuid/v5 uuid/+namespace-url+ "http://example.com/") "resource1#") ;; => #uuid "6a3944a4-f00e-5921-b8b6-2cea5a745132" user> (uuid/v5 (uuid/v5 uuid/+namespace-url+ "http://example.com/") "resource2#") ;; => #uuid "98879e2a-8511-59ab-877d-ac6f8667866d" user> (uuid/v5 (uuid/v5 uuid/+namespace-url+ "http://other.com/") "resource1#") ;; => #uuid "bc956d0c-7af3-5b81-89f2-a96e8f9fd1a8" user> (uuid/v5 (uuid/v5 uuid/+namespace-url+ "http://other.com/") "resource2#") ;; => #uuid "a38b24fe-7ab8-58c8-a3f8-d3adb308260b" ``` Because UUID's and namespaces can be chained together like this, one can be certain that the UUID resulting from a chain of calls such as the following will be unique -- if and only if the original namespace matches: ```clojure user> (-> (uuid/v1) (uuid/v5 "one") (uuid/v5 "two") (uuid/v5 "three")) ;; => #uuid "eb7a0c2b-eb0e-5bb2-9819-3c9edc2814fa" user> (-> (uuid/v1) (uuid/v5 "one") (uuid/v5 "two") (uuid/v5 "three")) ;; => #uuid "45e8c272-1660-57ba-8892-6844e1d3196a" ``` At each step, the local part string must be identical, in order for the the final UUID to match: ```clojure user> (-> uuid/+namespace-dns+ (uuid/v5 "one") (uuid/v5 "two") (uuid/v5 "three")) ;; => #uuid "617756cc-3b02-5a86-ad4a-ab3e1403dbd6" user> (-> uuid/+namespace-dns+ (uuid/v5 "two") (uuid/v5 "one") (uuid/v5 "three")) ;; => #uuid "52d5453e-2aa1-53c1-b093-0ea20ef57ad1" ``` This capability can be used to represent uniqueness of a sequence of computations in, for example, a transaction system such as the one used in the graph-object database system [de.setf.resource](http://github.com/lisp/de.setf.resource/) or this interesting new [CQRS/ES Server](http://yuppiechef.github.io/cqrs-server/). ### A Simple Example Ok, so now you know how to use this nifty new UUID library and you are burning up to do something awesome with UUID's... But, ah, hmmm... First you need to figure out what exactly you want to do with them. Well, before you start working on your distributed cloud-based secret weapon, here is a simple way you can generate cryptographically secure activation keys for your draconian licensing scheme. First, we pick a secret key. We might pick a time-based id, or we might begin with some secret namespace, secret identifier pair to compute that initial namespace deterministically. This is convenient, but not necessary -- the time-based or random private key could also be stored in some form of persistent memory. As unguessability important to deter hackers, we will choose a random namespace and record it in some secret, persistent storage to ensure we can regenerate a user's activation code identically on-demand in the future. ```clojure user> (def +secret-weapon-licensing-namespace+ (uuid/v4)) user> (uuid/v5 +secret-weapon-licensing-namespace+ "joe@example.com") ;; => #uuid "b6433d1e-d369-5282-8dbc-bdd3845c376c" user> (uuid/v5 +secret-weapon-licensing-namespace+ "mom@knitting-arts.edu") ;; => #uuid "81e4708c-85bb-5f3c-be56-bba4d8b0ac91" ``` Now, as the orders start rolling in for your product, you can crank out secret weapon activation codes just as well as if you were Microsoft. Each one will be keyed to a user's email address and is guaranteed to be irreversible. You will infuriate them with unreasonably high maintence support contract fees and intractible licensing terms. You truly are diabolical. ### Basic API * * * * * * #### Namespaces _(var)_ `+null+` > `#uuid "00000000-0000-0000-0000-000000000000"` _(var)_ `+max+` > `#uuid "ffffffff-ffff-ffff-ffff-ffffffffffff"` _(var)_ `+namespace-dns+` > `#uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8"` _(var)_ `+namespace-url+` > `#uuid "6ba7b811-9dad-11d1-80b4-00c04fd430c8"` _(var)_ `+namespace-oid+` > `#uuid "6ba7b812-9dad-11d1-80b4-00c04fd430c8"` _(var)_ `+namespace-x500+` > `#uuid "6ba7b814-9dad-11d1-80b4-00c04fd430c8"` * * * * * * #### Generators _(function)_ `null []` > Return the null UUID, a special form of sentinel UUID that is specified to have > all 128 bits set to zero: #uuid "00000000-0000-0000-0000-000000000000" _(function)_ `max []` > Return the max UUID, a special form of sentinel UUID that is specified to have > all 128 bits set to one: "#uuid "ffffffff-ffff-ffff-ffff-ffffffffffff" _(function)_ `v1 []` > Generate a v1 (time-based) unique identifier, guaranteed to be unique > and thread-safe regardless of clock precision or degree of concurrency. > Creation of v1 UUID's does not require any call to a cryptographic > generator and can be accomplished much more efficiently than v3, v4, v5, v7, > or squuid's. A v1 UUID reveals both the identity of the computer that > generated the UUID and the time at which it did so. Its uniqueness across > computers is guaranteed as long as MAC addresses are not duplicated. _(function)_ `v3 [^UUID namespace ^Object local-name]` > Generate a v3 (name based, MD5 hash) UUID. context' must be UUIDable. > v3 identifiers are intended for generating UUID's from names that are > drawn from, and unique within, some namespace. The concept of name and > namespace should be broadly construed, and not limited to textual names. > The requirements for a v3 UUID are as follows: > > * v3 UUID's generated at different times from the same name in the same > namespace MUST be equal. > > * v3 UUID's generated from two different names in the same namespace > SHOULD be distinct with a high degree of certainty. > > * v3 UUID's generated from the same name in two different namespaces > SHOULD be distinct with a high degree of certainty. > > * If two v3 UUID's are equal, then there is a high degree of certainty > that they were generated from the same name in the same namespace. _(function)_ `v4 []` _(function)_ `v4 [^long msb, ^long lsb]` > Generate a v4 (random) UUID. Uses default JVM implementation. If two > arguments, lsb and msb (both long) are provided, then construct a valid, > properly formatted v4 UUID based on those values. So, for example the > following UUID, created from all zero bits, is indeed distinct from the > null UUID: > > (v4) > => #uuid "dcf0035f-ea29-4d1c-b52e-4ea499c6323e" > > (v4 0 0) > => #uuid "00000000-0000-4000-8000-000000000000" > > (null) > => #uuid "00000000-0000-0000-0000-000000000000" _(function)_ `v5 [^UUID namespace ^Object local-name]` > Generate a v5 (name based, SHA1 hash) UUID. 'context' must be UUIDable. > v5 identifiers are intended for generating UUID's from names that are > drawn from, and unique within, some namespace. The concept of name and > namespace should be broadly construed, and not limited to textual names. > The requirements for a v5 UUID are as follows: > > * v5 UUID's generated at different times from the same name in the same > namespace MUST be equal. > > * v5 UUID's generated from two different names in the same namespace > SHOULD be distinct with a high degree of certainty. > > * v5 UUID's generated from the same name in two different namespaces > SHOULD be distinct with a high degree of certainty. > > * If two v5 UUID's are equal, then there is a high degree of certainty > that they were generated from the same name in the same namespace. _(function)_ `v6 []` > Generate a v6 (time-based), LEXICALLY SORTABLE, unique identifier, > v6 is a field-compatible version of v1, reordered for improved DB > locality. Creation of v6 UUID's does not require any call to a > cryptographic generator and can be accomplished much more efficiently > than v3, v4, v5, v7, or squuid's. A v6 UUID uses a cryptographically > secure, hard to guess random node id. It DOES NOT reveal the identity > of the computer on which it was created. _(function)_ `v7 []` > Generate a v7 unix time-based, LEXICALLY SORTABLE UUID with monotonic > counter, cryptographically secure random portion, and POSIX time encoding. > As such, creation of v7 UUIDs may be slower, but have improved > entropy chararacteristics compared to v1 or v6 UUIDs. _(function)_ `v7nc []` > Generate a v7 UUID using non-cryptographic randomness (ThreadLocalRandom). > Same timestamp/version/variant structure as v7, but uses a per-thread > monotonic counter and ThreadLocalRandom instead of SecureRandom and a > global AtomicLong. At ~39 ns, this is faster than JUG 5.2's v7 > generator (~50 ns). Use when cryptographic unguessability of the > random portion is not required. _(function)_ `v8 [^long msb, ^long lsb]` > Generate a v8 custom UUID with up to 122 bits of user data. _(function)_ `squuid []` > Generate a SQUUID (sequential, random) unique identifier. SQUUID's > are a nonstandard variation on v4 (random) UUIDs that have the > desirable property that they increase sequentially over time as well > as encode retrievably the posix time at which they were generated. > Splits and reassembles a v4 UUID to merge current POSIX > time (seconds since 12:00am January 1, 1970 UTC) with the most > significant 32 bits of the UUID _(function)_ `= [x]` _(function)_ `= [x y]` _(function)_ `= [x y & more]` > Directly compare two or more UUIDs for = relation based on the > ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL > EQUIVALENCE]. _(function)_ `> [x]` _(function)_ `> [x y]` _(function)_ `> [x y & more]` > Directly compare two or more UUIDs for > relation based on the > ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL > EQUIVALENCE]. _(function)_ `< [x]` _(function)_ `< [x y]` _(function)_ `< [x y & more]` > Directly compare two or more UUIDs for < relation based on the > ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL > EQUIVALENCE]. _(function)_ `monotonic-time []` > Return a monotonic timestamp (guaranteed always increasing) based on > the number of 100-nanosecond intervals elapsed since the adoption of the > Gregorian calendar in the West, 12:00am Friday October 15, 1582 UTC. * * * * * * #### Protocols _(protocol)_ `UUIDNameBytes` > A mechanism intended for user-level extension that defines the > decoding rules for the local-part representation of arbitrary > Clojure / Java Objects when used for computing namespaced > identifiers. > > _(member)_ `as-byte-array [self]` > >> Extract a byte serialization that represents the 'name' of x, >> typically unique within a given namespace. _(protocol)_ `UUIDable` > A UUIDable object directly represents a UUID. Examples of things which > might be conceptually 'uuidable' include string representation of a > UUID in canonical hex format, or an appropriate URN URI. > > _(member)_ `as-uuid [self]` > >> Coerce the value of `self` to a java.util.UUID. > > _(member)_ `uuidable? [self]` > >> Return 'true' if `self` can be coerced to UUID. _(protocol)_ `UUIDRfc4122` > Aliases UUIDRfc9526 _(protocol)_ `UUIDRfc9526` > A protocol that abstracts an unique identifier as described by > IETF RFC9526 . A UUID > represents a 128-bit value, however there are variant encoding > layouts used to assign and interpret information encoded in > those bits. This is a protocol for _variant 2_ (*Leach-Salz*) > UUID's. > > _(member)_ `null? [self]` > >> Return `true` only if `self` has all 128 bits set ot zero and is >> therefore equal to the null UUID, `00000000-0000-0000-0000-000000000000`. > > _(member)_ `uuid? [self]` > >> Return `true` if the class of `self` implements an RFC9526 unique identifier. > > _(member)_ `uuid= [self other]` > >> Directly compare two UUID's for `=` relation based on the equality >> semantics defined by [RFC4122:3 "RULES FOR LEXICAL EQUIVALENCE"]. > > _(member)_ `uuid< [self other]` > >> Directly compare two UUID's for `<` relation based on the ordinality >> semantics defined by [RFC4122:3 "RULES FOR LEXICAL EQUIVALENCE"]. > > _(member)_ `uuid> [self other]` > >> Directly compare two UUID's for `>` relation based on the ordinality >> semantics defined by [RFC4122:3 "RULES FOR LEXICAL EQUIVALENCE"]. > > _(member)_ `get-word-high [self]` > >> Return the most significant 64 bits of the 128 bit value of UUID `self`. > > _(member)_ `get-word-low [self]` > >> Return the least significant 64 bits of the 128 bit value of UUID `self`. > > _(member)_ `get-variant [self]` > >> Return the _variant_ number associated with this UUID. The variant field >> contains a value which identifies the layout of the UUID. The bit-layout >> implemented by this protocol supports UUID's with a variant value of 0x2, >> which indicates Leach-Salz layout. Defined UUID variant values are: >> >> 0x0 Null >> 0x2 Leach-Salz >> 0x6 Microsoft >> 0x7 Max >> >> In the canonical representation, `xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx`, >> the most significant bits of N indicate the variant (depending on the >> variant one, two, or three bits are used). The variant covered by RFC4122 >> is indicated by the two most significant bits of `N` being `1 0` (i.e., the >> hexadecimal `N` will always be `8`, `9`, `A`, or `B`). > > _(member)_ `get-version [self]` > >> Return the _version_ number associated with this UUID. The version >> field contains a value which describes the nature of the UUID. There >> are five versions of Leach-Salz UUID, plus the null and max UUIDs: >> >> 0x0 Null >> 0x1 Time based >> 0x2 DCE security with POSIX UID >> 0x3 Namespaced, deterministic (MD5 Digest) >> 0x4 Cryptographic random >> 0x5 Namespaced, deterministic (SHA1 Digest) >> 0x6 Time based, lexically ordered >> 0x7 POSIX Time based, lexically ordered, cryptographically secure >> 0x8 User Customizable >> 0xF Max >> >> In the canonical representation, xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx, >> the four bits of M indicate the UUID version (i.e., the hexadecimal M >> will be either 1, 2, 3, 4, 5, 6, 7, or 8).") > > _(member)_ `get-timestamp [self]` > >> Return the time of UUID creation. For Gregorian time-based (v1, >> v6) UUID's, this is 60 bit unsigned value that represents a >> temporally unique timestamp associated with this UUID. The result >> encodes the number of 100 nanosecond intervals since the adoption of >> the Gregorian calendar. For v7 UUID's this encodes the more common >> unix time in milliseconds since midnight, January 1, 1970 UTC. For >> non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns >> `nil`. > > _(member)_ `get-instant [self]` > >> For time-based (v1, v6, v7) UUID's, return a `java.util.Date` object >> that represents the system time at which this UUID was >> generated. NOTE: the returned value may not necessarily be >> temporally unique. For non-time-based (v3, v4, v5, squuid) UUID's, >> always returns `nil`. > > _(member)_ `get-unix-time [self]` > >> For time-based (v1, v6, v7) UUIDs return the timestamp portion in >> approximately milliseconds since the Unix epoch 1970-01-01T00:00:00.000Z. >> For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns >`nil`. > > _(member)_ `get-time-low [self]` > >> Return the 32 bit unsigned value that represents the _time-low_ field >> of the _timestamp_ associated with this UUID. > > _(member)_ `get-time-mid [self]` > >> Return the 16 bit unsigned value that represents the _time-mid_ field >> of the _timestamp_ associated with this UUID. > > _(member)_ `get-time-high [self]` > >> Return the 16 bit unsigned value that represents the _time-high_ field >> of the _timestamp_ multiplexed with the _version_ of this UUID. > > _(member)_ `get-clk-seq [self]` > >> Return the _clk-seq_ number associated with this UUID. For >> time-based (v1, v6) UUID's the _clock-sequence_ value is a somewhat >> counter-intuitively named seed-value that is used to reduce the >> potential that duplicate UUID's might be generated under unusual >> situations, such as if the system hardware clock is set backward in >> time or if, despite all efforts otherwise, a duplicate `+node-id+` >> happens to be generated. This value is initialized to a random >> 16-bit number once per lifetime of the system. For non-time-based >> (v3, v4, v5, squuid) UUID's, always returns `nil`. > > _(member)_ `get-clk-high [self]` > >> Return the 8 bit unsigned value that represents the most significant >> byte of the _clk-seq_ multiplexed with the _variant_ of this UUID. > > _(member)_ `get-clk-low [self]` > >> Return the 8 bit unsigned value that represents the least significant >> byte of the _clk-seq_ associated with this UUID. > > _(member)_ `get-node-id [self]` > >> Return the 48 bit unsigned value that represents the spatially unique >> _node identifier_ associated with this UUID. > > _(member)_ `hash-code [self]` > >> Return a suitable 64-bit hash value for `self`. Extend for >> specialized hash computation. > > _(member)_ `to-byte-array [self]` > >> Return an array of 16 bytes that represents `self` as a decomposed >> octet serialization encoded in most-significant-byte first order. > > _(member)_ `to-string [self]` > >> Return a String object that represents `self` in the canonical >> 36 character hexadecimal string format: >> >> "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" > > _(member)_ `to-hex-string [self]` > >> Return a String object that represents `self` as the 32 hexadecimal >> characters directly encodong the UUID's 128 bit value: >> >> "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" > > _(member)_ `to-urn-string [self]` > >> Return a String object that represents `uuid` as a the string >> serialization of the URN URI based on the canonical 36 character >> hex-string representation: >> >> "urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" > > _(member)_ `to-uri [self]` > >> Return the unique URN URI associated with this UUID. ### References * [IETF RFC-9562](http://www.ietf.org/rfc/rfc9562.txt) _Universally Unique IDentifiers (UUIDs)_ * [IETF RFC-4122](http://www.ietf.org/rfc/rfc4122.txt) _A Universally Unique IDentifier (UUID) URN Namespace_ * [Wikipedia/_Universally unique identifier_](http://en.wikipedia.org/wiki/Universally_unique_identifier) * [CL-UUID](http://www.dardoria.net/software/uuid.html) reference implementation * [UNICLY](https://github.com/mon-key/unicly) reference implementation * [java.util.UUID](http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html) JavaDoc * [Java and Unsigned Primitive Datatypes](http://www.darksleep.com/player/JavaAndUnsignedTypes.html) * [The web of names, hashes, and UUIDs](http://joearms.github.io/2015/03/12/The_web_of_names.html) * [Coding Katas Clojure -- Bloom Filters](http://blog.find-method.de/index.php?/archives/200-Coding-katas-Clojure-Bloom-filters.html) ### Special Thanks ![YourKit](https://www.yourkit.com/images/yklogo.png) YourKit supports open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/index.jsp) and [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/index.jsp), innovative and intelligent tools for profiling Java and .NET applications. ### License Copyright © 2024 Dan Lentz Distributed under the Eclipse Public License version 1.0 ================================================ FILE: deps.edn ================================================ {:paths ["src"] :deps {org.clojure/clojure {:mvn/version "1.12.0"} org.clj-commons/primitive-math {:mvn/version "1.0.1"}} :aliases {:test {:extra-paths ["test"]} :coverage {:extra-paths ["test"] :extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}} :main-opts ["-m" "cloverage.coverage" "--src-ns-path" "src" "--test-ns-path" "test"]}}} ================================================ FILE: doc/api/clj-uuid.bitmop.html ================================================ clj-uuid.bitmop documentation

clj-uuid.bitmop

Unsigned Long and ByteBuffer-based bitwise operation primitives for
UUID manipulation.

Provides the same mask/ldb/dpb fundamentals as in the past, plus a
16-byte ByteBuffer abstraction for direct UUID byte manipulation.

The ByteBuffer approach has two key advantages:

1. Performance: ByteBuffer provides direct typed access at byte offsets
   via single native operations (getLong, getInt, etc.) rather than
   manual shift/mask loops.
2. Portability: The buffer abstraction maps naturally to JavaScript's
   DataView/ArrayBuffer, enabling a future cljc implementation.

assemble-bytes

(assemble-bytes v)
Assemble a sequence of 8 bytes (big-endian) into a long.

bit-count

(bit-count x)
Count the number of set bits in `x`.

buf->bytes

(buf->bytes buf)
Extract the contents of a 16-byte UUID buffer as a byte array.

buf->uuid

(buf->uuid buf)
Convert a 16-byte UUID buffer to a java.util.UUID.

buf-compare

(buf-compare a b)
Lexicographic unsigned comparison of two 16-byte UUID buffers.
Returns negative if a < b, zero if equal, positive if a > b.
Comparison is unsigned (treats bytes as 0-255) which matches UUID
canonical string ordering.

buf-hex

(buf-hex buf)
Convert a 16-byte UUID buffer to a 32-character hex string.

buf-str

(buf-str buf)
Convert a 16-byte UUID buffer to the canonical 36-character UUID string:
  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Byte ranges correspond to UUID fields:
  bytes 0-3:   time-low
  bytes 4-5:   time-mid
  bytes 6-7:   time-high-and-version
  bytes 8-9:   clock-seq
  bytes 10-15: node

buffer

(buffer)(buffer msb lsb)
Create a 16-byte big-endian ByteBuffer.

0-arity:  zeroed buffer
2-arity:  from msb and lsb longs
The buffer uses absolute (position-independent) get/put operations.

buffer-from-bytes

(buffer-from-bytes arr)
Create a 16-byte ByteBuffer from a byte array (at least 16 bytes).

bytes->long

(bytes->long arr i)
Read 8 bytes from `arr` starting at offset `i`, returning a long.

dpb

(dpb bitmask num value)
Deposit Byte -- insert `value` into the bit field defined by `bitmask`
within `num`.

dpb-buf

(dpb-buf buf word-offset bitmask value)
Deposit bit field: insert `value` into the bits defined by `bitmask`
within the 64-bit word at `word-offset` in the buffer.  Mutates and
returns the buffer.

duplicate

(duplicate buf)
Create an independent copy of a UUID buffer.

expt2

(expt2 pow)
Compute 2^pow using bit-set.

get-byte

(get-byte buf offset)
Read an unsigned byte (0-255) at byte offset from buffer.

get-int

(get-int buf offset)
Read an unsigned 32-bit value at byte offset from buffer.

get-long

(get-long buf offset)
Read a 64-bit long at byte offset from buffer.

get-lsb

(get-lsb buf)
Read the least significant 64 bits (bytes 8-15) of a UUID buffer.

get-msb

(get-msb buf)
Read the most significant 64 bits (bytes 0-7) of a UUID buffer.

get-short

(get-short buf offset)
Read an unsigned 16-bit value at byte offset from buffer.

hex

(hex thing)
Convert a long or byte sequence to a hex string.
For a long, produces a 16-character zero-padded hex string.

hex->buf

(hex->buf s)
Parse a 32-character hex string into a 16-byte UUID buffer.

ldb

(ldb bitmask num)
Load Byte -- extract the bit field defined by `bitmask` from `num`.

ldb-buf

(ldb-buf buf word-offset bitmask)
Load bit field: extract bits defined by `bitmask` from the 64-bit word
at `word-offset` in the buffer.

long->bytes

(long->bytes x)(long->bytes x arr i)
Write `x` as 8 big-endian bytes.  With one argument returns a new byte
array.  With three arguments writes into `arr` at offset `i`.

mask

(mask width offset)
Create a bitmask of `width` bits starting at bit `offset`.

mask-offset

(mask-offset m)
Return the bit offset (position of least significant set bit) of a mask.

mask-width

(mask-width m)
Return the number of set bits in a contiguous bitmask.

octet-hex

(octet-hex num)
Convert a single byte value to a two-character uppercase hex string.

pphex

(pphex x)
Pretty-print a long value in both hexadecimal and binary.

put-byte

(put-byte buf offset val)
Write a byte value at byte offset in buffer.  Returns the buffer.

put-int

(put-int buf offset val)
Write a 32-bit value at byte offset in buffer.  Returns the buffer.

put-long

(put-long buf offset val)
Write a 64-bit long at byte offset in buffer.  Returns the buffer.

put-short

(put-short buf offset val)
Write a 16-bit value at byte offset in buffer.  Returns the buffer.

sb16

(sb16 num)

sb32

(sb32 num)

sb64

(sb64 num)

sb8

(sb8 num)

set-lsb

(set-lsb buf val)
Set the least significant 64 bits (bytes 8-15) of a UUID buffer.
Returns the buffer.

set-msb

(set-msb buf val)
Set the most significant 64 bits (bytes 0-7) of a UUID buffer.
Returns the buffer.

ub16

(ub16 num)

ub24

(ub24 num)

ub32

(ub32 num)

ub4

(ub4 num)

ub48

(ub48 num)

ub56

(ub56 num)

ub8

(ub8 num)

uuid->buf

(uuid->buf uuid)
Convert a java.util.UUID to a 16-byte ByteBuffer.
================================================ FILE: doc/api/clj-uuid.clock.html ================================================ clj-uuid.clock documentation

clj-uuid.clock

Lock-Free, Thread-safe Monotonic Clocks

+random-counter-resolution+

+subcounter-resolution+

-gregorian-packed-

monotonic-time

(monotonic-time)
Generate a guaranteed monotonically increasing timestamp based on
Gregorian time and a stateful subcounter

monotonic-unix-time-and-random-counter

(monotonic-unix-time-and-random-counter)
Generate guaranteed monotonically increasing number pairs based on
POSIX time and a randomly seeded subcounter

posix-time

(posix-time)(posix-time gregorian)
Generate the (Unix compatible) POSIX time -- the number of seconds
that have elapsed since 00:00 January 1, 1970 UTC

universal-time

(universal-time)(universal-time gregorian)
Generate the (Common-Lisp compatible) universal-time -- the number of
seconds that have elapsed since 00:00 January 1, 1900 GMT
================================================ FILE: doc/api/clj-uuid.constants.html ================================================ clj-uuid.constants documentation

clj-uuid.constants

+hex-chars+

+md5+

+sha1+

+ub1-mask+

+ub12-mask+

+ub16-mask+

+ub24-mask+

+ub32-mask+

+ub4-mask+

+ub40-mask+

+ub48-mask+

+ub56-mask+

+ub60-mask+

+ub63-mask+

+ub8-mask+

hex-regex

urn-regex

uuid-regex

================================================ FILE: doc/api/clj-uuid.core.html ================================================ clj-uuid.core documentation

clj-uuid.core

+max+

The MAX UUID is a special form of sentinel UUID that is specified to have
all 128 bits set to one.

+namespace-dns+

+namespace-oid+

+namespace-url+

+namespace-x500+

+null+

The NULL UUID is a special form of sentinel UUID that is specified to have
all 128 bits set to zero.

<

(< _)(< x y)(< x y & more)
Directly compare two or more UUIDs for < relation based on the
ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

=

(= _)(= x y)(= x y & more)
Directly compare two or more UUIDs for = relation based on the
equality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

>

(> _)(> x y)(> x y & more)
Directly compare two or more UUIDs for > relation based on the
ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

max

(max)
Generates the v15 (maximum) UUID, ffffffff-ffff-ffff-ffff-ffffffffffff.

monotonic-time

(monotonic-time)
Return a monotonic timestamp (guaranteed always increasing) based on
the number of 100-nanosecond intervals elapsed since the adoption of
the Gregorian calendar in the West, 12:00am Friday October 15, 1582 UTC.

null

(null)
Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000.

squuid

(squuid)
Generate a SQUUID (sequential, random) unique identifier.  SQUUID's
are a nonstandard variation on v4 (random) UUIDs that have the
desirable property that they increase sequentially over time as well
as encode retrievably the posix time at which they were generated.
Splits and reassembles a v4 UUID to merge current POSIX
time (seconds since 12:00am January 1, 1970 UTC) with the most
significant 32 bits of the UUID.

uuid-string?

(uuid-string? str)

uuid-urn-string?

(uuid-urn-string? str)

uuid-vec?

(uuid-vec? v)

UUIDable

protocol

A UUIDable object directly represents a UUID.  Examples of things which
might be conceptually 'uuidable' include string representation of a
UUID in canonical hex format, or an appropriate URN URI.

members

as-uuid

(as-uuid x)
Coerce the value 'x' to a UUID.

uuidable?

(uuidable? x)
Return 'true' if 'x' can be coerced to UUID.

UUIDNameBytes

protocol

A mechanism intended for user-level extension that defines the
decoding rules for the local-part representation of arbitrary
Clojure / Java Objects when used for computing namespaced
identifiers.

members

as-byte-array

(as-byte-array x)
Extract a byte serialization that represents the 'name' of x,
typically unique within a given namespace.

UUIDRfc4122

protocol

UUIDRfc9562

protocol

A protocol that abstracts an unique identifier as described by
IETF RFC9562 <http://www.ietf.org/rfc/rfc9562.txt>;. A UUID
represents a 128-bit value, however there are variant encoding
layouts used to assign and interpret information encoded in
those bits.  This is a protocol for  _variant 2_ (*Leach-Salz*)
UUID's.

members

get-clk-high

(get-clk-high uuid)
Return the 8 bit unsigned value that represents the most significant
byte of the `clk-seq` multiplexed with the `variant` of this UUID.

get-clk-low

(get-clk-low uuid)
Return the 8 bit unsigned value that represents the least significant
byte of the `clk-seq` associated with this UUID.

get-clk-seq

(get-clk-seq uuid)
Return the clock-sequence number associated with this UUID. For time-based
(v1, v6) UUID's the 'clock-sequence' value is a somewhat counter-intuitively
named seed-value that is used to reduce the potential that duplicate UUID's
might be generated under unusual situations, such as if the system hardware
clock is set backward in time or if, despite all efforts otherwise, a
duplicate node-id happens to be generated. This value is initialized to
a random 16-bit number once per lifetime of the system.  For
non-gregorian-time-based (v3, v4, v5, v7, v8, squuid) UUID's, always
returns `nil`.

get-instant

(get-instant uuid)
For time-based (v1, v6, v7) UUID's, return a java.util.Date
object that represents the system time at which this UUID was
generated. NOTE: the returned value may not necessarily be
temporally unique. For non-time-based
(v3, v4, v5, v8, squuid) UUID's, always returns `nil`.

get-node-id

(get-node-id uuid)
Return the 48 bit unsigned value that represents the spatially unique
node identifier associated with this UUID.

get-time-high

(get-time-high uuid)
Return the 16 bit unsigned value that represents the `time-high` field
of the `timestamp` multiplexed with the `version` of this UUID.

get-time-low

(get-time-low uuid)
Return the 32 bit unsigned value that represents the `time-low` field
of the `timestamp` associated with this UUID.

get-time-mid

(get-time-mid uuid)
Return the 16 bit unsigned value that represents the `time-mid` field
of the `timestamp` associated with this UUID.

get-timestamp

(get-timestamp uuid)
Return the time of UUID creation.  For Gregorian time-based (v1,
v6) UUID's, this is 60 bit unsigned value that represents a
temporally unique timestamp associated with this UUID.  The result
encodes the number of 100 nanosecond intervals since the adoption of
the Gregorian calendar.  For v7 UUID's this encodes the more common
unix time in milliseconds since midnight, January 1, 1970 UTC.  For
non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns
`nil`.

get-unix-time

(get-unix-time uuid)
For time-based (v1, v6, v7) UUIDs return the timestamp portion in
approximately milliseconds since the Unix epoch 1970-01-01T00:00:00.000Z.
For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns `nil`.

get-variant

(get-variant uuid)
Return the variant number associated with this UUID.  The variant field
contains a value which identifies the layout of the UUID.  The bit-layout
implemented by this protocol supports UUID's with a variant value of 0x2,
which indicates Leach-Salz layout.  Defined UUID variant values are:

0x0   Null
0x2   Leach-Salz
0x6   Microsoft
0x7   Max

In the canonical representation, xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx,
the most significant bits of N indicate the variant (depending on the
variant one, two, or three bits are used). The variant covered by RFC9562
is indicated by the two most significant bits of N being 1 0 (i.e., the
hexadecimal N will always be 8, 9, A, or B).

get-version

(get-version uuid)
Return the version number associated with this UUID.  The version
field contains a value which describes the nature of the UUID.  There
are five versions of Leach-Salz UUID, plus the null and max UUIDs:

0x0   Null
0x1   Time based
0x2   DCE security with POSIX UID
0x3   Namespaced, deterministic (MD5 Digest)
0x4   Cryptographic random
0x5   Namespaced, deterministic (SHA1 Digest)
0x6   Time based, lexically ordered
0x7   POSIX Time based, lexically ordered, cryptographically secure
0x8   User Customizable
0xF   Max

In the canonical representation, xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx,
the four bits of M indicate the UUID version (i.e., the hexadecimal M
will be either 1, 2, 3, 4, 5, 6, 7, or 8).

get-word-high

(get-word-high uuid)
Return the most significant 64 bits of UUID's 128 bit value.

get-word-low

(get-word-low uuid)
Return the least significant 64 bits of UUID's 128 bit value.

hash-code

(hash-code uuid)
Return a suitable 64-bit hash value for `uuid`.  Extend with
specialized hash computation.

max?

(max? uuid)
Return `true` only if `uuid` has all 128 bits set and is
therefore equal to the maximum UUID, ffffffff-ffff-ffff-ffff-ffffffffffff.

null?

(null? uuid)
Return `true` only if `uuid` has all 128 bits set to zero and is
therefore equal to the null UUID, 00000000-0000-0000-0000-000000000000.

to-byte-array

(to-byte-array uuid)
Return an array of 16 bytes that represents `uuid` as a decomposed
octet serialization encoded in most-significant-byte first order.

to-hex-string

(to-hex-string uuid)
Return a String object that represents `uuid` as the 32 hexadecimal
characters directly encodong the UUID's 128 bit value:

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

to-string

(to-string uuid)
Return a String object that represents `uuid` in the canonical
36 character hex-string format:

    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

to-uri

(to-uri uuid)
Return the unique URN URI associated with this UUID.

to-urn-string

(to-urn-string uuid)
Return a String object that represents `uuid` as a the string
serialization of the URN URI based on the canonical 36 character
hex-string representation:

    urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

uuid<

(uuid< x y)
Directly compare two UUID's for < relation based on the ordinality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/<`

uuid=

(uuid= x y)
Directly compare two UUID's for = relation based on the equality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/=`

uuid>

(uuid> x y)
Directly compare two UUID's for > relation based on the ordinality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/>`

uuid?

(uuid? x)
Return `true` if `x` implements an RFC9562 unique identifier.

v0

(v0)
Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000.

v1

(v1)
Generate a v1 (time-based) unique identifier, guaranteed to be unique
and thread-safe regardless of clock precision or degree of concurrency.
Creation of v1 UUID's does not require any call to a cryptographic
generator and can be accomplished much more efficiently than v3, v4, v5, v7,
or squuid's.  A v1 UUID reveals both the identity of the computer that
generated the UUID and the time at which it did so.  Its uniqueness across
computers is guaranteed as long as MAC addresses are not duplicated.

v3

(v3 context local-part)
Generate a v3 (name based, MD5 hash) UUID. 'context' must be UUIDable.
v3 identifiers are intended for generating UUID's from names that are
drawn from, and unique within, some namespace.  The concept of name and
namespace should be broadly construed, and not limited to textual names.
The requirements for a v3 UUID are as follows:

* v3 UUID's generated at different times from the same name in the same
  namespace MUST be equal.

* v3 UUID's generated from two different names in the same namespace
  SHOULD be distinct with a high degree of certainty.

* v3 UUID's generated from the same name in two different namespaces
  SHOULD be distinct with a high degree of certainty.

* If two v3 UUID's are equal, then there is a high degree of certainty
  that they were generated from the same name in the same namespace.

v4

(v4)(v4 msb lsb)
Generate a v4 (random) UUID.  Uses default JVM implementation.  If two
arguments, lsb and msb (both long) are provided, then construct a valid,
properly formatted v4 UUID based on those values.  So, for example the
following UUID, created from all zero bits, is indeed distinct from the
null UUID:

    (v4)
     => #uuid "dcf0035f-ea29-4d1c-b52e-4ea499c6323e"

    (v4 0 0)
     => #uuid "00000000-0000-4000-8000-000000000000"

    (null)
     => #uuid "00000000-0000-0000-0000-000000000000"

v5

(v5 context local-part)
Generate a v5 (name based, SHA1 hash) UUID. 'context' must be UUIDable.
v5 identifiers are intended for generating UUID's from names that are
drawn from, and unique within, some namespace.  The concept of name and
namespace should be broadly construed, and not limited to textual names.
The requirements for a v5 UUID are as follows:

* v5 UUID's generated at different times from the same name in the same
  namespace MUST be equal.

* v5 UUID's generated from two different names in the same namespace
  SHOULD be distinct with a high degree of certainty.

* v5 UUID's generated from the same name in two different namespaces
  SHOULD be distinct with a high degree of certainty.

* If two v5 UUID's are equal, then there is a high degree of certainty
  that they were generated from the same name in the same namespace.

v6

(v6)
Generate a v6 (time-based), LEXICALLY SORTABLE, unique identifier,
v6 is a field-compatible version of v1, reordered for improved DB
locality.  Creation of v6 UUID's does not require any call to a
cryptographic generator and can be accomplished much more efficiently
than v3, v4, v5, v7, or squuid's.  A v6 UUID uses a cryptographically
secure, hard to guess random node id. It DOES NOT reveal the identity
of the computer on which it was created.

v7

(v7)
Generate a v7 unix time-based, LEXICALLY SORTABLE UUID with monotonic
counter and cryptographically secure random portion and POSIX time encoding.
As such, creation of v7 UUIDs may be significantly slower, but have improved
entropy chararacteristics compared to v1 or v6 UUIDs.

v7nc

(v7nc)
Generate a v7 UUID using non-cryptographic randomness (ThreadLocalRandom).
Produces valid RFC 9562 v7 UUIDs with the same timestamp/version/variant
structure as v7, but uses java.util.concurrent.ThreadLocalRandom instead
of SecureRandom and a per-thread monotonic counter instead of a global
AtomicLong.  This trades cryptographic unguessability for significantly
higher throughput -- comparable to JUG's TimeBasedEpochGenerator.

The per-thread counter uses rand_a (12 bits) as a fixed seed per
millisecond and rand_b (62 bits) as a strictly increasing counter,
giving 2^62 UUIDs per millisecond per thread before overflow.

v8

(v8 msb lsb)
Generate a v8 custom UUID with up to 122 bits of user data.
================================================ FILE: doc/api/clj-uuid.html ================================================ clj-uuid documentation

clj-uuid

RFC 9562 UUID implementation for Clojure.

This namespace re-exports all public vars from clj-uuid.core. For backward
compatibility, both `(require '[clj-uuid :as uuid])` and
`(require '[clj-uuid.core :as uuid])` provide identical functionality.

Quick start:
  (require '[clj-uuid :as uuid])
  (uuid/v4)       ; random UUID
  (uuid/v7)       ; time-based, sortable, cryptographically secure
  (uuid/v7nc)     ; time-based, sortable, fast (non-cryptographic)

See clj-uuid.core for complete documentation.

+max+

The MAX UUID is a special form of sentinel UUID that is specified to have
all 128 bits set to one.

+namespace-dns+

+namespace-oid+

+namespace-url+

+namespace-x500+

+null+

The NULL UUID is a special form of sentinel UUID that is specified to have
all 128 bits set to zero.

<

Directly compare two or more UUIDs for < relation based on the
ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

=

Directly compare two or more UUIDs for = relation based on the
equality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

>

Directly compare two or more UUIDs for > relation based on the
ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL
EQUIVALENCE].

as-byte-array

Extract a byte serialization that represents the 'name' of x,
typically unique within a given namespace.

as-uuid

Coerce the value 'x' to a UUID.

get-clk-high

Return the 8 bit unsigned value that represents the most significant
byte of the `clk-seq` multiplexed with the `variant` of this UUID.

get-clk-low

Return the 8 bit unsigned value that represents the least significant
byte of the `clk-seq` associated with this UUID.

get-clk-seq

Return the clock-sequence number associated with this UUID. For time-based
(v1, v6) UUID's the 'clock-sequence' value is a somewhat counter-intuitively
named seed-value that is used to reduce the potential that duplicate UUID's
might be generated under unusual situations, such as if the system hardware
clock is set backward in time or if, despite all efforts otherwise, a
duplicate node-id happens to be generated. This value is initialized to
a random 16-bit number once per lifetime of the system.  For
non-gregorian-time-based (v3, v4, v5, v7, v8, squuid) UUID's, always
returns `nil`.

get-instant

For time-based (v1, v6, v7) UUID's, return a java.util.Date
object that represents the system time at which this UUID was
generated. NOTE: the returned value may not necessarily be
temporally unique. For non-time-based
(v3, v4, v5, v8, squuid) UUID's, always returns `nil`.

get-node-id

Return the 48 bit unsigned value that represents the spatially unique
node identifier associated with this UUID.

get-time-high

Return the 16 bit unsigned value that represents the `time-high` field
of the `timestamp` multiplexed with the `version` of this UUID.

get-time-low

Return the 32 bit unsigned value that represents the `time-low` field
of the `timestamp` associated with this UUID.

get-time-mid

Return the 16 bit unsigned value that represents the `time-mid` field
of the `timestamp` associated with this UUID.

get-timestamp

Return the time of UUID creation.  For Gregorian time-based (v1,
v6) UUID's, this is 60 bit unsigned value that represents a
temporally unique timestamp associated with this UUID.  The result
encodes the number of 100 nanosecond intervals since the adoption of
the Gregorian calendar.  For v7 UUID's this encodes the more common
unix time in milliseconds since midnight, January 1, 1970 UTC.  For
non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns
`nil`.

get-unix-time

For time-based (v1, v6, v7) UUIDs return the timestamp portion in
approximately milliseconds since the Unix epoch 1970-01-01T00:00:00.000Z.
For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns `nil`.

get-variant

Return the variant number associated with this UUID.  The variant field
contains a value which identifies the layout of the UUID.  The bit-layout
implemented by this protocol supports UUID's with a variant value of 0x2,
which indicates Leach-Salz layout.  Defined UUID variant values are:

0x0   Null
0x2   Leach-Salz
0x6   Microsoft
0x7   Max

In the canonical representation, xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx,
the most significant bits of N indicate the variant (depending on the
variant one, two, or three bits are used). The variant covered by RFC9562
is indicated by the two most significant bits of N being 1 0 (i.e., the
hexadecimal N will always be 8, 9, A, or B).

get-version

Return the version number associated with this UUID.  The version
field contains a value which describes the nature of the UUID.  There
are five versions of Leach-Salz UUID, plus the null and max UUIDs:

0x0   Null
0x1   Time based
0x2   DCE security with POSIX UID
0x3   Namespaced, deterministic (MD5 Digest)
0x4   Cryptographic random
0x5   Namespaced, deterministic (SHA1 Digest)
0x6   Time based, lexically ordered
0x7   POSIX Time based, lexically ordered, cryptographically secure
0x8   User Customizable
0xF   Max

In the canonical representation, xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx,
the four bits of M indicate the UUID version (i.e., the hexadecimal M
will be either 1, 2, 3, 4, 5, 6, 7, or 8).

get-word-high

Return the most significant 64 bits of UUID's 128 bit value.

get-word-low

Return the least significant 64 bits of UUID's 128 bit value.

hash-code

Return a suitable 64-bit hash value for `uuid`.  Extend with
specialized hash computation.

max

Generates the v15 (maximum) UUID, ffffffff-ffff-ffff-ffff-ffffffffffff.

max?

Return `true` only if `uuid` has all 128 bits set and is
therefore equal to the maximum UUID, ffffffff-ffff-ffff-ffff-ffffffffffff.

monotonic-time

Return a monotonic timestamp (guaranteed always increasing) based on
the number of 100-nanosecond intervals elapsed since the adoption of
the Gregorian calendar in the West, 12:00am Friday October 15, 1582 UTC.

null

Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000.

null?

Return `true` only if `uuid` has all 128 bits set to zero and is
therefore equal to the null UUID, 00000000-0000-0000-0000-000000000000.

squuid

Generate a SQUUID (sequential, random) unique identifier.  SQUUID's
are a nonstandard variation on v4 (random) UUIDs that have the
desirable property that they increase sequentially over time as well
as encode retrievably the posix time at which they were generated.
Splits and reassembles a v4 UUID to merge current POSIX
time (seconds since 12:00am January 1, 1970 UTC) with the most
significant 32 bits of the UUID.

to-byte-array

Return an array of 16 bytes that represents `uuid` as a decomposed
octet serialization encoded in most-significant-byte first order.

to-hex-string

Return a String object that represents `uuid` as the 32 hexadecimal
characters directly encodong the UUID's 128 bit value:

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

to-string

Return a String object that represents `uuid` in the canonical
36 character hex-string format:

    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

to-uri

Return the unique URN URI associated with this UUID.

to-urn-string

Return a String object that represents `uuid` as a the string
serialization of the URN URI based on the canonical 36 character
hex-string representation:

    urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

uuid-string?

uuid-urn-string?

uuid-vec?

uuid<

Directly compare two UUID's for < relation based on the ordinality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/<`

uuid=

Directly compare two UUID's for = relation based on the equality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/=`

uuid>

Directly compare two UUID's for > relation based on the ordinality
semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE].
See: `clj-uuid/>`

uuid?

Return `true` if `x` implements an RFC9562 unique identifier.

UUIDable

protocol

uuidable?

Return 'true' if 'x' can be coerced to UUID.

UUIDNameBytes

protocol

UUIDRfc4122

protocol

UUIDRfc9562

protocol

v0

Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000.

v1

Generate a v1 (time-based) unique identifier, guaranteed to be unique
and thread-safe regardless of clock precision or degree of concurrency.
Creation of v1 UUID's does not require any call to a cryptographic
generator and can be accomplished much more efficiently than v3, v4, v5, v7,
or squuid's.  A v1 UUID reveals both the identity of the computer that
generated the UUID and the time at which it did so.  Its uniqueness across
computers is guaranteed as long as MAC addresses are not duplicated.

v3

Generate a v3 (name based, MD5 hash) UUID. 'context' must be UUIDable.
v3 identifiers are intended for generating UUID's from names that are
drawn from, and unique within, some namespace.  The concept of name and
namespace should be broadly construed, and not limited to textual names.
The requirements for a v3 UUID are as follows:

* v3 UUID's generated at different times from the same name in the same
  namespace MUST be equal.

* v3 UUID's generated from two different names in the same namespace
  SHOULD be distinct with a high degree of certainty.

* v3 UUID's generated from the same name in two different namespaces
  SHOULD be distinct with a high degree of certainty.

* If two v3 UUID's are equal, then there is a high degree of certainty
  that they were generated from the same name in the same namespace.

v4

Generate a v4 (random) UUID.  Uses default JVM implementation.  If two
arguments, lsb and msb (both long) are provided, then construct a valid,
properly formatted v4 UUID based on those values.  So, for example the
following UUID, created from all zero bits, is indeed distinct from the
null UUID:

    (v4)
     => #uuid "dcf0035f-ea29-4d1c-b52e-4ea499c6323e"

    (v4 0 0)
     => #uuid "00000000-0000-4000-8000-000000000000"

    (null)
     => #uuid "00000000-0000-0000-0000-000000000000"

v5

Generate a v5 (name based, SHA1 hash) UUID. 'context' must be UUIDable.
v5 identifiers are intended for generating UUID's from names that are
drawn from, and unique within, some namespace.  The concept of name and
namespace should be broadly construed, and not limited to textual names.
The requirements for a v5 UUID are as follows:

* v5 UUID's generated at different times from the same name in the same
  namespace MUST be equal.

* v5 UUID's generated from two different names in the same namespace
  SHOULD be distinct with a high degree of certainty.

* v5 UUID's generated from the same name in two different namespaces
  SHOULD be distinct with a high degree of certainty.

* If two v5 UUID's are equal, then there is a high degree of certainty
  that they were generated from the same name in the same namespace.

v6

Generate a v6 (time-based), LEXICALLY SORTABLE, unique identifier,
v6 is a field-compatible version of v1, reordered for improved DB
locality.  Creation of v6 UUID's does not require any call to a
cryptographic generator and can be accomplished much more efficiently
than v3, v4, v5, v7, or squuid's.  A v6 UUID uses a cryptographically
secure, hard to guess random node id. It DOES NOT reveal the identity
of the computer on which it was created.

v7

Generate a v7 unix time-based, LEXICALLY SORTABLE UUID with monotonic
counter and cryptographically secure random portion and POSIX time encoding.
As such, creation of v7 UUIDs may be significantly slower, but have improved
entropy chararacteristics compared to v1 or v6 UUIDs.

v7nc

Generate a v7 UUID using non-cryptographic randomness (ThreadLocalRandom).
Produces valid RFC 9562 v7 UUIDs with the same timestamp/version/variant
structure as v7, but uses java.util.concurrent.ThreadLocalRandom instead
of SecureRandom and a per-thread monotonic counter instead of a global
AtomicLong.  This trades cryptographic unguessability for significantly
higher throughput -- comparable to JUG's TimeBasedEpochGenerator.

The per-thread counter uses rand_a (12 bits) as a fixed seed per
millisecond and rand_b (62 bits) as a strictly increasing counter,
giving 2^62 UUIDs per millisecond per thread before overflow.

v8

Generate a v8 custom UUID with up to 122 bits of user data.
================================================ FILE: doc/api/clj-uuid.node.html ================================================ clj-uuid.node documentation

clj-uuid.node

+clock-sequence+

+node-id+

+v1-lsb+

+v6-lsb+

node-id

================================================ FILE: doc/api/clj-uuid.random.html ================================================ clj-uuid.random documentation

clj-uuid.random

bytes

(bytes n)
Generate `n` random bytes.

eight-bits

(eight-bits)
Generate a hard-to-guess long value between 0 and 255

eleven-bits

(eleven-bits)
Generate a hard-to-guess long value between 0 and 2047

long

(long)(long n-bytes)
Generate a long value that is hard to guess. Randomness limited to the
number of bytes.

ten-bits

(ten-bits)
Generate a hard-to-guess long value between 0 and 1023

twelve-bits

(twelve-bits)
Generate a hard-to-guess long value between 0 and 4095
================================================ FILE: doc/api/clj-uuid.util.html ================================================ clj-uuid.util documentation

clj-uuid.util

compile-if

macro

(compile-if exp then else)
Evaluate `exp` and if it returns logical true and doesn't error, expand to
`then` otherwise expand to `else`.
credit: <clojure/src/clj/clojure/core/reducers.clj#L24>

(compile-if (Class/forName "java.util.concurrent.ForkJoinTask")
  (do-cool-stuff-with-fork-join)
  (fall-back-to-executor-services))

import-vars

macro

(import-vars src-ns & var-syms)
Import public vars from src-ns into the current namespace.

java6?

(java6?)

lines-of-file

(lines-of-file file-name)

returning

macro

(returning value & forms)
Compute a return value, then execute other forms for side effects.
Like prog1 in common lisp, or a (do) that returns the first form.

with-temp-file

macro

(with-temp-file f-sym & body)

with-timing

macro

(with-timing & body)
Same as clojure.core/time but returns a vector of a the result of
the code and the milliseconds rather than printing a string. Runs
the code in an implicit do.

wrap-fn

macro

(wrap-fn name args & body)
================================================ FILE: doc/api/css/default.css ================================================ body { font-family: Helvetica, Arial, sans-serif; font-size: 15px; } pre, code { font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; font-size: 9pt; margin: 15px 0; } h1 { font-weight: normal; font-size: 29px; margin: 10px 0 2px 0; padding: 0; } h2 { font-weight: normal; font-size: 25px; } h5.license { margin: 9px 0 22px 0; color: #555; font-weight: normal; font-size: 12px; font-style: italic; } .document h1, .namespace-index h1 { font-size: 32px; margin-top: 12px; } #header, #content, .sidebar { position: fixed; } #header { top: 0; left: 0; right: 0; height: 22px; color: #f5f5f5; padding: 5px 7px; } #content { top: 32px; right: 0; bottom: 0; overflow: auto; background: #fff; color: #333; padding: 0 18px; } .sidebar { position: fixed; top: 32px; bottom: 0; overflow: auto; } .sidebar.primary { background: #e2e2e2; border-right: solid 1px #cccccc; left: 0; width: 250px; } .sidebar.secondary { background: #f2f2f2; border-right: solid 1px #d7d7d7; left: 251px; width: 200px; } #content.namespace-index, #content.document { left: 251px; } #content.namespace-docs { left: 452px; } #content.document { padding-bottom: 10%; } #header { background: #3f3f3f; box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); z-index: 100; } #header h1 { margin: 0; padding: 0; font-size: 18px; font-weight: lighter; text-shadow: -1px -1px 0px #333; } #header h1 .project-version { font-weight: normal; } .project-version { padding-left: 0.15em; } #header a, .sidebar a { display: block; text-decoration: none; } #header a { color: #f5f5f5; } .sidebar a { color: #333; } #header h2 { float: right; font-size: 9pt; font-weight: normal; margin: 4px 3px; padding: 0; color: #bbb; } #header h2 a { display: inline; } .sidebar h3 { margin: 0; padding: 10px 13px 0 13px; font-size: 19px; font-weight: lighter; } .sidebar h3 a { color: #444; } .sidebar h3.no-link { color: #636363; } .sidebar ul { padding: 7px 0 6px 0; margin: 0; } .sidebar ul.index-link { padding-bottom: 4px; } .sidebar li { display: block; vertical-align: middle; } .sidebar li a, .sidebar li .no-link { border-left: 3px solid transparent; padding: 0 10px; white-space: nowrap; } .sidebar li .no-link { display: block; color: #777; font-style: italic; } .sidebar li .inner { display: inline-block; padding-top: 7px; height: 24px; } .sidebar li a, .sidebar li .tree { height: 31px; } .depth-1 .inner { padding-left: 2px; } .depth-2 .inner { padding-left: 6px; } .depth-3 .inner { padding-left: 20px; } .depth-4 .inner { padding-left: 34px; } .depth-5 .inner { padding-left: 48px; } .depth-6 .inner { padding-left: 62px; } .sidebar li .tree { display: block; float: left; position: relative; top: -10px; margin: 0 4px 0 0; padding: 0; } .sidebar li.depth-1 .tree { display: none; } .sidebar li .tree .top, .sidebar li .tree .bottom { display: block; margin: 0; padding: 0; width: 7px; } .sidebar li .tree .top { border-left: 1px solid #aaa; border-bottom: 1px solid #aaa; height: 19px; } .sidebar li .tree .bottom { height: 22px; } .sidebar li.branch .tree .bottom { border-left: 1px solid #aaa; } .sidebar.primary li.current a { border-left: 3px solid #a33; color: #a33; } .sidebar.secondary li.current a { border-left: 3px solid #33a; color: #33a; } .namespace-index h2 { margin: 30px 0 0 0; } .namespace-index h3 { font-size: 16px; font-weight: bold; margin-bottom: 0; } .namespace-index .topics { padding-left: 30px; margin: 11px 0 0 0; } .namespace-index .topics li { padding: 5px 0; } .namespace-docs h3 { font-size: 18px; font-weight: bold; } .public h3 { margin: 0; float: left; } .usage { clear: both; } .public { margin: 0; border-top: 1px solid #e0e0e0; padding-top: 14px; padding-bottom: 6px; } .public:last-child { margin-bottom: 20%; } .members .public:last-child { margin-bottom: 0; } .members { margin: 15px 0; } .members h4 { color: #555; font-weight: normal; font-variant: small-caps; margin: 0 0 5px 0; } .members .inner { padding-top: 5px; padding-left: 12px; margin-top: 2px; margin-left: 7px; border-left: 1px solid #bbb; } #content .members .inner h3 { font-size: 12pt; } .members .public { border-top: none; margin-top: 0; padding-top: 6px; padding-bottom: 0; } .members .public:first-child { padding-top: 0; } h4.type, h4.dynamic, h4.added, h4.deprecated { float: left; margin: 3px 10px 15px 0; font-size: 15px; font-weight: bold; font-variant: small-caps; } .public h4.type, .public h4.dynamic, .public h4.added, .public h4.deprecated { font-size: 13px; font-weight: bold; margin: 3px 0 0 10px; } .members h4.type, .members h4.added, .members h4.deprecated { margin-top: 1px; } h4.type { color: #717171; } h4.dynamic { color: #9933aa; } h4.added { color: #508820; } h4.deprecated { color: #880000; } .namespace { margin-bottom: 30px; } .namespace:last-child { margin-bottom: 10%; } .index { padding: 0; font-size: 80%; margin: 15px 0; line-height: 16px; } .index * { display: inline; } .index p { padding-right: 3px; } .index li { padding-right: 5px; } .index ul { padding-left: 0; } .type-sig { clear: both; color: #088; } .type-sig pre { padding-top: 10px; margin: 0; } .usage code { display: block; color: #008; margin: 2px 0; } .usage code:first-child { padding-top: 10px; } p { margin: 15px 0; } .public p:first-child, .public pre.plaintext { margin-top: 12px; } .doc { margin: 0 0 26px 0; clear: both; } .public .doc { margin: 0; } .namespace-index .doc { margin-bottom: 20px; } .namespace-index .namespace .doc { margin-bottom: 10px; } .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { line-height: 22px; } .markdown li { padding: 2px 0; } .markdown h2 { font-weight: normal; font-size: 25px; margin: 30px 0 10px 0; } .markdown h3 { font-weight: normal; font-size: 20px; margin: 30px 0 0 0; } .markdown h4 { font-size: 15px; margin: 22px 0 -4px 0; } .doc, .public, .namespace .index { max-width: 680px; overflow-x: visible; } .markdown pre > code { display: block; padding: 10px; } .markdown pre > code, .src-link a { border: 1px solid #e4e4e4; border-radius: 2px; } .markdown code:not(.hljs), .src-link a { background: #f6f6f6; } pre.deps { display: inline-block; margin: 0 10px; border: 1px solid #e4e4e4; border-radius: 2px; padding: 10px; background-color: #f6f6f6; } .markdown hr { border-style: solid; border-top: none; color: #ccc; } .doc ul, .doc ol { padding-left: 30px; } .doc table { border-collapse: collapse; margin: 0 10px; } .doc table td, .doc table th { border: 1px solid #dddddd; padding: 4px 6px; } .doc table th { background: #f2f2f2; } .doc dl { margin: 0 10px 20px 10px; } .doc dl dt { font-weight: bold; margin: 0; padding: 3px 0; border-bottom: 1px solid #ddd; } .doc dl dd { padding: 5px 0; margin: 0 0 5px 10px; } .doc abbr { border-bottom: 1px dotted #333; font-variant: none; cursor: help; } .src-link { margin-bottom: 15px; } .src-link a { font-size: 70%; padding: 1px 4px; text-decoration: none; color: #5555bb; } ================================================ FILE: doc/api/css/highlight.css ================================================ /* github.com style (c) Vasily Polovnyov */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hljs-comment, .hljs-quote { color: #998; font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { color: #333; font-weight: bold; } .hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { color: #008080; } .hljs-string, .hljs-doctag { color: #d14; } .hljs-title, .hljs-section, .hljs-selector-id { color: #900; font-weight: bold; } .hljs-subst { font-weight: normal; } .hljs-type, .hljs-class .hljs-title { color: #458; font-weight: bold; } .hljs-tag, .hljs-name, .hljs-attribute { color: #000080; font-weight: normal; } .hljs-regexp, .hljs-link { color: #009926; } .hljs-symbol, .hljs-bullet { color: #990073; } .hljs-built_in, .hljs-builtin-name { color: #0086b3; } .hljs-meta { color: #999; font-weight: bold; } .hljs-deletion { background: #fdd; } .hljs-addition { background: #dfd; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } ================================================ FILE: doc/api/index.html ================================================ clj-uuid 0.2.5-SNAPSHOT

clj-uuid 0.2.5-SNAPSHOT

Released under the Eclipse Public License

A Clojure library for generation and utilization of UUIDs (Universally Unique Identifiers) as described by RFC-9562. This library extends the standard Java UUID class to provide true v1, v6, v7 (time based) and v3/v5 (namespace based), and v8 (user customizable) identifier generation. Additionally, a number of useful utilities are provided to support serialization and manipulation of these UUIDs in a simple, efficient manner.

Installation

To install, add the following dependency to your project or build file:

[danlentz/clj-uuid "0.2.5-SNAPSHOT"]

Namespaces

================================================ FILE: doc/api/js/page_effects.js ================================================ function visibleInParent(element) { var position = $(element).position().top return position > -50 && position < ($(element).offsetParent().height() - 50) } function hasFragment(link, fragment) { return $(link).attr("href").indexOf("#" + fragment) != -1 } function findLinkByFragment(elements, fragment) { return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() } function scrollToCurrentVarLink(elements) { var elements = $(elements); var parent = elements.offsetParent(); if (elements.length == 0) return; var top = elements.first().position().top; var bottom = elements.last().position().top + elements.last().height(); if (top >= 0 && bottom <= parent.height()) return; if (top < 0) { parent.scrollTop(parent.scrollTop() + top); } else if (bottom > parent.height()) { parent.scrollTop(parent.scrollTop() + bottom - parent.height()); } } function setCurrentVarLink() { $('.secondary a').parent().removeClass('current') $('.anchor'). filter(function(index) { return visibleInParent(this) }). each(function(index, element) { findLinkByFragment(".secondary a", element.id). parent(). addClass('current') }); scrollToCurrentVarLink('.secondary .current'); } var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) function scrollPositionId(element) { var directory = window.location.href.replace(/[^\/]+\.html$/, '') return 'scroll::' + $(element).attr('id') + '::' + directory } function storeScrollPosition(element) { if (!hasStorage) return; localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) } function recallScrollPosition(element) { if (!hasStorage) return; $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) } function persistScrollPosition(element) { recallScrollPosition(element) $(element).scroll(function() { storeScrollPosition(element) }) } function sidebarContentWidth(element) { var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) return Math.max.apply(Math, widths) } function calculateSize(width, snap, margin, minimum) { if (width == 0) { return 0 } else { return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) } } function resizeSidebars() { var primaryWidth = sidebarContentWidth('.primary') var secondaryWidth = 0 if ($('.secondary').length != 0) { secondaryWidth = sidebarContentWidth('.secondary') } // snap to grid primaryWidth = calculateSize(primaryWidth, 32, 13, 160) secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) $('.primary').css('width', primaryWidth) $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) if (secondaryWidth > 0) { $('#content').css('left', primaryWidth + secondaryWidth + 2) } else { $('#content').css('left', primaryWidth + 1) } } $(window).ready(resizeSidebars) $(window).ready(setCurrentVarLink) $(window).ready(function() { persistScrollPosition('.primary')}) $(window).ready(function() { $('#content').scroll(setCurrentVarLink) $(window).resize(setCurrentVarLink) }) ================================================ FILE: doc/apples.md ================================================ # Comparative Benchmarks: clj-uuid vs JUG vs uuid-creator vs JDK Apples-to-apples performance comparison of UUID generation across four JVM implementations, measured on the same machine in the same JVM process. ## Libraries | Library | Version | Coordinates | |---|---|---| | **clj-uuid** | 0.2.5-SNAPSHOT | `danlentz/clj-uuid` | | **JUG** (FasterXML) | 5.2.0 | `com.fasterxml.uuid/java-uuid-generator` | | **uuid-creator** (f4b6a3) | 6.1.1 | `com.github.f4b6a3/uuid-creator` | | **JDK** | OpenJDK 25.0.1 | `java.util.UUID` (built-in) | ## Environment - **CPU:** Intel Core i9-9880H @ 2.30 GHz (8 cores / 16 threads) - **RAM:** 32 GB - **OS:** macOS (Darwin 25.2.0, x86_64) - **JVM:** OpenJDK 25.0.1 (Homebrew, mixed mode, sharing) - **Clojure:** 1.12.0 ## Method 500,000 iterations per benchmark after a 50,000 iteration JIT warmup. Each cell is the average ns/op. Source: `test/clj_uuid/compare_bench.clj`. ## Results | Operation | clj-uuid (ns) | JUG 5.2 (ns) | uuid-creator (ns) | JDK (ns) | |--------------------|---------------:|--------------:|-------------------:|---------:| | v1 (time-based) | 100.1 | 58.6 | 72.1 | -- | | v4 (random) | 340.9 | 340.9 | 369.1 | 338.1 | | v5 (SHA1) | 260.5 | 253.8 | 301.9 | -- | | v6 (time-ordered) | 100.0 | 46.4 | 54.9 | -- | | v7 (unix epoch) | 333.2 | 51.1 | 272.2 | -- | | v7nc (fast epoch) | 39.4 | 49.6 | -- | -- | | to-string | 18.8 | -- | -- | 14.4 | | to-byte-array | 13.7 | -- | -- | -- | ## Analysis ### v4 (random) All four implementations cluster between 338-370 ns. The dominant cost is `SecureRandom.nextLong()`, which is common to all of them. clj-uuid delegates directly to `java.util.UUID/randomUUID` and is effectively at parity with the JDK. ### v1 (time-based) clj-uuid is **1.7x** slower than JUG. JUG uses raw `System.currentTimeMillis()` with an internal synchronized counter. clj-uuid uses an `AtomicLong` CAS-based monotonic clock that computes a Gregorian-epoch 100 ns timestamp with inlined bit-field packing. The remaining gap is Clojure `defn` dispatch overhead and Gregorian epoch arithmetic. ### v5 (SHA1, name-based) clj-uuid is **1.03x** slower than JUG -- effectively at parity. Both use `ThreadLocal`. The fused ByteBuffer implementation eliminated intermediate allocations and var lookups in the digest pipeline, closing the gap that previously existed. ### v6 (time-ordered) clj-uuid is **2.2x** slower than JUG. Same clock as v1, with different bit-field ordering for lexical sorting. The gap is wider than v1 because JUG's v6 is exceptionally fast (46 ns), leaving Clojure `defn` dispatch and Gregorian arithmetic as a proportionally larger overhead. ### v7 (unix epoch, CSPRNG) clj-uuid `v7` is **6.5x** slower than JUG. This reflects a **design choice, not a deficiency**. The v7 UUID layout includes a 62-bit random field (`rand_b`). The libraries differ in how they fill it: | Library | rand_b source | Cost | |---|---|---| | **JUG** | Monotonic counter (no per-call randomness) | ~0 ns | | **uuid-creator** | `SecureRandom` | ~280 ns | | **clj-uuid v7** | `SecureRandom` | ~280 ns | RFC 9562 Section 6.9 ("Unguessability") recommends that implementations "utilize a cryptographically secure pseudorandom number generator (CSPRNG) to provide values that are both difficult to predict (unguessable) and have a low likelihood of collision." clj-uuid `v7` and uuid-creator follow the RFC recommendation by calling `SecureRandom` on every generation, paying ~280 ns for the CSPRNG call. ### v7nc (fast epoch, ThreadLocalRandom) -- fastest v7 clj-uuid `v7nc` is **1.26x faster** than JUG (39.4 ns vs 49.6 ns). `v7nc` uses `ThreadLocalRandom` instead of `SecureRandom` and a per-thread monotonic counter instead of a global `AtomicLong`. The per-thread design eliminates CAS contention entirely, and the hot path (same millisecond) requires no random number generation -- just an increment and a UUID constructor call. This is the appropriate choice for applications that prioritize throughput over cryptographic unguessability of the random portion. ### to-string clj-uuid calls `java.util.UUID.toString()` directly. The ~4 ns overhead versus the JDK measurement is Clojure protocol dispatch. ## Summary clj-uuid is at parity with JUG for v4 (random) and v5 (SHA1) generation, and **faster** than JUG for non-cryptographic v7 generation via `v7nc`. JUG is faster for time-based v1 and v6 generation due to pure-Java code paths with no Clojure dispatch overhead. | Category | vs JUG | |---|---| | v7nc (fast epoch) | **1.26x faster** | | v4 (random) | ~1.0x (parity) | | v5 (SHA1) | ~1.0x (parity) | | v1 (time-based) | 1.7x slower | | v6 (time-ordered) | 2.2x slower | | v7 (CSPRNG) | 6.5x slower (by design) | clj-uuid provides both `v7` (CSPRNG, RFC 6.9 compliant) and `v7nc` (ThreadLocalRandom, maximum throughput), letting applications choose the appropriate tradeoff. ================================================ FILE: doc/draft-peabody-dispatch-new-uuid-format-04.html ================================================ New UUID Formats
Internet-Draft new-uuid-format June 2022
Peabody & Davis Expires 25 December 2022 [Page]
Workgroup:
dispatch
Internet-Draft:
draft-peabody-dispatch-new-uuid-format-04
Updates:
4122 (if approved)
Published:
Intended Status:
Standards Track
Expires:
Authors:
BGP. Peabody
K. Davis

New UUID Formats

Abstract

This document presents new Universally Unique Identifier (UUID) formats for use in modern applications and databases.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 25 December 2022.

1. Introduction

Many things have changed in the time since UUIDs were originally created. Modern applications have a need to create and utilize UUIDs as the primary identifier for a variety of different items in complex computational systems, including but not limited to database keys, file names, machine or system names, and identifiers for event-driven transactions.

One area UUIDs have gained popularity is as database keys. This stems from the increasingly distributed nature of modern applications. In such cases, "auto increment" schemes often used by databases do not work well, as the effort required to coordinate unique numeric identifiers across a network can easily become a burden. The fact that UUIDs can be used to create unique, reasonably short values in distributed systems without requiring synchronization makes them a good alternative, but UUID versions 1-5 lack certain other desirable characteristics:

  1. Non-time-ordered UUID versions such as UUIDv4 have poor database index locality. Meaning new values created in succession are not close to each other in the index and thus require inserts to be performed at random locations. The negative performance effects of which on common structures used for this (B-tree and its variants) can be dramatic.

  2. The 100-nanosecond, Gregorian epoch used in UUIDv1 timestamps is uncommon and difficult to represent accurately using a standard number format such as [IEEE754].

  3. Introspection/parsing is required to order by time sequence; as opposed to being able to perform a simple byte-by-byte comparison.

  4. Privacy and network security issues arise from using a MAC address in the node field of Version 1 UUIDs. Exposed MAC addresses can be used as an attack surface to locate machines and reveal various other information about such machines (minimally manufacturer, potentially other details). Additionally, with the advent of virtual machines and containers, MAC address uniqueness is no longer guaranteed.

  5. Many of the implementation details specified in [RFC4122] involve trade offs that are neither possible to specify for all applications nor necessary to produce interoperable implementations.

  6. [RFC4122] does not distinguish between the requirements for generation of a UUID versus an application which simply stores one, which are often different.

Due to the aforementioned issue, many widely distributed database applications and large application vendors have sought to solve the problem of creating a better time-based, sortable unique identifier for use as a database key. This has lead to numerous implementations over the past 10+ years solving the same problem in slightly different ways.

While preparing this specification the following 16 different implementations were analyzed for trends in total ID length, bit Layout, lexical formatting/encoding, timestamp type, timestamp format, timestamp accuracy, node format/components, collision handling and multi-timestamp tick generation sequencing.

  1. [ULID] by A. Feerasta

  2. [LexicalUUID] by Twitter

  3. [Snowflake] by Twitter

  4. [Flake] by Boundary

  5. [ShardingID] by Instagram

  6. [KSUID] by Segment

  7. [Elasticflake] by P. Pearcy

  8. [FlakeID] by T. Pawlak

  9. [Sonyflake] by Sony

  10. [orderedUuid] by IT. Cabrera

  11. [COMBGUID] by R. Tallent

  12. [SID] by A. Chilton

  13. [pushID] by Google

  14. [XID] by O. Poitrey

  15. [ObjectID] by MongoDB

  16. [CUID] by E. Elliott

An inspection of these implementations and the issues described above has led to this document which attempts to adapt UUIDs to address these issues.

2. Terminology

2.1. Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

2.2. Abbreviations

The following abbreviations are used in this document:

UUID
Universally Unique Identifier [RFC4122]
CSPRNG
Cryptographically Secure Pseudo-Random Number Generator
MAC
Media Access Control
MSB
Most Significant Bit
DBMS
Database Management System

3. Summary of Changes

The following UUIDs are hereby introduced:

UUID version 6 (UUIDv6)
A re-ordering of UUID version 1 so it is sortable as an opaque sequence of bytes. Easy to implement given an existing UUIDv1 implementation. See Section 5.1
UUID version 7 (UUIDv7)
An entirely new time-based UUID bit layout sourced from the widely implemented and well known Unix Epoch timestamp source. See Section 5.2
UUID version 8 (UUIDv8)
A free-form UUID format which has no explicit requirements except maintaining backward compatibility. See Section 5.3
Max UUID
A specialized UUID which is the inverse of [RFC4122], Section 4.1.7 See Section 5.4

3.1. changelog

RFC EDITOR PLEASE DELETE THIS SECTION.

draft-04

  • - Fixed bad title in IEEE754 Normative Reference

  • - Fixed bad GMT offset in Test Vector Appendix

  • - Removed MAY in Counters section

  • - Condensed Counter Type into Counter Methods to reduce text

  • - Removed option for random increment along with fixed-length counter

  • - Described how to handle scenario where New UUID less than Old UUID

  • - Allow timestamp increment if counter overflows

  • - Replaced UUIDv8 C code snippet with full generation example

  • - Fixed RFC4086 Reference link

  • - Describe reseeding best practice for CSPRNG

  • - Changed MUST to SHOULD removing requirement for absolute monotonicity

draft-03

  • - Reworked the draft body to make the content more concise

  • - UUIDv6 section reworked to just the reorder of the timestamp

  • - UUIDv7 changed to simplify timestamp mechanism to just millisecond Unix timestamp

  • - UUIDv8 relaxed to be custom in all elements except version and variant

  • - Introduced Max UUID.

  • - Added C code samples in Appendix.

  • - Added test vectors in Appendix.

  • - Version and Variant section combined into one section.

  • - Changed from pseudo-random number generators to cryptographically secure pseudo-random number generator (CSPRNG).

  • - Combined redundant topics from all UUIDs into sections such as Timestamp granularity, Monotonicity and Counters, Collision Resistance, Sorting, and Unguessability, etc.

  • - Split Encoding and Storage into Opacity and DBMS and Database Considerations

  • - Reworked Global Uniqueness under new section Global and Local Uniqueness

  • - Node verbiage only used in UUIDv6 all others reference random/rand instead

  • - Clock sequence verbiage changed simply to counter in any section other than UUIDv6

  • - Added Abbreviations section

  • - Updated IETF Draft XML Layout

  • - Added information about little-endian UUIDs

draft-02

  • - Added Changelog

  • - Fixed misc. grammatical errors

  • - Fixed section numbering issue

  • - Fixed some UUIDvX reference issues

  • - Changed all instances of "motonic" to "monotonic"

  • - Changed all instances of "#-bit" to "# bit"

  • - Changed "proceeding" verbiage to "after" in section 7

  • - Added details on how to pad 32 bit Unix timestamp to 36 bits in UUIDv7

  • - Added details on how to truncate 64 bit Unix timestamp to 36 bits in UUIDv7

  • - Added forward reference and bullet to UUIDv8 if truncating 64 bit Unix Epoch is not an option.

  • - Fixed bad reference to non-existent "time_or_node" in section 4.5.4

draft-01

  • - Complete rewrite of entire document.

  • - The format, flow and verbiage used in the specification has been reworked to mirror the original RFC 4122 and current IETF standards.

  • - Removed the topics of UUID length modification, alternate UUID text formats, and alternate UUID encoding techniques.

  • - Research into 16 different historical and current implementations of time-based universal identifiers was completed at the end of 2020 in attempt to identify trends which have directly influenced design decisions in this draft document (https://github.com/uuid6/uuid6-ietf-draft/tree/master/research)

  • - Prototype implementation have been completed for UUIDv6, UUIDv7, and UUIDv8 in various languages by many GitHub community members. (https://github.com/uuid6/prototypes)

4. Variant and Version Fields

The variant bits utilized by UUIDs in this specification remain in the same octet as originally defined by [RFC4122], Section 4.1.1.

The next table details Variant 10xx (8/9/A/B) and the new versions defined by this specification. A complete guide to all versions within this variant has been includes in Appendix C.1.

Table 1: New UUID variant 10xx (8/9/A/B) versions defined by this specification
Msb0 Msb1 Msb2 Msb3 Version Description
0 1 1 0 6 Reordered Gregorian time-based UUID specified in this document.
0 1 1 1 7 Unix Epoch time-based UUID specified in this document.
1 0 0 0 8 Reserved for custom UUID formats specified in this document

For UUID version 6, 7 and 8 the variant field placement from [RFC4122] are unchanged. An example version/variant layout for UUIDv6 follows the table where M is the version and N is the variant.

00000000-0000-6000-8000-000000000000
00000000-0000-6000-9000-000000000000
00000000-0000-6000-A000-000000000000
00000000-0000-6000-B000-000000000000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Figure 1: UUIDv6 Variant Examples

5. New Formats

The UUID format is 16 octets; the variant bits in conjunction with the version bits described in the next section in determine finer structure.

5.1. UUID Version 6

UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality. It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs. Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead.

Instead of splitting the timestamp into the low, mid and high sections from UUIDv1, UUIDv6 changes this sequence so timestamp bytes are stored from most to least significant. That is, given a 60 bit timestamp value as specified for UUIDv1 in [RFC4122], Section 4.1.4, for UUIDv6, the first 48 most significant bits are stored first, followed by the 4 bit version (same position), followed by the remaining 12 bits of the original 60 bit timestamp.

The clock sequence bits remain unchanged from their usage and position in [RFC4122], Section 4.1.5.

The 48 bit node SHOULD be set to a pseudo-random value however implementations MAY choose to retain the old MAC address behavior from [RFC4122], Section 4.1.6 and [RFC4122], Section 4.5. For more information on MAC address usage within UUIDs see the Section 8

The format for the 16-byte, 128 bit UUIDv6 is shown in Figure 1

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                           time_high                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |           time_mid            |      time_low_and_version     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |clk_seq_hi_res |  clk_seq_low  |         node (0-1)            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                         node (2-5)                            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2: UUIDv6 Field and Bit Layout
time_high:
The most significant 32 bits of the 60 bit starting timestamp. Occupies bits 0 through 31 (octets 0-3)
time_mid:
The middle 16 bits of the 60 bit starting timestamp. Occupies bits 32 through 47 (octets 4-5)
time_low_and_version:
The first four most significant bits MUST contain the UUIDv6 version (0110) while the remaining 12 bits will contain the least significant 12 bits from the 60 bit starting timestamp. Occupies bits 48 through 63 (octets 6-7)
clk_seq_hi_res:
The first two bits MUST be set to the UUID variant (10) The remaining 6 bits contain the high portion of the clock sequence. Occupies bits 64 through 71 (octet 8)
clock_seq_low:
The 8 bit low portion of the clock sequence. Occupies bits 72 through 79 (octet 9)
node:
48 bit spatially unique identifier Occupies bits 80 through 127 (octets 10-15)

With UUIDv6 the steps for splitting the timestamp into time_high and time_mid are OPTIONAL since the 48 bits of time_high and time_mid will remain in the same order. An extra step of splitting the first 48 bits of the timestamp into the most significant 32 bits and least significant 16 bits proves useful when reusing an existing UUIDv1 implementation.

5.2. UUID Version 7

UUID version 7 features a time-ordered value field derived from the widely implemented and well known Unix Epoch timestamp source, the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded. As well as improved entropy characteristics over versions 1 or 6.

Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           unix_ts_ms                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          unix_ts_ms           |  ver  |       rand_a          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                        rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 3: UUIDv7 Field and Bit Layout
unix_ts_ms:
48 bit big-endian unsigned number of Unix epoch timestamp as per Section 6.1.
ver:
4 bit UUIDv7 version set as per Section 4
rand_a:
12 bits pseudo-random data to provide uniqueness as per Section 6.2 and Section 6.6.
var:
The 2 bit variant defined by Section 4.
rand_b:
The final 62 bits of pseudo-random data to provide uniqueness as per Section 6.2 and Section 6.6.

5.3. UUID Version 8

UUID version 8 provides an RFC-compatible format for experimental or vendor-specific use cases. The only requirement is that the variant and version bits MUST be set as defined in Section 4. UUIDv8's uniqueness will be implementation-specific and SHOULD NOT be assumed.

The only explicitly defined bits are the Version and Variant leaving 122 bits for implementation specific time-based UUIDs. To be clear: UUIDv8 is not a replacement for UUIDv4 where all 122 extra bits are filled with random data.

Some example situations in which UUIDv8 usage could occur:

  • An implementation would like to embed extra information within the UUID other than what is defined in this document.

  • An implementation has other application/language restrictions which inhibit the use of one of the current UUIDs.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           custom_a                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          custom_a             |  ver  |       custom_b        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                       custom_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           custom_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 4: UUIDv8 Field and Bit Layout
custom_a:
The first 48 bits of the layout that can be filled as an implementation sees fit.
ver:
The 4 bit version field as defined by Section 4
custom_b:
12 more bits of the layout that can be filled as an implementation sees fit.
var:
The 2 bit variant field as defined by Section 4.
custom_c:
The final 62 bits of the layout immediatly following the var field to be filled as an implementation sees fit.

5.4. Max UUID

The Max UUID is special form of UUID that is specified to have all 128 bits set to 1. This UUID can be thought of as the inverse of Nil UUID defined in [RFC4122], Section 4.1.7

FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF
Figure 5: Max UUID Format

6. UUID Best Practices

The minimum requirements for generating UUIDs are described in this document for each version. Everything else is an implementation detail and up to the implementer to decide what is appropriate for a given implementation. That being said, various relevant factors are covered below to help guide an implementer through the different trade-offs among differing UUID implementations.

6.1. Timestamp Granularity

UUID timestamp source, precision and length was the topic of great debate while creating this specification. As such choosing the right timestamp for your application is a very important topic. This section will detail some of the most common points on this topic.

Reliability:
Implementations SHOULD use the current timestamp from a reliable source to provide values that are time-ordered and continually increasing. Care SHOULD be taken to ensure that timestamp changes from the environment or operating system are handled in a way that is consistent with implementation requirements. For example, if it is possible for the system clock to move backward due to either manual adjustment or corrections from a time synchronization protocol, implementations must decide how to handle such cases. (See Altering, Fuzzing, or Smearing bullet below.)
Source:
UUID version 1 and 6 both utilize a Gregorian epoch timestamp while UUIDv7 utilizes a Unix Epoch timestamp. If other timestamp sources or a custom timestamp epoch are required UUIDv8 SHOULD be leveraged.
Sub-second Precision and Accuracy:
Many levels of precision exist for timestamps: milliseconds, microseconds, nanoseconds, and beyond. Additionally fractional representations of sub-second precision may be desired to mix various levels of precision in a time-ordered manner. Furthermore, system clocks themselves have an underlying granularity and it is frequently less than the precision offered by the operating system. With UUID version 1 and 6, 100-nanoseconds of precision are present while UUIDv7 features fixed millisecond level of precision within the Unix epoch that does not exceed the granularity capable in most modern systems. For other levels of precision UUIDv8 SHOULD be utilized.
Length:
The length of a given timestamp directly impacts how long a given UUID will be valid. That is, how many timestamp ticks can be contained in a UUID before the maximum value for the timestamp field is reached. Care should be given to ensure that the proper length is selected for a given timestamp. UUID version 1 and 6 utilize a 60 bit timestamp and UUIDv7 features a 48 bit timestamp.
Altering, Fuzzing, or Smearing:
Implementations MAY alter the actual timestamp. Some examples included security considerations around providing a real clock value within a UUID, to correct inaccurate clocks or to handle leap seconds. This specification makes no requirement or guarantee about how close the clock value needs to be to actual time.
Padding:
When timestamp padding is required, implementations MUST pad the most significant bits (left-most) bits with zeros. An example is padding the most significant, left-most bits of a 32 bit Unix timestamp with zero's to fill out the 48 bit timestamp in UUIDv7.
Truncating:
Similarly, when timestamps need to be truncated: the lower, least significant bits MUST be used. An example would be truncating a 64 bit Unix timestamp to the least significant, right-most 48 bits for UUIDv7.

6.2. Monotonicity and Counters

Monotonicity is the backbone of time-based sortable UUIDs. Naturally time-based UUIDs from this document will be monotonic due to an embedded timestamp however implementations can guarantee additional monotonicity via the concepts covered in this section.

Additionally, care SHOULD be taken to ensure UUIDs generated in batches are also monotonic. That is, if one-thousand UUIDs are generated for the same timestamp; there is sufficient logic for organizing the creation order of those one-thousand UUIDs. For batch UUID creation implementions MAY utilize a monotonic counter which SHOULD increment for each UUID created during a given timestamp.

For single-node UUID implementations that do not need to create batches of UUIDs, the embedded timestamp within UUID version 1, 6, and 7 can provide sufficient monotonicity guarantees by simply ensuring that timestamp increments before creating a new UUID. For the topic of Distributed Nodes please refer to Section 6.3

Implementations SHOULD choose one method for single-node UUID implementations that require batch UUID creation.

Fixed-Length Dedicated Counter Bits (Method 1):
This references the practice of allocating a specific number of bits in the UUID layout to the sole purpose of tallying the total number of UUIDs created during a given UUID timestamp tick. Positioning of a fixed bit-length counter SHOULD be immediatly after the embedded timestamp. This promotes sortability and allows random data generation for each counter increment. With this method rand_a section of UUIDv7 SHOULD be utilized as fixed-length dedicated counter bits that are incremented by one for every UUID generation. The trailing random bits generated for each new UUID in rand_b can help produce unguessable UUIDs. In the event more counter bits are required the most significant, left-most, bits of rand_b MAY be leveraged as additional counter bits.
Monotonic Random (Method 2):
With this method the random data is extended to also double as a counter. This monotonic random can be thought of as a "randomly seeded counter" which MUST be incremented in the least significant position for each UUID created on a given timestamp tick. UUIDv7's rand_b section SHOULD be utilized with this method to handle batch UUID generation during a single timestamp tick. The increment value for every UUID generation SHOULD be a random integer of any desired length larger than zero. It ensures the UUIDs retain the required level of unguessability characters provided by the underlying entropy. The increment value MAY be one when the amount of UUIDs generated in a particular period of time is important and guessability is not an issue. However, it SHOULD NOT be used by implementations that favor unguessiblity, as the resulting values are easily guessable.

The following sub-topics cover topics related solely with creating reliable fixed-length dedicated counters:

Fixed-Length Dedicated Counter Seeding:
Implementations utilizing fixed-length counter method SHOULD randomly initialize the counter with each new timestamp tick. However, when the timestamp has not incremented; the counter SHOULD be frozen and incremented via the desired increment logic. When utilizing a randomly seeded counter alongside Method 1; the random MAY be regenerated with each counter increment without impacting sortability. The downside is that Method 1 is prone to overflows if a counter of adequate length is not selected or the random data generated leaves little room for the required number of increments. Implementations utilizing fixed-length counter method MAY also choose to randomly initialize a portion counter rather than the entire counter. For example, a 24 bit counter could have the 23 bits in least-significant, right-most, position randomly initialized. The remaining most significant, left-most counter bits are initialized as zero for the sole purpose of guarding against counter rollovers.
Fixed-Length Dedicated Counter Length:
Care MUST be taken to select a counter bit-length that can properly handle the level of timestamp precision in use. For example, millisecond precision SHOULD require a larger counter than a timestamp with nanosecond precision. General guidance is that the counter SHOULD be at least 12 bits but no longer than 42 bits. Care SHOULD also be given to ensure that the counter length selected leaves room for sufficient entropy in the random portion of the UUID after the counter. This entropy helps improve the unguessability characteristics of UUIDs created within the batch.

The following sub-topics cover rollover handling with either type of counter method:

Counter Rollover Guards:
The technique from Fixed-Length Dedicated Counter Seeding which describes allocating a segment of the fixed-length counter as a rollover guard is also helpful to mitigate counter rollover issues. This same technique can be leveraged with Monotonic random counter methods by ensuring the total length of a possible increment in the least significant, right most position is less than the total length of the random being incremented. As such the most significant, left-most, bits can be incremented as rollover guarding.
Counter Rollover Handling:
Counter rollovers SHOULD be handled by the application to avoid sorting issues. The general guidance is that applications that care about absolute monotonicity and sortability SHOULD freeze the counter and wait for the timestamp to advance which ensures monotonicity is not broken. Alternatively, implementations MAY increment the timestamp ahead of the actual time and reinitialize the counter.

Implementations MAY use the following logic to ensure UUIDs featuring embedded counters are monotonic in nature:

  1. Compare the current timestamp against the previously stored timestamp.

  2. If the current timestamp is equal to the previous timestamp; increment the counter according to the desired method.

  3. If the current timestamp is greater than the previous timestamp; re-initialize the desired counter method to the new timestamp and generate new random bytes (if the bytes were frozen or being used as the seed for a monotonic counter).

Implementations SHOULD check if the the currently generated UUID is greater than the previously generated UUID. If this is not the case then any number of things could have occurred. Such as, but not limited to, clock rollbacks, leap second handling or counter rollovers. Applications SHOULD embed sufficient logic to catch these scenarios and correct the problem ensuring the next UUID generated is greater than the previous. To handle this scenario, the general guidance is that application MAY reuse the previous timestamp and increment the previous counter method.

6.3. Distributed UUID Generation

Some implementations MAY desire to utilize multi-node, clustered, applications which involve two or more nodes independently generating UUIDs that will be stored in a common location. While UUIDs already feature sufficient entropy to ensure that the chances of collision are low as the total number of nodes increase; so does the likelihood of a collision. This section will detail the approaches that MAY be utilized by multi-node UUID implementations in distributed environments.

Centralized Registry:
With this method all nodes tasked with creating UUIDs consult a central registry and confirm the generated value is unique. As applications scale the communication with the central registry could become a bottleneck and impact UUID generation in a negative way. Utilization of shared knowledge schemes with central/global registries is outside the scope of this specification.
Node IDs:
With this method, a pseudo-random Node ID value is placed within the UUID layout. This identifier helps ensure the bit-space for a given node is unique, resulting in UUIDs that do not conflict with any other UUID created by another node with a different node id. Implementations that choose to leverage an embedded node id SHOULD utilize UUIDv8. The node id SHOULD NOT be an IEEE 802 MAC address as per Section 8. The location and bit length are left to implementations and are outside the scope of this specification. Furthermore, the creation and negotiation of unique node ids among nodes is also out of scope for this specification.

Utilization of either a Centralized Registry or Node ID are not required for implementing UUIDs in this specification. However implementations SHOULD utilize one of the two aforementioned methods if distributed UUID generation is a requirement.

6.4. Collision Resistance

Implementations SHOULD weigh the consequences of UUID collisions within their application and when deciding between UUID versions that use entropy (random) versus the other components such as Section 6.1 and Section 6.2. This is especially true for distributed node collision resistance as defined by Section 6.3.

There are two example scenarios below which help illustrate the varying seriousness of a collision within an application.

Low Impact
A UUID collision generated a duplicate log entry which results in incorrect statistics derived from the data. Implementations that are not negatively affected by collisions may continue with the entropy and uniqueness provided by the traditional UUID format.
High Impact:
A duplicate key causes an airplane to receive the wrong course which puts people's lives at risk. In this scenario there is no margin for error. Collisions MUST be avoided and failure is unacceptable. Applications dealing with this type of scenario MUST employ as much collision resistance as possible within the given application context.

6.5. Global and Local Uniqueness

UUIDs created by this specification MAY be used to provide local uniqueness guarantees. For example, ensuring UUIDs created within a local application context are unique within a database MAY be sufficient for some implementations where global uniqueness outside of the application context, in other applications, or around the world is not required.

Although true global uniqueness is impossible to guarantee without a shared knowledge scheme; a shared knowledge scheme is not required by UUID to provide uniqueness guarantees. Implementations MAY implement a shared knowledge scheme introduced in Section 6.3 as they see fit to extend the uniqueness guaranteed this specification and [RFC4122].

6.6. Unguessability

Implementations SHOULD utilize a cryptographically secure pseudo-random number generator (CSPRNG) to provide values that are both difficult to predict ("unguessable") and have a low likelihood of collision ("unique"). Care SHOULD be taken to ensure the CSPRNG state is properly reseeded upon state changes, such as process forks, to ensure proper CSPRNG operation. CSPRNG ensures the best of Section 6.4 and Section 8 are present in modern UUIDs.

Advice on generating cryptographic-quality random numbers can be found in [RFC4086]

6.7. Sorting

UUIDv6 and UUIDv7 are designed so that implementations that require sorting (e.g. database indexes) SHOULD sort as opaque raw bytes, without need for parsing or introspection.

Time ordered monotonic UUIDs benefit from greater database index locality because the new values are near each other in the index. As a result objects are more easily clustered together for better performance. The real-world differences in this approach of index locality vs random data inserts can be quite large.

UUIDs formats created by this specification SHOULD be Lexicographically sortable while in the textual representation.

UUIDs created by this specification are crafted with big-ending byte order (network byte order) in mind. If Little-endian style is required a custom UUID format SHOULD be created using UUIDv8.

6.8. Opacity

UUIDs SHOULD be treated as opaque values and implementations SHOULD NOT examine the bits in a UUID to whatever extent is possible. However, where necessary, inspectors should refer to Section 4 for more information on determining UUID version and variant.

6.9. DBMS and Database Considerations

For many applications, such as databases, storing UUIDs as text is unnecessarily verbose, requiring 288 bits to represent 128 bit UUID values. Thus, where feasible, UUIDs SHOULD be stored within database applications as the underlying 128 bit binary value.

For other systems, UUIDs MAY be stored in binary form or as text, as appropriate. The trade-offs to both approaches are as such:

  • Storing as binary requires less space and may result in faster data access.

  • Storing as text requires more space but may require less translation if the resulting text form is to be used after retrieval and thus maybe simpler to implement.

DBMS vendors are encouraged to provide functionality to generate and store UUID formats defined by this specification for use as identifiers or left parts of identifiers such as, but not limited to, primary keys, surrogate keys for temporal databases, foreign keys included in polymorphic relationships, and keys for key-value pairs in JSON columns and key-value databases. Applications using a monolithic database may find using database-generated UUIDs (as opposed to client-generate UUIDs) provides the best UUID monotonicity. In addition to UUIDs, additional identifiers MAY be used to ensure integrity and feedback.

7. IANA Considerations

This document has no IANA actions.

8. Security Considerations

MAC addresses pose inherent security risks and SHOULD not be used within a UUID. Instead CSPRNG data SHOULD be selected from a source with sufficient entropy to ensure guaranteed uniqueness among UUID generation. See Section 6.6 for more information.

Timestamps embedded in the UUID do pose a very small attack surface. The timestamp in conjunction with an embedded counter does signal the order of creation for a given UUID and it's corresponding data but does not define anything about the data itself or the application as a whole. If UUIDs are required for use with any security operation within an application context in any shape or form then [RFC4122] UUIDv4 SHOULD be utilized.

9. Acknowledgements

The authors gratefully acknowledge the contributions of Ben Campbell, Ben Ramsey, Fabio Lima, Gonzalo Salgueiro, Martin Thomson, Murray S. Kucherawy, Rick van Rein, Rob Wilton, Sean Leonard, Theodore Y. Ts'o., Robert Kieffer, sergeyprokhorenko, LiosK As well as all of those in the IETF community and on GitHub to who contributed to the discussions which resulted in this document.

10. Normative References

[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC4122]
Leach, P., Mealling, M., and R. Salz, "A Universally Unique IDentifier (UUID) URN Namespace", RFC 4122, DOI 10.17487/RFC4122, , <https://www.rfc-editor.org/info/rfc4122>.
[RFC4086]
Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", RFC 4086, DOI 10.17487/RFC4086, , <https://www.rfc-editor.org/info/rfc4086>.

11. Informative References

[LexicalUUID]
Twitter, "A Scala client for Cassandra", commit f6da4e0, , <https://github.com/twitter-archive/cassie>.
[Snowflake]
Twitter, "Snowflake is a network service for generating unique ID numbers at high scale with some simple guarantees.", Commit b3f6a3c, , <https://github.com/twitter-archive/snowflake/releases/tag/snowflake-2010>.
[Flake]
Boundary, "Flake: A decentralized, k-ordered id generation service in Erlang", Commit 15c933a, , <https://github.com/boundary/flake>.
[ShardingID]
Instagram Engineering, "Sharding & IDs at Instagram", , <https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c>.
[KSUID]
Segment, "K-Sortable Globally Unique IDs", Commit bf376a7, , <https://github.com/segmentio/ksuid>.
[Elasticflake]
Pearcy, P., "Sequential UUID / Flake ID generator pulled out of elasticsearch common", Commit dd71c21, , <https://github.com/ppearcy/elasticflake>.
[FlakeID]
Pawlak, T., "Flake ID Generator", Commit fcd6a2f, , <https://github.com/T-PWK/flake-idgen>.
[Sonyflake]
Sony, "A distributed unique ID generator inspired by Twitter's Snowflake", Commit 848d664, , <https://github.com/sony/sonyflake>.
[orderedUuid]
Cabrera, IT., "Laravel: The mysterious "Ordered UUID"", , <https://itnext.io/laravel-the-mysterious-ordered-uuid-29e7500b4f8>.
[COMBGUID]
Tallent, R., "Creating sequential GUIDs in C# for MSSQL or PostgreSql", Commit 2759820, , <https://github.com/richardtallent/RT.Comb>.
[ULID]
Feerasta, A., "Universally Unique Lexicographically Sortable Identifier", Commit d0c7170, , <https://github.com/ulid/spec>.
[SID]
Chilton, A., "sid : generate sortable identifiers", Commit 660e947, , <https://github.com/chilts/sid>.
[pushID]
Google, "The 2^120 Ways to Ensure Unique Identifiers", , <https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html>.
[XID]
Poitrey, O., "Globally Unique ID Generator", Commit efa678f, , <https://github.com/rs/xid>.
[ObjectID]
MongoDB, "ObjectId - MongoDB Manual", <https://docs.mongodb.com/manual/reference/method/ObjectId/>.
[CUID]
Elliott, E., "Collision-resistant ids optimized for horizontal scaling and performance.", Commit 215b27b, , <https://github.com/ericelliott/cuid>.
[IEEE754]
IEEE, "IEEE Standard for Floating-Point Arithmetic.", Series 754-2019, , <https://standards.ieee.org/ieee/754/6210/>.

Appendix A. Example Code

A.1. Creating a UUIDv6 Value

This section details a function in C which converts from a UUID version 1 to version 6:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <uuid/uuid.h>

/* Converts UUID version 1 to version 6 in place. */
void uuidv1tov6(uuid_t u) {

  uint64_t ut;
  unsigned char *up = (unsigned char *)u;

  // load ut with the first 64 bits of the UUID
  ut = ((uint64_t)ntohl(*((uint32_t*)up))) << 32;
  ut |= ((uint64_t)ntohl(*((uint32_t*)&up[4])));

  // dance the bit-shift...
  ut =
    ((ut >> 32) & 0x0FFF) | // 12 least significant bits
    (0x6000) | // version number
    ((ut >> 28) & 0x0000000FFFFF0000) | // next 20 bits
    ((ut << 20) & 0x000FFFF000000000) | // next 16 bits
    (ut << 52); // 12 most significant bits

  // store back in UUID
  *((uint32_t*)up) = htonl((uint32_t)(ut >> 32));
  *((uint32_t*)&up[4]) = htonl((uint32_t)(ut));

}
Figure 6: UUIDv6 Function in C

A.2. Creating a UUIDv7 Value

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

// ...

// csprng data source
FILE *rndf;
rndf = fopen("/dev/urandom", "r");
if (rndf == 0) {
    printf("fopen /dev/urandom error\n");
    return 1;
}

// ...

// generate one UUIDv7E
uint8_t u[16];
struct timespec ts;
int ret;

ret = clock_gettime(CLOCK_REALTIME, &ts);
if (ret != 0) {
    printf("clock_gettime error: %d\n", ret);
    return 1;
}

uint64_t tms;

tms = ((uint64_t)ts.tv_sec) * 1000;
tms += ((uint64_t)ts.tv_nsec) / 1000000;

memset(u, 0, 16);

fread(&u[6], 10, 1, rndf); // fill everything after the timestamp with random bytes

*((uint64_t*)(u)) |= htonll(tms << 16); // shift time into first 48 bits and OR into place

u[8] = 0x80 | (u[8] & 0x3F); // set variant field, top two bits are 1, 0
u[6] = 0x70 | (u[6] & 0x0F); // set version field, top four bits are 0, 1, 1, 1
Figure 7: UUIDv7 Function in C

A.3. Creating a UUIDv8 Value

UUIDv8 will vary greatly from implementation to implementation.

The following example utilizes:

  • 32 bit custom-epoch timestamp (seconds elapsed since 2020-01-01 00:00:00 UTC)

  • 16 bit exotic resolution (~15 microsecond) subsecond timestamp encoded using the fractional representation

  • 58 bit random number

  • 8 bit application-specific unique node ID

  • 8 bit rolling sequence number

#include <stdint.h>
#include <time.h>

int get_random_bytes(uint8_t *buffer, int count) {
  // ...
}

int generate_uuidv8(uint8_t *uuid, uint8_t node_id) {
  struct timespec tp;
  if (clock_gettime(CLOCK_REALTIME, &tp) != 0)
    return -1; // real-time clock error

  // 32 bit biased timestamp (seconds elapsed since 2020-01-01 00:00:00 UTC)
  uint32_t timestamp_sec = tp.tv_sec - 1577836800;
  uuid[0] = timestamp_sec >> 24;
  uuid[1] = timestamp_sec >> 16;
  uuid[2] = timestamp_sec >> 8;
  uuid[3] = timestamp_sec;

  // 16 bit subsecond fraction (~15 microsecond resolution)
  uint16_t timestamp_subsec = ((uint64_t)tp.tv_nsec << 16) / 1000000000;
  uuid[4] = timestamp_subsec >> 8;
  uuid[5] = timestamp_subsec;

  // 58 bit random number and required ver and var fields
  if (get_random_bytes(&uuid[6], 8) != 0)
    return -1; // random number generator error
  uuid[6] = 0x80 | (uuid[6] & 0x0f);
  uuid[8] = 0x80 | (uuid[8] & 0x3f);

  // 8 bit application-specific node ID to guarantee application-wide uniqueness
  uuid[14] = node_id;

  // 8 bit rolling sequence number to help ensure process-wide uniqueness
  static uint8_t sequence = 0;
  uuid[15] = sequence++; // NOTE: unprotected from race conditions

  return 0;
}
Figure 8: UUIDv8 Function in C

Appendix B. Test Vectors

Both UUIDv1 and UUIDv6 test vectors utilize the same 60 bit timestamp: 0x1EC9414C232AB00 (138648505420000000) Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00

Both UUIDv1 and UUIDv6 utilize the same values in clk_seq_hi_res, clock_seq_low, and node. All of which have been generated with random data.

# Unix Nanosecond precision to Gregorian 100-nanosecond intervals
gregorian_100_ns = (Unix_64_bit_nanoseconds / 100) + gregorian_Unix_offset

# Gregorian to Unix Offset:
# The number of 100-ns intervals between the
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
# gregorian_Unix_offset = 0x01b21dd213814000 or 122192928000000000

# Unix 64 bit Nanosecond Timestamp:
# Unix NS: Tuesday, February 22, 2022 2:22:22 PM GMT-05:00
# Unix_64_bit_nanoseconds = 0x16D6320C3D4DCC00 or 1645557742000000000

# Work:
# gregorian_100_ns = (1645557742000000000 / 100) + 122192928000000000
# (138648505420000000 - 122192928000000000) * 100 = Unix_64_bit_nanoseconds

# Final:
# gregorian_100_ns = 0x1EC9414C232AB00 or 138648505420000000

# Original: 000111101100100101000001010011000010001100101010101100000000
# UUIDv1:   11000010001100101010101100000000|1001010000010100|0001|000111101100
# UUIDv6:   00011110110010010100000101001100|0010001100101010|0110|101100000000
Figure 9: Test Vector Timestamp Pseudo-code

B.1. Example of a UUIDv6 Value

----------------------------------------------
field                 bits    value_hex
----------------------------------------------
time_low              32      0xC232AB00
time_mid              16      0x9414
time_hi_and_version   16      0x11EC
clk_seq_hi_res         8      0xB3
clock_seq_low          8      0xC8
node                  48      0x9E6BDECED846
----------------------------------------------
total                128
----------------------------------------------
final_hex: C232AB00-9414-11EC-B3C8-9E6BDECED846
Figure 10: UUIDv1 Example Test Vector
-----------------------------------------------
field                 bits    value_hex
-----------------------------------------------
time_high              32      0x1EC9414C
time_mid               16      0x232A
time_low_and_version   16      0x6B00
clk_seq_hi_res          8      0xB3
clock_seq_low           8      0xC8
node                   48      0x9E6BDECED846
-----------------------------------------------
total                 128
-----------------------------------------------
final_hex: 1EC9414C-232A-6B00-B3C8-9E6BDECED846
Figure 11: UUIDv6 Example Test Vector

B.2. Example of a UUIDv7 Value

This example UUIDv7 test vector utilizes a well-known 32 bit Unix epoch with additional millisecond precision to fill the first 48 bits

rand_a and rand_b are filled with random data.

The timestamp is Tuesday, February 22, 2022 2:22:22.00 PM GMT-05:00 represented as 0x17F22E279B0 or 1645557742000

-------------------------------
field      bits    value
-------------------------------
unix_ts_ms   48    0x17F22E279B0
var           4    0x7
rand_a       12    0xCC3
var           2    b10
rand_b       62    0x18C4DC0C0C07398F
-------------------------------
total       128
-------------------------------
final: 017F22E2-79B0-7CC3-98C4-DC0C0C07398F
Figure 12: UUIDv7 Example Test Vector

B.3. Example of a UUIDv8 Value

This example UUIDv8 test vector utilizes a well-known 64 bit Unix epoch with nanosecond precision, truncated to the least-significant, right-most, bits to fill the first 48 bits through version.

The next two segments of custom_b and custom_c are are filled with random data.

Timestamp is Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00 represented as 0x16D6320C3D4DCC00 or 1645557742000000000

It should be noted that this example is just to illustrate one scenario for UUIDv8. Test vectors will likely be implementation specific and vary greatly from this simple example.

-------------------------------
field      bits    value
-------------------------------
custom_a     48    0x320C3D4DCC00
ver           4    0x8
custom_b     12    0x75B
var           2    b10
custom_c     62    0xEC932D5F69181C0
-------------------------------
total       128
-------------------------------
final: 320C3D4D-CC00-875B-8EC9-32D5F69181C0
Figure 13: UUIDv8 Example Test Vector

Appendix C. Version and Variant Tables

C.1. Variant 10xx Versions

Table 2: All UUID variant 10xx (8/9/A/B) version definitions.
Msb0 Msb1 Msb2 Msb3 Version Description
0 0 0 0 0 Unused
0 0 0 1 1 The Gregorian time-based UUID from in [RFC4122], Section 4.1.3
0 0 1 0 2 DCE Security version, with embedded POSIX UIDs from [RFC4122], Section 4.1.3
0 0 1 1 3 The name-based version specified in [RFC4122], Section 4.1.3 that uses MD5 hashing.
0 1 0 0 4 The randomly or pseudo-randomly generated version specified in [RFC4122], Section 4.1.3.
0 1 0 1 5 The name-based version specified in [RFC4122], Section 4.1.3 that uses SHA-1 hashing.
0 1 1 0 6 Reordered Gregorian time-based UUID specified in this document.
0 1 1 1 7 Unix Epoch time-based UUID specified in this document.
1 0 0 0 8 Reserved for custom UUID formats specified in this document.
1 0 0 1 9 Reserved for future definition.
1 0 1 0 10 Reserved for future definition.
1 0 1 1 11 Reserved for future definition.
1 1 0 0 12 Reserved for future definition.
1 1 0 1 13 Reserved for future definition.
1 1 1 0 14 Reserved for future definition.
1 1 1 1 15 Reserved for future definition.

Authors' Addresses

Brad G. Peabody
Kyzer R. Davis
================================================ FILE: doc/java-and-unsigned-numbers.html ================================================ Java and unsigned int, unsigned short, unsigned byte, unsigned long, etc. (Or rather, the lack thereof)

Java and unsigned int, unsigned short, unsigned byte, unsigned long, etc. (Or rather, the lack thereof)

Written by Sean R. Owens, sean at guild dot net, released to the public domain. Share and enjoy. Since some people argue that it is impossible to release software to the public domain, you are also free to use this code under any version of the GPL, LPGL, or BSD licenses, or contact me for use of another license.

If you are wondering if this means that you are allowed to use this in a class about java and/or unsigned types, you are allowed to use this in a class

http://darksleep.com/player

For other fine writings on Java and other subjects, check out notablog at http://darksleep.com/notablog

( Evangelos Haleplidis has been so kind as to write this up as a set of classes that may be easily used to read unsigned types. You can download a pre-compiled jar, a zip of the source, and a zip of the API docs from this website. )

Note: java.nio.ByteBuffer / java.nio.ByteOrder

If all you want to know is how to read a stream of bytes where you know the endianness, then take a close look at java.nio.ByteOrder and java.nio.ByteBuffer and it's order() methods, as well as it's get and put methods. If you want to roll your own and/or want to know how it works, read on.

So what's up with unsigned types in Java?

In languages like C and C++, there exist a variety of sizes of integers, char, short, int, long. (char isn't really an integer, but you can use it like an integer and most people use it in C for really small integers.) On most 32 bit systems, these correspond to 1 byte, 2 byte, 4 byte, and 8 byte numbers. But note, do not make the mistake of assuming this is true everywhere. On the other hand, Java, since it is designed to be a portable platform, guarantees that no matter what platform you are running on, a 'byte' is 1 byte, a 'short' is 2 bytes, an 'int' is 4 bytes, and a 'long' is 8 bytes. However, C also provides 'unsigned' types of each of its integers, which Java does not. I find it incredibly annoying that Java doesn't deal with unsigned types, considering the huge number of hardware interfaces, network protocols, and file formats that use them.

(The only exception is that Java does provide the 'char' type, which is a 2 byte representation of unicode, instead of the C 'char' type, which is 1 byte ASCII. Java's 'char' also can be used as an unsigned short, i.e. it represents numbers from 0 up to 2^16. The only gotchas are, weird things will happen if you try to assign it to a short, and if you try to print it, you'll get the unicode character it represents, instead of the integer value. If you need to print the value of a char, cast it to int first.)

So how do we work around this lack of unsigned types?

Well, you're probably not going to like this...

The answer is, you use the signed types that are larger than the original unsigned type. I.e. use a short to hold an unsigned byte, use a long to hold an unsigned int. (And use a char to hold an unsigned short.) Yeah, this kinda sucks because now you're using twice as much memory, but there really is no other solution. (Also bear in mind, access to longs is not guaranteed to be atomic - although if you're using multiple threads, you really should be using synchronization anyway.)

So...

Have you noticed that you seem to have this bad habit of starting your headings with "So"?

Hmm, good point. So let's stop doing that.

How do we get the values into and out of 'unsigned' form?

If someone sends you a bunch of bytes over the network (or you read them from a file on the disk, or whatever) that include some unsigned numbers, you need to do a few gymnastics to convert them into the larger java types.

One issue that I really need to go into here is byte order aka endianness, but for the moment we're going to assume (or hope) that whatever you're trying to read is in what is referred to as 'network byte order' aka 'big endian' aka Java's standard endianness.

Reading from network byte order

Let us assume we're starting with a byte array, just to keep things simple, and we want to read an unsigned byte, then an unsigned short, then an unsigned int.
 
	short anUnsignedByte = 0;
	char anUnsignedShort = 0;
	long anUnsignedInt = 0;

        int firstByte = 0;
        int secondByte = 0;
        int thirdByte = 0;
        int fourthByte = 0;

	byte buf[] = getMeSomeData();
	// Check to make sure we have enough bytes
	if(buf.length < (1 + 2 + 4)) 
	  doSomeErrorHandling();
	int index = 0;
	
        firstByte = (0x000000FF & ((int)buf[index]));
	index++;
	anUnsignedByte = (short)firstByte;

        firstByte = (0x000000FF & ((int)buf[index]));
        secondByte = (0x000000FF & ((int)buf[index+1]));
	index = index+2;
	anUnsignedShort  = (char) (firstByte << 8 | secondByte);

        firstByte = (0x000000FF & ((int)buf[index]));
        secondByte = (0x000000FF & ((int)buf[index+1]));
        thirdByte = (0x000000FF & ((int)buf[index+2]));
        fourthByte = (0x000000FF & ((int)buf[index+3]));
        index = index+4;
	anUnsignedInt  = ((long) (firstByte << 24
	                | secondByte << 16
                        | thirdByte << 8
                        | fourthByte))
                       & 0xFFFFFFFFL;
      

OK, now that all looks a little complicated. But really it is straightforward. First off, you see a lot of

	0x000000FF & (int)buf[index]

What is going on there is that we are promoting a (signed) byte to int, and then doing a bitwise AND operation on it to wipe out everything but the first 8 bits. Because Java treats the byte as signed, if its unsigned value is above > 127, the sign bit will be set (strictly speaking it is not "the sign bit" since numbers are encoded in two's complement) and it will appear to java to be negative. When it gets promoted to int, bits 0 through 7 will be the same as the byte, and bits 8 through 31 will be set to 1. So the bitwise AND with 0x000000FF clears out all of those bits. Note that this could have been written more compactly as;

	0xFF & buf[index]
Java assumes the leading zeros for 0xFF, and the bitwise & operator automatically promotes the byte to int. But I wanted to be a tad more explicit about it.

The next thing you'll see a lot of is the <<, or bitwise shift left operator. It's shifting the bit patterns of the left int operand left by as many bits as you specify in the right operand So, if you have some int foo = 0x000000FF, then (foo << 8) == 0x0000FF00, and (foo << 16) == 0x00FF0000.

The last piece of the puzzle is |, the bitwise OR operator. Assume you've loaded both bytes of an unsigned short into separate integers, so you have 0x00000012 and 0x00000034. Now you shift one of the bytes by 8 bits to the left, so you have 0x00001200 and 0x00000034, but you still need to stick them together. So you bitwise OR them, and you have 0x00001200 | 0x00000034 = 0x00001234. This is then stored into Java's 'char' type.

That's basically it, except that in the case of the unsigned int, you have to now store it into the long, and you're back up against that sign extension problem we started with. No problem, just cast your int to long, then do the bitwise AND with 0xFFFFFFFFL. (Note the trailing L to tell Java this is a literal of type 'long' integer.)

Writing to network byte order

Lets assume now that we want to write the values we read above back into the same buffer, but whereas we read an unsigned byte, then an unsigned short, then an unsigned int, now we instead (for some arcane reason) want to write it out as unsigned int, unsigned short, unsigned byte.
	buf[0] = (anUnsignedInt & 0xFF000000L) >> 24;
	buf[1] = (anUnsignedInt & 0x00FF0000L) >> 16;
	buf[2] = (anUnsignedInt & 0x0000FF00L) >> 8;
	buf[3] = (anUnsignedInt & 0x000000FFL);

	buf[4] = (anUnsignedShort & 0xFF00) >> 8;
	buf[5] = (anUnsignedShort & 0x00FF);

	buf[6] = (anUnsignedByte & 0xFF);
      

Whats all this about Byte Order? (or Endianness?)

What does it mean? Why do I care? (And what about Network Order?)

For the record, Java is 'big endian' also known as 'network byte order'. Intel x86 processors are 'little endian'. (Unless it's a Java program running on an Intel chip.) A data file created by an x86 system is likely but not required to be little endian. A data file created by a Java program is likely but not required to be big endian. Any system can output data in whatever format it wants and it's entirely possible the one you are dealing with is being careful to write data in 'network byte order' aka 'big endian'.

What does Byte Order / Endianness mean?

"Byte order" or "Endianness" refers to how a particular computer architecture stores numbers in memory. Typically a particular computer is either "big endian" or "little endian". (By the way, according to Wikipedia, these terms derive from a story in Gullivers Travels.)

You care about this because if you assume data is written in say, 'big endian' format, and write your code accordingly, and it turns out the data is in 'little endian' format, you're going to get garbage. And vice versa for assuming 'little endian' for data written in 'big endian'.

Every number, whether expressed as digits, i.e. the number 500,000,007 or as bytes, i.e. the four byte integer (as hexadecimal) 0x1DCD6507, can be thought of as a string of digits. And this string of digits can be thought of as having a starting, or left, 'end', and a finishing, or 'right' end. In English, the first digit in a number is always the biggest (or most significant) digit - i.e. the 5 in 500,000,007 actually represents the value 500,000,000. The last digit in a number is always the littlest (or least significant) digit - i.e. the 7 in 500,000,007 represents the value 7.

When we talk about Endianness, or byte order, we are talking about the order in which we write down the digits. Do we write the biggest (most significant) digit first, and then the next biggest, etc etc, until we reach the littlest (least significant) digit? Or do we start with the littlest digit first? In English we always write the biggest one first, hence English could be described as "big endian". (There are human languages where they do it the other way around. My friend Raichle suggests Hebrew as an example of one such language, but she's not really sure.)

In the example above, the value 50,0000,007, in hexadecimal, is 0x1DCD6507. And if we break it up into four separate bytes, the byte values are 0x1D, 0xCD, 0x65, and 0x07. In decimal those four bytes are (respectively) 29, 205, 101, and 7. The biggest byte is the first one, 29, which represents the value 29 * 256 * 256 * 256 = 486539264. The second biggest is the second byte, 205, which represents the value 205 * 256 * 256 = 13434880. The third biggest byte is 101, which represents the value 101 * 256 = 25856, and the littlest byte is 7, which represents the value 7 * 1 = 7. The values 486539264 + 13434880 + 25856 + 7 = 500,000,007.

When the computer stores those four bytes into four locations in memory, let's say into memory addresses 2056, 2057, 2058, and 2059, the question is, what order does it store them in? It might put 29 in 2056, 205 in 2057, 101 in 2058, and 7 in 2059, just like you'd write down the number in English. If it did, then we would say that computer is "big endian". However, a different computer architecture might just as easily store those bytes by putting 7 in 2056, 101 in 2057, 205 in 2058, and 29 in 2059. If the computer stored the bytes in that manner then we would say that it was "little endian".

Note that this also applies to how the computer stores 2 byte shorts and 8 byte longs. Also note that the "biggest" byte is also referred to as the "Most Significant Byte" (MSB) and the "littlest" byte is also referred to as the "Least Significant Byte" (LSB), so you will often times see those two phrases or acronyms popping up.

Ok, fine, so why do I care about Byte Order?

Well, it depends. A lot of the time you don't have to. In pure Java, byte order is always the same no matter what platform you're on, so no big deal, you can forget about it, as long as you're dealing with pure Java.

But what happens when you're dealing with data generated by other languages? Well, now you have to pay attention to things a bit. You have to make sure that you decode the bytes in the same order as they were originally encoded in, and likewise make sure that you encode the bytes in the same order as they will be decoded in. If you're lucky this will be specified somewhere in the spec or API for whatever protocol or file format you're dealing with. If you're unlucky... well. Good luck.

The biggest problem is simply remembering what your byte order is, and knowing what the byte order of the data you are reading in is. If they're not the same, you need to make sure you re-order them correctly, or in the case of dealing with unsigned numbers like above, you need to make sure you put the correct bytes into the correct parts of the integer/short/long.

What the heck is Network Order?

When the Internet Protocol (IP) was designed, "big endian" byte order was designated as "network order". All numeric values in IP packet headers are stored in "network order". The byte order of the computer creating the packets is referred to as "host order", although it may in fact be the same as "network order" if your host is a "big endian" machine. So that's why you'll some times see Java endianness/byte order referred to as "network order", since both Java byte order and network byte order are "big endian"

Why no unsigned types?

Why doesn't Java provide unsigned types? Good question. I always thought it was kind of strange, especially since there are lots of network protocols out there that use unsigned types. I first ran into this back in 1999. At the time I did a lot of digging on the web (google wasn't as good then as it is now) because I had a hard time believing this was true, but eventually I ran across an interview with one of the inventers of Java (was it Gosling? I can't remember. I wish I'd saved a copy of that web page). where the designer came right out and said something like "Hey, unsigned types make everything more complicated and nobody really needs them anyway, so we left them out." Here's another web page with an interview with James Gosling that might give you an idea of where he's coming from:

http://www.gotw.ca/publications/c_family_interview.htm

> Q: Programmers often talk about the advantages and disadvantages of
> programming in a "simple language."  What does that phrase mean to
> you, and is [C/C++/Java] a simple language in your view?
> 
> Ritchie: [deleted for brevity]
> 
> Stroustrup: [deleted for brevity]
> 
> Gosling: For me as a language designer, which I don't really count
> myself as these days, what "simple" really ended up meaning was could
> I expect J. Random Developer to hold the spec in his head. That
> definition says that, for instance, Java isn't -- and in fact a lot of
> these languages end up with a lot of corner cases, things that nobody
> really understands. Quiz any C developer about unsigned, and pretty
> soon you discover that almost no C developers actually understand what
> goes on with unsigned, what unsigned arithmetic is. Things like that
> made C complex. The language part of Java is, I think, pretty
> simple. The libraries you have to look up.

On the other hand.... According to http://www.artima.com/weblogs/viewpost.jsp?thread=7555

> Once Upon an Oak ...
> by Heinz Kabutz
> July 15, 2003
>
...
> Trying to fill my gaps of Java's history, I started digging around on
> Sun's website, and eventually stumbled across the Oak Language
> Specification for Oak version 0.2. Oak was the original name of what
> is now commonly known as Java, and this manual is the oldest manual
> available for Oak (i.e. Java).
...
> Unsigned integer values (Section 3.1)
> 
> The specification says: "The four integer types of widths of 8, 16, 32
> and 64 bits, and are signed unless prefixed by the unsigned modifier.
> 
> In the sidebar it says: "unsigned isn't implemented yet; it might
> never be." How right you were.
The Oak Language Specification for Oak version 0.2 can be downloaded in as postscript from https://duke.dev.java.net/green/OakSpec0.2.ps or zipped PDF from http://www.me.umn.edu/~shivane/blogs/cafefeed/resources/14-jun-2007/OakSpec0.2.zip (assuming these links haven't broken...)
Thanks goes to Derrick Coetzee (dc at moonflare dot com) for his advice and support.
Last modified: Sat Apr 7 01:48:45 UTC 2012 ================================================ FILE: doc/perf-analysis.md ================================================ # Performance Comparison: clj-uuid-old vs clj-uuid This document provides a thorough analysis of the performance characteristics of `clj-uuid-old` (based on `bitmop`) versus `clj-uuid` (based on `bitmop2`) for every UUID type and supporting operation. ## Architecture Overview | Layer | clj-uuid-old | clj-uuid | |--------------|---------------------------|------------------------------| | Primitives | `clj-uuid.bitmop` | `clj-uuid.bitmop2` | | Top-level NS | `clj-uuid-old` | `clj-uuid` | | Byte model | Manual shift/mask loops | `java.nio.ByteBuffer` | | Digest cache | ThreadLocal MessageDigest | ThreadLocal MessageDigest | | Shared deps | `clock`, `node`, `random`, `constants` | same | Both namespaces produce identical `java.util.UUID` output values. The difference lies entirely in how bitwise operations are performed internally. ### Core Primitive Change The fundamental performance change is replacing **manual 8-iteration shift/mask loops** with **single native ByteBuffer operations**: | Operation | bitmop (clj-uuid-old) | bitmop2 (clj-uuid) | |-----------------|-----------------------------------------|-----------------------------------------| | `bytes->long` | 8-iteration `dpb` loop | Single `ByteBuffer.getLong` | | `long->bytes` | 8-iteration `ldb` + `sb8` loop | Single `ByteBuffer.putLong` | | `assemble-bytes`| 8-iteration `dpb` loop over sequence | Direct shift-accumulation loop | | `hex` | `map ub8` + `long->bytes` + `map octet-hex` + `apply str` | `long->bytes` + `StringBuilder` direct append | | `mask-offset` | O(offset) loop scanning for lowest set bit | `Long/numberOfTrailingZeros` (single `TZCNT` instruction) | | `mask-width` | O(width) loop counting contiguous set bits | `Long/bitCount` (single `POPCNT` instruction) | | `bit-count` | O(64) loop counting all set bits | `Long/bitCount` (single `POPCNT` instruction) | Operations that are **unchanged** between the two (they operate on longs directly and don't involve byte conversion): - `mask` -- identical implementation - `ldb`, `dpb` -- identical implementation (but faster in bitmop2 due to O(1) `mask-offset`) - `ub*`, `sb*` byte casts -- identical implementation - `octet-hex` -- identical implementation - `expt2`, `pphex` -- identical implementation --- ## Per-Operation Primitive Benchmarks The following analysis is based on the `bitmop2_test.clj` benchmark framework (100K iterations with JIT warmup). ### `long->bytes` Converts a 64-bit long to an 8-byte big-endian array. | Impl | Approach | Ops per call | |--------|----------------------------------------------|--------------| | bitmop | Loop 8 times: `ldb(mask(8, j*8), x)` + `sb8` + `aset-byte` | 8x ldb + 8x sb8 + 8x aset-byte = ~40 ops | | bitmop2| `ByteBuffer.putLong(offset, x)` | 1 native call | **Measured speedup: 6-27x** The bitmop version executes 8 loop iterations, each calling `mask-offset` (a `cond` + bit-shift loop), `ldb` (2 shifts + 1 AND), `sb8` (2 casts + 1 AND), and `aset-byte`. The bitmop2 version delegates to a single JVM intrinsic `putLong` that writes 8 bytes in one native operation. ### `bytes->long` Reads 8 bytes from a byte array into a 64-bit long. | Impl | Approach | Ops per call | |--------|----------------------------------------------|--------------| | bitmop | Loop 8 times: `aget` + `dpb(mask(8, j*8), tot, byte)` | 8x aget + 8x dpb + 8x mask = ~48 ops | | bitmop2| `ByteBuffer.getLong(offset)` | 1 native call | **Measured speedup: 5-10x** Same pattern as `long->bytes` in reverse. Each bitmop iteration calls `dpb` (which internally calls `mask-offset`, performs 2 shifts, 2 ANDs, and 1 OR). The bitmop2 version is a single native read. ### `assemble-bytes` Assembles a sequence of 8 bytes into a long. | Impl | Approach | Ops per call | |--------|----------------------------------------------|--------------| | bitmop | Loop 8 times: `dpb(mask(8, k*8), tot, byte)` from seq | 8x dpb + seq traversal | | bitmop2| Direct shift-accumulation: `(bit-or (bit-shift-left tot 8) byte)` | 8x shift+or + seq traversal | **Measured speedup: 2.2-2.6x** The bitmop2 version uses a pure arithmetic accumulation loop (`bit-shift-left` + `bit-or`) instead of bitmop's `dpb`+`mask` per iteration. This avoids the function call overhead of `mask`, `mask-offset`, and `dpb` on each byte, with zero allocation. ### `hex` Converts a long to a 16-character hexadecimal string. | Impl | Approach | Allocs per call | |--------|----------------------------------------------|-----------------| | bitmop | `long->bytes` (8-iter loop) + `map ub8` (lazy seq) + `map octet-hex` (lazy seq of 2-char strs) + `apply str` | byte array + 2 lazy seqs + 8 temp strings + final concat | | bitmop2| `long->bytes` (1 putLong) + `StringBuilder` direct byte-by-byte append | byte array + 1 StringBuilder | **Measured speedup: 11-35x** The bitmop version creates multiple intermediate lazy sequences and 8 two-character strings before concatenating them all. The bitmop2 version writes directly to a pre-sized `StringBuilder`, eliminating all intermediate string and sequence allocation. --- ## Per-UUID-Type Performance Analysis For each UUID type, we trace the critical path through both implementations and identify where `bitmop2` provides measurable improvement. ### v0 (Null UUID) / v15 (Max UUID) ```clojure ;; Both implementations: (defn null [] +null+) (defn max [] +max+) ``` **Impact: None.** Returns a constant. No bitwise operations involved. --- ### v1 (Time-based, Gregorian) ```clojure ;; clj-uuid-old (bitmop): (let [ts (clock/monotonic-time) ;; atom + swap! + State alloc time-low (ldb #=(mask 32 0) ts) time-mid (ldb #=(mask 16 32) ts) time-high (dpb #=(mask 4 12) (ldb #=(mask 12 48) ts) 0x1) msb (bit-or time-high (bit-shift-left time-low 32) (bit-shift-left time-mid 16))] (UUID. msb (node/+v1-lsb+))) ;; memoized fn call ;; clj-uuid (bitmop2) -- inlined CAS + direct bit ops: (loop [] (let [current (.get packed) ;; AtomicLong, captured in closure millis (unsigned-bit-shift-right current 14) time-now (System/currentTimeMillis)] (cond (< millis time-now) (let [next (bit-shift-left time-now 14)] (if (.compareAndSet packed current next) (let [ts (+ 100103040000000000 (* (+ 2208988800000 time-now) 10000)) msb (bit-or (bit-shift-left (bit-and ts 0xFFFFFFFF) 32) (bit-shift-left (bit-and (unsigned-bit-shift-right ts 32) 0xFFFF) 16) 0x1000 (bit-and (unsigned-bit-shift-right ts 48) 0xFFF))] (UUID. msb v1-lsb)) ;; pre-captured long, no fn call (recur))) ...))) ``` | Operation | bitmop (clj-uuid-old) | bitmop2 (clj-uuid) | Difference | |----------------------|--------------------------------|--------------------------------|------------| | Clock | `atom` + `swap!` + `State` alloc | `AtomicLong.compareAndSet` (inlined) | **no var lookup, no alloc** | | Bit-field packing | 3x `ldb` + 1x `dpb` (4 var lookups) | Direct `bit-or`/`bit-and`/`bit-shift` | **no var lookups** | | Node LSB | `(node/+v1-lsb+)` (memoize lookup) | `v1-lsb` (pre-captured long) | **no fn call** | **Construction impact: ~1.5x speedup (120 ns -> 100 ns).** Three sources of overhead are eliminated: (1) `atom`/`swap!`/`State` allocation is replaced by `AtomicLong.compareAndSet` on a packed long; (2) `ldb`/`dpb` var lookups are replaced by inlined bit operations; (3) the memoized `+v1-lsb+` function call is replaced by a pre-captured long in the closure. **Post-construction impact:** Operations on the resulting UUID differ: | Post-construction op | bitmop (clj-uuid-old) | bitmop2 (clj-uuid) | Speedup | |-----------------------|------------------------------------------|---------------------------------------|----------| | `to-byte-array` | 2x `long->bytes` (16 loop iterations) | 2x `putLong` (2 native calls) | **60x** | | `to-hex-string` | 2x `hex` (lazy seqs + `apply str`) | `uuid->buf` + `buf-hex` (StringBuilder) | **38x** | | `to-string` | `UUID.toString` (JVM) | `UUID.toString` (JVM) | same | | Field extraction | `ldb`/`dpb` on longs | `ldb`/`dpb` on longs | same | --- ### v6 (Time-based, Lexically Sortable) Same inlined CAS + direct bit-op architecture as v1, with different bit-field ordering for lexical sorting. **Construction impact: ~1.4x speedup (106 ns -> 100 ns).** Same optimizations as v1. The smaller relative gain reflects v6's already lower baseline (fewer bit operations in the original layout). **Post-construction impact:** Same as v1 (see table above). --- ### v7 (Unix Time, Crypto-secure, Lexically Sortable) ```clojure ;; Both implementations (identical structure): (let [^State state (clock/monotonic-unix-time-and-random-counter) time (ldb #=(mask 48 0) (.millis state)) ver-and-counter (dpb #=(mask 4 12) (.seqid state) 0x7) msb (bit-or ver-and-counter (bit-shift-left time 16)) lsb (dpb #=(mask 2 62) (random/long) 0x2)] (UUID. msb lsb)) ``` | Operation | bitmop | bitmop2 | Difference | |----------------------------|---------|---------|------------| | `monotonic-unix-time-...` | shared | shared | none | | `ldb` x1, `dpb` x2 | O(offset) `mask-offset` | O(1) `Long/numberOfTrailingZeros` | **1.21x** | | `random/long` (SecureRandom) | shared | shared | none | **Construction impact: 1.21x speedup.** The `dpb #=(mask 2 62)` call in the LSB line previously invoked `mask-offset` with an O(offset) loop — for offset=62, that was 62 iterations per call. bitmop2's `mask-offset` uses `Long/numberOfTrailingZeros`, a JVM intrinsic that compiles to a single `TZCNT` instruction. This eliminates the v7 regression seen in earlier benchmarks. `SecureRandom.nextLong()` still dominates total latency. **Post-construction impact:** Same as v1/v6 (see table above). --- ### v7nc (Non-cryptographic V7, ThreadLocalRandom) ```clojure ;; clj-uuid (bitmop2) -- per-thread counter + ThreadLocalRandom: (let [^longs state (.get v7nc-tl) ;; ThreadLocal long[3] ^ThreadLocalRandom tlr ...] (loop [] (let [time-now (System/currentTimeMillis) last-ms (aget state 0)] (cond (> time-now last-ms) ;; new millisecond: reseed (let [lsb-ctr (bit-and (.nextLong tlr) 0x3FFFFFFFFFFFFFFF) msb (bit-or (bit-shift-left (bit-and time-now 0xFFFFFFFFFFFF) 16) (bit-or 0x7000 (bit-and (.nextLong tlr) 0xFFF)))] (aset state 0 time-now) (aset state 1 msb) (aset state 2 lsb-ctr) (UUID. msb (bit-or lsb-ctr variant-bits))) true ;; same millisecond: increment (let [lsb-ctr (bit-and (unchecked-inc (aget state 2)) 0x3FFFFFFFFFFFFFFF)] (aset state 2 lsb-ctr) (UUID. (aget state 1) (bit-or lsb-ctr variant-bits))))))) ``` No clj-uuid-old equivalent exists. `v7nc` is a new constructor in 0.2.5. | Operation | v7 (CSPRNG) | v7nc | |----------------------------|------------------------------|-------------------------------| | Clock | Global `AtomicLong` CAS | Per-thread `long[]` (no CAS) | | Counter reseed | `SecureRandom` (~300 ns) | `ThreadLocalRandom` (~5 ns) | | rand_b | `SecureRandom.nextLong()` | Monotonic counter (increment) | | Hot path (same ms) | CAS + SecureRandom | Array load + increment | **Construction: ~39 ns.** The hot path (same millisecond) is just: `ThreadLocal.get()` + `System.currentTimeMillis()` + array load + comparison + `unchecked-inc` + `bit-and` + array store + `UUID.` constructor. No random number generation, no atomics, no var lookups. **vs JUG 5.2:** `v7nc` at 39 ns is **1.26x faster** than JUG's `TimeBasedEpochGenerator` at ~50 ns. --- ### v4 (Random) ```clojure ;; 0-arity (both implementations): (UUID/randomUUID) ;; 2-arity (both implementations): (UUID. (dpb #=(mask 4 12) msb 0x4) (dpb #=(mask 2 62) lsb 0x2)) ``` **Construction impact: None (0-arity) / Negligible (2-arity).** The 0-arity form delegates directly to `UUID/randomUUID` (JVM built-in, dominated by SecureRandom). The 2-arity form uses only 2 `dpb` calls, which are identical between bitmop and bitmop2. **Post-construction impact:** Same as other UUID types. --- ### v3 (Namespaced, MD5) / v5 (Namespaced, SHA-1) ```clojure ;; clj-uuid-old (bitmop): (build-digested-uuid version (digest-bytes +md5+|+sha1+ (to-byte-array (as-uuid context)) (as-byte-array local-part))) ;; clj-uuid (bitmop2) -- fused pipeline: (let [^MessageDigest md (.get md5-tl) ;; ThreadLocal, captured in closure ^ByteBuffer nsbuf (.get ns-buf-tl) ;; ThreadLocal reusable buffer _ (.reset md) _ (.putLong nsbuf 0 (.getMostSignificantBits (as-uuid context))) _ (.putLong nsbuf 8 (.getLeastSignificantBits (as-uuid context))) _ (.update md (.array nsbuf)) digest (.digest md ^bytes (as-byte-array local-part)) ^ByteBuffer dbuf (ByteBuffer/wrap digest) ;; wrap, no copy msb (bit-or (bit-and (.getLong dbuf 0) version-clear-mask) 0x3000) lsb (bit-or (bit-and (.getLong dbuf 8) variant-clear-mask) variant-bits)] (UUID. msb lsb)) ``` The v3/v5 construction path is the most interesting for performance comparison, as it touches multiple bitmop operations in sequence: #### clj-uuid-old pipeline (4 function calls, ~8 var lookups) | Step | Operation | Cost | |---|---|---| | 1 | `to-byte-array` (serialize context UUID) | ~800 ns (16-iter loop) | | 2 | `digest-bytes` (MD5 or SHA-1 hash) | ~150-300 ns | | 3 | `build-digested-uuid` → `bytes->long` x2 | ~800 ns (16-iter loop) | | 4 | `dpb` x2 (version + variant) | ~5 ns | | | **Total (v3):** | **~1400 ns** | | | **Total (v5):** | **~1670 ns** | #### clj-uuid pipeline (fused, 0 var lookups on hot path) | Step | Operation | Cost | |---|---|---| | 1 | Reuse ThreadLocal ByteBuffer + 2x `putLong` | ~3 ns | | 2 | `MessageDigest` (ThreadLocal, `.reset` + `.update` + `.digest`) | ~150-250 ns | | 3 | `ByteBuffer/wrap` digest + 2x `.getLong` | ~3 ns | | 4 | Inline `bit-and`/`bit-or` (compile-time constant masks) | ~2 ns | | | **Total (v3):** | **~175 ns** | | | **Total (v5):** | **~260 ns** | **Overall v3 speedup: ~8x.** **Overall v5 speedup: ~6.4x.** Three optimizations compound: (1) ThreadLocal ByteBuffer reuse for namespace serialization eliminates per-call allocation; (2) `ByteBuffer/wrap` on the digest output avoids copying 16 bytes; (3) inline bit-and/bit-or with compile-time constant masks (`#=(bit-not #=(bitmop/mask ...))`) eliminates all `dpb-buf`, `buf->uuid`, and `buffer-from-bytes` var lookups. **vs JUG 5.2:** v5 at ~260 ns is now at parity with JUG's ~254 ns. --- ### v8 (Custom) ```clojure ;; Both implementations: (UUID. (dpb #=(mask 4 12) msb 0x8) (dpb #=(mask 2 62) lsb 0x2)) ``` **Construction impact: 4.21x speedup (46 ns -> 11 ns).** The v8 constructor is just two `dpb` calls (`mask(4,12)` and `mask(2,62)`). With bitmop's O(offset) `mask-offset` loop, the `mask(2,62)` call alone required 62 loop iterations. bitmop2's O(1) `Long/numberOfTrailingZeros` eliminates this overhead, making `dpb` nearly free. **Post-construction impact:** Same as other UUID types. --- ### squuid (Sequential UUID) ```clojure ;; Both implementations: (let [uuid (v4) secs (clock/posix-time) lsb (get-word-low uuid) msb (get-word-high uuid) timed-msb (bit-or (bit-shift-left secs 32) (bit-and +ub32-mask+ msb))] (UUID. timed-msb lsb)) ``` **Construction impact: None.** The squuid constructor uses only `get-word-high`/`get-word-low` (direct `.getMostSignificantBits`/ `.getLeastSignificantBits` calls) and `bit-or`/`bit-and`/`bit-shift-left` native operations. Dominated by `v4` -> `UUID/randomUUID` internally. **Post-construction impact:** Same as other UUID types. --- ## Post-Construction Operations Summary These operations are called on UUID values *after* construction and show the largest measurable differences between clj-uuid-old and clj-uuid: ### `to-byte-array` | Impl | Code path | Cost | |--------------|------------------------------------------------------------|----------| | clj-uuid-old | `bitmop/long->bytes` x2 (16 shift/mask iterations total) | ~804 ns | | clj-uuid | `bitmop2/long->bytes` x2 (2 `putLong` calls) | ~14 ns | **Speedup: ~57x** This operation is called internally during v3/v5 construction (to serialize the namespace UUID) and is also part of the public API for any UUID. ### `to-hex-string` | Impl | Code path | Cost | |--------------|------------------------------------------------------------|----------| | clj-uuid-old | `bitmop/hex(msb)` + `bitmop/hex(lsb)` + `str` concat. Each `hex` call: `long->bytes` (8-iter loop) + `map ub8` (lazy seq) + `map octet-hex` (lazy seq of 8 temp strings) + `apply str` | ~5840 ns | | clj-uuid | `uuid->buf` (2 putLong) + `buf-hex` (single StringBuilder, 16-byte direct loop) | ~199 ns | **Speedup: ~29x** The bitmop version allocates: 2 byte arrays, 4 lazy sequences, 16 intermediate 2-character strings, and performs 2 final string concatenations. The bitmop2 version allocates: 1 ByteBuffer + 1 pre-sized StringBuilder and appends 32 characters directly. ### `to-string` Both call `UUID.toString()`. **No difference.** ### `to-urn-string` Both call `(str "urn:uuid:" (.toString uuid))`. **No difference.** ### `to-uri` Both call `URI/create` on the URN string. **No difference.** ### Field extraction (`get-time-low`, `get-time-mid`, etc.) Both use `ldb`/`dpb` on `.getMostSignificantBits`/`.getLeastSignificantBits`. The `#=(mask ...)` reader macros are compile-time constants. **No difference.** ### Comparison operations (`uuid=`, `uuid<`, `uuid>`) Both directly compare `.getMostSignificantBits`/`.getLeastSignificantBits`. **No difference.** (bitmop2 additionally provides `buf-compare` with unsigned semantics for buffer-level comparison, but `clj-uuid` uses the same `uuid=`/`uuid<`/`uuid>` implementation as `clj-uuid-old`.) ### `as-uuid` (byte array to UUID) | Impl | Code path | |--------------|------------------------------------------------------------| | clj-uuid-old | `ByteBuffer/wrap` + 2 relative `.getLong` calls | | clj-uuid | `ByteBuffer/wrap` + 2 absolute `.getLong(0)` / `.getLong(8)` | **Impact: Negligible.** Both use ByteBuffer; the difference is absolute vs relative positioning. The absolute form is marginally more predictable (no position state) but performance is equivalent. --- ## Complete Impact Matrix This table summarizes the impact of bitmop2 on every UUID type, separating construction from post-construction operations: | UUID Type | Construction Speedup | Hot Path Bottleneck | `to-byte-array` | `to-hex-string` | |-----------|---------------------|---------------------------------|------------------|------------------| | v0 (null) | -- | constant | **57x** | **29x** | | v1 | **1.5x** | `AtomicLong` CAS (inlined) | **57x** | **29x** | | v3 | **~8x** | MD5 digest (fused pipeline) | **57x** | **29x** | | v4 (0) | none | `SecureRandom` (CSPRNG) | **57x** | **29x** | | v4 (2) | negligible | caller-provided longs | **57x** | **29x** | | v5 | **~6.4x** | SHA-1 digest (fused pipeline) | **57x** | **29x** | | v6 | **1.4x** | `AtomicLong` CAS (inlined) | **57x** | **29x** | | v7 | **1.2x** | `SecureRandom` (CSPRNG) | **57x** | **29x** | | v7nc | *new* | `ThreadLocalRandom` (per-thread)| **57x** | **29x** | | v8 | **4.2x** | caller-provided longs | **57x** | **29x** | | squuid | none | `SecureRandom` via v4 | **57x** | **29x** | | max | -- | constant | **57x** | **29x** | **Key takeaway:** The bitmop->bitmop2 change provides the largest speedup in byte serialization and hex string rendering, which are post-construction operations common to *all* UUID types. Additionally, `mask-offset`, `mask-width`, and `bit-count` now use JVM intrinsics (`Long/numberOfTrailingZeros` and `Long/bitCount`), replacing O(n) loops with single CPU instructions. This particularly benefits v7 (1.2x, eliminating a previous regression) and v8 (4.2x, where `dpb` is the entire constructor cost). v3/v5 continue to show the largest gains from byte conversion optimization and ThreadLocal digest caching. --- ## Where the Gains Matter Most ### High-throughput serialization Applications that generate UUIDs and immediately serialize them (to byte arrays for database storage, or to hex strings for logging/wire format) benefit from the cumulative improvement: ``` clj-uuid-old (v1 + to-byte-array): ~120 ns (v1) + ~804 ns (bytes) = ~926 ns clj-uuid (v1 + to-byte-array): ~100 ns (v1) + ~14 ns (bytes) = ~114 ns ~8.1x ``` ``` clj-uuid-old (v1 + to-hex-string): ~120 ns (v1) + ~5840 ns (hex) = ~5960 ns clj-uuid (v1 + to-hex-string): ~100 ns (v1) + ~126 ns (hex) = ~226 ns ~26x ``` ### Batch name-based UUID generation (v3/v5) When generating many v3/v5 UUIDs (e.g., deterministic ID generation from a dataset), both the namespace serialization and digest-result extraction are improved: ``` clj-uuid-old (v3): ~800 ns (to-byte-array) + ~200 ns (MD5) + ~800 ns (bytes->long x2) + ~5 ns (dpb) = ~1400 ns clj-uuid (v3): ~14 ns (to-byte-array) + ~140 ns (MD5) + ~14 ns (bytes->long x2) + ~3 ns (dpb) = ~160 ns ~9.0x ``` ``` clj-uuid-old (v5): ~800 ns (to-byte-array) + ~300 ns (SHA-1) + ~800 ns (bytes->long x2) + ~5 ns (dpb) = ~1670 ns clj-uuid (v5): ~14 ns (to-byte-array) + ~250 ns (SHA-1) + ~14 ns (bytes->long x2) + ~3 ns (dpb) = ~280 ns ~6.0x ``` ### UUID comparison and field extraction No improvement -- these paths use `ldb`/`dpb` on longs, which are identical. In practice these operations are already extremely fast (single-digit nanoseconds). --- ## Allocation Profile Comparison Beyond raw speed, bitmop2 reduces GC pressure through fewer intermediate allocations: | Operation | bitmop allocations | bitmop2 allocations | |---------------|---------------------------------------------|---------------------------------------| | `long->bytes` | 1 byte array | 1 byte array + 1 ByteBuffer (wrap) | | `bytes->long` | none (returns primitive) | 1 ByteBuffer (wrap) | | `hex (long)` | 1 byte array + 2 lazy seqs + 8 temp strings + 1 final string | 1 byte array + 1 StringBuilder + 1 string | | `to-hex-string`| 2 byte arrays + 4 lazy seqs + 16 temp strings + 2 hex strings + 1 concat | 1 ByteBuffer + 1 StringBuilder + 1 string | | `assemble-bytes` | none (returns primitive, seq traversal only) | none (returns primitive, seq traversal only) | The `ByteBuffer/wrap` call in bitmop2 does **not** copy the array (it creates a view), so `long->bytes` and `bytes->long` have minimal allocation overhead beyond the existing array. The biggest allocation win is in `to-hex-string`, where bitmop creates ~25 intermediate objects (lazy seq chunks, 2-character strings, intermediate hex strings) versus bitmop2's 3 objects (ByteBuffer, StringBuilder, result string). The `assemble-bytes` optimization in bitmop2 now uses a zero-allocation shift-accumulation loop (no byte-array, no ByteBuffer), matching bitmop's allocation-free approach while being 2.4x faster due to avoiding per-byte `dpb`/`mask`/`mask-offset` function calls. --- ## Future: cljc/ClojureScript Performance The ByteBuffer abstraction in bitmop2 was designed to map to JavaScript's `DataView` over `ArrayBuffer`: | bitmop2 (JVM) | Future cljc (JS) | |----------------------------|-------------------------------------| | `ByteBuffer/allocate 16` | `new DataView(new ArrayBuffer(16))` | | `.getLong buf offset` | `.getBigInt64(offset)` | | `.putLong buf offset val` | `.setBigInt64(offset, val)` | | `.getInt buf offset` | `.getInt32(offset)` | | `.get buf offset` | `.getUint8(offset)` | This means the performance characteristics of bitmop2 will carry over to ClojureScript, where manual shift/mask loops in JavaScript would be significantly more expensive than native DataView operations (which are implemented in C++ by the JS engine). --- ## Summary | Category | clj-uuid-old (bitmop) | clj-uuid (bitmop2) | Improvement | |-------------------------|------------------------|----------------------|----------------| | UUID construction | baseline | **1.2-9x faster** | see below* | | `to-byte-array` | baseline | **57x faster** | ByteBuffer | | `to-hex-string` | baseline | **29x faster** | StringBuilder | | `bytes->long` | baseline | **5-10x faster** | ByteBuffer | | `long->bytes` | baseline | **6-27x faster** | ByteBuffer | | `hex` | baseline | **11-35x faster** | StringBuilder | | `assemble-bytes` | baseline | **2.4x faster** | shift-accum | | `mask-offset` | baseline | **O(1)** | `TZCNT` intrinsic | | `mask-width`/`bit-count`| baseline | **O(1)** | `POPCNT` intrinsic | | Field extraction | baseline | same | n/a | | Comparison | baseline | same | n/a | | GC pressure | higher | **lower** | fewer allocs | | cljc readiness | no | **yes** (DataView) | architecture | *Construction speedup varies by UUID type: v3 sees **~8x** and v5 sees **~6.4x** from the fused digest pipeline with ThreadLocal ByteBuffer reuse (v5 is now at parity with JUG 5.2). v8 sees **4.2x** from O(1) `mask-offset`. v1 sees **~1.5x** and v6 sees **~1.4x** from inlined `AtomicLong` CAS, direct bit operations, and pre-captured node LSBs. v7nc is a new constructor at ~39 ns -- **1.26x faster** than JUG 5.2's v7 generator, using per-thread `ThreadLocalRandom` instead of `SecureRandom`. v4 (0-arity) delegates to `UUID/randomUUID` and is unchanged. The largest gains are in **serialization-heavy workloads** where UUIDs are frequently converted to byte arrays or hex strings -- common in database drivers, logging frameworks, and wire protocols. Additional gains come from **O(1) mask-offset/mask-width/bit-count** using JVM intrinsics (`Long/numberOfTrailingZeros` and `Long/bitCount`), which particularly benefits v8 (4.2x) where `dpb` calls with high-offset masks were previously bottlenecked by an O(offset) loop. ================================================ FILE: doc/rfc4122.txt ================================================ Network Working Group P. Leach Request for Comments: 4122 Microsoft Category: Standards Track M. Mealling Refactored Networks, LLC R. Salz DataPower Technology, Inc. July 2005 A Universally Unique IDentifier (UUID) URN Namespace Status of This Memo This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. Copyright Notice Copyright (C) The Internet Society (2005). Abstract This specification defines a Uniform Resource Name namespace for UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDentifier). A UUID is 128 bits long, and can guarantee uniqueness across space and time. UUIDs were originally used in the Apollo Network Computing System and later in the Open Software Foundation's (OSF) Distributed Computing Environment (DCE), and then in Microsoft Windows platforms. This specification is derived from the DCE specification with the kind permission of the OSF (now known as The Open Group). Information from earlier versions of the DCE specification have been incorporated into this document. Leach, et al. Standards Track [Page 1] RFC 4122 A UUID URN Namespace July 2005 Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 2. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3. Namespace Registration Template . . . . . . . . . . . . . . . 3 4. Specification . . . . . . . . . . . . . . . . . . . . . . . . 5 4.1. Format. . . . . . . . . . . . . . . . . . . . . . . . . . 5 4.1.1. Variant. . . . . . . . . . . . . . . . . . . . . . 6 4.1.2. Layout and Byte Order. . . . . . . . . . . . . . . 6 4.1.3. Version. . . . . . . . . . . . . . . . . . . . . . 7 4.1.4. Timestamp. . . . . . . . . . . . . . . . . . . . . 8 4.1.5. Clock Sequence . . . . . . . . . . . . . . . . . . 8 4.1.6. Node . . . . . . . . . . . . . . . . . . . . . . . 9 4.1.7. Nil UUID . . . . . . . . . . . . . . . . . . . . . 9 4.2. Algorithms for Creating a Time-Based UUID . . . . . . . . 9 4.2.1. Basic Algorithm. . . . . . . . . . . . . . . . . . 10 4.2.2. Generation Details . . . . . . . . . . . . . . . . 12 4.3. Algorithm for Creating a Name-Based UUID. . . . . . . . . 13 4.4. Algorithms for Creating a UUID from Truly Random or Pseudo-Random Numbers . . . . . . . . . . . . . . . . . . 14 4.5. Node IDs that Do Not Identify the Host. . . . . . . . . . 15 5. Community Considerations . . . . . . . . . . . . . . . . . . . 15 6. Security Considerations . . . . . . . . . . . . . . . . . . . 16 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 16 8. Normative References . . . . . . . . . . . . . . . . . . . . . 16 A. Appendix A - Sample Implementation . . . . . . . . . . . . . . 18 B. Appendix B - Sample Output of utest . . . . . . . . . . . . . 29 C. Appendix C - Some Name Space IDs . . . . . . . . . . . . . . . 30 1. Introduction This specification defines a Uniform Resource Name namespace for UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDentifier). A UUID is 128 bits long, and requires no central registration process. The information here is meant to be a concise guide for those wishing to implement services using UUIDs as URNs. Nothing in this document should be construed to override the DCE standards that defined UUIDs. There is an ITU-T Recommendation and ISO/IEC Standard [3] that are derived from earlier versions of this document. Both sets of specifications have been aligned, and are fully technically compatible. In addition, a global registration function is being provided by the Telecommunications Standardisation Bureau of ITU-T; for details see . Leach, et al. Standards Track [Page 2] RFC 4122 A UUID URN Namespace July 2005 2. Motivation One of the main reasons for using UUIDs is that no centralized authority is required to administer them (although one format uses IEEE 802 node identifiers, others do not). As a result, generation on demand can be completely automated, and used for a variety of purposes. The UUID generation algorithm described here supports very high allocation rates of up to 10 million per second per machine if necessary, so that they could even be used as transaction IDs. UUIDs are of a fixed size (128 bits) which is reasonably small compared to other alternatives. This lends itself well to sorting, ordering, and hashing of all sorts, storing in databases, simple allocation, and ease of programming in general. Since UUIDs are unique and persistent, they make excellent Uniform Resource Names. The unique ability to generate a new UUID without a registration process allows for UUIDs to be one of the URNs with the lowest minting cost. 3. Namespace Registration Template Namespace ID: UUID Registration Information: Registration date: 2003-10-01 Declared registrant of the namespace: JTC 1/SC6 (ASN.1 Rapporteur Group) Declaration of syntactic structure: A UUID is an identifier that is unique across both space and time, with respect to the space of all UUIDs. Since a UUID is a fixed size and contains a time field, it is possible for values to rollover (around A.D. 3400, depending on the specific algorithm used). A UUID can be used for multiple purposes, from tagging objects with an extremely short lifetime, to reliably identifying very persistent objects across a network. The internal representation of a UUID is a specific sequence of bits in memory, as described in Section 4. To accurately represent a UUID as a URN, it is necessary to convert the bit sequence to a string representation. Each field is treated as an integer and has its value printed as a zero-filled hexadecimal digit string with the most significant digit first. The hexadecimal values "a" through "f" are output as lower case characters and are case insensitive on input. Leach, et al. Standards Track [Page 3] RFC 4122 A UUID URN Namespace July 2005 The formal definition of the UUID string representation is provided by the following ABNF [7]: UUID = time-low "-" time-mid "-" time-high-and-version "-" clock-seq-and-reserved clock-seq-low "-" node time-low = 4hexOctet time-mid = 2hexOctet time-high-and-version = 2hexOctet clock-seq-and-reserved = hexOctet clock-seq-low = hexOctet node = 6hexOctet hexOctet = hexDigit hexDigit hexDigit = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "a" / "b" / "c" / "d" / "e" / "f" / "A" / "B" / "C" / "D" / "E" / "F" The following is an example of the string representation of a UUID as a URN: urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 Relevant ancillary documentation: [1][2] Identifier uniqueness considerations: This document specifies three algorithms to generate UUIDs: the first leverages the unique values of 802 MAC addresses to guarantee uniqueness, the second uses pseudo-random number generators, and the third uses cryptographic hashing and application-provided text strings. As a result, the UUIDs generated according to the mechanisms here will be unique from all other UUIDs that have been or will be assigned. Identifier persistence considerations: UUIDs are inherently very difficult to resolve in a global sense. This, coupled with the fact that UUIDs are temporally unique within their spatial context, ensures that UUIDs will remain as persistent as possible. Process of identifier assignment: Generating a UUID does not require that a registration authority be contacted. One algorithm requires a unique value over space for each generator. This value is typically an IEEE 802 MAC address, usually already available on network-connected hosts. The address can be assigned from an address block obtained from the IEEE registration authority. If no such address is available, Leach, et al. Standards Track [Page 4] RFC 4122 A UUID URN Namespace July 2005 or privacy concerns make its use undesirable, Section 4.5 specifies two alternatives. Another approach is to use version 3 or version 4 UUIDs as defined below. Process for identifier resolution: Since UUIDs are not globally resolvable, this is not applicable. Rules for Lexical Equivalence: Consider each field of the UUID to be an unsigned integer as shown in the table in section Section 4.1.2. Then, to compare a pair of UUIDs, arithmetically compare the corresponding fields from each UUID in order of significance and according to their data type. Two UUIDs are equal if and only if all the corresponding fields are equal. As an implementation note, equality comparison can be performed on many systems by doing the appropriate byte-order canonicalization, and then treating the two UUIDs as 128-bit unsigned integers. UUIDs, as defined in this document, can also be ordered lexicographically. For a pair of UUIDs, the first one follows the second if the most significant field in which the UUIDs differ is greater for the first UUID. The second precedes the first if the most significant field in which the UUIDs differ is greater for the second UUID. Conformance with URN Syntax: The string representation of a UUID is fully compatible with the URN syntax. When converting from a bit-oriented, in-memory representation of a UUID into a URN, care must be taken to strictly adhere to the byte order issues mentioned in the string representation section. Validation mechanism: Apart from determining whether the timestamp portion of the UUID is in the future and therefore not yet assignable, there is no mechanism for determining whether a UUID is 'valid'. Scope: UUIDs are global in scope. 4. Specification 4.1. Format The UUID format is 16 octets; some bits of the eight octet variant field specified below determine finer structure. Leach, et al. Standards Track [Page 5] RFC 4122 A UUID URN Namespace July 2005 4.1.1. Variant The variant field determines the layout of the UUID. That is, the interpretation of all other bits in the UUID depends on the setting of the bits in the variant field. As such, it could more accurately be called a type field; we retain the original term for compatibility. The variant field consists of a variable number of the most significant bits of octet 8 of the UUID. The following table lists the contents of the variant field, where the letter "x" indicates a "don't-care" value. Msb0 Msb1 Msb2 Description 0 x x Reserved, NCS backward compatibility. 1 0 x The variant specified in this document. 1 1 0 Reserved, Microsoft Corporation backward compatibility 1 1 1 Reserved for future definition. Interoperability, in any form, with variants other than the one defined here is not guaranteed, and is not likely to be an issue in practice. 4.1.2. Layout and Byte Order To minimize confusion about bit assignments within octets, the UUID record definition is defined only in terms of fields that are integral numbers of octets. The fields are presented with the most significant one first. Field Data Type Octet Note # time_low unsigned 32 0-3 The low field of the bit integer timestamp time_mid unsigned 16 4-5 The middle field of the bit integer timestamp time_hi_and_version unsigned 16 6-7 The high field of the bit integer timestamp multiplexed with the version number Leach, et al. Standards Track [Page 6] RFC 4122 A UUID URN Namespace July 2005 clock_seq_hi_and_rese unsigned 8 8 The high field of the rved bit integer clock sequence multiplexed with the variant clock_seq_low unsigned 8 9 The low field of the bit integer clock sequence node unsigned 48 10-15 The spatially unique bit integer node identifier In the absence of explicit application or presentation protocol specification to the contrary, a UUID is encoded as a 128-bit object, as follows: The fields are encoded as 16 octets, with the sizes and order of the fields defined above, and with each field encoded with the Most Significant Byte first (known as network byte order). Note that the field names, particularly for multiplexed fields, follow historical practice. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_mid | time_hi_and_version | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |clk_seq_hi_res | clk_seq_low | node (0-1) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | node (2-5) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4.1.3. Version The version number is in the most significant 4 bits of the time stamp (bits 4 through 7 of the time_hi_and_version field). The following table lists the currently-defined versions for this UUID variant. Msb0 Msb1 Msb2 Msb3 Version Description 0 0 0 1 1 The time-based version specified in this document. 0 0 1 0 2 DCE Security version, with embedded POSIX UIDs. Leach, et al. Standards Track [Page 7] RFC 4122 A UUID URN Namespace July 2005 0 0 1 1 3 The name-based version specified in this document that uses MD5 hashing. 0 1 0 0 4 The randomly or pseudo- randomly generated version specified in this document. 0 1 0 1 5 The name-based version specified in this document that uses SHA-1 hashing. The version is more accurately a sub-type; again, we retain the term for compatibility. 4.1.4. Timestamp The timestamp is a 60-bit value. For UUID version 1, this is represented by Coordinated Universal Time (UTC) as a count of 100- nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar). For systems that do not have UTC available, but do have the local time, they may use that instead of UTC, as long as they do so consistently throughout the system. However, this is not recommended since generating the UTC from local time only needs a time zone offset. For UUID version 3 or 5, the timestamp is a 60-bit value constructed from a name as described in Section 4.3. For UUID version 4, the timestamp is a randomly or pseudo-randomly generated 60-bit value, as described in Section 4.4. 4.1.5. Clock Sequence For UUID version 1, the clock sequence is used to help avoid duplicates that could arise when the clock is set backwards in time or if the node ID changes. If the clock is set backwards, or might have been set backwards (e.g., while the system was powered off), and the UUID generator can not be sure that no UUIDs were generated with timestamps larger than the value to which the clock was set, then the clock sequence has to be changed. If the previous value of the clock sequence is known, it can just be incremented; otherwise it should be set to a random or high-quality pseudo-random value. Leach, et al. Standards Track [Page 8] RFC 4122 A UUID URN Namespace July 2005 Similarly, if the node ID changes (e.g., because a network card has been moved between machines), setting the clock sequence to a random number minimizes the probability of a duplicate due to slight differences in the clock settings of the machines. If the value of clock sequence associated with the changed node ID were known, then the clock sequence could just be incremented, but that is unlikely. The clock sequence MUST be originally (i.e., once in the lifetime of a system) initialized to a random number to minimize the correlation across systems. This provides maximum protection against node identifiers that may move or switch from system to system rapidly. The initial value MUST NOT be correlated to the node identifier. For UUID version 3 or 5, the clock sequence is a 14-bit value constructed from a name as described in Section 4.3. For UUID version 4, clock sequence is a randomly or pseudo-randomly generated 14-bit value as described in Section 4.4. 4.1.6. Node For UUID version 1, the node field consists of an IEEE 802 MAC address, usually the host address. For systems with multiple IEEE 802 addresses, any available one can be used. The lowest addressed octet (octet number 10) contains the global/local bit and the unicast/multicast bit, and is the first octet of the address transmitted on an 802.3 LAN. For systems with no IEEE address, a randomly or pseudo-randomly generated value may be used; see Section 4.5. The multicast bit must be set in such addresses, in order that they will never conflict with addresses obtained from network cards. For UUID version 3 or 5, the node field is a 48-bit value constructed from a name as described in Section 4.3. For UUID version 4, the node field is a randomly or pseudo-randomly generated 48-bit value as described in Section 4.4. 4.1.7. Nil UUID The nil UUID is special form of UUID that is specified to have all 128 bits set to zero. 4.2. Algorithms for Creating a Time-Based UUID Various aspects of the algorithm for creating a version 1 UUID are discussed in the following sections. Leach, et al. Standards Track [Page 9] RFC 4122 A UUID URN Namespace July 2005 4.2.1. Basic Algorithm The following algorithm is simple, correct, and inefficient: o Obtain a system-wide global lock o From a system-wide shared stable store (e.g., a file), read the UUID generator state: the values of the timestamp, clock sequence, and node ID used to generate the last UUID. o Get the current time as a 60-bit count of 100-nanosecond intervals since 00:00:00.00, 15 October 1582. o Get the current node ID. o If the state was unavailable (e.g., non-existent or corrupted), or the saved node ID is different than the current node ID, generate a random clock sequence value. o If the state was available, but the saved timestamp is later than the current timestamp, increment the clock sequence value. o Save the state (current timestamp, clock sequence, and node ID) back to the stable store. o Release the global lock. o Format a UUID from the current timestamp, clock sequence, and node ID values according to the steps in Section 4.2.2. If UUIDs do not need to be frequently generated, the above algorithm may be perfectly adequate. For higher performance requirements, however, issues with the basic algorithm include: o Reading the state from stable storage each time is inefficient. o The resolution of the system clock may not be 100-nanoseconds. o Writing the state to stable storage each time is inefficient. o Sharing the state across process boundaries may be inefficient. Each of these issues can be addressed in a modular fashion by local improvements in the functions that read and write the state and read the clock. We address each of them in turn in the following sections. Leach, et al. Standards Track [Page 10] RFC 4122 A UUID URN Namespace July 2005 4.2.1.1. Reading Stable Storage The state only needs to be read from stable storage once at boot time, if it is read into a system-wide shared volatile store (and updated whenever the stable store is updated). If an implementation does not have any stable store available, then it can always say that the values were unavailable. This is the least desirable implementation because it will increase the frequency of creation of new clock sequence numbers, which increases the probability of duplicates. If the node ID can never change (e.g., the net card is inseparable from the system), or if any change also reinitializes the clock sequence to a random value, then instead of keeping it in stable store, the current node ID may be returned. 4.2.1.2. System Clock Resolution The timestamp is generated from the system time, whose resolution may be less than the resolution of the UUID timestamp. If UUIDs do not need to be frequently generated, the timestamp can simply be the system time multiplied by the number of 100-nanosecond intervals per system time interval. If a system overruns the generator by requesting too many UUIDs within a single system time interval, the UUID service MUST either return an error, or stall the UUID generator until the system clock catches up. A high resolution timestamp can be simulated by keeping a count of the number of UUIDs that have been generated with the same value of the system time, and using it to construct the low order bits of the timestamp. The count will range between zero and the number of 100-nanosecond intervals per system time interval. Note: If the processors overrun the UUID generation frequently, additional node identifiers can be allocated to the system, which will permit higher speed allocation by making multiple UUIDs potentially available for each time stamp value. 4.2.1.3. Writing Stable Storage The state does not always need to be written to stable store every time a UUID is generated. The timestamp in the stable store can be periodically set to a value larger than any yet used in a UUID. As long as the generated UUIDs have timestamps less than that value, and Leach, et al. Standards Track [Page 11] RFC 4122 A UUID URN Namespace July 2005 the clock sequence and node ID remain unchanged, only the shared volatile copy of the state needs to be updated. Furthermore, if the timestamp value in stable store is in the future by less than the typical time it takes the system to reboot, a crash will not cause a reinitialization of the clock sequence. 4.2.1.4. Sharing State Across Processes If it is too expensive to access shared state each time a UUID is generated, then the system-wide generator can be implemented to allocate a block of time stamps each time it is called; a per- process generator can allocate from that block until it is exhausted. 4.2.2. Generation Details Version 1 UUIDs are generated according to the following algorithm: o Determine the values for the UTC-based timestamp and clock sequence to be used in the UUID, as described in Section 4.2.1. o For the purposes of this algorithm, consider the timestamp to be a 60-bit unsigned integer and the clock sequence to be a 14-bit unsigned integer. Sequentially number the bits in a field, starting with zero for the least significant bit. o Set the time_low field equal to the least significant 32 bits (bits zero through 31) of the timestamp in the same order of significance. o Set the time_mid field equal to bits 32 through 47 from the timestamp in the same order of significance. o Set the 12 least significant bits (bits zero through 11) of the time_hi_and_version field equal to bits 48 through 59 from the timestamp in the same order of significance. o Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the 4-bit version number corresponding to the UUID version being created, as shown in the table above. o Set the clock_seq_low field to the eight least significant bits (bits zero through 7) of the clock sequence in the same order of significance. Leach, et al. Standards Track [Page 12] RFC 4122 A UUID URN Namespace July 2005 o Set the 6 least significant bits (bits zero through 5) of the clock_seq_hi_and_reserved field to the 6 most significant bits (bits 8 through 13) of the clock sequence in the same order of significance. o Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively. o Set the node field to the 48-bit IEEE address in the same order of significance as the address. 4.3. Algorithm for Creating a Name-Based UUID The version 3 or 5 UUID is meant for generating UUIDs from "names" that are drawn from, and unique within, some "name space". The concept of name and name space should be broadly construed, and not limited to textual names. For example, some name spaces are the domain name system, URLs, ISO Object IDs (OIDs), X.500 Distinguished Names (DNs), and reserved words in a programming language. The mechanisms or conventions used for allocating names and ensuring their uniqueness within their name spaces are beyond the scope of this specification. The requirements for these types of UUIDs are as follows: o The UUIDs generated at different times from the same name in the same namespace MUST be equal. o The UUIDs generated from two different names in the same namespace should be different (with very high probability). o The UUIDs generated from the same name in two different namespaces should be different with (very high probability). o If two UUIDs that were generated from names are equal, then they were generated from the same name in the same namespace (with very high probability). The algorithm for generating a UUID from a name and a name space are as follows: o Allocate a UUID to use as a "name space ID" for all UUIDs generated from names in that name space; see Appendix C for some pre-defined values. o Choose either MD5 [4] or SHA-1 [8] as the hash algorithm; If backward compatibility is not an issue, SHA-1 is preferred. Leach, et al. Standards Track [Page 13] RFC 4122 A UUID URN Namespace July 2005 o Convert the name to a canonical sequence of octets (as defined by the standards or conventions of its name space); put the name space ID in network byte order. o Compute the hash of the name space ID concatenated with the name. o Set octets zero through 3 of the time_low field to octets zero through 3 of the hash. o Set octets zero and one of the time_mid field to octets 4 and 5 of the hash. o Set octets zero and one of the time_hi_and_version field to octets 6 and 7 of the hash. o Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3. o Set the clock_seq_hi_and_reserved field to octet 8 of the hash. o Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively. o Set the clock_seq_low field to octet 9 of the hash. o Set octets zero through five of the node field to octets 10 through 15 of the hash. o Convert the resulting UUID to local byte order. 4.4. Algorithms for Creating a UUID from Truly Random or Pseudo-Random Numbers The version 4 UUID is meant for generating UUIDs from truly-random or pseudo-random numbers. The algorithm is as follows: o Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively. o Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the 4-bit version number from Section 4.1.3. o Set all the other bits to randomly (or pseudo-randomly) chosen values. Leach, et al. Standards Track [Page 14] RFC 4122 A UUID URN Namespace July 2005 See Section 4.5 for a discussion on random numbers. 4.5. Node IDs that Do Not Identify the Host This section describes how to generate a version 1 UUID if an IEEE 802 address is not available, or its use is not desired. One approach is to contact the IEEE and get a separate block of addresses. At the time of writing, the application could be found at , and the cost was US$550. A better solution is to obtain a 47-bit cryptographic quality random number and use it as the low 47 bits of the node ID, with the least significant bit of the first octet of the node ID set to one. This bit is the unicast/multicast bit, which will never be set in IEEE 802 addresses obtained from network cards. Hence, there can never be a conflict between UUIDs generated by machines with and without network cards. (Recall that the IEEE 802 spec talks about transmission order, which is the opposite of the in-memory representation that is discussed in this document.) For compatibility with earlier specifications, note that this document uses the unicast/multicast bit, instead of the arguably more correct local/global bit. Advice on generating cryptographic-quality random numbers can be found in RFC1750 [5]. In addition, items such as the computer's name and the name of the operating system, while not strictly speaking random, will help differentiate the results from those obtained by other systems. The exact algorithm to generate a node ID using these data is system specific, because both the data available and the functions to obtain them are often very system specific. A generic approach, however, is to accumulate as many sources as possible into a buffer, use a message digest such as MD5 [4] or SHA-1 [8], take an arbitrary 6 bytes from the hash value, and set the multicast bit as described above. 5. Community Considerations The use of UUIDs is extremely pervasive in computing. They comprise the core identifier infrastructure for many operating systems (Microsoft Windows) and applications (the Mozilla browser) and in many cases, become exposed to the Web in many non-standard ways. Leach, et al. Standards Track [Page 15] RFC 4122 A UUID URN Namespace July 2005 This specification attempts to standardize that practice as openly as possible and in a way that attempts to benefit the entire Internet. 6. Security Considerations Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access), for example. A predictable random number source will exacerbate the situation. Do not assume that it is easy to determine if a UUID has been slightly transposed in order to redirect a reference to another object. Humans do not have the ability to easily check the integrity of a UUID by simply glancing at it. Distributed applications generating UUIDs at a variety of hosts must be willing to rely on the random number source at all hosts. If this is not feasible, the namespace variant should be used. 7. Acknowledgments This document draws heavily on the OSF DCE specification for UUIDs. Ted Ts'o provided helpful comments, especially on the byte ordering section which we mostly plagiarized from a proposed wording he supplied (all errors in that section are our responsibility, however). We are also grateful to the careful reading and bit-twiddling of Ralf S. Engelschall, John Larmouth, and Paul Thorpe. Professor Larmouth was also invaluable in achieving coordination with ISO/IEC. 8. Normative References [1] Zahn, L., Dineen, T., and P. Leach, "Network Computing Architecture", ISBN 0-13-611674-4, January 1990. [2] "DCE: Remote Procedure Call", Open Group CAE Specification C309, ISBN 1-85912-041-5, August 1994. [3] ISO/IEC 9834-8:2004 Information Technology, "Procedures for the operation of OSI Registration Authorities: Generation and registration of Universally Unique Identifiers (UUIDs) and their use as ASN.1 Object Identifier components" ITU-T Rec. X.667, 2004. [4] Rivest, R., "The MD5 Message-Digest Algorithm ", RFC 1321, April 1992. Leach, et al. Standards Track [Page 16] RFC 4122 A UUID URN Namespace July 2005 [5] Eastlake, D., 3rd, Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, June 2005. [6] Moats, R., "URN Syntax", RFC 2141, May 1997. [7] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", RFC 2234, November 1997. [8] National Institute of Standards and Technology, "Secure Hash Standard", FIPS PUB 180-1, April 1995, . Leach, et al. Standards Track [Page 17] RFC 4122 A UUID URN Namespace July 2005 Appendix A. Appendix A - Sample Implementation This implementation consists of 5 files: uuid.h, uuid.c, sysdep.h, sysdep.c and utest.c. The uuid.* files are the system independent implementation of the UUID generation algorithms described above, with all the optimizations described above except efficient state sharing across processes included. The code has been tested on Linux (Red Hat 4.0) with GCC (2.7.2), and Windows NT 4.0 with VC++ 5.0. The code assumes 64-bit integer support, which makes it much clearer. All the following source files should have the following copyright notice included: copyrt.h /* ** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. ** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & ** Digital Equipment Corporation, Maynard, Mass. ** Copyright (c) 1998 Microsoft. ** To anyone who acknowledges that this file is provided "AS IS" ** without any express or implied warranty: permission to use, copy, ** modify, and distribute this file for any purpose is hereby ** granted without fee, provided that the above copyright notices and ** this notice appears in all source code copies, and that none of ** the names of Open Software Foundation, Inc., Hewlett-Packard ** Company, Microsoft, or Digital Equipment Corporation be used in ** advertising or publicity pertaining to distribution of the software ** without specific, written prior permission. Neither Open Software ** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital ** Equipment Corporation makes any representations about the ** suitability of this software for any purpose. */ uuid.h #include "copyrt.h" #undef uuid_t typedef struct { unsigned32 time_low; unsigned16 time_mid; unsigned16 time_hi_and_version; unsigned8 clock_seq_hi_and_reserved; unsigned8 clock_seq_low; byte node[6]; } uuid_t; Leach, et al. Standards Track [Page 18] RFC 4122 A UUID URN Namespace July 2005 /* uuid_create -- generate a UUID */ int uuid_create(uuid_t * uuid); /* uuid_create_md5_from_name -- create a version 3 (MD5) UUID using a "name" from a "name space" */ void uuid_create_md5_from_name( uuid_t *uuid, /* resulting UUID */ uuid_t nsid, /* UUID of the namespace */ void *name, /* the name from which to generate a UUID */ int namelen /* the length of the name */ ); /* uuid_create_sha1_from_name -- create a version 5 (SHA-1) UUID using a "name" from a "name space" */ void uuid_create_sha1_from_name( uuid_t *uuid, /* resulting UUID */ uuid_t nsid, /* UUID of the namespace */ void *name, /* the name from which to generate a UUID */ int namelen /* the length of the name */ ); /* uuid_compare -- Compare two UUID's "lexically" and return -1 u1 is lexically before u2 0 u1 is equal to u2 1 u1 is lexically after u2 Note that lexical ordering is not temporal ordering! */ int uuid_compare(uuid_t *u1, uuid_t *u2); uuid.c #include "copyrt.h" #include #include #include #include #include "sysdep.h" #include "uuid.h" /* various forward declarations */ static int read_state(unsigned16 *clockseq, uuid_time_t *timestamp, uuid_node_t *node); static void write_state(unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node); static void format_uuid_v1(uuid_t *uuid, unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node); Leach, et al. Standards Track [Page 19] RFC 4122 A UUID URN Namespace July 2005 static void format_uuid_v3or5(uuid_t *uuid, unsigned char hash[16], int v); static void get_current_time(uuid_time_t *timestamp); static unsigned16 true_random(void); /* uuid_create -- generator a UUID */ int uuid_create(uuid_t *uuid) { uuid_time_t timestamp, last_time; unsigned16 clockseq; uuid_node_t node; uuid_node_t last_node; int f; /* acquire system-wide lock so we're alone */ LOCK; /* get time, node ID, saved state from non-volatile storage */ get_current_time(×tamp); get_ieee_node_identifier(&node); f = read_state(&clockseq, &last_time, &last_node); /* if no NV state, or if clock went backwards, or node ID changed (e.g., new network card) change clockseq */ if (!f || memcmp(&node, &last_node, sizeof node)) clockseq = true_random(); else if (timestamp < last_time) clockseq++; /* save the state for next time */ write_state(clockseq, timestamp, node); UNLOCK; /* stuff fields into the UUID */ format_uuid_v1(uuid, clockseq, timestamp, node); return 1; } /* format_uuid_v1 -- make a UUID from the timestamp, clockseq, and node ID */ void format_uuid_v1(uuid_t* uuid, unsigned16 clock_seq, uuid_time_t timestamp, uuid_node_t node) { /* Construct a version 1 uuid with the information we've gathered plus a few constants. */ uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF); uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF); uuid->time_hi_and_version = Leach, et al. Standards Track [Page 20] RFC 4122 A UUID URN Namespace July 2005 (unsigned short)((timestamp >> 48) & 0x0FFF); uuid->time_hi_and_version |= (1 << 12); uuid->clock_seq_low = clock_seq & 0xFF; uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8; uuid->clock_seq_hi_and_reserved |= 0x80; memcpy(&uuid->node, &node, sizeof uuid->node); } /* data type for UUID generator persistent state */ typedef struct { uuid_time_t ts; /* saved timestamp */ uuid_node_t node; /* saved node ID */ unsigned16 cs; /* saved clock sequence */ } uuid_state; static uuid_state st; /* read_state -- read UUID generator state from non-volatile store */ int read_state(unsigned16 *clockseq, uuid_time_t *timestamp, uuid_node_t *node) { static int inited = 0; FILE *fp; /* only need to read state once per boot */ if (!inited) { fp = fopen("state", "rb"); if (fp == NULL) return 0; fread(&st, sizeof st, 1, fp); fclose(fp); inited = 1; } *clockseq = st.cs; *timestamp = st.ts; *node = st.node; return 1; } /* write_state -- save UUID generator state back to non-volatile storage */ void write_state(unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node) { static int inited = 0; static uuid_time_t next_save; FILE* fp; Leach, et al. Standards Track [Page 21] RFC 4122 A UUID URN Namespace July 2005 if (!inited) { next_save = timestamp; inited = 1; } /* always save state to volatile shared state */ st.cs = clockseq; st.ts = timestamp; st.node = node; if (timestamp >= next_save) { fp = fopen("state", "wb"); fwrite(&st, sizeof st, 1, fp); fclose(fp); /* schedule next save for 10 seconds from now */ next_save = timestamp + (10 * 10 * 1000 * 1000); } } /* get-current_time -- get time as 60-bit 100ns ticks since UUID epoch. Compensate for the fact that real clock resolution is less than 100ns. */ void get_current_time(uuid_time_t *timestamp) { static int inited = 0; static uuid_time_t time_last; static unsigned16 uuids_this_tick; uuid_time_t time_now; if (!inited) { get_system_time(&time_now); uuids_this_tick = UUIDS_PER_TICK; inited = 1; } for ( ; ; ) { get_system_time(&time_now); /* if clock reading changed since last UUID generated, */ if (time_last != time_now) { /* reset count of uuids gen'd with this clock reading */ uuids_this_tick = 0; time_last = time_now; break; } if (uuids_this_tick < UUIDS_PER_TICK) { uuids_this_tick++; break; } Leach, et al. Standards Track [Page 22] RFC 4122 A UUID URN Namespace July 2005 /* going too fast for our clock; spin */ } /* add the count of uuids to low order bits of the clock reading */ *timestamp = time_now + uuids_this_tick; } /* true_random -- generate a crypto-quality random number. **This sample doesn't do that.** */ static unsigned16 true_random(void) { static int inited = 0; uuid_time_t time_now; if (!inited) { get_system_time(&time_now); time_now = time_now / UUIDS_PER_TICK; srand((unsigned int) (((time_now >> 32) ^ time_now) & 0xffffffff)); inited = 1; } return rand(); } /* uuid_create_md5_from_name -- create a version 3 (MD5) UUID using a "name" from a "name space" */ void uuid_create_md5_from_name(uuid_t *uuid, uuid_t nsid, void *name, int namelen) { MD5_CTX c; unsigned char hash[16]; uuid_t net_nsid; /* put name space ID in network byte order so it hashes the same no matter what endian machine we're on */ net_nsid = nsid; net_nsid.time_low = htonl(net_nsid.time_low); net_nsid.time_mid = htons(net_nsid.time_mid); net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version); MD5Init(&c); MD5Update(&c, &net_nsid, sizeof net_nsid); MD5Update(&c, name, namelen); MD5Final(hash, &c); /* the hash is in network byte order at this point */ format_uuid_v3or5(uuid, hash, 3); } Leach, et al. Standards Track [Page 23] RFC 4122 A UUID URN Namespace July 2005 void uuid_create_sha1_from_name(uuid_t *uuid, uuid_t nsid, void *name, int namelen) { SHA_CTX c; unsigned char hash[20]; uuid_t net_nsid; /* put name space ID in network byte order so it hashes the same no matter what endian machine we're on */ net_nsid = nsid; net_nsid.time_low = htonl(net_nsid.time_low); net_nsid.time_mid = htons(net_nsid.time_mid); net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version); SHA1_Init(&c); SHA1_Update(&c, &net_nsid, sizeof net_nsid); SHA1_Update(&c, name, namelen); SHA1_Final(hash, &c); /* the hash is in network byte order at this point */ format_uuid_v3or5(uuid, hash, 5); } /* format_uuid_v3or5 -- make a UUID from a (pseudo)random 128-bit number */ void format_uuid_v3or5(uuid_t *uuid, unsigned char hash[16], int v) { /* convert UUID to local byte order */ memcpy(uuid, hash, sizeof *uuid); uuid->time_low = ntohl(uuid->time_low); uuid->time_mid = ntohs(uuid->time_mid); uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version); /* put in the variant and version bits */ uuid->time_hi_and_version &= 0x0FFF; uuid->time_hi_and_version |= (v << 12); uuid->clock_seq_hi_and_reserved &= 0x3F; uuid->clock_seq_hi_and_reserved |= 0x80; } /* uuid_compare -- Compare two UUID's "lexically" and return */ #define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1; int uuid_compare(uuid_t *u1, uuid_t *u2) { int i; CHECK(u1->time_low, u2->time_low); CHECK(u1->time_mid, u2->time_mid); Leach, et al. Standards Track [Page 24] RFC 4122 A UUID URN Namespace July 2005 CHECK(u1->time_hi_and_version, u2->time_hi_and_version); CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved); CHECK(u1->clock_seq_low, u2->clock_seq_low) for (i = 0; i < 6; i++) { if (u1->node[i] < u2->node[i]) return -1; if (u1->node[i] > u2->node[i]) return 1; } return 0; } #undef CHECK sysdep.h #include "copyrt.h" /* remove the following define if you aren't running WIN32 */ #define WININC 0 #ifdef WININC #include #else #include #include #include #endif #include "global.h" /* change to point to where MD5 .h's live; RFC 1321 has sample implementation */ #include "md5.h" /* set the following to the number of 100ns ticks of the actual resolution of your system's clock */ #define UUIDS_PER_TICK 1024 /* Set the following to a calls to get and release a global lock */ #define LOCK #define UNLOCK typedef unsigned long unsigned32; typedef unsigned short unsigned16; typedef unsigned char unsigned8; typedef unsigned char byte; /* Set this to what your compiler uses for 64-bit data type */ #ifdef WININC Leach, et al. Standards Track [Page 25] RFC 4122 A UUID URN Namespace July 2005 #define unsigned64_t unsigned __int64 #define I64(C) C #else #define unsigned64_t unsigned long long #define I64(C) C##LL #endif typedef unsigned64_t uuid_time_t; typedef struct { char nodeID[6]; } uuid_node_t; void get_ieee_node_identifier(uuid_node_t *node); void get_system_time(uuid_time_t *uuid_time); void get_random_info(char seed[16]); sysdep.c #include "copyrt.h" #include #include "sysdep.h" /* system dependent call to get IEEE node ID. This sample implementation generates a random node ID. */ void get_ieee_node_identifier(uuid_node_t *node) { static inited = 0; static uuid_node_t saved_node; char seed[16]; FILE *fp; if (!inited) { fp = fopen("nodeid", "rb"); if (fp) { fread(&saved_node, sizeof saved_node, 1, fp); fclose(fp); } else { get_random_info(seed); seed[0] |= 0x01; memcpy(&saved_node, seed, sizeof saved_node); fp = fopen("nodeid", "wb"); if (fp) { fwrite(&saved_node, sizeof saved_node, 1, fp); fclose(fp); } } Leach, et al. Standards Track [Page 26] RFC 4122 A UUID URN Namespace July 2005 inited = 1; } *node = saved_node; } /* system dependent call to get the current system time. Returned as 100ns ticks since UUID epoch, but resolution may be less than 100ns. */ #ifdef _WINDOWS_ void get_system_time(uuid_time_t *uuid_time) { ULARGE_INTEGER time; /* NT keeps time in FILETIME format which is 100ns ticks since Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) + 18 years and 5 leap days. */ GetSystemTimeAsFileTime((FILETIME *)&time); time.QuadPart += (unsigned __int64) (1000*1000*10) // seconds * (unsigned __int64) (60 * 60 * 24) // days * (unsigned __int64) (17+30+31+365*18+5); // # of days *uuid_time = time.QuadPart; } /* Sample code, not for use in production; see RFC 1750 */ void get_random_info(char seed[16]) { MD5_CTX c; struct { MEMORYSTATUS m; SYSTEM_INFO s; FILETIME t; LARGE_INTEGER pc; DWORD tc; DWORD l; char hostname[MAX_COMPUTERNAME_LENGTH + 1]; } r; MD5Init(&c); GlobalMemoryStatus(&r.m); GetSystemInfo(&r.s); GetSystemTimeAsFileTime(&r.t); QueryPerformanceCounter(&r.pc); r.tc = GetTickCount(); Leach, et al. Standards Track [Page 27] RFC 4122 A UUID URN Namespace July 2005 r.l = MAX_COMPUTERNAME_LENGTH + 1; GetComputerName(r.hostname, &r.l); MD5Update(&c, &r, sizeof r); MD5Final(seed, &c); } #else void get_system_time(uuid_time_t *uuid_time) { struct timeval tp; gettimeofday(&tp, (struct timezone *)0); /* Offset between UUID formatted times and Unix formatted times. UUID UTC base time is October 15, 1582. Unix base time is January 1, 1970.*/ *uuid_time = ((unsigned64)tp.tv_sec * 10000000) + ((unsigned64)tp.tv_usec * 10) + I64(0x01B21DD213814000); } /* Sample code, not for use in production; see RFC 1750 */ void get_random_info(char seed[16]) { MD5_CTX c; struct { struct sysinfo s; struct timeval t; char hostname[257]; } r; MD5Init(&c); sysinfo(&r.s); gettimeofday(&r.t, (struct timezone *)0); gethostname(r.hostname, 256); MD5Update(&c, &r, sizeof r); MD5Final(seed, &c); } #endif utest.c #include "copyrt.h" #include "sysdep.h" #include #include "uuid.h" Leach, et al. Standards Track [Page 28] RFC 4122 A UUID URN Namespace July 2005 uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; /* puid -- print a UUID */ void puid(uuid_t u) { int i; printf("%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", u.time_low, u.time_mid, u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low); for (i = 0; i < 6; i++) printf("%2.2x", u.node[i]); printf("\n"); } /* Simple driver for UUID generator */ void main(int argc, char **argv) { uuid_t u; int f; uuid_create(&u); printf("uuid_create(): "); puid(u); f = uuid_compare(&u, &u); printf("uuid_compare(u,u): %d\n", f); /* should be 0 */ f = uuid_compare(&u, &NameSpace_DNS); printf("uuid_compare(u, NameSpace_DNS): %d\n", f); /* s.b. 1 */ f = uuid_compare(&NameSpace_DNS, &u); printf("uuid_compare(NameSpace_DNS, u): %d\n", f); /* s.b. -1 */ uuid_create_md5_from_name(&u, NameSpace_DNS, "www.widgets.com", 15); printf("uuid_create_md5_from_name(): "); puid(u); } Appendix B. Appendix B - Sample Output of utest uuid_create(): 7d444840-9dc0-11d1-b245-5ffdce74fad2 uuid_compare(u,u): 0 uuid_compare(u, NameSpace_DNS): 1 uuid_compare(NameSpace_DNS, u): -1 uuid_create_md5_from_name(): e902893a-9d22-3c7e-a7b8-d6e313b71d9f Leach, et al. Standards Track [Page 29] RFC 4122 A UUID URN Namespace July 2005 Appendix C. Appendix C - Some Name Space IDs This appendix lists the name space IDs for some potentially interesting name spaces, as initialized C structures and in the string representation defined above. /* Name string is a fully-qualified domain name */ uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; /* Name string is a URL */ uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */ 0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; /* Name string is an ISO OID */ uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */ 0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; /* Name string is an X.500 DN (in DER or a text output format) */ uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */ 0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; Leach, et al. Standards Track [Page 30] RFC 4122 A UUID URN Namespace July 2005 Authors' Addresses Paul J. Leach Microsoft 1 Microsoft Way Redmond, WA 98052 US Phone: +1 425-882-8080 EMail: paulle@microsoft.com Michael Mealling Refactored Networks, LLC 1635 Old Hwy 41 Suite 112, Box 138 Kennesaw, GA 30152 US Phone: +1-678-581-9656 EMail: michael@refactored-networks.com URI: http://www.refactored-networks.com Rich Salz DataPower Technology, Inc. 1 Alewife Center Cambridge, MA 02142 US Phone: +1 617-864-0455 EMail: rsalz@datapower.com URI: http://www.datapower.com Leach, et al. Standards Track [Page 31] RFC 4122 A UUID URN Namespace July 2005 Full Copyright Statement Copyright (C) The Internet Society (2005). This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights. This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Intellectual Property The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79. Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr. The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf- ipr@ietf.org. Acknowledgement Funding for the RFC Editor function is currently provided by the Internet Society. Leach, et al. Standards Track [Page 32] ================================================ FILE: doc/rfc9562.txt ================================================  Internet Engineering Task Force (IETF) K. Davis Request for Comments: 9562 Cisco Systems Obsoletes: 4122 B. Peabody Category: Standards Track Uncloud ISSN: 2070-1721 P. Leach University of Washington May 2024 Universally Unique IDentifiers (UUIDs) Abstract This specification defines UUIDs (Universally Unique IDentifiers) -- also known as GUIDs (Globally Unique IDentifiers) -- and a Uniform Resource Name namespace for UUIDs. A UUID is 128 bits long and is intended to guarantee uniqueness across space and time. UUIDs were originally used in the Apollo Network Computing System (NCS), later in the Open Software Foundation's (OSF's) Distributed Computing Environment (DCE), and then in Microsoft Windows platforms. This specification is derived from the OSF DCE specification with the kind permission of the OSF (now known as "The Open Group"). Information from earlier versions of the OSF DCE specification have been incorporated into this document. This document obsoletes RFC 4122. Status of This Memo This is an Internet Standards Track document. This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 7841. Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://www.rfc-editor.org/info/rfc9562. Copyright Notice Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License. Table of Contents 1. Introduction 2. Motivation 2.1. Update Motivation 3. Terminology 3.1. Requirements Language 3.2. Abbreviations 4. UUID Format 4.1. Variant Field 4.2. Version Field 5. UUID Layouts 5.1. UUID Version 1 5.2. UUID Version 2 5.3. UUID Version 3 5.4. UUID Version 4 5.5. UUID Version 5 5.6. UUID Version 6 5.7. UUID Version 7 5.8. UUID Version 8 5.9. Nil UUID 5.10. Max UUID 6. UUID Best Practices 6.1. Timestamp Considerations 6.2. Monotonicity and Counters 6.3. UUID Generator States 6.4. Distributed UUID Generation 6.5. Name-Based UUID Generation 6.6. Namespace ID Usage and Allocation 6.7. Collision Resistance 6.8. Global and Local Uniqueness 6.9. Unguessability 6.10. UUIDs That Do Not Identify the Host 6.11. Sorting 6.12. Opacity 6.13. DBMS and Database Considerations 7. IANA Considerations 7.1. IANA UUID Subtype Registry and Registration 7.2. IANA UUID Namespace ID Registry and Registration 8. Security Considerations 9. References 9.1. Normative References 9.2. Informative References Appendix A. Test Vectors A.1. Example of a UUIDv1 Value A.2. Example of a UUIDv3 Value A.3. Example of a UUIDv4 Value A.4. Example of a UUIDv5 Value A.5. Example of a UUIDv6 Value A.6. Example of a UUIDv7 Value Appendix B. Illustrative Examples B.1. Example of a UUIDv8 Value (Time-Based) B.2. Example of a UUIDv8 Value (Name-Based) Acknowledgements Authors' Addresses 1. Introduction This specification defines a Uniform Resource Name namespace for Universally Unique IDentifiers (UUIDs), also known as Globally Unique IDentifiers (GUIDs). A UUID is 128 bits long and requires no central registration process. The use of UUIDs is extremely pervasive in computing. They comprise the core identifier infrastructure for many operating systems such as Microsoft Windows and applications such as the Mozilla Web browser; in many cases, they can become exposed in many non-standard ways. This specification attempts to standardize that practice as openly as possible and in a way that attempts to benefit the entire Internet. The information here is meant to be a concise guide for those wishing to implement services using UUIDs either in combination with URNs [RFC8141] or otherwise. There is an ITU-T Recommendation and an ISO/IEC Standard [X667] that are derived from [RFC4122]. Both sets of specifications have been aligned and are fully technically compatible. Nothing in this document should be construed to override the DCE standards that defined UUIDs. 2. Motivation One of the main reasons for using UUIDs is that no centralized authority is required to administer them (although two formats may leverage optional IEEE 802 Node IDs, others do not). As a result, generation on demand can be completely automated and used for a variety of purposes. The UUID generation algorithm described here supports very high allocation rates of 10 million per second per machine or more, if necessary, so that they could even be used as transaction IDs. UUIDs are of a fixed size (128 bits), which is reasonably small compared to other alternatives. This lends itself well to sorting, ordering, and hashing of all sorts; storing in databases; simple allocation; and ease of programming in general. Since UUIDs are unique and persistent, they make excellent URNs. The unique ability to generate a new UUID without a registration process allows for UUIDs to be one of the URNs with the lowest minting cost. 2.1. Update Motivation Many things have changed in the time since UUIDs were originally created. Modern applications have a need to create and utilize UUIDs as the primary identifier for a variety of different items in complex computational systems, including but not limited to database keys, file names, machine or system names, and identifiers for event-driven transactions. One area in which UUIDs have gained popularity is database keys. This stems from the increasingly distributed nature of modern applications. In such cases, "auto-increment" schemes that are often used by databases do not work well: the effort required to coordinate sequential numeric identifiers across a network can easily become a burden. The fact that UUIDs can be used to create unique, reasonably short values in distributed systems without requiring coordination makes them a good alternative, but UUID versions 1-5, which were originally defined by [RFC4122], lack certain other desirable characteristics, such as: 1. UUID versions that are not time ordered, such as UUIDv4 (described in Section 5.4), have poor database-index locality. This means that new values created in succession are not close to each other in the index; thus, they require inserts to be performed at random locations. The resulting negative performance effects on the common structures used for this (B-tree and its variants) can be dramatic. 2. The 100-nanosecond Gregorian Epoch used in UUIDv1 timestamps (described in Section 5.1) is uncommon and difficult to represent accurately using a standard number format such as that described in [IEEE754]. 3. Introspection/parsing is required to order by time sequence, as opposed to being able to perform a simple byte-by-byte comparison. 4. Privacy and network security issues arise from using a Media Access Control (MAC) address in the node field of UUIDv1. Exposed MAC addresses can be used as an attack surface to locate network interfaces and reveal various other information about such machines (minimally, the manufacturer and, potentially, other details). Additionally, with the advent of virtual machines and containers, uniqueness of the MAC address is no longer guaranteed. 5. Many of the implementation details specified in [RFC4122] involved trade-offs that are neither possible to specify for all applications nor necessary to produce interoperable implementations. 6. [RFC4122] did not distinguish between the requirements for generating a UUID and those for simply storing one, although they are often different. Due to the aforementioned issues, many widely distributed database applications and large application vendors have sought to solve the problem of creating a better time-based, sortable unique identifier for use as a database key. This has led to numerous implementations over the past 10+ years solving the same problem in slightly different ways. While preparing this specification, the following 16 different implementations were analyzed for trends in total ID length, bit layout, lexical formatting and encoding, timestamp type, timestamp format, timestamp accuracy, node format and components, collision handling, and multi-timestamp tick generation sequencing: 1. [ULID] 2. [LexicalUUID] 3. [Snowflake] 4. [Flake] 5. [ShardingID] 6. [KSUID] 7. [Elasticflake] 8. [FlakeID] 9. [Sonyflake] 10. [orderedUuid] 11. [COMBGUID] 12. [SID] 13. [pushID] 14. [XID] 15. [ObjectID] 16. [CUID] An inspection of these implementations and the issues described above has led to this document, in which new UUIDs are adapted to address these issues. Further, [RFC4122] itself was in need of an overhaul to address a number of topics such as, but not limited to, the following: 1. Implementation of miscellaneous errata reports. Mostly around bit-layout clarifications, which lead to inconsistent implementations [Err1957], [Err3546], [Err4975], [Err4976], [Err5560], etc. 2. Decoupling other UUID versions from the UUIDv1 bit layout so that fields like "time_hi_and_version" do not need to be referenced within a UUID that is not time based while also providing definition sections similar to that for UUIDv1 for UUIDv3, UUIDv4, and UUIDv5. 3. Providing implementation best practices around many real-world scenarios and corner cases observed by existing and prototype implementations. 4. Addressing security best practices and considerations for the modern age as it pertains to MAC addresses, hashing algorithms, secure randomness, and other topics. 5. Providing implementations a standard-based option for implementation-specific and/or experimental UUID designs. 6. Providing more test vectors that illustrate real UUIDs created as per the specification. 3. Terminology 3.1. Requirements Language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. 3.2. Abbreviations The following abbreviations are used in this document: ABNF Augmented Backus-Naur Form CSPRNG Cryptographically Secure Pseudorandom Number Generator DBMS Database Management System IEEE Institute of Electrical and Electronics Engineers ITU International Telecommunication Union MAC Media Access Control MD5 Message Digest 5 MSB Most Significant Bit OID Object Identifier SHA Secure Hash Algorithm SHA-1 Secure Hash Algorithm 1 (with message digest of 160 bits) SHA-3 Secure Hash Algorithm 3 (arbitrary size) SHA-224 Secure Hash Algorithm 2 with message digest size of 224 bits SHA-256 Secure Hash Algorithm 2 with message digest size of 256 bits SHA-512 Secure Hash Algorithm 2 with message digest size of 512 bits SHAKE Secure Hash Algorithm 3 based on the KECCAK algorithm URN Uniform Resource Names UTC Coordinated Universal Time UUID Universally Unique Identifier UUIDv1 Universally Unique Identifier version 1 UUIDv2 Universally Unique Identifier version 2 UUIDv3 Universally Unique Identifier version 3 UUIDv4 Universally Unique Identifier version 4 UUIDv5 Universally Unique Identifier version 5 UUIDv6 Universally Unique Identifier version 6 UUIDv7 Universally Unique Identifier version 7 UUIDv8 Universally Unique Identifier version 8 4. UUID Format The UUID format is 16 octets (128 bits) in size; the variant bits in conjunction with the version bits described in the next sections determine finer structure. In terms of these UUID formats and layout, bit definitions start at 0 and end at 127, while octet definitions start at 0 and end at 15. In the absence of explicit application or presentation protocol specification to the contrary, each field is encoded with the most significant byte first (known as "network byte order"). Saving UUIDs to binary format is done by sequencing all fields in big-endian format. However, there is a known caveat that Microsoft's Component Object Model (COM) GUIDs leverage little-endian when saving GUIDs. The discussion of this (see [MS_COM_GUID]) is outside the scope of this specification. UUIDs MAY be represented as binary data or integers. When in use with URNs or as text in applications, any given UUID should be represented by the "hex-and-dash" string format consisting of multiple groups of uppercase or lowercase alphanumeric hexadecimal characters separated by single dashes/hyphens. When used with databases, please refer to Section 6.13. The formal definition of the UUID string representation is provided by the following ABNF [RFC5234]: UUID = 4hexOctet "-" 2hexOctet "-" 2hexOctet "-" 2hexOctet "-" 6hexOctet hexOctet = HEXDIG HEXDIG DIGIT = %x30-39 HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" Note that the alphabetic characters may be all uppercase, all lowercase, or mixed case, as per Section 2.3 of [RFC5234]. An example UUID using this textual representation from the above ABNF is shown in Figure 1. f81d4fae-7dec-11d0-a765-00a0c91e6bf6 Figure 1: Example String UUID Format The same UUID from Figure 1 is represented in binary (Figure 2), as an unsigned integer (Figure 3), and as a URN (Figure 4) defined by [RFC8141]. 111110000001110101001111101011100111110111101100000100011101000\ 01010011101100101000000001010000011001001000111100110101111110110 Figure 2: Example Binary UUID 329800735698586629295641978511506172918 Figure 3: Example Unsigned Integer UUID (Shown as a Decimal Number) urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 Figure 4: Example URN Namespace for UUID There are many other ways to define a UUID format; some examples are detailed below. Please note that this is not an exhaustive list and is only provided for informational purposes. * Some UUID implementations, such as those found in [Python] and [Microsoft], will output UUID with the string format, including dashes, enclosed in curly braces. * [X667] provides UUID format definitions for use of UUID with an OID. * [IBM_NCS] is a legacy implementation that produces a unique UUID format compatible with Variant 0xx of Table 1. 4.1. Variant Field The variant field determines the layout of the UUID. That is, the interpretation of all other bits in the UUID depends on the setting of the bits in the variant field. As such, it could more accurately be called a "type" field; we retain the original term for compatibility. The variant field consists of a variable number of the most significant bits of octet 8 of the UUID. Table 1 lists the contents of the variant field, where the letter "x" indicates a "don't-care" value. +======+======+======+======+=========+=========================+ | MSB0 | MSB1 | MSB2 | MSB3 | Variant | Description | +======+======+======+======+=========+=========================+ | 0 | x | x | x | 1-7 | Reserved. Network | | | | | | | Computing System (NCS) | | | | | | | backward compatibility, | | | | | | | and includes Nil UUID | | | | | | | as per Section 5.9. | +------+------+------+------+---------+-------------------------+ | 1 | 0 | x | x | 8-9,A-B | The variant specified | | | | | | | in this document. | +------+------+------+------+---------+-------------------------+ | 1 | 1 | 0 | x | C-D | Reserved. Microsoft | | | | | | | Corporation backward | | | | | | | compatibility. | +------+------+------+------+---------+-------------------------+ | 1 | 1 | 1 | x | E-F | Reserved for future | | | | | | | definition and includes | | | | | | | Max UUID as per | | | | | | | Section 5.10. | +------+------+------+------+---------+-------------------------+ Table 1: UUID Variants Interoperability, in any form, with variants other than the one defined here is not guaranteed but is not likely to be an issue in practice. Specifically for UUIDs in this document, bits 64 and 65 of the UUID (bits 0 and 1 of octet 8) MUST be set to 1 and 0 as specified in row 2 of Table 1. Accordingly, all bit and field layouts avoid the use of these bits. 4.2. Version Field The version number is in the most significant 4 bits of octet 6 (bits 48 through 51 of the UUID). Table 2 lists all of the versions for this UUID variant 10xx specified in this document. +======+======+======+======+=========+============================+ | MSB0 | MSB1 | MSB2 | MSB3 | Version | Description | +======+======+======+======+=========+============================+ | 0 | 0 | 0 | 0 | 0 | Unused. | +------+------+------+------+---------+----------------------------+ | 0 | 0 | 0 | 1 | 1 | The Gregorian time-based | | | | | | | UUID specified in this | | | | | | | document. | +------+------+------+------+---------+----------------------------+ | 0 | 0 | 1 | 0 | 2 | Reserved for DCE Security | | | | | | | version, with embedded | | | | | | | POSIX UUIDs. | +------+------+------+------+---------+----------------------------+ | 0 | 0 | 1 | 1 | 3 | The name-based version | | | | | | | specified in this document | | | | | | | that uses MD5 hashing. | +------+------+------+------+---------+----------------------------+ | 0 | 1 | 0 | 0 | 4 | The randomly or | | | | | | | pseudorandomly generated | | | | | | | version specified in this | | | | | | | document. | +------+------+------+------+---------+----------------------------+ | 0 | 1 | 0 | 1 | 5 | The name-based version | | | | | | | specified in this document | | | | | | | that uses SHA-1 hashing. | +------+------+------+------+---------+----------------------------+ | 0 | 1 | 1 | 0 | 6 | Reordered Gregorian time- | | | | | | | based UUID specified in | | | | | | | this document. | +------+------+------+------+---------+----------------------------+ | 0 | 1 | 1 | 1 | 7 | Unix Epoch time-based UUID | | | | | | | specified in this | | | | | | | document. | +------+------+------+------+---------+----------------------------+ | 1 | 0 | 0 | 0 | 8 | Reserved for custom UUID | | | | | | | formats specified in this | | | | | | | document. | +------+------+------+------+---------+----------------------------+ | 1 | 0 | 0 | 1 | 9 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 0 | 1 | 0 | 10 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 0 | 1 | 1 | 11 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 1 | 0 | 0 | 12 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 1 | 0 | 1 | 13 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 1 | 1 | 0 | 14 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ | 1 | 1 | 1 | 1 | 15 | Reserved for future | | | | | | | definition. | +------+------+------+------+---------+----------------------------+ Table 2: UUID Variant 10xx Versions Defined by This Specification An example version/variant layout for UUIDv4 follows the table where "M" represents the version placement for the hexadecimal representation of 0x4 (0b0100) and the "N" represents the variant placement for one of the four possible hexadecimal representation of variant 10xx: 0x8 (0b1000), 0x9 (0b1001), 0xA (0b1010), 0xB (0b1011). 00000000-0000-4000-8000-000000000000 00000000-0000-4000-9000-000000000000 00000000-0000-4000-A000-000000000000 00000000-0000-4000-B000-000000000000 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx Figure 5: UUIDv4 Variant Examples It should be noted that the other remaining UUID variants found in Table 1 leverage different sub-typing or versioning mechanisms. The recording and definition of the remaining UUID variant and sub-typing combinations are outside of the scope of this document. 5. UUID Layouts To minimize confusion about bit assignments within octets and among differing versions, the UUID record definition is provided as a grouping of fields within a bit layout consisting of four octets per row. The fields are presented with the most significant one first. 5.1. UUID Version 1 UUIDv1 is a time-based UUID featuring a 60-bit timestamp represented by Coordinated Universal Time (UTC) as a count of 100-nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar). UUIDv1 also features a clock sequence field that is used to help avoid duplicates that could arise when the clock is set backwards in time or if the Node ID changes. The node field consists of an IEEE 802 MAC address, usually the host address or a randomly derived value per Sections 6.9 and 6.10. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_mid | ver | time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| clock_seq | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 6: UUIDv1 Field and Bit Layout time_low: The least significant 32 bits of the 60-bit starting timestamp. Occupies bits 0 through 31 (octets 0-3). time_mid: The middle 16 bits of the 60-bit starting timestamp. Occupies bits 32 through 47 (octets 4-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0001 (1). Occupies bits 48 through 51 of octet 6. time_high: The least significant 12 bits from the 60-bit starting timestamp. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. clock_seq: The 14 bits containing the clock sequence. Occupies bits 66 through 79 (octets 8-9). node: 48-bit spatially unique identifier. Occupies bits 80 through 127 (octets 10-15). For systems that do not have UTC available but do have the local time, they may use that instead of UTC as long as they do so consistently throughout the system. However, this is not recommended since generating the UTC from local time only needs a time-zone offset. If the clock is set backwards, or if it might have been set backwards (e.g., while the system was powered off), and the UUID generator cannot be sure that no UUIDs were generated with timestamps larger than the value to which the clock was set, then the clock sequence MUST be changed. If the previous value of the clock sequence is known, it MAY be incremented; otherwise it SHOULD be set to a random or high-quality pseudorandom value. Similarly, if the Node ID changes (e.g., because a network card has been moved between machines), setting the clock sequence to a random number minimizes the probability of a duplicate due to slight differences in the clock settings of the machines. If the value of the clock sequence associated with the changed Node ID were known, then the clock sequence MAY be incremented, but that is unlikely. The clock sequence MUST be originally (i.e., once in the lifetime of a system) initialized to a random number to minimize the correlation across systems. This provides maximum protection against Node IDs that may move or switch from system to system rapidly. The initial value MUST NOT be correlated to the Node ID. Notes about nodes derived from IEEE 802: * On systems with multiple IEEE 802 addresses, any available one MAY be used. * On systems with no IEEE address, a randomly or pseudorandomly generated value MUST be used; see Sections 6.9 and 6.10. * On systems utilizing a 64-bit MAC address, the least significant, rightmost 48 bits MAY be used. * Systems utilizing an IEEE 802.15.4 16-bit address SHOULD instead utilize their 64-bit MAC address where the least significant, rightmost 48 bits MAY be used. An alternative is to generate 32 bits of random data and postfix at the end of the 16-bit MAC address to create a 48-bit value. 5.2. UUID Version 2 UUIDv2 is for DCE Security UUIDs (see [C309] and [C311]). As such, the definition of these UUIDs is outside the scope of this specification. 5.3. UUID Version 3 UUIDv3 is meant for generating UUIDs from names that are drawn from, and unique within, some namespace as per Section 6.5. UUIDv3 values are created by computing an MD5 hash [RFC1321] over a given Namespace ID value (Section 6.6) concatenated with the desired name value after both have been converted to a canonical sequence of octets, as defined by the standards or conventions of its namespace, in network byte order. This MD5 value is then used to populate all 128 bits of the UUID layout. The UUID version and variant then replace the respective bits as defined by Sections 4.2 and 4.1. An example of this bit substitution can be found in Appendix A.2. Information around selecting a desired name's canonical format within a given namespace can be found in Section 6.5 under the heading "A note on names". Where possible, UUIDv5 SHOULD be used in lieu of UUIDv3. For more information on MD5 security considerations, see [RFC6151]. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | md5_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | md5_high | ver | md5_mid | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| md5_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | md5_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 7: UUIDv3 Field and Bit Layout md5_high: The first 48 bits of the layout are filled with the most significant, leftmost 48 bits from the computed MD5 value. Occupies bits 0 through 47 (octets 0-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0011 (3). Occupies bits 48 through 51 of octet 6. md5_mid: 12 more bits of the layout consisting of the least significant, rightmost 12 bits of 16 bits immediately following md5_high from the computed MD5 value. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. md5_low: The final 62 bits of the layout immediately following the var field to be filled with the least significant, rightmost bits of the final 64 bits from the computed MD5 value. Occupies bits 66 through 127 (octets 8-15) 5.4. UUID Version 4 UUIDv4 is meant for generating UUIDs from truly random or pseudorandom numbers. An implementation may generate 128 bits of random data that is used to fill out the UUID fields in Figure 8. The UUID version and variant then replace the respective bits as defined by Sections 4.1 and 4.2. Alternatively, an implementation MAY choose to randomly generate the exact required number of bits for random_a, random_b, and random_c (122 bits total) and then concatenate the version and variant in the required position. For guidelines on random data generation, see Section 6.9. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_a | ver | random_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| random_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 8: UUIDv4 Field and Bit Layout random_a: The first 48 bits of the layout that can be filled with random data as specified in Section 6.9. Occupies bits 0 through 47 (octets 0-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0100 (4). Occupies bits 48 through 51 of octet 6. random_b: 12 more bits of the layout that can be filled random data as per Section 6.9. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. random_c: The final 62 bits of the layout immediately following the var field to be filled with random data as per Section 6.9. Occupies bits 66 through 127 (octets 8-15). 5.5. UUID Version 5 UUIDv5 is meant for generating UUIDs from "names" that are drawn from, and unique within, some "namespace" as per Section 6.5. UUIDv5 values are created by computing an SHA-1 hash [FIPS180-4] over a given Namespace ID value (Section 6.6) concatenated with the desired name value after both have been converted to a canonical sequence of octets, as defined by the standards or conventions of its namespace, in network byte order. The most significant, leftmost 128 bits of the SHA-1 value are then used to populate all 128 bits of the UUID layout, and the remaining 32 least significant, rightmost bits of SHA-1 output are discarded. The UUID version and variant then replace the respective bits as defined by Sections 4.2 and 4.1. An example of this bit substitution and discarding excess bits can be found in Appendix A.4. Information around selecting a desired name's canonical format within a given namespace can be found in Section 6.5 under the heading "A note on names". There may be scenarios, usually depending on organizational security policies, where SHA-1 libraries may not be available or may be deemed unsafe for use. As such, it may be desirable to generate name-based UUIDs derived from SHA-256 or newer SHA methods. These name-based UUIDs MUST NOT utilize UUIDv5 and MUST be within the UUIDv8 space defined by Section 5.8. An illustrative example of UUIDv8 for SHA-256 name-based UUIDs is provided in Appendix B.2. For more information on SHA-1 security considerations, see [RFC6194]. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sha1_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sha1_high | ver | sha1_mid | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| sha1_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sha1_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 9: UUIDv5 Field and Bit Layout sha1_high: The first 48 bits of the layout are filled with the most significant, leftmost 48 bits from the computed SHA-1 value. Occupies bits 0 through 47 (octets 0-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0101 (5). Occupies bits 48 through 51 of octet 6. sha1_mid: 12 more bits of the layout consisting of the least significant, rightmost 12 bits of 16 bits immediately following sha1_high from the computed SHA-1 value. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. sha1_low: The final 62 bits of the layout immediately following the var field to be filled by skipping the two most significant, leftmost bits of the remaining SHA-1 hash and then using the next 62 most significant, leftmost bits. Any leftover SHA-1 bits are discarded and unused. Occupies bits 66 through 127 (octets 8-15). 5.6. UUID Version 6 UUIDv6 is a field-compatible version of UUIDv1 (Section 5.1), reordered for improved DB locality. It is expected that UUIDv6 will primarily be implemented in contexts where UUIDv1 is used. Systems that do not involve legacy UUIDv1 SHOULD use UUIDv7 (Section 5.7) instead. Instead of splitting the timestamp into the low, mid, and high sections from UUIDv1, UUIDv6 changes this sequence so timestamp bytes are stored from most to least significant. That is, given a 60-bit timestamp value as specified for UUIDv1 in Section 5.1, for UUIDv6 the first 48 most significant bits are stored first, followed by the 4-bit version (same position), followed by the remaining 12 bits of the original 60-bit timestamp. The clock sequence and node bits remain unchanged from their position in Section 5.1. The clock sequence and node bits SHOULD be reset to a pseudorandom value for each new UUIDv6 generated; however, implementations MAY choose to retain the old clock sequence and MAC address behavior from Section 5.1. For more information on MAC address usage within UUIDs, see the Section 8. The format for the 16-byte, 128-bit UUIDv6 is shown in Figure 10. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_mid | ver | time_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| clock_seq | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 10: UUIDv6 Field and Bit Layout time_high: The most significant 32 bits of the 60-bit starting timestamp. Occupies bits 0 through 31 (octets 0-3). time_mid: The middle 16 bits of the 60-bit starting timestamp. Occupies bits 32 through 47 (octets 4-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0110 (6). Occupies bits 48 through 51 of octet 6. time_low: 12 bits that will contain the least significant 12 bits from the 60-bit starting timestamp. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. clock_seq: The 14 bits containing the clock sequence. Occupies bits 66 through 79 (octets 8-9). node: 48-bit spatially unique identifier. Occupies bits 80 through 127 (octets 10-15). With UUIDv6, the steps for splitting the timestamp into time_high and time_mid are OPTIONAL since the 48 bits of time_high and time_mid will remain in the same order. An extra step of splitting the first 48 bits of the timestamp into the most significant 32 bits and least significant 16 bits proves useful when reusing an existing UUIDv1 implementation. 5.7. UUID Version 7 UUIDv7 features a time-ordered value field derived from the widely implemented and well-known Unix Epoch timestamp source, the number of milliseconds since midnight 1 Jan 1970 UTC, leap seconds excluded. Generally, UUIDv7 has improved entropy characteristics over UUIDv1 (Section 5.1) or UUIDv6 (Section 5.6). UUIDv7 values are created by allocating a Unix timestamp in milliseconds in the most significant 48 bits and filling the remaining 74 bits, excluding the required version and variant bits, with random bits for each new UUIDv7 generated to provide uniqueness as per Section 6.9. Alternatively, implementations MAY fill the 74 bits, jointly, with a combination of the following subfields, in this order from the most significant bits to the least, to guarantee additional monotonicity within a millisecond: 1. An OPTIONAL sub-millisecond timestamp fraction (12 bits at maximum) as per Section 6.2 (Method 3). 2. An OPTIONAL carefully seeded counter as per Section 6.2 (Method 1 or 2). 3. Random data for each new UUIDv7 generated for any remaining space. Implementations SHOULD utilize UUIDv7 instead of UUIDv1 and UUIDv6 if possible. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | ver | rand_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 11: UUIDv7 Field and Bit Layout unix_ts_ms: 48-bit big-endian unsigned number of the Unix Epoch timestamp in milliseconds as per Section 6.1. Occupies bits 0 through 47 (octets 0-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b0111 (7). Occupies bits 48 through 51 of octet 6. rand_a: 12 bits of pseudorandom data to provide uniqueness as per Section 6.9 and/or optional constructs to guarantee additional monotonicity as per Section 6.2. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. rand_b: The final 62 bits of pseudorandom data to provide uniqueness as per Section 6.9 and/or an optional counter to guarantee additional monotonicity as per Section 6.2. Occupies bits 66 through 127 (octets 8-15). 5.8. UUID Version 8 UUIDv8 provides a format for experimental or vendor-specific use cases. The only requirement is that the variant and version bits MUST be set as defined in Sections 4.1 and 4.2. UUIDv8's uniqueness will be implementation specific and MUST NOT be assumed. The only explicitly defined bits are those of the version and variant fields, leaving 122 bits for implementation-specific UUIDs. To be clear, UUIDv8 is not a replacement for UUIDv4 (Section 5.4) where all 122 extra bits are filled with random data. Some example situations in which UUIDv8 usage could occur: * An implementation would like to embed extra information within the UUID other than what is defined in this document. * An implementation has other application and/or language restrictions that inhibit the use of one of the current UUIDs. Appendix B provides two illustrative examples of custom UUIDv8 algorithms to address two example scenarios. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_a | ver | custom_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| custom_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 12: UUIDv8 Field and Bit Layout custom_a: The first 48 bits of the layout that can be filled as an implementation sees fit. Occupies bits 0 through 47 (octets 0-5). ver: The 4-bit version field as defined by Section 4.2, set to 0b1000 (8). Occupies bits 48 through 51 of octet 6. custom_b: 12 more bits of the layout that can be filled as an implementation sees fit. Occupies bits 52 through 63 (octets 6-7). var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8. custom_c: The final 62 bits of the layout immediately following the var field to be filled as an implementation sees fit. Occupies bits 66 through 127 (octets 8-15). 5.9. Nil UUID The Nil UUID is special form of UUID that is specified to have all 128 bits set to zero. 00000000-0000-0000-0000-000000000000 Figure 13: Nil UUID Format A Nil UUID value can be useful to communicate the absence of any other UUID value in situations that otherwise require or use a 128-bit UUID. A Nil UUID can express the concept "no such value here". Thus, it is reserved for such use as needed for implementation-specific situations. Note that the Nil UUID value falls within the range of the Apollo NCS variant as per the first row of Table 1 rather than the variant defined by this document. 5.10. Max UUID The Max UUID is a special form of UUID that is specified to have all 128 bits set to 1. This UUID can be thought of as the inverse of the Nil UUID defined in Section 5.9. FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF Figure 14: Max UUID Format A Max UUID value can be used as a sentinel value in situations where a 128-bit UUID is required, but a concept such as "end of UUID list" needs to be expressed and is reserved for such use as needed for implementation-specific situations. Note that the Max UUID value falls within the range of the "yet-to-be defined" future UUID variant as per the last row of Table 1 rather than the variant defined by this document. 6. UUID Best Practices The minimum requirements for generating UUIDs of each version are described in this document. Everything else is an implementation detail, and it is up to the implementer to decide what is appropriate for a given implementation. Various relevant factors are covered below to help guide an implementer through the different trade-offs among differing UUID implementations. 6.1. Timestamp Considerations UUID timestamp source, precision, and length were topics of great debate while creating UUIDv7 for this specification. Choosing the right timestamp for your application is very important. This section will detail some of the most common points on this issue. Reliability: Implementations acquire the current timestamp from a reliable source to provide values that are time ordered and continually increasing. Care must be taken to ensure that timestamp changes from the environment or operating system are handled in a way that is consistent with implementation requirements. For example, if it is possible for the system clock to move backward due to either manual adjustment or corrections from a time synchronization protocol, implementations need to determine how to handle such cases. (See "Altering, Fuzzing, or Smearing" below.) Source: UUIDv1 and UUIDv6 both utilize a Gregorian Epoch timestamp, while UUIDv7 utilizes a Unix Epoch timestamp. If other timestamp sources or a custom timestamp Epoch are required, UUIDv8 MUST be used. Sub-second Precision and Accuracy: Many levels of precision exist for timestamps: milliseconds, microseconds, nanoseconds, and beyond. Additionally, fractional representations of sub-second precision may be desired to mix various levels of precision in a time-ordered manner. Furthermore, system clocks themselves have an underlying granularity, which is frequently less than the precision offered by the operating system. With UUIDv1 and UUIDv6, 100 nanoseconds of precision are present, while UUIDv7 features a millisecond level of precision by default within the Unix Epoch that does not exceed the granularity capable in most modern systems. For other levels of precision, UUIDv8 is available. Similar to Section 6.2, with UUIDv1 or UUIDv6, a high-resolution timestamp can be simulated by keeping a count of the number of UUIDs that have been generated with the same value of the system time and using that count to construct the low order bits of the timestamp. The count of the high-resolution timestamp will range between zero and the number of 100-nanosecond intervals per system-time interval. Length: The length of a given timestamp directly impacts how many timestamp ticks can be contained in a UUID before the maximum value for the timestamp field is reached. Take care to ensure that the proper length is selected for a given timestamp. UUIDv1 and UUIDv6 utilize a 60-bit timestamp valid until 5623 AD; UUIDv7 features a 48-bit timestamp valid until the year 10889 AD. Altering, Fuzzing, or Smearing: Implementations MAY alter the actual timestamp. Some examples include security considerations around providing a real-clock value within a UUID to 1) correct inaccurate clocks, 2) handle leap seconds, or 3) obtain a millisecond value by dividing by 1024 (or some other value) for performance reasons (instead of dividing a number of microseconds by 1000). This specification makes no requirement or guarantee about how close the clock value needs to be to the actual time. If UUIDs do not need to be frequently generated, the UUIDv1 or UUIDv6 timestamp can simply be the system time multiplied by the number of 100-nanosecond intervals per system-time interval. Padding: When timestamp padding is required, implementations MUST pad the most significant bits (leftmost) with data. An example for this padding data is to fill the most significant, leftmost bits of a Unix timestamp with zeroes to complete the 48-bit timestamp in UUIDv7. An alternative approach for padding data is to fill the most significant, leftmost bits with the number of 32-bit Unix timestamp rollovers after 2038-01-19. Truncating: When timestamps need to be truncated, the lower, least significant bits MUST be used. An example would be truncating a 64-bit Unix timestamp to the least significant, rightmost 48 bits for UUIDv7. Error Handling: If a system overruns the generator by requesting too many UUIDs within a single system-time interval, the UUID service can return an error or stall the UUID generator until the system clock catches up and MUST NOT knowingly return duplicate values due to a counter rollover. Note that if the processors overrun the UUID generation frequently, additional Node IDs can be allocated to the system, which will permit higher speed allocation by making multiple UUIDs potentially available for each timestamp value. Similar techniques are discussed in Section 6.4. 6.2. Monotonicity and Counters Monotonicity (each subsequent value being greater than the last) is the backbone of time-based sortable UUIDs. Normally, time-based UUIDs from this document will be monotonic due to an embedded timestamp; however, implementations can guarantee additional monotonicity via the concepts covered in this section. Take care to ensure UUIDs generated in batches are also monotonic. That is, if one thousand UUIDs are generated for the same timestamp, there should be sufficient logic for organizing the creation order of those one thousand UUIDs. Batch UUID creation implementations MAY utilize a monotonic counter that increments for each UUID created during a given timestamp. For single-node UUID implementations that do not need to create batches of UUIDs, the embedded timestamp within UUIDv6 and UUIDv7 can provide sufficient monotonicity guarantees by simply ensuring that timestamp increments before creating a new UUID. Distributed nodes are discussed in Section 6.4. Implementations SHOULD employ the following methods for single-node UUID implementations that require batch UUID creation or are otherwise concerned about monotonicity with high-frequency UUID generation. Fixed Bit-Length Dedicated Counter (Method 1): Some implementations allocate a specific number of bits in the UUID layout to the sole purpose of tallying the total number of UUIDs created during a given UUID timestamp tick. If present, a fixed bit-length counter MUST be positioned immediately after the embedded timestamp. This promotes sortability and allows random data generation for each counter increment. With this method, the rand_a section (or a subset of its leftmost bits) of UUIDv7 is used as a fixed bit-length dedicated counter that is incremented for every UUID generation. The trailing random bits generated for each new UUID in rand_b can help produce unguessable UUIDs. In the event that more counter bits are required, the most significant (leftmost) bits of rand_b MAY be used as additional counter bits. Monotonic Random (Method 2): With this method, the random data is extended to also function as a counter. This monotonic value can be thought of as a "randomly seeded counter" that MUST be incremented in the least significant position for each UUID created on a given timestamp tick. UUIDv7's rand_b section SHOULD be utilized with this method to handle batch UUID generation during a single timestamp tick. The increment value for every UUID generation is a random integer of any desired length larger than zero. It ensures that the UUIDs retain the required level of unguessability provided by the underlying entropy. The increment value MAY be 1 when the number of UUIDs generated in a particular period of time is important and guessability is not an issue. However, incrementing the counter by 1 SHOULD NOT be used by implementations that favor unguessability, as the resulting values are easily guessable. Replace Leftmost Random Bits with Increased Clock Precision (Method 3): For UUIDv7, which has millisecond timestamp precision, it is possible to use additional clock precision available on the system to substitute for up to 12 random bits immediately following the timestamp. This can provide values that are time ordered with sub-millisecond precision, using however many bits are appropriate in the implementation environment. With this method, the additional time precision bits MUST follow the timestamp as the next available bit in the rand_a field for UUIDv7. To calculate this value, start with the portion of the timestamp expressed as a fraction of the clock's tick value (fraction of a millisecond for UUIDv7). Compute the count of possible values that can be represented in the available bit space, 4096 for the UUIDv7 rand_a field. Using floating point or scaled integer arithmetic, multiply this fraction of a millisecond value by 4096 and round down (toward zero) to an integer result to arrive at a number between 0 and the maximum allowed for the indicated bits, which sorts monotonically based on time. Each increasing fractional value will result in an increasing bit field value to the precision available with these bits. For example, let's assume a system timestamp of 1 Jan 2023 12:34:56.1234567. Taking the precision greater than 1 ms gives us a value of 0.4567, as a fraction of a millisecond. If we wish to encode this as 12 bits, we can take the count of possible values that fit in those bits (4096 or 2^12), multiply it by our millisecond fraction value of 0.4567, and truncate the result to an integer, which gives an integer value of 1870. Expressed as hexadecimal, it is 0x74E or the binary bits 0b011101001110. One can then use those 12 bits as the most significant (leftmost) portion of the random section of the UUID (e.g., the rand_a field in UUIDv7). This works for any desired bit length that fits into a UUID, and applications can decide the appropriate length based on available clock precision; for UUIDv7, it is limited to 12 bits at maximum to reserve sufficient space for random bits. The main benefit to encoding additional timestamp precision is that it utilizes additional time precision already available in the system clock to provide values that are more likely to be unique; thus, it may simplify certain implementations. This technique can also be used in conjunction with one of the other methods, where this additional time precision would immediately follow the timestamp. Then, if any bits are to be used as a clock sequence, they would follow next. The following sub-topics cover issues related solely to creating reliable fixed bit-length dedicated counters: Fixed Bit-Length Dedicated Counter Seeding: Implementations utilizing the fixed bit-length counter method randomly initialize the counter with each new timestamp tick. However, when the timestamp has not increased, the counter is instead incremented by the desired increment logic. When utilizing a randomly seeded counter alongside Method 1, the random value MAY be regenerated with each counter increment without impacting sortability. The downside is that Method 1 is prone to overflows if a counter of adequate length is not selected or the random data generated leaves little room for the required number of increments. Implementations utilizing fixed bit-length counter method MAY also choose to randomly initialize a portion of the counter rather than the entire counter. For example, a 24-bit counter could have the 23 bits in least significant, rightmost position randomly initialized. The remaining most significant, leftmost counter bit is initialized as zero for the sole purpose of guarding against counter rollovers. Fixed Bit-Length Dedicated Counter Length: Select a counter bit-length that can properly handle the level of timestamp precision in use. For example, millisecond precision generally requires a larger counter than a timestamp with nanosecond precision. General guidance is that the counter SHOULD be at least 12 bits but no longer than 42 bits. Care must be taken to ensure that the counter length selected leaves room for sufficient entropy in the random portion of the UUID after the counter. This entropy helps improve the unguessability characteristics of UUIDs created within the batch. The following sub-topics cover rollover handling with either type of counter method: Counter Rollover Guards: The technique from "Fixed Bit-Length Dedicated Counter Seeding" above that describes allocating a segment of the fixed bit-length counter as a rollover guard is also helpful to mitigate counter rollover issues. This same technique can be used with monotonic random counter methods by ensuring that the total length of a possible increment in the least significant, rightmost position is less than the total length of the random value being incremented. As such, the most significant, leftmost bits can be incremented as rollover guarding. Counter Rollover Handling: Counter rollovers MUST be handled by the application to avoid sorting issues. The general guidance is that applications that care about absolute monotonicity and sortability should freeze the counter and wait for the timestamp to advance, which ensures monotonicity is not broken. Alternatively, implementations MAY increment the timestamp ahead of the actual time and reinitialize the counter. Implementations MAY use the following logic to ensure UUIDs featuring embedded counters are monotonic in nature: 1. Compare the current timestamp against the previously stored timestamp. 2. If the current timestamp is equal to the previous timestamp, increment the counter according to the desired method. 3. If the current timestamp is greater than the previous timestamp, re-initialize the desired counter method to the new timestamp and generate new random bytes (if the bytes were frozen or being used as the seed for a monotonic counter). Monotonic Error Checking: Implementations SHOULD check if the currently generated UUID is greater than the previously generated UUID. If this is not the case, then any number of things could have occurred, such as clock rollbacks, leap second handling, and counter rollovers. Applications SHOULD embed sufficient logic to catch these scenarios and correct the problem to ensure that the next UUID generated is greater than the previous, or they should at least report an appropriate error. To handle this scenario, the general guidance is that the application MAY reuse the previous timestamp and increment the previous counter method. 6.3. UUID Generator States The (optional) UUID generator state only needs to be read from stable storage once at boot time, if it is read into a system-wide shared volatile store (and updated whenever the stable store is updated). This stable storage MAY be used to record various portions of the UUID generation, which prove useful for batch UUID generation purposes and monotonic error checking with UUIDv6 and UUIDv7. These stored values include but are not limited to last known timestamp, clock sequence, counters, and random data. If an implementation does not have any stable store available, then it MAY proceed with UUID generation as if this were the first UUID created within a batch. This is the least desirable implementation because it will increase the frequency of creation of values such as clock sequence, counters, or random data, which increases the probability of duplicates. Further, frequent generation of random numbers also puts more stress on any entropy source and/or entropy pool being used as the basis for such random numbers. An implementation MAY also return an application error in the event that collision resistance is of the utmost concern. The semantics of this error are up to the application and implementation. See Section 6.7 for more information on weighting collision tolerance in applications. For UUIDv1 and UUIDv6, if the Node ID can never change (e.g., the network interface card from which the Node ID is derived is inseparable from the system), or if any change also re-initializes the clock sequence to a random value, then instead of keeping it in stable store, the current Node ID may be returned. For UUIDv1 and UUIDv6, the state does not always need to be written to stable store every time a UUID is generated. The timestamp in the stable store can periodically be set to a value larger than any yet used in a UUID. As long as the generated UUIDs have timestamps less than that value, and the clock sequence and Node ID remain unchanged, only the shared volatile copy of the state needs to be updated. Furthermore, if the timestamp value in stable store is in the future by less than the typical time it takes the system to reboot, a crash will not cause a re-initialization of the clock sequence. If it is too expensive to access shared state each time a UUID is generated, then the system-wide generator can be implemented to allocate a block of timestamps each time it is called; a per-process generator can allocate from that block until it is exhausted. 6.4. Distributed UUID Generation Some implementations MAY desire the utilization of multi-node, clustered, applications that involve two or more nodes independently generating UUIDs that will be stored in a common location. While UUIDs already feature sufficient entropy to ensure that the chances of collision are low, as the total number of UUID generating nodes increases, so does the likelihood of a collision. This section will detail the two additional collision resistance approaches that have been observed by multi-node UUID implementations in distributed environments. It should be noted that, although this section details two methods for the sake of completeness, implementations should utilize the pseudorandom Node ID option if additional collision resistance for distributed UUID generation is a requirement. Likewise, utilization of either method is not required for implementing UUID generation in distributed environments. Node IDs: With this method, a pseudorandom Node ID value is placed within the UUID layout. This identifier helps ensure the bit space for a given node is unique, resulting in UUIDs that do not conflict with any other UUID created by another node with a different node id. Implementations that choose to leverage an embedded node id SHOULD utilize UUIDv8. The node id SHOULD NOT be an IEEE 802 MAC address per Section 8. The location and bit length are left to implementations and are outside the scope of this specification. Furthermore, the creation and negotiation of unique node ids among nodes is also out of scope for this specification. Centralized Registry: With this method, all nodes tasked with creating UUIDs consult a central registry and confirm the generated value is unique. As applications scale, the communication with the central registry could become a bottleneck and impact UUID generation in a negative way. Shared knowledge schemes with central/global registries are outside the scope of this specification and are NOT RECOMMENDED. Distributed applications generating UUIDs at a variety of hosts MUST be willing to rely on the random number source at all hosts. 6.5. Name-Based UUID Generation Although some prefer to use the word "hash-based" to describe UUIDs featuring hashing algorithms (MD5 or SHA-1), this document retains the usage of the term "name-based" in order to maintain consistency with previously published documents and existing implementations. The requirements for name-based UUIDs are as follows: * UUIDs generated at different times from the same name (using the same canonical format) in the same namespace MUST be equal. * UUIDs generated from two different names (same or differing canonical format) in the same namespace should be different (with very high probability). * UUIDs generated from the same name (same or differing canonical format) in two different namespaces should be different (with very high probability). * If two UUIDs that were generated from names (using the same canonical format) are equal, then they were generated from the same name in the same namespace (with very high probability). A note on names: The concept of name (and namespace) should be broadly construed and not limited to textual names. A canonical sequence of octets is one that conforms to the specification for that name form's canonical representation. A name can have many usual forms, only one of which can be canonical. An implementer of new namespaces for UUIDs needs to reference the specification for the canonical form of names in that space or define such a canonical form for the namespace if it does not exist. For example, at the time of writing, Domain Name System (DNS) [RFC9499] has three conveyance formats: common (www.example.com), presentation (www.example.com.), and wire format (3www7example3com0). Looking at [X500] Distinguished Names (DNs), [RFC4122] allowed either text-based or binary DER-based names as inputs. For Uniform Resource Locators (URLs) [RFC1738], one could provide a Fully Qualified Domain Name (FQDN) with or without the protocol identifier www.example.com or https://www.example.com. When it comes to Object Identifiers (OIDs) [X660], one could choose dot notation without the leading dot (2.999), choose to include the leading dot (.2.999), or select one of the many formats from [X680] such as OID Internationalized Resource Identifier (OID-IRI) (/Joint-ISO-ITU-T/Example). While most users may default to the common format for DNS, FQDN format for a URL, text format for X.500, and dot notation without a leading dot for OID, name-based UUID implementations generally SHOULD allow arbitrary input that will compute name-based UUIDs for any of the aforementioned example names and others not defined here. Each name format within a namespace will output different UUIDs. As such, the mechanisms or conventions used for allocating names and ensuring their uniqueness within their namespaces are beyond the scope of this specification. 6.6. Namespace ID Usage and Allocation This section details the namespace IDs for some potentially interesting namespaces such as those for DNS [RFC9499], URLs [RFC1738], OIDs [X660], and DNs [X500]. Further, this section also details allocation, IANA registration, and other details pertinent to Namespace IDs. +=========+====================================+=========+==========+ |Namespace|Namespace ID Value |Name |Namespace | | | |Reference|ID | | | | |Reference | +=========+====================================+=========+==========+ |DNS |6ba7b810-9dad-11d1-80b4-00c04fd430c8|[RFC9499]|[RFC4122],| | | | |RFC 9562 | +---------+------------------------------------+---------+----------+ |URL |6ba7b811-9dad-11d1-80b4-00c04fd430c8|[RFC1738]|[RFC4122],| | | | |RFC 9562 | +---------+------------------------------------+---------+----------+ |OID |6ba7b812-9dad-11d1-80b4-00c04fd430c8|[X660] |[RFC4122],| | | | |RFC 9562 | +---------+------------------------------------+---------+----------+ |X500 |6ba7b814-9dad-11d1-80b4-00c04fd430c8|[X500] |[RFC4122],| | | | |RFC 9562 | +---------+------------------------------------+---------+----------+ Table 3: Namespace IDs Items may be added to this registry using the Specification Required policy as per [RFC8126]. For designated experts, generally speaking, Namespace IDs are allocated as follows: * The first Namespace ID value, for DNS, was calculated from a time- based UUIDv1 and "6ba7b810-9dad-11d1-80b4-00c04fd430c8", used as a starting point. * Subsequent Namespace ID values increment the least significant, rightmost bit of time_low "6ba7b810" while freezing the rest of the UUID to "9dad-11d1-80b4-00c04fd430c8". * New Namespace ID values MUST use this same logic and MUST NOT use a previously used Namespace ID value. * Thus, "6ba7b815" is the next available time_low for a new Namespace ID value with the full ID being "6ba7b815-9dad- 11d1-80b4-00c04fd430c8". * The upper bound for time_low in this special use, Namespace ID values, is "ffffffff" or "ffffffff-9dad-11d1-80b4-00c04fd430c8", which should be sufficient space for future Namespace ID values. Note that the Namespace ID value "6ba7b813-9dad- 11d1-80b4-00c04fd430c8" and its usage are not defined by this document or by [RFC4122]; thus, it SHOULD NOT be used as a Namespace ID value. New Namespace ID values MUST be documented as per Section 7 if they are to be globally available and fully interoperable. Implementations MAY continue to use vendor-specific, application- specific, and deployment-specific Namespace ID values; but know that interoperability is not guaranteed. These custom Namespace ID values MUST NOT use the logic above; instead, generating a UUIDv4 or UUIDv7 Namespace ID value is RECOMMENDED. If collision probability (Section 6.7) and uniqueness (Section 6.8) of the final name-based UUID are not a problem, an implementation MAY also leverage UUIDv8 instead to create a custom, application-specific Namespace ID value. Implementations SHOULD provide the ability to input a custom namespace to account for newly registered IANA Namespace ID values outside of those listed in this section or custom, application- specific Namespace ID values. 6.7. Collision Resistance Implementations should weigh the consequences of UUID collisions within their application and when deciding between UUID versions that use entropy (randomness) versus the other components such as those in Sections 6.1 and 6.2. This is especially true for distributed node collision resistance as defined by Section 6.4. There are two example scenarios below that help illustrate the varying seriousness of a collision within an application. Low Impact: A UUID collision generated a duplicate log entry, which results in incorrect statistics derived from the data. Implementations that are not negatively affected by collisions may continue with the entropy and uniqueness provided by UUIDs defined in this document. High Impact: A duplicate key causes an airplane to receive the wrong course, which puts people's lives at risk. In this scenario, there is no margin for error. Collisions must be avoided: failure is unacceptable. Applications dealing with this type of scenario must employ as much collision resistance as possible within the given application context. 6.8. Global and Local Uniqueness UUIDs created by this specification MAY be used to provide local uniqueness guarantees. For example, ensuring UUIDs created within a local application context are unique within a database MAY be sufficient for some implementations where global uniqueness outside of the application context, in other applications, or around the world is not required. Although true global uniqueness is impossible to guarantee without a shared knowledge scheme, a shared knowledge scheme is not required by a UUID to provide uniqueness for practical implementation purposes. Implementations MAY use a shared knowledge scheme, introduced in Section 6.4, as they see fit to extend the uniqueness guaranteed by this specification. 6.9. Unguessability Implementations SHOULD utilize a cryptographically secure pseudorandom number generator (CSPRNG) to provide values that are both difficult to predict ("unguessable") and have a low likelihood of collision ("unique"). The exception is when a suitable CSPRNG is unavailable in the execution environment. Take care to ensure the CSPRNG state is properly reseeded upon state changes, such as process forks, to ensure proper CSPRNG operation. CSPRNG ensures the best of Sections 6.7 and 8 are present in modern UUIDs. Further advice on generating cryptographic-quality random numbers can be found in [RFC4086], [RFC8937], and [RANDOM]. 6.10. UUIDs That Do Not Identify the Host This section describes how to generate a UUIDv1 or UUIDv6 value if an IEEE 802 address is not available or its use is not desired. Implementations MAY leverage MAC address randomization techniques [IEEE802.11bh] as an alternative to the pseudorandom logic provided in this section. Alternatively, implementations MAY elect to obtain a 48-bit cryptographic-quality random number as per Section 6.9 to use as the Node ID. After generating the 48-bit fully randomized node value, implementations MUST set the least significant bit of the first octet of the Node ID to 1. This bit is the unicast or multicast bit, which will never be set in IEEE 802 addresses obtained from network cards. Hence, there can never be a conflict between UUIDs generated by machines with and without network cards. An example of generating a randomized 48-bit node value and the subsequent bit modification is detailed in Appendix A. For more information about IEEE 802 address and the unicast or multicast or local/global bits, please review [RFC9542]. For compatibility with earlier specifications, note that this document uses the unicast or multicast bit instead of the arguably more correct local/global bit because MAC addresses with the local/ global bit set or not set are both possible in a network. This is not the case with the unicast or multicast bit. One node cannot have a MAC address that multicasts to multiple nodes. In addition, items such as the computer's name and the name of the operating system, while not strictly speaking random, will help differentiate the results from those obtained by other systems. The exact algorithm to generate a Node ID using these data is system specific because both the data available and the functions to obtain them are often very system specific. However, a generic approach is to accumulate as many sources as possible into a buffer, use a message digest (such as SHA-256 or SHA-512 defined by [FIPS180-4]), take an arbitrary 6 bytes from the hash value, and set the multicast bit as described above. 6.11. Sorting UUIDv6 and UUIDv7 are designed so that implementations that require sorting (e.g., database indexes) sort as opaque raw bytes without the need for parsing or introspection. Time-ordered monotonic UUIDs benefit from greater database-index locality because the new values are near each other in the index. As a result, objects are more easily clustered together for better performance. The real-world differences in this approach of index locality versus random data inserts can be one order of magnitude or more. UUID formats created by this specification are intended to be lexicographically sortable while in the textual representation. UUIDs created by this specification are crafted with big-endian byte order (network byte order) in mind. If little-endian style is required, UUIDv8 is available for custom UUID formats. 6.12. Opacity As general guidance, avoiding parsing UUID values unnecessarily is recommended; instead, treat UUIDs as opaquely as possible. Although application-specific concerns could, of course, require some degree of introspection (e.g., to examine Sections 4.1 or 4.2 or perhaps the timestamp of a UUID), the advice here is to avoid this or other parsing unless absolutely necessary. Applications typically tend to be simpler, be more interoperable, and perform better when this advice is followed. 6.13. DBMS and Database Considerations For many applications, such as databases, storing UUIDs as text is unnecessarily verbose, requiring 288 bits to represent 128-bit UUID values. Thus, where feasible, UUIDs SHOULD be stored within database applications as the underlying 128-bit binary value. For other systems, UUIDs MAY be stored in binary form or as text, as appropriate. The trade-offs to both approaches are as follows: * Storing in binary form requires less space and may result in faster data access. * Storing as text requires more space but may require less translation if the resulting text form is to be used after retrieval, which may make it simpler to implement. DBMS vendors are encouraged to provide functionality to generate and store UUID formats defined by this specification for use as identifiers or left parts of identifiers such as, but not limited to, primary keys, surrogate keys for temporal databases, foreign keys included in polymorphic relationships, and keys for key-value pairs in JSON columns and key-value databases. Applications using a monolithic database may find using database-generated UUIDs (as opposed to client-generated UUIDs) provides the best UUID monotonicity. In addition to UUIDs, additional identifiers MAY be used to ensure integrity and feedback. Designers of database schema are cautioned against using name-based UUIDs (see Sections 5.3 and 5.5) as primary keys in tables. A common issue observed in database schema design is the assumption that a particular value will never change, which later turns out to be an incorrect assumption. Postal codes, license or other identification numbers, and numerous other such identifiers seem unique and unchanging at a given point time -- only later to have edge cases where they need to change. The subsequent change of the identifier, used as a "name" input for name-based UUIDs, can invalidate a given database structure. In such scenarios, it is observed that using any non-name-based UUID version would have resulted in the field in question being placed somewhere that would have been easier to adapt to such changes (primary key excluded from this statement). The general advice is to avoid name-based UUID natural keys and, instead, to utilize time-based UUID surrogate keys based on the aforementioned problems detailed in this section. 7. IANA Considerations All references to [RFC4122] in IANA registries (outside of those created by this document) have been replaced with references to this document, including the IANA URN namespace registration [URNNamespaces] for UUID. References to Section 4.1.2 of [RFC4122] have been updated to refer to Section 4 of this document. Finally, IANA should track UUID Subtypes and Special Case "Namespace IDs Values" as specified in Sections 7.1 and 7.2 at the following location: . When evaluating requests, the designated expert should consider community feedback, how well-defined the reference specification is, and this specification's requirements. Vendor-specific, application- specific, and deployment-specific values are unable to be registered. Specification documents should be published in a stable, freely available manner (ideally, located with a URL) but need not be standards. The designated expert will either approve or deny the registration request and communicate this decision to IANA. Denials should include an explanation and, if applicable, suggestions as to how to make the request successful. 7.1. IANA UUID Subtype Registry and Registration This specification defines the "UUID Subtypes" registry for common widely used UUID standards. +======================+====+=========+================+============+ | Name | ID | Subtype | Variant | Reference | +======================+====+=========+================+============+ | Gregorian Time-based | 1 | version | OSF DCE | [RFC4122], | | | | | / IETF | RFC 9562 | +----------------------+----+---------+----------------+------------+ | DCE Security | 2 | version | OSF DCE | [C309], | | | | | / IETF | [C311] | +----------------------+----+---------+----------------+------------+ | MD5 Name-based | 3 | version | OSF DCE | [RFC4122], | | | | | / IETF | RFC 9562 | +----------------------+----+---------+----------------+------------+ | Random | 4 | version | OSF DCE | [RFC4122], | | | | | / IETF | RFC 9562 | +----------------------+----+---------+----------------+------------+ | SHA-1 Name-based | 5 | version | OSF DCE | [RFC4122], | | | | | / IETF | RFC 9562 | +----------------------+----+---------+----------------+------------+ | Reordered Gregorian | 6 | version | OSF DCE | RFC 9562 | | Time-based | | | / IETF | | +----------------------+----+---------+----------------+------------+ | Unix Time-based | 7 | version | OSF DCE | RFC 9562 | | | | | / IETF | | +----------------------+----+---------+----------------+------------+ | Custom | 8 | version | OSF DCE | RFC 9562 | | | | | / IETF | | +----------------------+----+---------+----------------+------------+ Table 4: IANA UUID Subtypes This table may be extended by Standards Action as per [RFC8126]. For designated experts: * The minimum and maximum "ID" value for the subtype "version" within the "OSF DCE / IETF" variant is 0 through 15. The versions within Table 1 described as "Reserved for future definition" or "unused" are omitted from this IANA registry until properly defined. * The "Subtype" column is free-form text. However, at the time of publication, "version" and "family" are the only known UUID subtypes. The "family" subtype is part of the "Apollo NCS" variant space (both are outside the scope of this specification). The Microsoft variant may have subtyping mechanisms defined; however, they are unknown and outside of the scope of this specification. Similarly, the final "Reserved for future definition" variant may introduce new subtyping logic at a future date. Subtype IDs are permitted to overlap. That is, an ID of "1" may exist in multiple variant spaces. * The "Variant" column is free-form text. However, it is likely that one of four values will be included: the first three are "OSF DCE / IETF", "Apollo NCS", and "Microsoft", and the final variant value belongs to the "Reserved for future definition" variant and may introduce a new name at a future date. 7.2. IANA UUID Namespace ID Registry and Registration This specification defines the "UUID Namespace IDs" registry for common, widely used Namespace ID values. The full details of this registration, including information for designated experts, can be found in Section 6.6. 8. Security Considerations Implementations SHOULD NOT assume that UUIDs are hard to guess. For example, they MUST NOT be used as security capabilities (identifiers whose mere possession grants access). Discovery of predictability in a random number source will result in a vulnerability. Implementations MUST NOT assume that it is easy to determine if a UUID has been slightly modified in order to redirect a reference to another object. Humans do not have the ability to easily check the integrity of a UUID by simply glancing at it. MAC addresses pose inherent security risks around privacy and SHOULD NOT be used within a UUID. Instead CSPRNG data SHOULD be selected from a source with sufficient entropy to ensure guaranteed uniqueness among UUID generation. See Sections 6.9 and 6.10 for more information. Timestamps embedded in the UUID do pose a very small attack surface. The timestamp in conjunction with an embedded counter does signal the order of creation for a given UUID and its corresponding data but does not define anything about the data itself or the application as a whole. If UUIDs are required for use with any security operation within an application context in any shape or form, then UUIDv4 (Section 5.4) SHOULD be utilized. See [RFC6151] for MD5 security considerations and [RFC6194] for SHA-1 security considerations. 9. References 9.1. Normative References [C309] X/Open Company Limited, "X/Open DCE: Remote Procedure Call", ISBN 1-85912-041-5, Open CAE Specification C309, August 1994, . [C311] The Open Group, "DCE 1.1: Authentication and Security Services", Open Group CAE Specification C311, August 1997, . [FIPS180-4] National Institute of Standards and Technology (NIST), "Secure Hash Standard (SHS)", FIPS PUB 180-4, DOI 10.6028/NIST.FIPS.180-4, August 2015, . [FIPS202] National Institute of Standards and Technology (NIST), "SHA-3 Standard: Permutation-Based Hash and Extendable- Output Functions", FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015, . [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . [RFC8141] Saint-Andre, P. and J. Klensin, "Uniform Resource Names (URNs)", RFC 8141, DOI 10.17487/RFC8141, April 2017, . [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . [X667] ITU-T, "Information technology - Open Systems Interconnection - Procedures for the operation of OSI Registration Authorities: Generation and registration of Universally Unique Identifiers (UUIDs) and their use as ASN.1 object identifier components", ISO/IEC 9834-8:2004, ITU-T Recommendation X.667, September 2004. 9.2. Informative References [COMBGUID] "Creating sequential GUIDs in C# for MSSQL or PostgreSql", commit 2759820, December 2020, . [CUID] "Collision-resistant ids optimized for horizontal scaling and performance.", commit 215b27b, October 2020, . [Elasticflake] Pearcy, P., "Sequential UUID / Flake ID generator pulled out of elasticsearch common", commit dd71c21, January 2015, . [Err1957] RFC Errata, Erratum ID 1957, RFC 4122, . [Err3546] RFC Errata, Erratum ID 3546, RFC 4122, . [Err4975] RFC Errata, Erratum ID 4975, RFC 4122, . [Err4976] RFC Errata, Erratum ID 4976, RFC 4122, . [Err5560] RFC Errata, Erratum ID 5560, RFC 4122, . [Flake] Boundary, "Flake: A decentralized, k-ordered id generation service in Erlang", commit 15c933a, February 2017, . [FlakeID] "Flake ID Generator", commit fcd6a2f, April 2020, . [IBM_NCS] IBM, "uuid_gen Command (NCS)", March 2023, . [IEEE754] IEEE, "IEEE Standard for Floating-Point Arithmetic.", IEEE Std 754-2019, DOI 10.1109/IEEESTD.2019.8766229, July 2019, . [IEEE802.11bh] IEEE, "IEEE Draft Standard for Information technology-- Telecommunications and information exchange between systems Local and metropolitan area networks--Specific requirements - Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications Amendment: Enhancements for Extremely High Throughput (EHT)", Electronic ISBN 978-1-5044-9520-2, March 2023, . [KSUID] Segment, "K-Sortable Globally Unique IDs", commit bf376a7, July 2020, . [LexicalUUID] Twitter, "Cassie", commit f6da4e0, November 2012, . [Microsoft] Microsoft, "2.3.4.3 GUID - Curly Braced String Representation", April 2023, . [MS_COM_GUID] Chen, R., "Why does COM express GUIDs in a mix of big- endian and little-endian? Why can't it just pick a side and stick with it?", September 2022, . [ObjectID] MongoDB, "ObjectId", . [orderedUuid] Cabrera, I. B., "Laravel: The mysterious "Ordered UUID"", January 2020, . [pushID] Lehenbauer, M., "The 2^120 Ways to Ensure Unique Identifiers", February 2015, . [Python] Python, "uuid - UUID objects according to RFC 4122", . [RANDOM] Occil, P., "Random Number Generator Recommendations for Applications", June 2023, . [RFC1321] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, DOI 10.17487/RFC1321, April 1992, . [RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform Resource Locators (URL)", RFC 1738, DOI 10.17487/RFC1738, December 1994, . [RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, . [RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally Unique IDentifier (UUID) URN Namespace", RFC 4122, DOI 10.17487/RFC4122, July 2005, . [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, DOI 10.17487/RFC5234, January 2008, . [RFC6151] Turner, S. and L. Chen, "Updated Security Considerations for the MD5 Message-Digest and the HMAC-MD5 Algorithms", RFC 6151, DOI 10.17487/RFC6151, March 2011, . [RFC6194] Polk, T., Chen, L., Turner, S., and P. Hoffman, "Security Considerations for the SHA-0 and SHA-1 Message-Digest Algorithms", RFC 6194, DOI 10.17487/RFC6194, March 2011, . [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, June 2017, . [RFC8937] Cremers, C., Garratt, L., Smyshlyaev, S., Sullivan, N., and C. Wood, "Randomness Improvements for Security Protocols", RFC 8937, DOI 10.17487/RFC8937, October 2020, . [RFC9499] Hoffman, P. and K. Fujiwara, "DNS Terminology", BCP 219, RFC 9499, DOI 10.17487/RFC9499, March 2024, . [RFC9542] Eastlake 3rd, D., Abley, J., and Y. Li, "IANA Considerations and IETF Protocol and Documentation Usage for IEEE 802 Parameters", BCP 141, RFC 9542, DOI 10.17487/RFC9542, April 2024, . [ShardingID] Instagram Engineering, "Sharding & IDs at Instagram", December 2012, . [SID] "sid : generate sortable identifiers", Commit 660e947, June 2019, . [Snowflake] Twitter, "Snowflake is a network service for generating unique ID numbers at high scale with some simple guarantees.", commit ec40836, May 2014, . [Sonyflake] Sony, "A distributed unique ID generator inspired by Twitter's Snowflake", commit 848d664, August 2020, . [ULID] "Universally Unique Lexicographically Sortable Identifier", Commit d0c7170, May 2019, . [URNNamespaces] IANA, "Uniform Resource Names (URN) Namespaces", . [X500] ITU-T, "Information technology - Open Systems Interconnection - The Directory: Overview of concepts, models and services", ISO/IEC 9594-1, ITU-T Recommendation X.500, October 2019. [X660] ITU-T, "Information technology - Procedures for the operation of object identifier registration authorities: General procedures and top arcs of the international object identifier tree", ISO/IEC 9834-1, ITU-T Recommendation X.660, July 2011. [X680] ITU-T, "Information Technology - Abstract Syntax Notation One (ASN.1) & ASN.1 encoding rules", ISO/IEC 8824-1:2021, ITU-T Recommendation X.680, February 2021. [XID] "Globally Unique ID Generator", commit efa678f, October 2020, . Appendix A. Test Vectors Both UUIDv1 and UUIDv6 test vectors utilize the same 60-bit timestamp: 0x1EC9414C232AB00 (138648505420000000) Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00. Both UUIDv1 and UUIDv6 utilize the same values in clock_seq and node; all of which have been generated with random data. For the randomized node, the least significant bit of the first octet is set to a value of 1 as per Section 6.10. Thus, the starting value 0x9E6BDECED846 was changed to 0x9F6BDECED846. The pseudocode used for converting from a 64-bit Unix timestamp to a 100 ns Gregorian timestamp value has been left in the document for reference purposes. # Gregorian-to-Unix Offset: # The number of 100 ns intervals between the # UUID Epoch 1582-10-15 00:00:00 # and the Unix Epoch 1970-01-01 00:00:00 # Greg_Unix_offset = 0x01b21dd213814000 or 122192928000000000 # Unix 64-bit Nanosecond Timestamp: # Unix NS: Tuesday, February 22, 2022 2:22:22 PM GMT-05:00 # Unix_64_bit_ns = 0x16D6320C3D4DCC00 or 1645557742000000000 # Unix Nanosecond precision to Gregorian 100-nanosecond intervals # Greg_100_ns = (Unix_64_bit_ns/100)+Greg_Unix_offset # Work: # Greg_100_ns = (1645557742000000000/100)+122192928000000000 # Unix_64_bit_ns = (138648505420000000-122192928000000000)*100 # Final: # Greg_100_ns = 0x1EC9414C232AB00 or 138648505420000000 Figure 15: Test Vector Timestamp Pseudocode A.1. Example of a UUIDv1 Value ------------------------------------------- field bits value ------------------------------------------- time_low 32 0xC232AB00 time_mid 16 0x9414 ver 4 0x1 time_high 12 0x1EC var 2 0b10 clock_seq 14 0b11, 0x3C8 node 48 0x9F6BDECED846 ------------------------------------------- total 128 ------------------------------------------- final: C232AB00-9414-11EC-B3C8-9F6BDECED846 Figure 16: UUIDv1 Example Test Vector A.2. Example of a UUIDv3 Value The MD5 computation from is detailed in Figure 17 using the DNS Namespace ID value and the Name "www.example.com". The field mapping and all values are illustrated in Figure 18. Finally, to further illustrate the bit swapping for version and variant, see Figure 19. Namespace (DNS): 6ba7b810-9dad-11d1-80b4-00c04fd430c8 Name: www.example.com ------------------------------------------------------ MD5: 5df418813aed051548a72f4a814cf09e Figure 17: UUIDv3 Example MD5 ------------------------------------------- field bits value ------------------------------------------- md5_high 48 0x5df418813aed ver 4 0x3 md5_mid 12 0x515 var 2 0b10 md5_low 62 0b00, 0x8a72f4a814cf09e ------------------------------------------- total 128 ------------------------------------------- final: 5df41881-3aed-3515-88a7-2f4a814cf09e Figure 18: UUIDv3 Example Test Vector MD5 hex and dash: 5df41881-3aed-0515-48a7-2f4a814cf09e Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx Final: 5df41881-3aed-3515-88a7-2f4a814cf09e Figure 19: UUIDv3 Example Ver/Var Bit Swaps A.3. Example of a UUIDv4 Value This UUIDv4 example was created by generating 16 bytes of random data resulting in the hexadecimal value of 919108F752D133205BACF847DB4148A8. This is then used to fill out the fields as shown in Figure 20. Finally, to further illustrate the bit swapping for version and variant, see Figure 21. ------------------------------------------- field bits value ------------------------------------------- random_a 48 0x919108f752d1 ver 4 0x4 random_b 12 0x320 var 2 0b10 random_c 62 0b01, 0xbacf847db4148a8 ------------------------------------------- total 128 ------------------------------------------- final: 919108f7-52d1-4320-9bac-f847db4148a8 Figure 20: UUIDv4 Example Test Vector Random hex: 919108f752d133205bacf847db4148a8 Random hex and dash: 919108f7-52d1-3320-5bac-f847db4148a8 Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx Final: 919108f7-52d1-4320-9bac-f847db4148a8 Figure 21: UUIDv4 Example Ver/Var Bit Swaps A.4. Example of a UUIDv5 Value The SHA-1 computation form is detailed in Figure 22, using the DNS Namespace ID value and the Name "www.example.com". The field mapping and all values are illustrated in Figure 23. Finally, to further illustrate the bit swapping for version and variant and the unused/ discarded part of the SHA-1 value, see Figure 24. Namespace (DNS): 6ba7b810-9dad-11d1-80b4-00c04fd430c8 Name: www.example.com ---------------------------------------------------------- SHA-1: 2ed6657de927468b55e12665a8aea6a22dee3e35 Figure 22: UUIDv5 Example SHA-1 ------------------------------------------- field bits value ------------------------------------------- sha1_high 48 0x2ed6657de927 ver 4 0x5 sha1_mid 12 0x68b var 2 0b10 sha1_low 62 0b01, 0x5e12665a8aea6a2 ------------------------------------------- total 128 ------------------------------------------- final: 2ed6657d-e927-568b-95e1-2665a8aea6a2 Figure 23: UUIDv5 Example Test Vector SHA-1 hex and dash: 2ed6657d-e927-468b-55e1-2665a8aea6a2-2dee3e35 Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx Final: 2ed6657d-e927-568b-95e1-2665a8aea6a2 Discarded: -2dee3e35 Figure 24: UUIDv5 Example Ver/Var Bit Swaps and Discarded SHA-1 Segment A.5. Example of a UUIDv6 Value ------------------------------------------- field bits value ------------------------------------------- time_high 32 0x1EC9414C time_mid 16 0x232A ver 4 0x6 time_high 12 0xB00 var 2 0b10 clock_seq 14 0b11, 0x3C8 node 48 0x9F6BDECED846 ------------------------------------------- total 128 ------------------------------------------- final: 1EC9414C-232A-6B00-B3C8-9F6BDECED846 Figure 25: UUIDv6 Example Test Vector A.6. Example of a UUIDv7 Value This example UUIDv7 test vector utilizes a well-known Unix Epoch timestamp with millisecond precision to fill the first 48 bits. rand_a and rand_b are filled with random data. The timestamp is Tuesday, February 22, 2022 2:22:22.00 PM GMT-05:00, represented as 0x017F22E279B0 or 1645557742000. ------------------------------------------- field bits value ------------------------------------------- unix_ts_ms 48 0x017F22E279B0 ver 4 0x7 rand_a 12 0xCC3 var 2 0b10 rand_b 62 0b01, 0x8C4DC0C0C07398F ------------------------------------------- total 128 ------------------------------------------- final: 017F22E2-79B0-7CC3-98C4-DC0C0C07398F Figure 26: UUIDv7 Example Test Vector Appendix B. Illustrative Examples The following sections contain illustrative examples that serve to show how one may use UUIDv8 (Section 5.8) for custom and/or experimental application-based logic. The examples below have not been through the same rigorous testing, prototyping, and feedback loop that other algorithms in this document have undergone. The authors encourage implementers to create their own UUIDv8 algorithm rather than use the items defined in this section. B.1. Example of a UUIDv8 Value (Time-Based) This example UUIDv8 test vector utilizes a well-known 64-bit Unix Epoch timestamp with 10 ns precision, truncated to the least significant, rightmost bits to fill the first 60 bits of custom_a and custom_b, while setting the version bits between these two segments to the version value of 8. The variant bits are set; and the final segment, custom_c, is filled with random data. Timestamp is Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00, represented as 0x2489E9AD2EE2E00 or 164555774200000000 (10 ns-steps). ------------------------------------------- field bits value ------------------------------------------- custom_a 48 0x2489E9AD2EE2 ver 4 0x8 custom_b 12 0xE00 var 2 0b10 custom_c 62 0b00, 0xEC932D5F69181C0 ------------------------------------------- total 128 ------------------------------------------- final: 2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 Figure 27: UUIDv8 Example Time-Based Illustrative Example B.2. Example of a UUIDv8 Value (Name-Based) As per Section 5.5, name-based UUIDs that want to use modern hashing algorithms MUST be created within the UUIDv8 space. These MAY leverage newer hashing algorithms such as SHA-256 or SHA-512 (as defined by [FIPS180-4]), SHA-3 or SHAKE (as defined by [FIPS202]), or even algorithms that have not been defined yet. A SHA-256 version of the SHA-1 computation in Appendix A.4 is detailed in Figure 28 as an illustrative example detailing how this can be achieved. The creation of the name-based UUIDv8 value in this section follows the same logic defined in Section 5.5 with the difference being SHA-256 in place of SHA-1. The field mapping and all values are illustrated in Figure 29. Finally, to further illustrate the bit swapping for version and variant and the unused/discarded part of the SHA-256 value, see Figure 30. An important note for secure hashing algorithms that produce outputs of an arbitrary size, such as those found in SHAKE, is that the output hash MUST be 128 bits or larger. Namespace (DNS): 6ba7b810-9dad-11d1-80b4-00c04fd430c8 Name: www.example.com ---------------------------------------------------------------- SHA-256: 5c146b143c524afd938a375d0df1fbf6fe12a66b645f72f6158759387e51f3c8 Figure 28: UUIDv8 Example SHA256 ------------------------------------------- field bits value ------------------------------------------- custom_a 48 0x5c146b143c52 ver 4 0x8 custom_b 12 0xafd var 2 0b10 custom_c 62 0b00, 0x38a375d0df1fbf6 ------------------------------------------- total 128 ------------------------------------------- final: 5c146b14-3c52-8afd-938a-375d0df1fbf6 Figure 29: UUIDv8 Example Name-Based SHA-256 Illustrative Example A: 5c146b14-3c52-4afd-938a-375d0df1fbf6-fe12a66b645f72f6158759387e51f3c8 B: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx C: 5c146b14-3c52-8afd-938a-375d0df1fbf6 D: -fe12a66b645f72f6158759387e51f3c8 Figure 30: UUIDv8 Example Ver/Var Bit Swaps and Discarded SHA-256 Segment Examining Figure 30: * Line A details the full SHA-256 as a hexadecimal value with the dashes inserted. * Line B details the version and variant hexadecimal positions, which must be overwritten. * Line C details the final value after the ver and var have been overwritten. * Line D details the discarded leftover values from the original SHA-256 computation. Acknowledgements The authors gratefully acknowledge the contributions of Rich Salz, Michael Mealling, Ben Campbell, Ben Ramsey, Fabio Lima, Gonzalo Salgueiro, Martin Thomson, Murray S. Kucherawy, Rick van Rein, Rob Wilton, Sean Leonard, Theodore Y. Ts'o, Robert Kieffer, Sergey Prokhorenko, and LiosK. As well as all of those in the IETF community and on GitHub to who contributed to the discussions that resulted in this document. This document draws heavily on the OSF DCE specification (Appendix A of [C309]) for UUIDs. Ted Ts'o provided helpful comments. We are also grateful to the careful reading and bit-twiddling of Ralf S. Engelschall, John Larmouth, and Paul Thorpe. Professor Larmouth was also invaluable in achieving coordination with ISO/IEC. Authors' Addresses Kyzer R. Davis Cisco Systems Email: kydavis@cisco.com Brad G. Peabody Uncloud Email: brad@peabody.io Paul J. Leach University of Washington Email: pjl7@uw.edu ================================================ FILE: doc/uuid-generation-benchmarks.md ================================================ # UUID Generation Benchmarks: clj-uuid-old vs clj-uuid Performance comparison of UUID generation across all RFC 9562 versions, measuring `clj-uuid-old` (bitmop, shift/mask loops) against `clj-uuid` (bitmop2, ByteBuffer primitives + JVM intrinsic mask operations + ThreadLocal MessageDigest). ## Test Environment ### Hardware - **CPU:** Intel Core i9-9880H @ 2.30 GHz (8 cores / 16 threads) - **RAM:** 32 GB - **Architecture:** x86_64 ### Software - **OS:** macOS 26.2 (Darwin 25.2.0) - **JVM:** OpenJDK 64-Bit Server VM 25.0.1 (Homebrew, mixed mode, sharing) - **Clojure:** 1.12.0 - **Leiningen:** 2.12.0 ### Benchmark Parameters - **Iterations:** 500,000 per benchmark - **Warmup:** 50,000 iterations (JIT compilation) - **Reflection warnings:** none (verified via `lein check` and `*warn-on-reflection*`) - **Source:** `test/clj_uuid/bench.clj` ## 1. UUID Generation (Pure Construction) Measures only the time to call the constructor and return a `java.util.UUID` value. No serialization. | UUID Version | clj-uuid-old (ns) | clj-uuid (ns) | Speedup | |--------------------------|-------------------:|---------------:|--------:| | v1 (time-based) | 120.3 | 100.1 | 1.20x | | v3 (MD5, namespace) | 1409.1 | 165.9 | 8.49x | | v4 (random) | 326.3 | 334.8 | 0.97x | | v5 (SHA1, namespace) | 1531.4 | 264.8 | 5.78x | | v6 (time-based, sorted) | 106.3 | 100.0 | 1.06x | | v7 (unix time, crypto) | 408.8 | 340.0 | 1.20x | | v7nc (unix time, fast) | -- | 38.5 | -- | | v8 (custom) | 46.4 | 7.9 | 5.87x | ### Analysis **v3 and v5 show 5-9x generation speedup.** These are the only versions where the optimized implementation changes the generation path itself. Three optimizations compound: 1. **ThreadLocal MessageDigest:** `clj-uuid` reuses a per-thread `MessageDigest` instance via `ThreadLocal/withInitial`, avoiding the ~200 ns `MessageDigest/getInstance` allocation on every call. `clj-uuid-old` also uses ThreadLocal (both were optimized in this pass). 2. **Byte serialization:** Serialize the namespace UUID via `to-byte-array` (bitmop: 2x 8-iteration `ldb`+`sb8` loops; bitmop2: 2x `ByteBuffer.putLong`). 3. **Digest extraction:** Read back the digest result via `bytes->long` (bitmop: 2x 8-iteration `dpb` loops; bitmop2: 2x `ByteBuffer.getLong`). In `clj-uuid-old`, byte manipulation overhead adds ~1200 ns on top of the ~200 ns digest. In `clj-uuid`, that overhead is nearly eliminated, leaving the digest as the dominant cost. **v7 shows 1.21x speedup** from `mask-offset` optimization. The v7 constructor calls `dpb #=(mask 2 62)` to set the variant bits in the LSB. The `#=(mask 2 62)` is a compile-time constant, but `dpb` calls `mask-offset` at runtime to find the lowest set bit. Previously, `mask-offset` used an O(offset) loop -- for offset=62, that meant 62 iterations per call. Now `mask-offset` uses `Long/numberOfTrailingZeros`, a JVM intrinsic that compiles to a single `TZCNT` instruction. This eliminates the v7 regression that was visible in earlier benchmarks. `SecureRandom.nextLong()` still dominates total latency. **v8 shows 4.21x speedup** from `mask-offset` optimization. The v8 constructor is just two `dpb` calls (`mask(4,12)` and `mask(2,62)`), so the `mask-offset` cost was a significant fraction of the total. With O(1) `Long/numberOfTrailingZeros`, the two `dpb` calls drop from ~46 ns to ~11 ns. **v1 shows ~1.2x and v6 shows ~1.1x** relative to clj-uuid-old. In addition to the O(1) `mask-offset` improvement, v1 and v6 now inline the `AtomicLong` CAS loop, bit-field packing, and pre-captured node LSBs directly in the constructor closure, eliminating var lookup and function dispatch overhead. The `System/currentTimeMillis` + CAS cost remains the dominant factor. **v7nc is new in 0.2.5** and has no clj-uuid-old equivalent. It uses `ThreadLocalRandom` and a per-thread monotonic counter, achieving 38.5 ns/op -- faster than JUG 5.2's `TimeBasedEpochGenerator` (~50 ns). **v4 shows ~1x.** The 0-arity form delegates directly to `UUID/randomUUID` (JVM built-in, dominated by SecureRandom). ## 2. Post-Generation Operations Measures operations on a pre-existing UUID value. These are the operations where bitmop2's ByteBuffer approach has the most impact. | Operation | clj-uuid-old (ns) | clj-uuid (ns) | Speedup | |----------------|-------------------:|---------------:|--------:| | to-byte-array | 803.6 | 14.0 | 57.40x | | to-hex-string | 5840.4 | 126.1 | 46.32x | | to-string | 22.1 | 17.6 | 1.26x | | to-urn-string | 110.1 | 110.9 | 0.99x | | get-version | 7.1 | 7.5 | 0.95x | | get-node-id | 11.9 | 9.8 | 1.21x | ### Analysis **`to-byte-array`: 57x faster.** This is the biggest win. bitmop requires two 8-iteration loops (each doing `ldb` + `sb8` per byte, 16 iterations total). bitmop2 does two `ByteBuffer.putLong` calls -- single JVM intrinsics. **`to-hex-string`: 29x faster.** bitmop builds two separate hex strings via `(hex msb)` and `(hex lsb)`, each involving `long->bytes` (8-iteration loop), `map ub8`, `map octet-hex`, and `apply str` (lazy sequence materialization + string concatenation). bitmop2 uses `uuid->buf` + `buf-hex`: a single `StringBuilder` with direct byte iteration over a `ByteBuffer`. **`to-string` and `to-urn-string`: ~1x (no change).** Both delegate to `UUID.toString()`, a JVM-native method that neither bitmop touches. **Field extraction (`get-version`, `get-node-id`): ~1x.** These use `ldb`/`dpb` on the UUID's long words, which are identical between the two implementations. The slight variation is measurement noise. ## 3. Combined: Generate + Serialize The real-world pattern -- generate a UUID and immediately serialize it for storage, transmission, or indexing. | Operation | clj-uuid-old (ns) | clj-uuid (ns) | Speedup | |------------------------|-------------------:|---------------:|--------:| | v1 + to-byte-array | 925.9 | 112.8 | 8.21x | | v3 + to-byte-array | 2223.6 | 157.1 | 14.15x | | v3 + to-hex-string | 6425.8 | 297.8 | 21.58x | | v4 + to-byte-array | 1170.8 | 350.4 | 3.34x | | v4 + to-hex-string | 5366.4 | 476.8 | 11.25x | | v5 + to-byte-array | 2327.4 | 269.0 | 8.65x | | v5 + to-hex-string | 6509.5 | 409.0 | 15.92x | | v7 + to-byte-array | 1303.1 | 324.8 | 4.01x | ### Analysis The combined numbers reflect the sum of generation and serialization gains. **v3 + to-hex-string: 18.9x.** This is the largest combined win. v3 benefits from faster generation (9x from ThreadLocal + `bytes->long` and `to-byte-array` in the digest path) AND faster serialization (29x from the hex output path). The two effects compound. **v5 + to-hex-string: 14.6x.** Same compounding effect as v3, but SHA-1 is slightly slower than MD5, so the digest fraction is larger and the byte manipulation speedup contributes proportionally less. **v3 + to-byte-array: 14.1x / v5 + to-byte-array: 8.6x.** Byte-array serialization is faster than hex (57x vs 29x), but takes less absolute time, so the generation speedup contributes more to the total ratio. **v1 + to-byte-array: 8.0x.** Generation is ~1.2x but serialization is 57x. The serialization dominates total time in clj-uuid-old (~87% of 926 ns) but becomes negligible in clj-uuid (~14 ns of 115 ns). **v4 + to-byte-array: 3.4x.** `UUID/randomUUID` is the bottleneck (~340 ns), so the serialization savings (804 ns -> 14 ns) yield a 3.4x total win. **v4 + to-hex-string: 9.9x.** The hex path has even more overhead in clj-uuid-old (~5840 ns) so the combined win is larger than the byte-array case. **v7 + to-byte-array: 3.9x.** Similar profile to v4 -- crypto RNG dominates, but the O(1) mask-offset now contributes a generation speedup on top of the serialization win. ## 4. Absolute Throughput UUIDs generated per second (generation only, single thread). | UUID Version | clj-uuid-old (ops/s) | clj-uuid (ops/s) | |--------------------------|---------------------:|------------------:| | v1 (time-based) | 6,353,878 | 9,385,662 | | v3 (MD5, namespace) | 668,818 | 3,239,594 | | v4 (random) | 2,715,643 | 2,680,676 | | v5 (SHA1, namespace) | 648,846 | 3,024,212 | | v6 (time-based, sorted) | 8,569,979 | 10,024,839 | | v7 (unix time, crypto) | 2,492,207 | 2,960,438 | | v7nc (unix time, fast) | -- | 25,974,026 | | v8 (custom) | 19,381,529 | 149,993,940 | ## 5. v3/v5 Detailed Breakdown Since v3 and v5 are the UUID types with the largest generation-time improvements, this section breaks down the per-operation costs. ### v3 (MD5, Namespace) | Operation | clj-uuid-old (ns) | clj-uuid (ns) | Speedup | |------------------|-------------------:|---------------:|--------:| | v3 generation | 1412.0 | 156.9 | 9.00x | | v3 to-byte-array | 804.7 | 13.7 | 58.81x | | v3 to-hex-string | 5225.1 | 197.7 | 26.43x | | v3 to-string | 23.7 | 24.3 | 0.98x | ### v5 (SHA1, Namespace) | Operation | clj-uuid-old (ns) | clj-uuid (ns) | Speedup | |------------------|-------------------:|---------------:|--------:| | v5 generation | 1667.6 | 279.0 | 5.98x | | v5 to-byte-array | 849.0 | 16.4 | 51.71x | | v5 to-hex-string | 5128.9 | 185.4 | 27.66x | | v5 to-string | 22.9 | 22.0 | 1.04x | ### v3/v5 Generation Path Breakdown The v3/v5 generation path consists of four steps. The following shows where time is spent in each implementation: ``` clj-uuid-old clj-uuid to-byte-array: ~800 ns (2x long->bytes ~14 ns (2x putLong) 8-iter ldb+sb8 loop) digest (MD5/SHA1): ~200-300 ns ~200-300 ns bytes->long: ~800 ns (2x 8-iter dpb) ~14 ns (2x getLong) dpb: ~5 ns (2 calls) ~3 ns (2 calls, O(1) mask-offset) ──────────────────────────────────────────────────────── Total (v3): ~1400 ns ~160 ns Total (v5): ~1670 ns ~280 ns ``` In `clj-uuid-old`, byte manipulation overhead (~1600 ns) dominates over the digest (~200-300 ns). In `clj-uuid`, byte manipulation is eliminated (~28 ns total), leaving the digest as the dominant cost. ## 6. Summary ### Where clj-uuid wins | Category | Speedup | |------------------------------------|------------| | `to-byte-array` | **57x** | | `to-hex-string` | **29x** | | v3 + to-hex-string (combined) | **18.9x** | | v5 + to-hex-string (combined) | **14.6x** | | v3 + to-byte-array (combined) | **14.1x** | | v4 + to-hex-string (combined) | **9.9x** | | v3 generation | **9.0x** | | v5 + to-byte-array (combined) | **8.6x** | | v1 + to-byte-array (combined) | **8.0x** | | v5 generation | **6.0x** | | v8 generation | **4.2x** | | v7 + to-byte-array (combined) | **3.9x** | | v4 + to-byte-array (combined) | **3.4x** | | v1 generation | **1.5x** | | v6 generation | **1.4x** | | v7 generation | **1.3x** | ### Where they are equal | Category | Speedup | |------------------------------------|------------| | v4 generation (0-arity) | ~1.0x | | `to-string` / `to-urn-string` | ~1.0x | | Field extraction (version, node) | ~1.0x | ### Where clj-uuid has no impact Operations that delegate entirely to the JVM (`UUID.toString()`, `UUID/randomUUID`, `UUID.version()`) see no change, as expected. The bitmop2 layer only affects byte-level serialization, the `bytes->long` / `long->bytes` paths, and `ldb`/`dpb` calls (which now benefit from O(1) `mask-offset` via `Long/numberOfTrailingZeros`). ### New in 0.2.5: v7nc `v7nc` is a non-cryptographic v7 variant that uses `ThreadLocalRandom` and a per-thread monotonic counter. At 38.5 ns/op it is faster than JUG 5.2's `TimeBasedEpochGenerator` (~50 ns), making it the fastest time-based UUID generator available on the JVM from Clojure. ### Key Takeaway The largest gains appear in **serialization** (`to-byte-array`, `to-hex-string`) and in **v3/v5 generation** (which serialize the namespace UUID internally as part of the digest computation). Additional gains come from **O(1) mask-offset** using JVM intrinsics, which particularly benefits v8 (4.2x) where `dpb` calls with high-offset masks were previously bottlenecked by an O(offset) loop. The new `v7nc` constructor provides the fastest time-based UUID generation at ~39 ns, beating JUG 5.2. For applications that generate UUIDs and immediately serialize them -- the common case for database keys, wire protocols, and log correlation IDs -- clj-uuid delivers **3-19x end-to-end improvement** depending on the UUID version and serialization format. ================================================ FILE: project.clj ================================================ (defproject danlentz/clj-uuid "0.2.5" :description "A Clojure library for generation and utilization of UUIDs (Universally Unique Identifiers) as described by RFC-9562. This library extends the standard Java UUID class to provide true v1, v6, v7 (time based) and v3/v5 (namespace based), and v8 (user customizable) identifier generation. Additionally, a number of useful utilities are provided to support serialization and manipulation of these UUIDs in a simple, efficient manner." :author "Dan Lentz" :jvm-opts ^:replace [] :signing {:gpg-key "0CA466A1AB48F0C0264AF55307BAD70176C4B179"} :url "https://github.com/danlentz/clj-uuid" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.12.0" :scope "provided"] [org.clj-commons/primitive-math "1.0.1"]] :plugins [[lein-cloverage "1.2.4"] [lein-codox "0.10.8"]] :cloverage {:test-ns-regex [#"clj-uuid\.(?!bench|compare-bench).*"]} :profiles {:test {:dependencies [[com.fasterxml.uuid/java-uuid-generator "5.2.0"] [com.github.f4b6a3/uuid-creator "6.1.1"]]}} :codox {:output-path "doc/api" :src-dir-uri "https://github.com/danlentz/clj-uuid/blob/master/" :doc-files [] :src-linenum-anchor-prefix "L" :project {:name "clj-uuid"}} :global-vars {*warn-on-reflection* true}) ================================================ FILE: src/clj_uuid/bitmop.clj ================================================ (ns clj-uuid.bitmop "Unsigned Long and ByteBuffer-based bitwise operation primitives for UUID manipulation. Provides the same mask/ldb/dpb fundamentals as in the past, plus a 16-byte ByteBuffer abstraction for direct UUID byte manipulation. The ByteBuffer approach has two key advantages: 1. Performance: ByteBuffer provides direct typed access at byte offsets via single native operations (getLong, getInt, etc.) rather than manual shift/mask loops. 2. Portability: The buffer abstraction maps naturally to JavaScript's DataView/ArrayBuffer, enabling a future cljc implementation." (:refer-clojure :exclude [* + - / < > <= >= == rem bit-or bit-and bit-xor bit-not bit-shift-left bit-shift-right byte short int float long double inc dec zero? min max true? false? unsigned-bit-shift-right]) (:require [primitive-math :refer :all] [clojure.pprint :refer [cl-format]] [clj-uuid.constants :refer :all] [clj-uuid.util :refer :all]) (:import [java.nio ByteBuffer] [java.util UUID])) ;; NOTE: this module uses copious amounts of unchecked/primitive-math. ;; These should be considered internal implementation details. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Simple Arithmetic Utils ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn expt2 "Compute 2^pow using bit-set." ^long [^long pow] (bit-set 0 pow)) (defn pphex "Pretty-print a long value in both hexadecimal and binary." [x] (returning x (cl-format *out* "~&[~A] [~64,,,'0@A]~%" (format "%1$016X" x) (Long/toBinaryString x)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Bit-masking ;; ;; So, much of the pain involved in handling UUID's correctly on the JVM ;; derives from the fact that there is no primitive unsigned numeric datatype ;; that can represent the full range of possible values of the msb and lsb. ;; ;; we encapsulate the basic primitives of working with ;; unsigned numbers entirely within the abstraction of "mask" and ;; "mask offset". Using these, we built the two fundamental unsigned ;; bitwise operations that are used for most of the UUID calculation: ;; ldb (load-byte) and dpb (deposit-byte). ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn mask "Create a bitmask of `width` bits starting at bit `offset`." ^long [^long width ^long offset] (if (< (+ width offset) 64) (bit-shift-left (dec (bit-shift-left 1 width)) offset) (let [x (expt2 offset)] (bit-and-not -1 (dec x))))) ;; Uses Long/numberOfTrailingZeros which compiles to a single TZCNT/BSF ;; instruction via JVM intrinsic. O(1) vs the previous O(offset) loop. (defn mask-offset "Return the bit offset (position of least significant set bit) of a mask." ^long [^long m] (if (zero? m) 0 (Long/numberOfTrailingZeros m))) ;; Uses Long/bitCount which compiles to a single POPCNT instruction ;; via JVM intrinsic. O(1) vs the previous O(width) loop. (defn mask-width "Return the number of set bits in a contiguous bitmask." ^long [^long m] (Long/bitCount m)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; LDB, DPB: Fundamental Bitwise Operations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn ldb "Load Byte -- extract the bit field defined by `bitmask` from `num`." ^long [^long bitmask ^long num] (let [off (mask-offset bitmask)] (bit-and (>>> bitmask off) (bit-shift-right num off)))) (defn dpb "Deposit Byte -- insert `value` into the bit field defined by `bitmask` within `num`." ^long [^long bitmask ^long num ^long value] (bit-or (bit-and-not num bitmask) (bit-and bitmask (bit-shift-left value (mask-offset bitmask))))) ;; Uses Long/bitCount which compiles to a single POPCNT instruction ;; via JVM intrinsic. O(1) vs the previous O(64) loop. (defn bit-count "Count the number of set bits in `x`." ^long [^long x] (Long/bitCount x)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Byte Casting ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn ub4 [num] (byte (bit-and num +ub4-mask+))) (defn ub8 [^long num] (unchecked-short (bit-and num +ub8-mask+))) (defn ub16 [num] (int (bit-and num +ub16-mask+))) (defn ub24 [num] (int (bit-and num +ub24-mask+))) (defn ub32 [num] (long (bit-and num +ub32-mask+))) (defn ub48 [num] (long (bit-and num +ub48-mask+))) (defn ub56 [num] (long (bit-and num +ub56-mask+))) (defn sb8 [num] (unchecked-byte (ub8 num))) (defn sb16 [num] (unchecked-short (ub16 num))) (defn sb32 [num] (unchecked-int (ub32 num))) (defn sb64 [num] (unchecked-long num)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Byte (dis)Assembly via ByteBuffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn assemble-bytes "Assemble a sequence of 8 bytes (big-endian) into a long." ^long [v] (loop [tot (long 0) bytes v c (int 8)] (if (zero? c) tot (recur (bit-or (bit-shift-left tot 8) (bit-and (long (first bytes)) 0xFF)) (next bytes) (dec c))))) (defn bytes->long "Read 8 bytes from `arr` starting at offset `i`, returning a long." [^bytes arr ^long i] (.getLong (ByteBuffer/wrap arr) (int i))) (defn long->bytes "Write `x` as 8 big-endian bytes. With one argument returns a new byte array. With three arguments writes into `arr` at offset `i`." ([^long x] (long->bytes x (byte-array 8) 0)) ([^long x ^bytes arr ^long i] (.putLong (ByteBuffer/wrap arr) (int i) x) arr)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Hexadecimal String Representation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn octet-hex "Convert a single byte value to a two-character uppercase hex string." [num] (str (+hex-chars+ (bit-shift-right num 4)) (+hex-chars+ (bit-and 0x0F num)))) (defn hex "Convert a long or byte sequence to a hex string. For a long, produces a 16-character zero-padded hex string." [thing] (if (number? thing) (let [^bytes arr (long->bytes (clojure.core/long thing)) sb (StringBuilder. 16)] (dotimes [i 8] (let [b (Byte/toUnsignedLong (aget arr (int i)))] (.append sb ^char (+hex-chars+ (bit-shift-right b 4))) (.append sb ^char (+hex-chars+ (bit-and 0x0F b))))) (.toString sb)) (apply str (map octet-hex thing)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Creation ;; ;; The central abstraction: a 16-byte big-endian ByteBuffer representing ;; 128 bits of UUID data. On the JVM this is java.nio.ByteBuffer; in a ;; future cljc build this would map to DataView over an ArrayBuffer. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn buffer "Create a 16-byte big-endian ByteBuffer. 0-arity: zeroed buffer 2-arity: from msb and lsb longs The buffer uses absolute (position-independent) get/put operations." (^ByteBuffer [] (ByteBuffer/allocate 16)) (^ByteBuffer [^long msb ^long lsb] (doto (ByteBuffer/allocate 16) (.putLong 0 msb) (.putLong 8 lsb)))) (defn buffer-from-bytes "Create a 16-byte ByteBuffer from a byte array (at least 16 bytes)." ^ByteBuffer [^bytes arr] (let [buf (ByteBuffer/allocate 16) ^ByteBuffer src (ByteBuffer/wrap arr)] (.putLong buf 0 (.getLong src 0)) (.putLong buf 8 (.getLong src 8)) buf)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Typed Access ;; ;; Direct typed access at byte offsets. All operations use absolute ;; positions (no buffer position tracking). Unsigned getter variants ;; return longs to preserve full value range on the JVM. ;; ;; These map directly to DataView methods in JavaScript: ;; get-byte -> DataView.getUint8 ;; get-short -> DataView.getUint16 ;; get-int -> DataView.getUint32 ;; get-long -> DataView.getBigInt64 ;; put-byte -> DataView.setUint8 ;; put-short -> DataView.setUint16 ;; put-int -> DataView.setUint32 ;; put-long -> DataView.setBigInt64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn get-byte "Read an unsigned byte (0-255) at byte offset from buffer." ^long [^ByteBuffer buf ^long offset] (Byte/toUnsignedLong (.get buf (int offset)))) (defn get-short "Read an unsigned 16-bit value at byte offset from buffer." ^long [^ByteBuffer buf ^long offset] (Short/toUnsignedLong (.getShort buf (int offset)))) (defn get-int "Read an unsigned 32-bit value at byte offset from buffer." ^long [^ByteBuffer buf ^long offset] (Integer/toUnsignedLong (.getInt buf (int offset)))) (defn get-long "Read a 64-bit long at byte offset from buffer." ^long [^ByteBuffer buf ^long offset] (.getLong buf (int offset))) (defn put-byte "Write a byte value at byte offset in buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long offset ^long val] (doto buf (.put (int offset) (byte val)))) (defn put-short "Write a 16-bit value at byte offset in buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long offset ^long val] (doto buf (.putShort (int offset) (short val)))) (defn put-int "Write a 32-bit value at byte offset in buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long offset ^long val] (doto buf (.putInt (int offset) (int val)))) (defn put-long "Write a 64-bit long at byte offset in buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long offset ^long val] (doto buf (.putLong (int offset) val))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Word Access (MSB/LSB) ;; ;; Convenience accessors for the two 64-bit halves of a UUID buffer. ;; bytes 0-7: most significant bits (MSB) ;; bytes 8-15: least significant bits (LSB) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn get-msb "Read the most significant 64 bits (bytes 0-7) of a UUID buffer." ^long [^ByteBuffer buf] (.getLong buf 0)) (defn get-lsb "Read the least significant 64 bits (bytes 8-15) of a UUID buffer." ^long [^ByteBuffer buf] (.getLong buf 8)) (defn set-msb "Set the most significant 64 bits (bytes 0-7) of a UUID buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long val] (doto buf (.putLong 0 val))) (defn set-lsb "Set the least significant 64 bits (bytes 8-15) of a UUID buffer. Returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long val] (doto buf (.putLong 8 val))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Bit Field Operations ;; ;; Buffer-aware ldb/dpb that operate on 64-bit words within the buffer. ;; The word-offset identifies which 8-byte-aligned long to operate on: ;; 0 = MSB (bytes 0-7) ;; 8 = LSB (bytes 8-15) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn ldb-buf "Load bit field: extract bits defined by `bitmask` from the 64-bit word at `word-offset` in the buffer." ^long [^ByteBuffer buf ^long word-offset ^long bitmask] (ldb bitmask (.getLong buf (int word-offset)))) (defn dpb-buf "Deposit bit field: insert `value` into the bits defined by `bitmask` within the 64-bit word at `word-offset` in the buffer. Mutates and returns the buffer." ^ByteBuffer [^ByteBuffer buf ^long word-offset ^long bitmask ^long value] (let [current (.getLong buf (int word-offset)) updated (dpb bitmask current value)] (doto buf (.putLong (int word-offset) updated)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Conversion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn buf->bytes "Extract the contents of a 16-byte UUID buffer as a byte array." ^bytes [^ByteBuffer buf] (let [arr (byte-array 16) ^ByteBuffer dest (ByteBuffer/wrap arr)] (.putLong dest 0 (.getLong buf 0)) (.putLong dest 8 (.getLong buf 8)) arr)) (defn buf->uuid "Convert a 16-byte UUID buffer to a java.util.UUID." ^UUID [^ByteBuffer buf] (UUID. (.getLong buf 0) (.getLong buf 8))) (defn uuid->buf "Convert a java.util.UUID to a 16-byte ByteBuffer." ^ByteBuffer [^UUID uuid] (buffer (.getMostSignificantBits uuid) (.getLeastSignificantBits uuid))) (defn duplicate "Create an independent copy of a UUID buffer." ^ByteBuffer [^ByteBuffer buf] (let [copy (ByteBuffer/allocate 16)] (.putLong copy 0 (.getLong buf 0)) (.putLong copy 8 (.getLong buf 8)) copy)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Hex and String Representation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- append-hex-bytes "Append hex characters for bytes [start, end) from buffer to StringBuilder." [^StringBuilder sb ^ByteBuffer buf ^long start ^long end] (loop [i start] (when (< i end) (let [b (Byte/toUnsignedLong (.get buf (int i)))] (.append sb ^char (+hex-chars+ (bit-shift-right b 4))) (.append sb ^char (+hex-chars+ (bit-and 0x0F b)))) (recur (inc i))))) (defn buf-hex "Convert a 16-byte UUID buffer to a 32-character hex string." ^String [^ByteBuffer buf] (let [sb (StringBuilder. 32)] (append-hex-bytes sb buf 0 16) (.toString sb))) (defn buf-str "Convert a 16-byte UUID buffer to the canonical 36-character UUID string: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Byte ranges correspond to UUID fields: bytes 0-3: time-low bytes 4-5: time-mid bytes 6-7: time-high-and-version bytes 8-9: clock-seq bytes 10-15: node" ^String [^ByteBuffer buf] (let [sb (StringBuilder. 36)] (append-hex-bytes sb buf 0 4) (.append sb \-) (append-hex-bytes sb buf 4 6) (.append sb \-) (append-hex-bytes sb buf 6 8) (.append sb \-) (append-hex-bytes sb buf 8 10) (.append sb \-) (append-hex-bytes sb buf 10 16) (.toString sb))) (defn hex->buf "Parse a 32-character hex string into a 16-byte UUID buffer." ^ByteBuffer [^String s] (let [buf (ByteBuffer/allocate 16)] (dotimes [i 16] (let [hi (Character/digit (.charAt s (int (* 2 i))) (int 16)) lo (Character/digit (.charAt s (int (inc (* 2 i)))) (int 16))] (.put buf (int i) (byte (bit-or (bit-shift-left hi 4) lo))))) buf)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ByteBuffer Comparison ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn buf-compare "Lexicographic unsigned comparison of two 16-byte UUID buffers. Returns negative if a < b, zero if equal, positive if a > b. Comparison is unsigned (treats bytes as 0-255) which matches UUID canonical string ordering." ^long [^ByteBuffer a ^ByteBuffer b] (let [c (Long/compareUnsigned (.getLong a 0) (.getLong b 0))] (if (zero? c) (clojure.core/long (Long/compareUnsigned (.getLong a 8) (.getLong b 8))) (clojure.core/long c)))) ================================================ FILE: src/clj_uuid/clock.clj ================================================ (ns clj-uuid.clock "Lock-Free, Thread-safe Monotonic Clocks" (:require [clj-uuid.random :as random]) (:import [java.util.concurrent.atomic AtomicLong])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Timestamp Epochs [RFC4122:4.1.4 "TIMESTAMP"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Universal time is represented as the number of seconds that have ;; elapsed since 00:00 January 1, 1900 GMT. ;; ;; POSIX time is represented as the number of seconds that have ;; elapsed since 00:00 January 1, 1970 UTC ;; ;; Java time is represented as the difference, measured in milliseconds, ;; between the current time and midnight, January 1, 1970 UTC ;; ;; UUIDs use Gregorian epoch, 12am Friday October 15, 1582 UTC ;; ;; Difference between Gregorian epoch and Java Epoch = 141427 days = ;; 12219292800 seconds ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Clock Resolution [RFC4122:4.2.1.2 "SYSTEM CLOCK RESOLUTION"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; "The timestamp is generated from the system time, whose resolution may ;; be less than the resolution of the UUID timestamp... If a system ;; overruns the generator by requesting too many UUIDs within a single ;; system time interval, the UUID service MUST... stall the UUID generator ;; until the system clock catches up. ;; ;; A high resolution timestamp can be simulated by keeping a count of ;; the number of UUIDs that have been generated with the same value of ;; the system time, and using it to construct the low order bits of the ;; timestamp. The count will range between zero and the number of ;; 100-nanosecond intervals per system time interval." ;; ;; We implement this general theory of operation, but make use of Clojure's ;; native lock-free concurrency primatives in order to ensure thread safety. ;; The system time is adjusted to achieve the rfc4122 gregorian timestamp and ;; and combined with the value read from a lock-free, atomic ;; incremental timestamps-this-tick subcounter on the low order bits. ;; This ensures no two monotonic timestamps ever collide, regardless of ;; system clock precision or degree of concurrency: ;; ;; 113914335216380000 (+ (* (universal-time) 10000) 100103040000000000) ;; 113914335216380001 first contending timestamp ;; 113914335216380002 second contending timestamp ;; ... and so forth ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftype State [^long seqid ^long millis]) (def ^:const +subcounter-resolution+ 9999) (def ^AtomicLong -gregorian-packed- (AtomicLong. 0)) (defn monotonic-time "Generate a guaranteed monotonically increasing timestamp based on Gregorian time and a stateful subcounter" [] (loop [] (let [current (.get -gregorian-packed-) millis (unsigned-bit-shift-right current 14) time-now (System/currentTimeMillis)] (cond (< millis time-now) (let [next (bit-shift-left time-now 14)] (if (.compareAndSet -gregorian-packed- current next) (+ 100103040000000000 (* (+ 2208988800000 time-now) 10000)) (recur))) (> millis time-now) (recur) true (let [seqid (bit-and current 0x3FFF) tt (inc seqid)] (if (<= tt +subcounter-resolution+) (let [next (bit-or (bit-shift-left time-now 14) tt)] (if (.compareAndSet -gregorian-packed- current next) (+ tt 100103040000000000 (* (+ 2208988800000 time-now) 10000)) (recur))) (recur))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Monotonicity and Counters [RFC9562:6.2.2 "Monotonic Random"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; "With this method, the random data is extended to also function as ;; a counter. This monotonic value can be thought of as a "randomly ;; seeded counter" that MUST be incremented in the least significant ;; position for each UUID created on a given timestamp tick. UUIDv7's ;; rand_b section SHOULD be utilized with this method to handle batch ;; UUID generation during a single timestamp tick. The increment value ;; for every UUID generation is a random integer of any desired length ;; larger than zero. It ensures that the UUIDs retain the required level ;; of unguessability provided by the underlying entropy. The increment ;; value MAY be 1 when the number of UUIDs generated in a particular ;; period of time is important" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:const +random-counter-resolution+ 0xfff) (let [^AtomicLong -packed- (AtomicLong. 0)] (defn monotonic-unix-time-and-random-counter "Generate guaranteed monotonically increasing number pairs based on POSIX time and a randomly seeded subcounter" [] (loop [] (let [current (.get -packed-) millis (unsigned-bit-shift-right current 14) time-now (System/currentTimeMillis)] (cond (< millis time-now) (let [new-seqid (random/ten-bits) next (bit-or (bit-shift-left time-now 14) new-seqid)] (if (.compareAndSet -packed- current next) (->State new-seqid time-now) (recur))) (> millis time-now) (recur) true (let [seqid (bit-and current 0x3FFF) tt (inc seqid)] (if (<= tt +random-counter-resolution+) (let [next (bit-or (bit-shift-left time-now 14) tt)] (if (.compareAndSet -packed- current next) (->State tt time-now) (recur))) (recur)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Time Utilities ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn posix-time "Generate the (Unix compatible) POSIX time -- the number of seconds that have elapsed since 00:00 January 1, 1970 UTC" ([] (posix-time (System/currentTimeMillis))) ([^long gregorian] (- (quot gregorian 10000) 12219292800000))) (defn universal-time "Generate the (Common-Lisp compatible) universal-time -- the number of seconds that have elapsed since 00:00 January 1, 1900 GMT" ([] (universal-time (monotonic-time))) ([^long gregorian] (+ (posix-time gregorian) 2208988800))) ================================================ FILE: src/clj_uuid/constants.clj ================================================ (ns clj-uuid.constants) (def +md5+ "MD5") (def +sha1+ "SHA1") (def uuid-regex #"[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}") (def hex-regex #"[0-9A-Fa-f]{32}") (def urn-regex #"urn:uuid:[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}") (def +hex-chars+ [\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \A \B \C \D \E \F]) (def ^:const +ub63-mask+ 0x7fffffffffffffff) (def ^:const +ub60-mask+ 0x0fffffffffffffff) (def ^:const +ub56-mask+ 0x00ffffffffffffff) (def ^:const +ub48-mask+ 0x0000ffffffffffff) (def ^:const +ub40-mask+ 0x000000ffffffffff) (def ^:const +ub32-mask+ 0x00000000ffffffff) (def ^:const +ub24-mask+ 0x0000000000ffffff) (def ^:const +ub16-mask+ 0x000000000000ffff) (def ^:const +ub12-mask+ 0x0000000000000fff) (def ^:const +ub8-mask+ 0x00000000000000ff) (def ^:const +ub4-mask+ 0x000000000000000f) (def ^:const +ub1-mask+ 0x0000000000000001) ================================================ FILE: src/clj_uuid/core.clj ================================================ (ns clj-uuid.core (:refer-clojure :exclude [== uuid? max < > =]) (:require [clojure.core :as clojure] [clj-uuid [constants :refer :all] [util :as util] [bitmop :as bitmop] [clock :as clock] [node :as node] [random :as random]]) (:import [java.io ByteArrayOutputStream ObjectOutputStream] [java.lang IllegalArgumentException] [java.net URI URL] [java.nio ByteBuffer] [java.security MessageDigest] [java.util UUID Date])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Leach-Salz UUID Representation [RFC4122:4.1.2 "LAYOUT AND BYTE ORDER"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The string representation of A Leach-Salz UUID has the format: ;; ;; clock-seq-and-reserved ;; time-mid | clock-seq-low ;; | | | ;; 6ba7b810-9dad-11d1-80b4-00c04fd430c8 ;; | | | ;; ` time-low | ` node ;; ` time-high-and-version ;; ;; ;; Each field is treated as integer and has its value printed as a zero-filled ;; hexadecimal digit string with the most significant digit first (unsigned, ;; big-endian). ;; ;; 0 1 2 3 ;; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | %uuid_time-low | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | %uuid_time-mid | %uuid_time-high-and-version | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; |clk-seq-hi-res | clock-seq-low | %uuid_node (0-1) | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | %uuid_node (2-5) | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; ;; ;; The following table enumerates a slot/type/value correspondence: ;; ;; SLOT SIZE TYPE BYTE-ARRAY ;; ---------------------------------------------------------------------- ;; time-low 4 ub32 [ ] ;; time-mid 2 ub16 [ ] ;; time-high 2 ub16 [ ] ;; clock-high 1 ub8 [] ;; clock-low 1 ub8 [] ;; node 6 ub48 [ ] ;; ;; Or, as 8-bit bytes mapping into 128-bit unsigned integer values: ;; ;; (0 7) (8 15) (16 23) (24 31) ;; time-low ;; (32 39) (40 47) ;; time-mid ;; (48 55) (56 63) ;; time-high-and-version ;; ;; (64 71) ;; clock-seq-and-reserved ;; (72 79) ;; clock-seq-low ;; (80 87) (88 95) (96 103) ;; ;; (104 111) (112 119) (120 127) ;; node ;; ;; This has been updated with additional layouts. See RFC9562:5.7 and ;; the description of v7 UUIDs, below. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UUID Variant [RFC9562:4.1] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The variant indicates the layout of the UUID. The UUID specification ;; covers one particular variant. Other variants are reserved or exist ;; for backward compatibility reasons (e.g., for values assigned before ;; the UUID specification was produced). An example of a UUID that is a ;; different variant is the null UUID, which is a UUID that has all 128 ;; bits set to zero. ;; ;; In the canonical representation: ;; ;; xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx ;; ;; the most significant bits of N indicate the variant (depending on the ;; variant one, two, or three bits are used). The principal variants covered ;; by the RFC9562 are indicated by the two most significant bits of N being 1 0 ;; (i.e., the hexadecimal N will always be 8, 9, A, or B). As seen below, ;; there are two additional variants, currently used for special "sentinal" ;; UUID's as defined below. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UUID Version [RFC9562:4.2] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The Leach-Salz UUID variant has five defined versions. In the canonical ;; representation: ;; ;; xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx ;; ;; the four bits of M indicates the UUID version (i.e., the hexadecimal ;; digit M will be either 1, 2, 3, 4, 5, 6, 7, or 8). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The NULL (variant 0) UUID [RFC9562:5.9] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:const +null+ "The NULL UUID is a special form of sentinel UUID that is specified to have all 128 bits set to zero." #uuid "00000000-0000-0000-0000-000000000000") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The MAX (variant 7) UUID [RFC9562:5.10] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:const +max+ "The MAX UUID is a special form of sentinel UUID that is specified to have all 128 bits set to one." #uuid "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Well-Known UUIDs [RFC4122:Appendix-C "SOME NAMESPACE IDs"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The following UUID's are the canonical top-level namespace identifiers ;; defined in RFC9562 Appendix C. (def ^:const +namespace-dns+ #uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8") (def ^:const +namespace-url+ #uuid "6ba7b811-9dad-11d1-80b4-00c04fd430c8") (def ^:const +namespace-oid+ #uuid "6ba7b812-9dad-11d1-80b4-00c04fd430c8") (def ^:const +namespace-x500+ #uuid "6ba7b814-9dad-11d1-80b4-00c04fd430c8") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Monotonic Clock (guaranteed always increasing value for time) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn monotonic-time "Return a monotonic timestamp (guaranteed always increasing) based on the number of 100-nanosecond intervals elapsed since the adoption of the Gregorian calendar in the West, 12:00am Friday October 15, 1582 UTC." [] (clock/monotonic-time)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UUID Protocols ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defprotocol UUIDNameBytes "A mechanism intended for user-level extension that defines the decoding rules for the local-part representation of arbitrary Clojure / Java Objects when used for computing namespaced identifiers." (as-byte-array [x] "Extract a byte serialization that represents the 'name' of x, typically unique within a given namespace.")) (defprotocol UUIDable "A UUIDable object directly represents a UUID. Examples of things which might be conceptually 'uuidable' include string representation of a UUID in canonical hex format, or an appropriate URN URI." (as-uuid ^java.util.UUID [x] "Coerce the value 'x' to a UUID.") (uuidable? [x] "Return 'true' if 'x' can be coerced to UUID.")) ;; (defprotocol UUIDRfc9562 ;; "A protocol that abstracts an unique identifier as described by ;; IETF RFC9562 . A UUID ;; represents a 128-bit value, however there are variant encoding ;; layouts used to assign and interpret information encoded in ;; those bits. This is a protocol for _variant 2_ (*Leach-Salz*) ;; UUID's." ;; (hash-code [uuid]) ;; (null? [uuid]) ;; (max? [uuid]) ;; (uuid? [x]) ;; (uuid= [x y]) ;; (uuid< [x y]) ;; (uuid> [x y]) ;; (get-word-high [uuid]) ;; (get-word-low [uuid]) ;; (get-version [uuid]) ;; (get-variant [uuid]) ;; (get-time-low [uuid]) ;; (get-time-mid [uuid]) ;; (get-time-high [uuid]) ;; (get-clk-high [uuid]) ;; (get-clk-low [uuid]) ;; (get-clk-seq [uuid]) ;; (get-node-id [uuid]) ;; (get-timestamp [uuid]) ;; (get-instant ^java.util.Date [uuid]) ;; (get-unix-time [uuid]) ;; (to-byte-array [uuid]) ;; (to-string ^String [uuid]) ;; (to-hex-string ^String [uuid]) ;; (to-urn-string ^String [uuid]) ;; (to-uri ^java.net.URI [uuid])) (defprotocol UUIDRfc9562 "A protocol that abstracts an unique identifier as described by IETF RFC9562 . A UUID represents a 128-bit value, however there are variant encoding layouts used to assign and interpret information encoded in those bits. This is a protocol for _variant 2_ (*Leach-Salz*) UUID's." (hash-code [uuid] "Return a suitable 64-bit hash value for `uuid`. Extend with specialized hash computation.") (null? [uuid] "Return `true` only if `uuid` has all 128 bits set to zero and is therefore equal to the null UUID, 00000000-0000-0000-0000-000000000000.") (max? [uuid] "Return `true` only if `uuid` has all 128 bits set and is therefore equal to the maximum UUID, ffffffff-ffff-ffff-ffff-ffffffffffff.") (uuid? [x] "Return `true` if `x` implements an RFC9562 unique identifier.") (uuid= [x y] "Directly compare two UUID's for = relation based on the equality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]. See: `clj-uuid/=`") (uuid< [x y] "Directly compare two UUID's for < relation based on the ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]. See: `clj-uuid/<`") (uuid> [x y] "Directly compare two UUID's for > relation based on the ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]. See: `clj-uuid/>`") (get-word-high [uuid] "Return the most significant 64 bits of UUID's 128 bit value.") (get-word-low [uuid] "Return the least significant 64 bits of UUID's 128 bit value.") (get-version [uuid] "Return the version number associated with this UUID. The version field contains a value which describes the nature of the UUID. There are five versions of Leach-Salz UUID, plus the null and max UUIDs: 0x0 Null 0x1 Time based 0x2 DCE security with POSIX UID 0x3 Namespaced, deterministic (MD5 Digest) 0x4 Cryptographic random 0x5 Namespaced, deterministic (SHA1 Digest) 0x6 Time based, lexically ordered 0x7 POSIX Time based, lexically ordered, cryptographically secure 0x8 User Customizable 0xF Max In the canonical representation, xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx, the four bits of M indicate the UUID version (i.e., the hexadecimal M will be either 1, 2, 3, 4, 5, 6, 7, or 8).") (get-variant [uuid] "Return the variant number associated with this UUID. The variant field contains a value which identifies the layout of the UUID. The bit-layout implemented by this protocol supports UUID's with a variant value of 0x2, which indicates Leach-Salz layout. Defined UUID variant values are: 0x0 Null 0x2 Leach-Salz 0x6 Microsoft 0x7 Max In the canonical representation, xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx, the most significant bits of N indicate the variant (depending on the variant one, two, or three bits are used). The variant covered by RFC9562 is indicated by the two most significant bits of N being 1 0 (i.e., the hexadecimal N will always be 8, 9, A, or B).") (get-time-low [uuid] "Return the 32 bit unsigned value that represents the `time-low` field of the `timestamp` associated with this UUID.") (get-time-mid [uuid] "Return the 16 bit unsigned value that represents the `time-mid` field of the `timestamp` associated with this UUID.") (get-time-high [uuid] "Return the 16 bit unsigned value that represents the `time-high` field of the `timestamp` multiplexed with the `version` of this UUID.") (get-clk-high [uuid] "Return the 8 bit unsigned value that represents the most significant byte of the `clk-seq` multiplexed with the `variant` of this UUID.") (get-clk-low [uuid] "Return the 8 bit unsigned value that represents the least significant byte of the `clk-seq` associated with this UUID.") (get-clk-seq [uuid] "Return the clock-sequence number associated with this UUID. For time-based (v1, v6) UUID's the 'clock-sequence' value is a somewhat counter-intuitively named seed-value that is used to reduce the potential that duplicate UUID's might be generated under unusual situations, such as if the system hardware clock is set backward in time or if, despite all efforts otherwise, a duplicate node-id happens to be generated. This value is initialized to a random 16-bit number once per lifetime of the system. For non-gregorian-time-based (v3, v4, v5, v7, v8, squuid) UUID's, always returns `nil`.") (get-node-id [uuid] "Return the 48 bit unsigned value that represents the spatially unique node identifier associated with this UUID.") (get-timestamp [uuid] "Return the time of UUID creation. For Gregorian time-based (v1, v6) UUID's, this is 60 bit unsigned value that represents a temporally unique timestamp associated with this UUID. The result encodes the number of 100 nanosecond intervals since the adoption of the Gregorian calendar. For v7 UUID's this encodes the more common unix time in milliseconds since midnight, January 1, 1970 UTC. For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns `nil`.") (get-instant ^java.util.Date [uuid] "For time-based (v1, v6, v7) UUID's, return a java.util.Date object that represents the system time at which this UUID was generated. NOTE: the returned value may not necessarily be temporally unique. For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns `nil`.") (get-unix-time [uuid] "For time-based (v1, v6, v7) UUIDs return the timestamp portion in approximately milliseconds since the Unix epoch 1970-01-01T00:00:00.000Z. For non-time-based (v3, v4, v5, v8, squuid) UUID's, always returns `nil`.") (to-byte-array [uuid] "Return an array of 16 bytes that represents `uuid` as a decomposed octet serialization encoded in most-significant-byte first order.") (to-string ^String [uuid] "Return a String object that represents `uuid` in the canonical 36 character hex-string format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") (to-hex-string ^String [uuid] "Return a String object that represents `uuid` as the 32 hexadecimal characters directly encodong the UUID's 128 bit value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") (to-urn-string ^String [uuid] "Return a String object that represents `uuid` as a the string serialization of the URN URI based on the canonical 36 character hex-string representation: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") (to-uri ^java.net.URI [uuid] "Return the unique URN URI associated with this UUID.")) ;; For backwards compatibility (def UUIDRfc4122 UUIDRfc9562) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; RFC9562 Unique Identifier extended java.util.UUID ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (extend-type UUID UUIDable (as-uuid [u] u) (uuidable? [_] true) UUIDRfc9562 (uuid? ^boolean [_] true) (uuid= ^boolean [^UUID x ^UUID y] (.equals x y)) (uuid< ^boolean [^UUID x ^UUID y] (let [xh (.getMostSignificantBits x) yh (.getMostSignificantBits y)] (or (clojure/< xh yh) (and (clojure/= xh yh) (clojure/< (.getLeastSignificantBits x) (.getLeastSignificantBits y)))))) (uuid> ^boolean [^UUID x ^UUID y] (let [xh (.getMostSignificantBits x) yh (.getMostSignificantBits y)] (or (clojure/> xh yh) (and (clojure/= xh yh) (clojure/> (.getLeastSignificantBits x) (.getLeastSignificantBits y)))))) (get-word-high ^long [uuid] (.getMostSignificantBits uuid)) (get-word-low ^long [uuid] (.getLeastSignificantBits uuid)) (null? ^boolean [uuid] (clojure/= 0 (.getMostSignificantBits uuid) (.getLeastSignificantBits uuid))) (max? ^boolean [uuid] (uuid= uuid +max+)) ;; two putLong calls (one per word) instead of bitmop's ;; 16-iteration loop of ldb+sb8 per byte. (to-byte-array ^bytes [uuid] (let [arr (byte-array 16)] (bitmop/long->bytes (.getMostSignificantBits uuid) arr 0) (bitmop/long->bytes (.getLeastSignificantBits uuid) arr 8) arr)) (hash-code ^long [uuid] (long (.hashCode uuid))) (get-version ^int [uuid] (.version uuid)) (get-variant ^int [uuid] (.variant uuid)) (to-string [uuid] (.toString uuid)) (to-urn-string [uuid] (str "urn:uuid:" (.toString uuid))) ;; buf-hex renders all 32 hex chars via a single StringBuilder ;; from a ByteBuffer, instead of two separate hex() calls + string concat. (to-hex-string [uuid] (bitmop/buf-hex (bitmop/uuid->buf uuid))) (to-uri [uuid] (URI/create (to-urn-string uuid))) (get-time-low ^long [uuid] (let [msb (.getMostSignificantBits uuid)] (if (clojure/= 6 (get-version uuid)) (bitmop/ldb #=(bitmop/mask 16 0) msb) (bitmop/ldb #=(bitmop/mask 32 0) (bit-shift-right msb 32))))) (get-time-mid ^long [uuid] (bitmop/ldb #=(bitmop/mask 16 16) (.getMostSignificantBits uuid))) (get-time-high ^long [uuid] (let [msb (.getMostSignificantBits uuid)] (if (clojure/= 6 (get-version uuid)) (bitmop/ldb #=(bitmop/mask 32 0) (bit-shift-right msb 32)) (bitmop/ldb #=(bitmop/mask 16 0) msb)))) (get-clk-low ^long [uuid] (bitmop/ldb #=(bitmop/mask 8 0) (bit-shift-right (.getLeastSignificantBits uuid) 56))) (get-clk-high ^long [uuid] (bitmop/ldb #=(bitmop/mask 8 48) (.getLeastSignificantBits uuid))) (get-clk-seq ^short [uuid] (when (#{1 6} (.version uuid)) (bitmop/ldb #=(bitmop/mask 14 48) (.getLeastSignificantBits uuid)))) (get-node-id ^long [uuid] (bitmop/ldb #=(bitmop/mask 48 0) (.getLeastSignificantBits uuid))) (get-timestamp ^long [uuid] (case (.version uuid) 1 (.timestamp uuid) 6 (bit-or (bitmop/ldb #=(bitmop/mask 12 0) (.getMostSignificantBits uuid)) (bit-shift-left (get-time-mid uuid) 12) (bit-shift-left (get-time-high uuid) 28)) 7 (bitmop/ldb #=(bitmop/mask 48 16) (.getMostSignificantBits uuid)) nil)) (get-unix-time ^long [uuid] (case (.version uuid) (1 6) (clock/posix-time (get-timestamp uuid)) 7 (get-timestamp uuid) nil)) (get-instant [uuid] (when-let [ts (get-unix-time uuid)] (Date. (long ts)))) UUIDNameBytes (as-byte-array ^bytes [this] (to-byte-array this))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; V0 UUID Constructor [RFC9562:5.9] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn null "Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000." ^java.util.UUID [] +null+) (defn v0 "Generates the v0 (null) UUID, 00000000-0000-0000-0000-000000000000." ^java.util.UUID [] +null+) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The MAX UUID Constructor [RFC9562:5.10] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn max "Generates the v15 (maximum) UUID, ffffffff-ffff-ffff-ffff-ffffffffffff." ^java.util.UUID [] +max+) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; V1, V6 (time-coded) UUID Constructors [RFC9562:5.1, 5.6] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Concatenate the UUID version with a unique per-node identifier (the ;; MAC address of the computer that is generating the UUID, or securely ;; generated once-per-session random identifier) and with a monotonic ;; timestamp based on the number of 100-nanosecond intervals since the ;; adoption of the Gregorian calendar in the West, 12:00am Friday ;; October 15, 1582 UTC. (let [^java.util.concurrent.atomic.AtomicLong packed clock/-gregorian-packed- subcounter-max clock/+subcounter-resolution+ v1-lsb (long node/+v1-lsb+) v6-lsb (long node/+v6-lsb+)] (defn v1 "Generate a v1 (time-based) unique identifier, guaranteed to be unique and thread-safe regardless of clock precision or degree of concurrency. Creation of v1 UUID's does not require any call to a cryptographic generator and can be accomplished much more efficiently than v3, v4, v5, v7, or squuid's. A v1 UUID reveals both the identity of the computer that generated the UUID and the time at which it did so. Its uniqueness across computers is guaranteed as long as MAC addresses are not duplicated." ^java.util.UUID [] (loop [] (let [current (.get packed) millis (unsigned-bit-shift-right current 14) time-now (System/currentTimeMillis)] (cond (clojure/< millis time-now) (let [next (bit-shift-left time-now 14)] (if (.compareAndSet packed current next) (let [ts (+ 100103040000000000 (* (+ 2208988800000 time-now) 10000)) msb (bit-or (bit-shift-left (bit-and ts 0xFFFFFFFF) 32) (bit-shift-left (bit-and (unsigned-bit-shift-right ts 32) 0xFFFF) 16) 0x1000 (bit-and (unsigned-bit-shift-right ts 48) 0xFFF))] (UUID. msb v1-lsb)) (recur))) (clojure/> millis time-now) (recur) true (let [seqid (bit-and current 0x3FFF) tt (inc seqid)] (if (<= tt subcounter-max) (let [next (bit-or (bit-shift-left time-now 14) tt)] (if (.compareAndSet packed current next) (let [ts (+ tt 100103040000000000 (* (+ 2208988800000 time-now) 10000)) msb (bit-or (bit-shift-left (bit-and ts 0xFFFFFFFF) 32) (bit-shift-left (bit-and (unsigned-bit-shift-right ts 32) 0xFFFF) 16) 0x1000 (bit-and (unsigned-bit-shift-right ts 48) 0xFFF))] (UUID. msb v1-lsb)) (recur))) (recur))))))) (defn v6 "Generate a v6 (time-based), LEXICALLY SORTABLE, unique identifier, v6 is a field-compatible version of v1, reordered for improved DB locality. Creation of v6 UUID's does not require any call to a cryptographic generator and can be accomplished much more efficiently than v3, v4, v5, v7, or squuid's. A v6 UUID uses a cryptographically secure, hard to guess random node id. It DOES NOT reveal the identity of the computer on which it was created." ^java.util.UUID [] (loop [] (let [current (.get packed) millis (unsigned-bit-shift-right current 14) time-now (System/currentTimeMillis)] (cond (clojure/< millis time-now) (let [next (bit-shift-left time-now 14)] (if (.compareAndSet packed current next) (let [ts (+ 100103040000000000 (* (+ 2208988800000 time-now) 10000)) msb (bit-or (bit-shift-left (bit-and (unsigned-bit-shift-right ts 28) 0xFFFFFFFF) 32) (bit-shift-left (bit-and (unsigned-bit-shift-right ts 12) 0xFFFF) 16) 0x6000 (bit-and ts 0xFFF))] (UUID. msb v6-lsb)) (recur))) (clojure/> millis time-now) (recur) true (let [seqid (bit-and current 0x3FFF) tt (inc seqid)] (if (<= tt subcounter-max) (let [next (bit-or (bit-shift-left time-now 14) tt)] (if (.compareAndSet packed current next) (let [ts (+ tt 100103040000000000 (* (+ 2208988800000 time-now) 10000)) msb (bit-or (bit-shift-left (bit-and (unsigned-bit-shift-right ts 28) 0xFFFFFFFF) 32) (bit-shift-left (bit-and (unsigned-bit-shift-right ts 12) 0xFFFF) 16) 0x6000 (bit-and ts 0xFFF))] (UUID. msb v6-lsb)) (recur))) (recur)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; V7 (crypto secure, posix time-coded) UUID Constructor [RFC9562:5.7] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 0 1 2 3 ;; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | unix_ts_ms | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | unix_ts_ms | ver | rand_a | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; |var| rand_b | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; | rand_b | ;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;; The following table enumerates a slot/type/value correspondence: ;; ;; SLOT SIZE Description ;; ---------------------------------------------------------------------- ;; unix_ts_ms 6 bytes POSIX millis timestamp ;; ver 4 bits version, set to 0b0111 (7) ;; rand_a 12 bits randomly initialized subcounter ;; var 2 bits variant, set to 0b10 (2) ;; rand_b 62 bits cryptographically secure pseudorandom data (defn v7 "Generate a v7 unix time-based, LEXICALLY SORTABLE UUID with monotonic counter and cryptographically secure random portion and POSIX time encoding. As such, creation of v7 UUIDs may be significantly slower, but have improved entropy chararacteristics compared to v1 or v6 UUIDs." ^java.util.UUID [] (let [^clj_uuid.clock.State state (clock/monotonic-unix-time-and-random-counter) time (bitmop/ldb #=(bitmop/mask 48 0) (.millis state)) ver-and-counter (bitmop/dpb #=(bitmop/mask 4 12) (.seqid state) 0x7) msb (bit-or ver-and-counter (bit-shift-left time 16)) lsb (bitmop/dpb #=(bitmop/mask 2 62) (random/long) 0x2)] (UUID. msb lsb))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; V7nc (non-cryptographic V7) UUID Constructor ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (let [^ThreadLocal v7nc-tl (ThreadLocal/withInitial (reify java.util.function.Supplier (get [_] (long-array 3))))] (defn v7nc "Generate a v7 UUID using non-cryptographic randomness (ThreadLocalRandom). Produces valid RFC 9562 v7 UUIDs with the same timestamp/version/variant structure as v7, but uses java.util.concurrent.ThreadLocalRandom instead of SecureRandom and a per-thread monotonic counter instead of a global AtomicLong. This trades cryptographic unguessability for significantly higher throughput -- comparable to JUG's TimeBasedEpochGenerator. The per-thread counter uses rand_a (12 bits) as a fixed seed per millisecond and rand_b (62 bits) as a strictly increasing counter, giving 2^62 UUIDs per millisecond per thread before overflow." ^java.util.UUID [] (let [^longs state (.get v7nc-tl) ^java.util.concurrent.ThreadLocalRandom tlr (java.util.concurrent.ThreadLocalRandom/current)] (loop [] (let [time-now (System/currentTimeMillis) last-ms (aget state 0)] (cond (clojure/> time-now last-ms) (let [lsb-ctr (bit-and (.nextLong tlr) 0x3FFFFFFFFFFFFFFF) msb (bit-or (bit-shift-left (bit-and time-now 0xFFFFFFFFFFFF) 16) (bit-or 0x7000 (bit-and (.nextLong tlr) 0xFFF)))] (aset state 0 time-now) (aset state 1 msb) (aset state 2 lsb-ctr) (UUID. msb (bit-or lsb-ctr #=(bit-shift-left 2 62)))) (clojure/< time-now last-ms) (recur) true (let [lsb-ctr (bit-and (unchecked-inc (aget state 2)) 0x3FFFFFFFFFFFFFFF)] (aset state 2 lsb-ctr) (UUID. (aget state 1) (bit-or lsb-ctr #=(bit-shift-left 2 62)))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; V4 (random) UUID Constructor [RFC9562:5.4] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn v4 "Generate a v4 (random) UUID. Uses default JVM implementation. If two arguments, lsb and msb (both long) are provided, then construct a valid, properly formatted v4 UUID based on those values. So, for example the following UUID, created from all zero bits, is indeed distinct from the null UUID: (v4) => #uuid \"dcf0035f-ea29-4d1c-b52e-4ea499c6323e\" (v4 0 0) => #uuid \"00000000-0000-4000-8000-000000000000\" (null) => #uuid \"00000000-0000-0000-0000-000000000000\"" (^java.util.UUID [] (UUID/randomUUID)) (^java.util.UUID [msb lsb] (UUID. (bitmop/dpb #=(bitmop/mask 4 12) msb 0x4) (bitmop/dpb #=(bitmop/mask 2 62) lsb 0x2)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; v8 (custom) UUID Constructor [RFC9562:5.8: UUID Version 8];; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn v8 "Generate a v8 custom UUID with up to 122 bits of user data." ^java.util.UUID [^long msb ^long lsb] (UUID. (bitmop/dpb #=(bitmop/mask 4 12) msb 0x8) (bitmop/dpb #=(bitmop/mask 2 62) lsb 0x2))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SQUUID (sequential) UUID Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn squuid "Generate a SQUUID (sequential, random) unique identifier. SQUUID's are a nonstandard variation on v4 (random) UUIDs that have the desirable property that they increase sequentially over time as well as encode retrievably the posix time at which they were generated. Splits and reassembles a v4 UUID to merge current POSIX time (seconds since 12:00am January 1, 1970 UTC) with the most significant 32 bits of the UUID." ^java.util.UUID [] (let [uuid (v4) secs (clock/posix-time) lsb (get-word-low uuid) msb (get-word-high uuid) timed-msb (bit-or (bit-shift-left secs 32) (bit-and +ub32-mask+ msb))] (UUID. timed-msb lsb))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; "Local-Part" Representation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The following represent a default set of local-part encoding rules. As a ;; default, a plain byte-array will be passed through unchanged and a ;; generic java.lang.Object is represented by the bytes of its serialization. ;; Strings are represented using UTF8 encoding. URL's are digested as the ;; UTF bytes of their string representation. (def ^:private ByteArray (class (byte-array 0))) (extend-protocol UUIDNameBytes java.lang.Object (as-byte-array ^bytes [this] (if (instance? ByteArray this) this (let [baos (ByteArrayOutputStream.) oos (ObjectOutputStream. baos)] (.writeObject oos this) (.close oos) (.toByteArray baos)))) java.lang.String (as-byte-array ^bytes [this] (util/compile-if (util/java6?) (.getBytes this) (.getBytes this java.nio.charset.StandardCharsets/UTF_8))) java.net.URL (as-byte-array ^bytes [this] (as-byte-array (.toString this))) nil (as-byte-array [x] (throw (IllegalArgumentException. (format "%s cannot be converted to byte array." x))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Digest Instance ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private -md5- (ThreadLocal/withInitial (reify java.util.function.Supplier (get [_] (MessageDigest/getInstance "MD5"))))) (def ^:private -sha1- (ThreadLocal/withInitial (reify java.util.function.Supplier (get [_] (MessageDigest/getInstance "SHA1"))))) (defn- make-digest ^java.security.MessageDigest [^String designator] (let [^MessageDigest md (case designator "MD5" (.get ^ThreadLocal -md5-) "SHA1" (.get ^ThreadLocal -sha1-) (MessageDigest/getInstance designator))] (.reset md) md)) (defn- digest-bytes ^bytes [^String kind ^bytes ns-bytes ^bytes local-bytes] (let [m (make-digest kind)] (.update m ns-bytes) (.digest m local-bytes))) (defn- build-digested-uuid ^java.util.UUID [^long version ^bytes arr] {:pre [(or (clojure/= version 3) (clojure/= version 5))]} (let [msb (bitmop/bytes->long arr 0) lsb (bitmop/bytes->long arr 8)] (UUID. (bitmop/dpb #=(bitmop/mask 4 12) msb version) (bitmop/dpb #=(bitmop/mask 2 62) lsb 0x2)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Namespaced UUIDs [RFC9562:5.3, 5.5] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (let [^ThreadLocal md5-tl -md5- ^ThreadLocal sha1-tl -sha1- ^ThreadLocal ns-buf-tl (ThreadLocal/withInitial (reify java.util.function.Supplier (get [_] (ByteBuffer/allocate 16))))] (defn v3 "Generate a v3 (name based, MD5 hash) UUID. 'context' must be UUIDable. v3 identifiers are intended for generating UUID's from names that are drawn from, and unique within, some namespace. The concept of name and namespace should be broadly construed, and not limited to textual names. The requirements for a v3 UUID are as follows: * v3 UUID's generated at different times from the same name in the same namespace MUST be equal. * v3 UUID's generated from two different names in the same namespace SHOULD be distinct with a high degree of certainty. * v3 UUID's generated from the same name in two different namespaces SHOULD be distinct with a high degree of certainty. * If two v3 UUID's are equal, then there is a high degree of certainty that they were generated from the same name in the same namespace." ^java.util.UUID [context local-part] (let [^MessageDigest md (.get md5-tl) _ (.reset md) ^UUID ns-uuid (as-uuid context) ^ByteBuffer nsbuf (.get ns-buf-tl) _ (.putLong nsbuf 0 (.getMostSignificantBits ns-uuid)) _ (.putLong nsbuf 8 (.getLeastSignificantBits ns-uuid)) _ (.update md (.array nsbuf)) digest (.digest md ^bytes (as-byte-array local-part)) ^ByteBuffer dbuf (ByteBuffer/wrap digest) msb (bit-or (bit-and (.getLong dbuf 0) #=(bit-not #=(bitmop/mask 4 12))) 0x3000) lsb (bit-or (bit-and (.getLong dbuf 8) #=(bit-not #=(bitmop/mask 2 62))) #=(bit-shift-left 2 62))] (UUID. msb lsb))) (defn v5 "Generate a v5 (name based, SHA1 hash) UUID. 'context' must be UUIDable. v5 identifiers are intended for generating UUID's from names that are drawn from, and unique within, some namespace. The concept of name and namespace should be broadly construed, and not limited to textual names. The requirements for a v5 UUID are as follows: * v5 UUID's generated at different times from the same name in the same namespace MUST be equal. * v5 UUID's generated from two different names in the same namespace SHOULD be distinct with a high degree of certainty. * v5 UUID's generated from the same name in two different namespaces SHOULD be distinct with a high degree of certainty. * If two v5 UUID's are equal, then there is a high degree of certainty that they were generated from the same name in the same namespace." ^java.util.UUID [context local-part] (let [^MessageDigest md (.get sha1-tl) _ (.reset md) ^UUID ns-uuid (as-uuid context) ^ByteBuffer nsbuf (.get ns-buf-tl) _ (.putLong nsbuf 0 (.getMostSignificantBits ns-uuid)) _ (.putLong nsbuf 8 (.getLeastSignificantBits ns-uuid)) _ (.update md (.array nsbuf)) digest (.digest md ^bytes (as-byte-array local-part)) ^ByteBuffer dbuf (ByteBuffer/wrap digest) msb (bit-or (bit-and (.getLong dbuf 0) #=(bit-not #=(bitmop/mask 4 12))) 0x5000) lsb (bit-or (bit-and (.getLong dbuf 8) #=(bit-not #=(bitmop/mask 2 62))) #=(bit-shift-left 2 62))] (UUID. msb lsb)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- compare-many [f x y more] (if (f x y) (if (next more) (recur f y (first more) (next more)) (f y (first more))) false)) (defn = "Directly compare two or more UUIDs for = relation based on the equality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]." ([_] true) ([x y] (uuid= x y)) ([x y & more] (compare-many uuid= x y more))) (defn > "Directly compare two or more UUIDs for > relation based on the ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]." ([_] true) ([x y] (uuid> x y)) ([x y & more] (compare-many uuid> x y more))) (defn < "Directly compare two or more UUIDs for < relation based on the ordinality semantics defined by [RFC4122:3 RULES FOR LEXICAL EQUIVALENCE]." ([_] true) ([x y] (uuid< x y)) ([x y & more] (compare-many uuid< x y more))) (defn uuid-string? [str] (and (string? str) (some? (re-matches uuid-regex str)))) (defn uuid-urn-string? [str] (and (string? str) (some? (re-matches urn-regex str)))) (defn uuid-vec? [v] (and (clojure/= (count v) 16) (every? #(and (integer? %) (>= -128 % 127)) v))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UUID Polymorphism ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- str->uuid [s] (cond (uuid-string? s) (UUID/fromString s) (uuid-urn-string? s) (UUID/fromString (subs s 9)) :else (throw (IllegalArgumentException. (format "Invalid UUID: %s" s))))) (extend-protocol UUIDRfc9562 Object (uuid? [x] false) nil (uuid? [_] false)) (extend-protocol UUIDable (Class/forName "[B") ; byte array (as-uuid [^bytes ba] (let [bb (ByteBuffer/wrap ba)] (UUID. (.getLong bb 0) (.getLong bb 8)))) (uuidable? [^bytes ba] (clojure/= 16 (alength ^bytes ba))) String (uuidable? ^boolean [s] (or (uuid-string? s) (uuid-urn-string? s))) (as-uuid [s] (str->uuid s)) URI (uuidable? ^boolean [u] (uuid-urn-string? (str u))) (as-uuid [u] (str->uuid (str u))) Object (uuidable? ^boolean [_] false) (as-uuid [x] (throw (IllegalArgumentException. (format "%s Cannot be coerced to UUID." x)))) nil (as-uuid [x] (throw (IllegalArgumentException. (format "%s cannot be coerced to UUID." x)))) (uuidable? ^boolean [_] false)) ================================================ FILE: src/clj_uuid/node.clj ================================================ (ns clj-uuid.node (:require [clj-uuid.util :refer [java6? compile-if]] [clj-uuid.bitmop :refer [sb8 assemble-bytes ldb dpb mask]] [clj-uuid.constants :refer :all] [clj-uuid.random :as random]) (:import [java.net InetAddress NetworkInterface] [java.security MessageDigest] [java.util Properties])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Clock Sequence [RFC4122:4.1.5 "CLOCK SEQUENCE"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; For time-based UUID's the "clock-sequence" value is a somewhat counter- ;; intuitively named value that is used to reduce the potential that duplicate ;; UUID's might be generated under unusual situations, such as if the system ;; hardware clock is set backward in time or if, despite all efforts otherwise, ;; a duplicate +node-id+ (see below) happens to be generated. This value is ;; initialized to a random 16-bit number once per lifetime of the system. (defonce +clock-sequence+ (inc (rand-int 0xffff))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NodeID Representation [RFC4122:4.1.6 "NODE"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The representation of NodeID used for construction of time-based (v1) UUIDs ;; is a list with the following encoding semantics: ;; ;; SIZE TYPE REPRESENTATION ;; -----------+------+---------+--------------------------------------------- ;; node | 6 | ub48 | ( ) ;; ;; prepending two other (computed) bytes to the node-id before ;; bitwise assembly. ;; ;; (cons clock-high (cons clock-low @+node-id+)) ;; ;; ;; ( . . ) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NodeID Calculation [RFC4122:4.5 "NODE IDS THAT DO NOT IDENTIFY THE HOST"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This turns out to be surprisingly problematic. I've tried various ;; approaches. The most straightforward is the use of IEEE 802 MAC Address: ;; ;; (.getHardwareAddress ;; (java.net.NetworkInterface/getByInetAddress ;; (java.net.InetAddress/getLocalHost)))))) ;; ;; Unfortunately got reports of NPE on some platforms (openjdk?). Also, it ;; discloses the hardware address of the host system -- this is how the ;; creator of the melissa virus was actually tracked down and caught. ;; ;; choosing node-id randomly does not provide consistent generation of UUID's ;; across runtimes. ;; ;; This topic is specifically addressed by the RFC: ;; ;; ;; "A better solution is to obtain a 47-bit cryptographic quality random ;; number and use it as the low 47-bits of the Node-ID, with the least ;; significant bit of the first octet of the Node-ID set to one. This ;; bit is the unicast/multicast bit, which will never be set in IEEE 802 ;; addresses obtained from network cards. Hence, there can never be a ;; conflict between UUID's generated by machines with and without network ;; cards." ;; ;; . . . ;; ;; "In addition, items such as the computer's name and the name of the ;; operating system, while not strictly speaking random, will help ;; differentiate the results from those obtained by other systems... ;; ... A generic approach... IS TO ACCUMULATE AS MANY SOURCES AS POSSIBLE ;; INTO A BUFFER, USE A MESSAGE DIGEST SUCH AS MD5 OR SHA1, TAKE AN ;; ARBITRARY 6 BYTES FROM THE HASH VALUE, AND SET THE MULTICAST BIT ;; AS DESCRIBED ABOVE." ;; ;; -- [RFC4122:4.5 "Node IDs that do not Identify the Host"] ;; ;; ;; We do exactly that. Taking into account that the term "first octet" ;; in the above excerpt refers to network transmission order, and we ;; 'bit-or' the corresponding bytes: ;; ;; hi-byte | byte5 | byte4 | byte3 | byte2 | lo-byte ;; ---------+-------+-------+-------+-------+--------- ;; 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x01 ;; ;; Thanks to Datastax and to @jjcomer for submitting the original patch ;; from which this current implementation is largely derived. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private datasources ["java.vendor" "java.vendor.url" "java.version" "os.arch" "os.name" "os.version"]) (defn- all-local-addresses [] (let [^InetAddress local-host (InetAddress/getLocalHost) host-name (.getCanonicalHostName local-host) base-addresses #{(str local-host) host-name} network-interfaces (reduce (fn [acc ^NetworkInterface ni] (concat acc (map str (enumeration-seq (.getInetAddresses ni))))) base-addresses (enumeration-seq (NetworkInterface/getNetworkInterfaces)))] (reduce conj network-interfaces (map str (InetAddress/getAllByName host-name))))) (defn- make-node-id [] (let [addresses (all-local-addresses) ^MessageDigest digest (MessageDigest/getInstance "MD5") ^Properties props (System/getProperties) to-digest (reduce (fn [acc key] (conj acc (.getProperty props key))) addresses datasources)] (doseq [^String d to-digest] (compile-if (java6?) (.update digest (.getBytes d)) (.update digest (.getBytes d java.nio.charset.StandardCharsets/UTF_8)))) (map bit-or [0x00 0x00 0x00 0x00 0x00 0x01] (take 6 (seq (.digest digest)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Public NodeID API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def node-id make-node-id) (def +node-id+ (delay (assemble-bytes (cons 0 (cons 0 (node-id)))))) (defonce +v1-lsb+ (let [clk-high (dpb (mask 2 6) (ldb (mask 6 8) +clock-sequence+) 0x2) clk-low (ldb (mask 8 0) +clock-sequence+)] (dpb (mask 8 56) (dpb (mask 8 48) @+node-id+ clk-low) clk-high))) ;; v6 lsb uses a cryptographically secure random node identifier that is ;; initialized at runtime. (defonce +v6-lsb+ (let [clk-high (dpb (mask 2 6) (ldb (mask 6 8) +clock-sequence+) 0x2) clk-low (ldb (mask 8 0) +clock-sequence+)] (dpb (mask 8 56) (dpb (mask 8 48) (random/long) clk-low) clk-high))) ================================================ FILE: src/clj_uuid/random.clj ================================================ (ns clj-uuid.random (:refer-clojure :exclude [bytes long]) (:import (java.security SecureRandom))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Randomness [RFC9562:6.9 "Unguessability"] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; "Implementations SHOULD utilize a cryptographically secure ;; pseudorandom number generator (CSPRNG) to provide values that are ;; both difficult to predict (unguessable) and have a low likelihood ;; of collision" ;; ;; UUID variants calling for hard-to-guess random components (currently ;; v7) are generated using java.security.SecureRandom -- A ;; cryptographically strong nondeterministic random number generator ;; that minimally complies with the statistical random number generator ;; tests specified in FIPS 140-2, Security Requirements for ;; Cryptographic Modules, section 4.9.1. (defonce ^:private ^SecureRandom secure-random (SecureRandom.)) (defn bytes "Generate `n` random bytes." [n] (let [bs (byte-array n)] (.nextBytes secure-random bs) bs)) (defn long "Generate a long value that is hard to guess. Randomness limited to the number of bytes." ([] (.nextLong secure-random)) ([n-bytes] (reduce (fn [n b] (+ (bit-shift-left n 8) b)) 0 (bytes n-bytes)))) (defn eight-bits "Generate a hard-to-guess long value between 0 and 255" [] (bit-and (.nextLong secure-random) 0xff)) (defn ten-bits "Generate a hard-to-guess long value between 0 and 1023" [] (bit-and (.nextLong secure-random) 0x3ff)) (defn eleven-bits "Generate a hard-to-guess long value between 0 and 2047" [] (bit-and (.nextLong secure-random) 0x7ff)) (defn twelve-bits "Generate a hard-to-guess long value between 0 and 4095" [] (bit-and (.nextLong secure-random) 0xfff)) ================================================ FILE: src/clj_uuid/util.clj ================================================ (ns clj-uuid.util (:import (java.util UUID))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROG1 but with more idiomatic clojure name ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro returning "Compute a return value, then execute other forms for side effects. Like prog1 in common lisp, or a (do) that returns the first form." [value & forms] `(let [value# ~value] ~@forms value#)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Conditional Compilation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn java6? [] (neg? (compare (System/getProperty "java.version") "1.7"))) (defmacro compile-if "Evaluate `exp` and if it returns logical true and doesn't error, expand to `then` otherwise expand to `else`. credit: (compile-if (Class/forName \"java.util.concurrent.ForkJoinTask\") (do-cool-stuff-with-fork-join) (fall-back-to-executor-services))" [exp then else] (if (try (eval exp) (catch Throwable _ false)) `(do ~then) `(do ~else))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Timing and Performance Metric ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro with-timing "Same as clojure.core/time but returns a vector of a the result of the code and the milliseconds rather than printing a string. Runs the code in an implicit do." [& body] `(let [start# (System/nanoTime) ret# ~(cons 'do body)] [ret# (/ (double (- (System/nanoTime) start#)) 1000000.0)])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Debugging ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro wrap-fn [name args & body] `(let [old-fn# (var-get (var ~name)) new-fn# (fn [& p#] (let [~args p#] (do ~@body))) wrapper# (fn [& params#] (if (= ~(count args) (count params#)) (apply new-fn# params#) (apply old-fn# params#)))] (alter-var-root (var ~name) (constantly wrapper#)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; IO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro with-temp-file [f-sym & body] `(let [prefix# (.toString (UUID/randomUUID)) postfix# (.toString (UUID/randomUUID)) ~f-sym (java.io.File/createTempFile prefix# postfix#)] (try (do ~@body) (finally (.delete ~f-sym))))) (defn lines-of-file [^String file-name] (line-seq (java.io.BufferedReader. (java.io.InputStreamReader. (java.io.FileInputStream. file-name))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Namespace Re-export ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro import-vars "Import public vars from src-ns into the current namespace." [src-ns & var-syms] `(do ~@(map (fn [sym] `(def ~(vary-meta sym assoc :doc (:doc (meta (resolve (symbol (name src-ns) (name sym)))))) ~(symbol (name src-ns) (name sym)))) var-syms))) ================================================ FILE: src/clj_uuid.clj ================================================ (ns clj-uuid "RFC 9562 UUID implementation for Clojure. This namespace re-exports all public vars from clj-uuid.core, as Babashka does not like single-segment namespaces. For backward compatibility, both `(require '[clj-uuid :as uuid])` and `(require '[clj-uuid.core :as uuid])` provide identical functionality. Quick start: (require '[clj-uuid :as uuid]) (uuid/v4) ; random UUID (uuid/v7) ; time-based, sortable, cryptographically secure (uuid/v7nc) ; time-based, sortable, fast (non-cryptographic) See clj-uuid.core for complete documentation." (:refer-clojure :exclude [== uuid? max < > =]) (:require [clj-uuid.util :refer [import-vars]] [clj-uuid.core :as impl])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl +null+ +max+ +namespace-dns+ +namespace-url+ +namespace-oid+ +namespace-x500+) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Protocols ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def UUIDNameBytes impl/UUIDNameBytes) (def UUIDable impl/UUIDable) (def UUIDRfc9562 impl/UUIDRfc9562) (def UUIDRfc4122 impl/UUIDRfc4122) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl null v0 max v1 v3 v4 v5 v6 v7 v7nc v8 squuid) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Protocol Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl uuid? null? max? as-uuid uuidable? as-byte-array hash-code get-word-high get-word-low get-version get-variant get-time-low get-time-mid get-time-high get-clk-low get-clk-high get-clk-seq get-node-id get-timestamp get-instant get-unix-time to-byte-array to-string to-hex-string to-urn-string to-uri) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Comparison Operators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl uuid= uuid< uuid> = < >) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Predicates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl uuid-string? uuid-urn-string? uuid-vec?) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Clock ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import-vars impl monotonic-time) ================================================ FILE: test/clj_uuid/api_test.clj ================================================ (ns clj-uuid.api-test (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clojure.string :as str] [clj-uuid :refer :all :exclude [= > <]] [clj-uuid.clock :as clock]) (:import (java.lang IllegalArgumentException) (java.net URI URL) (java.util Date))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Protocol Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-unique-identifier-protocol (testing "v0 uuid protocol..." (let [tmpid +null+] (is (= (get-word-high tmpid) 0)) (is (= (get-word-low tmpid) 0)) (is (= (null? tmpid) true)) (is (= (seq (to-byte-array tmpid)) [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0])) (is (= (hash-code tmpid) 0)) (is (= (get-version tmpid) 0)) (is (= (to-string tmpid) "00000000-0000-0000-0000-000000000000")) (is (= (to-urn-string tmpid) "urn:uuid:00000000-0000-0000-0000-000000000000")) (is (= (get-time-low tmpid) 0)) (is (= (get-time-mid tmpid) 0)) (is (= (get-time-high tmpid) 0)) (is (= (get-clk-low tmpid) 0)) (is (= (get-clk-high tmpid) 0)) (is (= (get-node-id tmpid) 0)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil)))) (testing "v1 uuid protocol..." (let [tmpid +namespace-x500+] (is (= (get-word-high tmpid) 7757371281853190609)) (is (= (get-word-low tmpid) -9172705715073830712)) (is (= (null? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [107 -89 -72 20 -99 -83 17 -47 -128 -76 0 -64 79 -44 48 -56])) (is (= (hash-code tmpid) 963287501)) (is (= (get-version tmpid) 1)) (is (= (to-string tmpid) "6ba7b814-9dad-11d1-80b4-00c04fd430c8")) (is (= (to-urn-string tmpid) "urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8")) (is (= (get-time-low tmpid) 1806153748)) (is (= (get-time-mid tmpid) 40365)) (is (= (get-time-high tmpid) 4561)) (is (= (get-clk-low tmpid) 128)) (is (= (get-clk-high tmpid) 180)) (is (= (get-node-id tmpid) 825973027016)) (is (= (get-timestamp tmpid) 131059232331511828)) (is (= (get-unix-time tmpid) 886630433151)))) (testing "v3 uuid protocol..." (let [tmpid (as-uuid "d9c53a66-fde2-3d04-b5ad-dce3848df07e")] (is (= (get-word-high tmpid) -2754731383046652668)) (is (= (get-word-low tmpid) -5355381512134070146)) (is (= (null? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [-39 -59 58 102 -3 -30 61 4 -75 -83 -36 -29 -124 -115 -16 126])) (is (= (hash-code tmpid) 352791551)) (is (= (get-version tmpid) 3)) (is (= (to-string tmpid) "d9c53a66-fde2-3d04-b5ad-dce3848df07e")) (is (= (to-urn-string tmpid) "urn:uuid:d9c53a66-fde2-3d04-b5ad-dce3848df07e")) (is (= (get-time-low tmpid) 3653581414)) (is (= (get-time-mid tmpid) 64994)) (is (= (get-time-high tmpid) 15620)) (is (= (get-clk-low tmpid) 181)) (is (= (get-clk-high tmpid) 173)) (is (= (get-node-id tmpid) 242869739581566)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil)))) (testing "v4 uuid protocol..." (let [tmpid #uuid "3eb1e29a-4747-4a7d-8e40-94e245f57dc0"] (is (= (get-word-high tmpid) 4517641053478013565)) (is (= (get-word-low tmpid) -8196387622257066560)) (is (= (null? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [62 -79 -30 -102 71 71 74 125 -114 64 -108 -30 69 -11 125 -64])) (is (= (hash-code tmpid) -1304215099)) (is (= (get-version tmpid) 4)) (is (= (to-string tmpid) "3eb1e29a-4747-4a7d-8e40-94e245f57dc0")) (is (= (to-urn-string tmpid) "urn:uuid:3eb1e29a-4747-4a7d-8e40-94e245f57dc0")) (is (= (get-time-low tmpid) 1051845274)) (is (= (get-time-mid tmpid) 18247)) (is (= (get-time-high tmpid) 19069)) (is (= (get-clk-low tmpid) 142)) (is (= (get-clk-high tmpid) 64)) (is (= (get-node-id tmpid) 163699557236160)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil)))) (testing "max uuid protocol..." (let [tmpid +max+] (is (= (get-word-high tmpid) -1)) (is (= (get-word-low tmpid) -1)) (is (= (null? tmpid) false)) (is (= (max? tmpid) true)) (is (= (seq (to-byte-array tmpid)) [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1])) (is (= (hash-code tmpid) 0)) (is (= (get-version tmpid) 0xf)) (is (= (to-string tmpid) "ffffffff-ffff-ffff-ffff-ffffffffffff")) (is (= (to-urn-string tmpid) "urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff")) (is (= (get-time-low tmpid) 0xffffffff)) (is (= (get-time-mid tmpid) 0xffff)) (is (= (get-time-high tmpid) 0xffff)) (is (= (get-clk-low tmpid) 0xff)) (is (= (get-clk-high tmpid) 0xff)) (is (= (get-node-id tmpid) 0xffffffffffff)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil)))) (testing "v6 uuid protocol..." (let [tmpid #uuid "1ef3f06f-16db-6ff0-bb01-1b50e6f39e7f"] (is (= (get-word-high tmpid) 2230390600394043376)) (is (= (get-word-low tmpid) -4971662479354257793)) (is (= (null? tmpid) false)) (is (= (max? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [30 -13 -16 111 22 -37 111 -16 -69 1 27 80 -26 -13 -98 127])) (is (= (hash-code tmpid) 1440357040)) (is (= (get-version tmpid) 6)) (is (= (to-string tmpid) "1ef3f06f-16db-6ff0-bb01-1b50e6f39e7f")) (is (= (to-urn-string tmpid) "urn:uuid:1ef3f06f-16db-6ff0-bb01-1b50e6f39e7f")) (is (= (get-time-low tmpid) 0x6ff0)) (is (= (get-time-mid tmpid) 0x16db)) (is (= (get-time-high tmpid) 0x1ef3f06f)) (is (= (get-clk-low tmpid) 0xbb)) (is (= (get-clk-high tmpid) 0x1)) (is (= (get-node-id tmpid) 0x1b50e6f39e7f)) (is (= (get-timestamp tmpid) 0x1ef3f06f16dbff0)) (is (= (get-unix-time tmpid) 1720648452463)))) (testing "v7 uuid protocol..." (let [tmpid #uuid "01909eae-4801-753a-bcd5-0889c34ac129"] (is (= (get-word-high tmpid) 112764462053815610)) (is (= (get-word-low tmpid) -4839952836759731927)) (is (= (null? tmpid) false)) (is (= (max? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [1 -112 -98 -82 72 1 117 58 -68 -43 8 -119 -61 74 -63 41])) (is (= (hash-code tmpid) 906895924)) (is (= (get-version tmpid) 7)) (is (= (to-string tmpid) "01909eae-4801-753a-bcd5-0889c34ac129")) (is (= (to-urn-string tmpid) "urn:uuid:01909eae-4801-753a-bcd5-0889c34ac129")) (is (= (get-time-low tmpid) 0x01909eae)) (is (= (get-time-mid tmpid) 0x4801)) (is (= (get-time-high tmpid) 0x753a)) (is (= (get-clk-low tmpid) 0xbc)) (is (= (get-clk-high tmpid) 0xd5)) (is (= (get-node-id tmpid) 0x0889c34ac129)) (is (= (get-timestamp tmpid) 1720649140225)) (is (= (get-unix-time tmpid) 0x01909eae4801)))) (testing "v8 uuid protocol..." (let [tmpid #uuid "ffffffff-ffff-8fff-bfff-ffffffffffff"] (is (= (get-word-high tmpid) -28673)) (is (= (get-word-low tmpid) -4611686018427387905)) (is (= (null? tmpid) false)) (is (= (max? tmpid) false)) (is (= (seq (to-byte-array tmpid)) [-1 -1 -1 -1 -1 -1 -113 -1 -65 -1 -1 -1 -1 -1 -1 -1])) (is (= (hash-code tmpid) 1073770496)) (is (= (get-version tmpid) 8)) (is (= (to-string tmpid) "ffffffff-ffff-8fff-bfff-ffffffffffff")) (is (= (to-urn-string tmpid) "urn:uuid:ffffffff-ffff-8fff-bfff-ffffffffffff")) (is (= (get-time-low tmpid) 0xffffffff)) (is (= (get-time-mid tmpid) 0xffff)) (is (= (get-time-high tmpid) 0x8fff)) (is (= (get-clk-low tmpid) 0xbf)) (is (= (get-clk-high tmpid) 0xff)) (is (= (get-node-id tmpid) 0xffffffffffff)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Predicate Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-predicates (testing "string predicates..." (is (uuid-string? (to-string (v4)))) (is (uuid-urn-string? (to-urn-string (v4)))))) (deftest nil-test (testing "Calling certain functions/methods on nil returns nil" (testing "UUIDNameBytes" (is (thrown? IllegalArgumentException (as-byte-array nil)))) (testing "UUIDable" (is (thrown? IllegalArgumentException (as-uuid nil))) (is (false? (uuidable? nil)))) (testing "UUIDRfc4122" (is (false? (uuid? nil)))) (is (false? (uuid-string? nil))) (is (false? (uuid-urn-string? nil))) (is (false? (uuid-vec? nil))))) (deftest byte-array-round-trip-test (testing "round-trip via byte-array" (let [uuid #uuid "4787199e-c0e2-4609-b5b8-284f2b7d117d"] (is (= uuid (as-uuid (as-byte-array uuid))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; get-variant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-get-variant (testing "null UUID variant" (is (= 0 (get-variant +null+)))) (testing "Leach-Salz (variant 2) for all standard versions" (is (= 2 (get-variant (v1)))) (is (= 2 (get-variant (v3 +namespace-dns+ "test")))) (is (= 2 (get-variant (v4)))) (is (= 2 (get-variant (v5 +namespace-dns+ "test")))) (is (= 2 (get-variant (v6)))) (is (= 2 (get-variant (v7)))) (is (= 2 (get-variant (v8 0x123 0x456))))) (testing "max UUID variant" (is (= 7 (get-variant +max+)))) (testing "well-known namespace UUIDs are variant 2" (is (= 2 (get-variant +namespace-dns+))) (is (= 2 (get-variant +namespace-url+))) (is (= 2 (get-variant +namespace-oid+))) (is (= 2 (get-variant +namespace-x500+))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; get-instant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-get-instant (testing "v1 returns a Date" (let [before (System/currentTimeMillis) inst (get-instant (v1)) after (System/currentTimeMillis)] (is (instance? Date inst)) (is (<= before (.getTime ^Date inst) after)))) (testing "v6 returns a Date" (let [before (System/currentTimeMillis) inst (get-instant (v6)) after (System/currentTimeMillis)] (is (instance? Date inst)) (is (<= before (.getTime ^Date inst) after)))) (testing "v7 returns a Date" (let [before (System/currentTimeMillis) inst (get-instant (v7)) after (System/currentTimeMillis)] (is (instance? Date inst)) (is (<= before (.getTime ^Date inst) after)))) (testing "non-time-based versions return nil" (is (nil? (get-instant (v3 +namespace-dns+ "x")))) (is (nil? (get-instant (v4)))) (is (nil? (get-instant (v5 +namespace-dns+ "x")))) (is (nil? (get-instant (v8 0 0)))) (is (nil? (get-instant +null+))) (is (nil? (get-instant +max+))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; get-clk-seq ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-get-clk-seq (testing "v1 returns a clock sequence" (let [cs (get-clk-seq (v1))] (is (some? cs)) (is (integer? cs)))) (testing "v6 returns a clock sequence" (let [cs (get-clk-seq (v6))] (is (some? cs)) (is (integer? cs)) (is (<= 0 cs 0x3FFF) "clock sequence is 14 bits"))) (testing "non-gregorian-time versions return nil" (is (nil? (get-clk-seq (v3 +namespace-dns+ "x")))) (is (nil? (get-clk-seq (v4)))) (is (nil? (get-clk-seq (v5 +namespace-dns+ "x")))) (is (nil? (get-clk-seq (v7)))) (is (nil? (get-clk-seq (v8 0 0))))) (testing "known v1 UUID clock sequence" ;; +namespace-x500+ is a v1 UUID with clock-seq = 180 (is (= 180 (get-clk-seq +namespace-x500+))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; squuid ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-squuid (testing "squuid returns a UUID" (is (uuid? (squuid)))) (testing "squuid preserves v4 version bits" (let [s (squuid)] (is (= 4 (get-version s))))) (testing "squuid preserves variant 2" (let [s (squuid)] (is (= 2 (get-variant s))))) (testing "squuids are unique" (let [uuids (repeatedly 1000 squuid)] (is (= 1000 (count (set uuids)))))) (testing "squuid embeds time in high 32 bits of MSB" ;; The high 32 bits of MSB come from clock/posix-time which is ;; derived from System/currentTimeMillis. All squuids generated ;; within the same ~10s window share the same high 32 bits. (let [uuids (repeatedly 100 squuid) hi32s (map #(unsigned-bit-shift-right (get-word-high %) 32) uuids)] (is (= 1 (count (set hi32s))) "all squuids in a burst share the same time-derived high bits")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; to-uri ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-to-uri (testing "to-uri returns a java.net.URI" (let [u (v4) uri (to-uri u)] (is (instance? URI uri)))) (testing "to-uri produces correct URN format" (let [u #uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8" uri (to-uri u)] (is (= "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" (str uri))))) (testing "to-uri round-trips through as-uuid" (let [u (v4) uri (to-uri u)] (is (= u (as-uuid uri))))) (testing "to-uri for null and max" (is (= "urn:uuid:00000000-0000-0000-0000-000000000000" (str (to-uri +null+)))) (is (= "urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff" (str (to-uri +max+)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Multi-arity comparison operators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-multi-arity-comparisons (testing "single-arg always returns true" (let [u (v4)] (is (clj-uuid/= u)) (is (clj-uuid/< u)) (is (clj-uuid/> u)))) (testing "two-arg = works" (let [u (v3 +namespace-dns+ "same")] (is (clj-uuid/= u u)) (is (not (clj-uuid/= u (v4)))))) (testing "three-arg =" (let [u (v3 +namespace-dns+ "same")] (is (clj-uuid/= u u u)) (is (not (clj-uuid/= u u (v4)))))) (testing "four-arg =" (let [u (v3 +namespace-dns+ "same")] (is (clj-uuid/= u u u u)) (is (not (clj-uuid/= u u u (v4)))))) (testing "multi-arg < with ordered UUIDs" ;; uuid< uses signed long comparison on MSB then LSB. ;; Use UUIDs whose MSBs are in signed-ascending order. (let [a (v4 0 0) ; MSB starts with 0000...4... b (v4 0x3000000000000000 0) ; MSB starts with 3...4... c (v4 0x5000000000000000 0)] ; MSB starts with 5...4... (is (clj-uuid/< a b c)) (is (not (clj-uuid/< a c b))))) (testing "multi-arg > with ordered UUIDs" (let [a (v4 0 0) b (v4 0x3000000000000000 0) c (v4 0x5000000000000000 0)] (is (clj-uuid/> c b a)) (is (not (clj-uuid/> c a b))))) (testing "< and > with equal elements returns false" (let [u (v4)] (is (not (clj-uuid/< u u))) (is (not (clj-uuid/> u u)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; v4 and v8 version+variant bit verification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-v4-version-variant-bits (testing "v4 always has version=4 and variant=2" (dotimes [_ 10000] (let [u (v4)] (is (= 4 (get-version u))) (is (= 2 (get-variant u))))))) (deftest check-v4-two-arg-version-variant-bits (testing "v4 with explicit msb/lsb always stamps version=4 and variant=2" (is (= 4 (get-version (v4 0 0)))) (is (= 2 (get-variant (v4 0 0)))) (is (= 4 (get-version (v4 -1 -1)))) (is (= 2 (get-variant (v4 -1 -1)))) (is (= 4 (get-version (v4 Long/MAX_VALUE Long/MIN_VALUE)))) (is (= 2 (get-variant (v4 Long/MAX_VALUE Long/MIN_VALUE)))))) (deftest check-v8-version-variant-bits (testing "v8 always has version=8 and variant=2" (is (= 8 (get-version (v8 0 0)))) (is (= 2 (get-variant (v8 0 0)))) (is (= 8 (get-version (v8 -1 -1)))) (is (= 2 (get-variant (v8 -1 -1)))) (is (= 8 (get-version (v8 Long/MAX_VALUE Long/MIN_VALUE)))) (is (= 2 (get-variant (v8 Long/MAX_VALUE Long/MIN_VALUE)))))) (deftest check-v7-version-variant-bits (testing "v7 always has version=7 and variant=2" (dotimes [_ 10000] (let [u (v7)] (is (= 7 (get-version u))) (is (= 2 (get-variant u))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; as-byte-array on various types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-as-byte-array-types (testing "byte array passthrough" (let [ba (byte-array [1 2 3 4 5])] (is (identical? ba (as-byte-array ba))))) (testing "String produces UTF-8 bytes" (let [ba (as-byte-array "hello")] (is (instance? (Class/forName "[B") ba)) (is (= (count "hello") (alength ^bytes ba))) (is (= (seq (.getBytes "hello" "UTF-8")) (seq ba))))) (testing "URL produces bytes of string representation" (let [url (URL. "http://example.com") ba (as-byte-array url)] (is (instance? (Class/forName "[B") ba)) (is (= (seq (as-byte-array "http://example.com")) (seq ba))))) (testing "java.lang.Object produces serialized bytes" (let [ba (as-byte-array (Long. 42))] (is (instance? (Class/forName "[B") ba)) (is (pos? (alength ^bytes ba))))) (testing "nil throws IllegalArgumentException" (is (thrown? IllegalArgumentException (as-byte-array nil))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; as-uuid and uuidable? from various sources ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-as-uuid-from-string (testing "canonical hex string" (let [u (as-uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ u)))) (testing "URN string" (let [u (as-uuid "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ u)))) (testing "invalid string throws" (is (thrown? IllegalArgumentException (as-uuid "not-a-uuid"))) (is (thrown? IllegalArgumentException (as-uuid "")))) (testing "nil throws" (is (thrown? IllegalArgumentException (as-uuid nil))))) (deftest check-as-uuid-from-byte-array (testing "16-byte array round-trips" (let [u (v4) ba (to-byte-array u)] (is (= u (as-uuid ba))))) (testing "null UUID round-trips" (is (= +null+ (as-uuid (to-byte-array +null+))))) (testing "max UUID round-trips" (is (= +max+ (as-uuid (to-byte-array +max+)))))) (deftest check-as-uuid-from-uri (testing "URI with valid UUID URN" (let [uri (URI/create "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ (as-uuid uri))))) (testing "URI round-trip through to-uri" (let [u (v4)] (is (= u (as-uuid (to-uri u))))))) (deftest check-as-uuid-identity (testing "UUID passes through as-uuid unchanged" (let [u (v4)] (is (identical? u (as-uuid u)))))) (deftest check-uuidable? (testing "UUID is uuidable" (is (true? (uuidable? (v4))))) (testing "valid UUID strings are uuidable" (is (true? (uuidable? "6ba7b810-9dad-11d1-80b4-00c04fd430c8"))) (is (true? (uuidable? "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")))) (testing "invalid strings are not uuidable" (is (false? (uuidable? "not-a-uuid"))) (is (false? (uuidable? "")))) (testing "16-byte arrays are uuidable" (is (true? (uuidable? (byte-array 16))))) (testing "wrong-length byte arrays are not uuidable" (is (false? (uuidable? (byte-array 15)))) (is (false? (uuidable? (byte-array 17))))) (testing "URI with valid URN is uuidable" (is (true? (uuidable? (URI/create "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"))))) (testing "arbitrary objects are not uuidable" (is (false? (uuidable? 42))) (is (false? (uuidable? nil))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; uuid? on non-UUID types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-uuid? (testing "UUID instances return true" (is (true? (uuid? (v4)))) (is (true? (uuid? +null+))) (is (true? (uuid? +max+)))) (testing "non-UUID types return false" (is (false? (uuid? "6ba7b810-9dad-11d1-80b4-00c04fd430c8"))) (is (false? (uuid? 42))) (is (false? (uuid? nil))) (is (false? (uuid? :keyword))) (is (false? (uuid? [1 2 3]))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; null? and max? edge cases ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-null?-and-max?-edge-cases (testing "null? only true for null UUID" (is (true? (null? +null+))) (is (false? (null? +max+))) (is (false? (null? (v4)))) (is (false? (null? (v1))))) (testing "max? only true for max UUID" (is (true? (max? +max+))) (is (false? (max? +null+))) (is (false? (max? (v4)))) (is (false? (max? (v1))))) (testing "null and max are not equal" (is (not (clj-uuid/= +null+ +max+)))) (testing "comparison uses signed semantics" ;; uuid< compares MSB then LSB using signed longs. ;; max has MSB=-1, LSB=-1; null has MSB=0, LSB=0. ;; In signed comparison: -1 < 0, so max < null. (is (clj-uuid/< +max+ +null+)) (is (clj-uuid/> +null+ +max+)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; get-unix-time ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-get-unix-time (testing "v1 returns approximate current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v1)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "v6 returns approximate current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v6)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "v7 returns approximate current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v7)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "non-time-based versions return nil" (is (nil? (get-unix-time (v3 +namespace-dns+ "x")))) (is (nil? (get-unix-time (v4)))) (is (nil? (get-unix-time (v5 +namespace-dns+ "x")))) (is (nil? (get-unix-time (v8 0 0)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; to-hex-string ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-to-hex-string (testing "produces 32-char hex string" (let [u (v4) h (to-hex-string u)] (is (= 32 (count h))) (is (re-matches #"[0-9A-Fa-f]{32}" h)))) (testing "null UUID" (is (= "00000000000000000000000000000000" (to-hex-string +null+)))) (testing "max UUID" (is (= "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" (to-hex-string +max+)))) (testing "hex string matches canonical string with dashes removed" (let [u (v4)] (is (= (str/upper-case (str/replace (to-string u) "-" "")) (to-hex-string u)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; posix-time conversion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-posix-time-conversion (testing "known v1 namespace UUID timestamp" ;; +namespace-x500+ has get-timestamp = 131059232331511828 ;; get-unix-time should convert to POSIX millis (let [ut (get-unix-time +namespace-x500+)] (is (some? ut)) (is (= 886630433151 ut)))) (testing "known v6 UUID unix time" (let [u #uuid "1ef3f06f-16db-6ff0-bb01-1b50e6f39e7f" ut (get-unix-time u)] (is (= 1720648452463 ut)))) (testing "known v7 UUID unix time" (let [u #uuid "01909eae-4801-753a-bcd5-0889c34ac129" ut (get-unix-time u)] (is (= 0x01909eae4801 ut))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; v0/null and max constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-constructors-v0-max (testing "null constructor" (is (= +null+ (null))) (is (= +null+ (v0)))) (testing "max constructor" (is (= +max+ (max)))) (testing "null and max are distinct" (is (not= (null) (max))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UUIDRfc4122 backwards compatibility alias ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-backwards-compat-alias (testing "UUIDRfc4122 refers to the same protocol interface as UUIDRfc9562" (is (= (:on-interface UUIDRfc4122) (:on-interface UUIDRfc9562))))) ================================================ FILE: test/clj_uuid/bench.clj ================================================ (ns clj-uuid.bench "Benchmarks for clj-uuid UUID generation and post-generation operations. Run: lein test :only clj-uuid.bench" (:require [clojure.test :refer :all] [clj-uuid :as uuid])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Benchmark Infrastructure ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private ^:const +bench-n+ 500000) (def ^:private ^:const +warmup+ 50000) (defn- bench-ns "Run `f` for `n` iterations after warmup. Returns average ns/op." ^double [^long n f] (dotimes [_ +warmup+] (f)) (let [start (System/nanoTime)] (dotimes [_ n] (f)) (double (/ (- (System/nanoTime) start) n)))) (defn- bench-print "Benchmark `f` and print a formatted result line. Returns map with label and ns." [label f] (let [ns-val (bench-ns +bench-n+ f)] (println (format " %-34s %8.1f ns" label ns-val)) {:label label :ns ns-val})) (defn- print-md-table "Print results as a markdown table." [header-label results] (println) (println (format "| %-34s | %14s |" header-label "ns/op")) (println (format "|%s|%s:|" (apply str (repeat 36 "-")) (apply str (repeat 16 "-")))) (doseq [{:keys [label ns]} results] (println (format "| %-34s | %14.1f |" label ns)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 1. UUID Generation (Pure Construction) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-uuid-generation (testing "UUID Generation (Pure Construction)" (println) (println "=== 1. UUID Generation (Pure Construction) ===") (println) (let [ns-uuid uuid/+namespace-dns+ test-name "www.example.com" results [(bench-print "v1 (time-based)" #(uuid/v1)) (bench-print "v3 (MD5, namespace)" #(uuid/v3 ns-uuid test-name)) (bench-print "v4 (random)" #(uuid/v4)) (bench-print "v5 (SHA1, namespace)" #(uuid/v5 ns-uuid test-name)) (bench-print "v6 (time-based, sorted)" #(uuid/v6)) (bench-print "v7 (unix time, crypto)" #(uuid/v7)) (bench-print "v7nc (unix time, fast)" #(uuid/v7nc)) (bench-print "v8 (custom)" #(uuid/v8 0x123456789ABCDEF -1))]] (println) (print-md-table "UUID Version" results) ;; Verify correctness (is (= 1 (uuid/get-version (uuid/v1)))) (is (= 3 (uuid/get-version (uuid/v3 ns-uuid test-name)))) (is (= 4 (uuid/get-version (uuid/v4)))) (is (= 5 (uuid/get-version (uuid/v5 ns-uuid test-name)))) (is (= 6 (uuid/get-version (uuid/v6)))) (is (= 7 (uuid/get-version (uuid/v7)))) (is (= 7 (uuid/get-version (uuid/v7nc)))) (is (= 8 (uuid/get-version (uuid/v8 0x123456789ABCDEF -1)))) ;; v3/v5 are deterministic (is (= (uuid/v3 ns-uuid test-name) (uuid/v3 ns-uuid test-name))) (is (= (uuid/v5 ns-uuid test-name) (uuid/v5 ns-uuid test-name)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 2. Post-Generation Operations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-post-generation (testing "Post-Generation Operations" (println) (println "=== 2. Post-Generation Operations ===") (println) (let [test-uuid (java.util.UUID/randomUUID) results [(bench-print "to-byte-array" #(uuid/to-byte-array test-uuid)) (bench-print "to-hex-string" #(uuid/to-hex-string test-uuid)) (bench-print "to-string" #(uuid/to-string test-uuid)) (bench-print "to-urn-string" #(uuid/to-urn-string test-uuid)) (bench-print "get-version" #(uuid/get-version test-uuid)) (bench-print "get-node-id" #(uuid/get-node-id test-uuid))]] (println) (print-md-table "Operation" results)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 3. Combined: Generate + Serialize ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-combined (testing "Combined: Generate + Serialize" (println) (println "=== 3. Combined: Generate + Serialize ===") (println) (let [ns-uuid uuid/+namespace-dns+ test-name "www.example.com" results [(bench-print "v1 + to-byte-array" #(uuid/to-byte-array (uuid/v1))) (bench-print "v3 + to-byte-array" #(uuid/to-byte-array (uuid/v3 ns-uuid test-name))) (bench-print "v3 + to-hex-string" #(uuid/to-hex-string (uuid/v3 ns-uuid test-name))) (bench-print "v4 + to-byte-array" #(uuid/to-byte-array (uuid/v4))) (bench-print "v4 + to-hex-string" #(uuid/to-hex-string (uuid/v4))) (bench-print "v5 + to-byte-array" #(uuid/to-byte-array (uuid/v5 ns-uuid test-name))) (bench-print "v5 + to-hex-string" #(uuid/to-hex-string (uuid/v5 ns-uuid test-name))) (bench-print "v7 + to-byte-array" #(uuid/to-byte-array (uuid/v7)))]] (println) (print-md-table "Operation" results)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 4. Absolute Throughput ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-throughput (testing "Absolute Throughput (ops/sec)" (println) (println "=== 4. Absolute Throughput ===") (println) (let [ns-uuid uuid/+namespace-dns+ test-name "www.example.com" versions [["v1 (time-based)" #(uuid/v1)] ["v3 (MD5, namespace)" #(uuid/v3 ns-uuid test-name)] ["v4 (random)" #(uuid/v4)] ["v5 (SHA1, namespace)" #(uuid/v5 ns-uuid test-name)] ["v6 (time-based, sorted)" #(uuid/v6)] ["v7 (unix time, crypto)" #(uuid/v7)] ["v8 (custom)" #(uuid/v8 0x123 0x456)]]] (println (format " %-34s %18s" "UUID Version" "ops/s")) (println (format " %-34s %18s" (apply str (repeat 34 "-")) (apply str (repeat 18 "-")))) (doseq [[label f] versions] (let [ns-val (bench-ns +bench-n+ f) ops (long (/ 1e9 ns-val))] (println (format " %-34s %,18d" label ops))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 5. v3/v5 Detailed Breakdown ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-v3-v5-detailed (testing "v3/v5 Detailed Breakdown" (println) (println "=== 5. v3/v5 Detailed Breakdown ===") (println) (let [ns-uuid uuid/+namespace-dns+ test-name "www.example.com"] (println "--- v3 (MD5) ---") (bench-print "v3 generation" #(uuid/v3 ns-uuid test-name)) (let [v3-uuid (uuid/v3 ns-uuid test-name)] (bench-print "v3 + to-byte-array" #(uuid/to-byte-array v3-uuid)) (bench-print "v3 + to-hex-string" #(uuid/to-hex-string v3-uuid)) (bench-print "v3 + to-string" #(uuid/to-string v3-uuid))) (println) (println "--- v5 (SHA1) ---") (bench-print "v5 generation" #(uuid/v5 ns-uuid test-name)) (let [v5-uuid (uuid/v5 ns-uuid test-name)] (bench-print "v5 + to-byte-array" #(uuid/to-byte-array v5-uuid)) (bench-print "v5 + to-hex-string" #(uuid/to-hex-string v5-uuid)) (bench-print "v5 + to-string" #(uuid/to-string v5-uuid))) ;; Correctness (is (= 3 (uuid/get-version (uuid/v3 ns-uuid test-name)))) (is (= 5 (uuid/get-version (uuid/v5 ns-uuid test-name))))))) ================================================ FILE: test/clj_uuid/bitmop_test.clj ================================================ (ns clj-uuid.bitmop-test (:require [clojure.test :refer :all] [clj-uuid.bitmop :as b :refer :all]) (:import [java.nio ByteBuffer] [java.util UUID])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 1. Correctness -- Bitwise Primitives ;; ;; Tests for mask, ldb, dpb, bit-count, byte casts, byte reassembly, ;; and hex conversion. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-bit-mask-operators (testing "bit-mask construction..." (is (= (mask 0 0) 0)) (is (= (mask 0 1) 0)) (is (= (mask 1 0) 1)) (is (= (mask 2 0) 3)) (is (= (mask 4 0) 15)) (is (= (mask 8 0) 255)) (is (= (mask 16 0) 65535)) (is (= (mask 7 0) 127)) (is (= (mask 8 8) 65280)) (is (= (mask 8 4) 4080)) (is (= (mask 64 0) -1)) (is (= (mask 63 1) -2)) (is (= (mask 60 4) -16)) (is (= (mask 32 0) 4294967295)) (is (= (mask 32 16) 281474976645120)) (is (= (mask 32 32) -4294967296)) (is (= (mask 3 60) 8070450532247928832)) (is (= (mask 3 61) -2305843009213693952)) (is (= (mask 4 60) -1152921504606846976)) (is (= (mask 8 48) 71776119061217280)) (is (= (mask 16 48) -281474976710656)) (is (= 3 (mask 2 0) (bit-or (mask 1 1) (mask 1 0)))) (is (= 15 (mask 4 0) (bit-or (mask 2 2) (mask 2 0)))) (is (= 15 (mask 4 0) (bit-or (mask 1 3) (mask 3 0)))) (is (= 255 (mask 8 0) (bit-or (mask 4 4) (mask 4 0)))) (is (= 255 (mask 8 0) (bit-or (mask 2 6) (mask 6 0)))) (is (= 255 (mask 8 0) (bit-or (mask 1 7) (mask 7 0)))) (is (= 65535 (mask 16 0) (bit-or (mask 8 8) (mask 8 0)))) (is (= 65535 (mask 16 0) (bit-or (mask 15 1) (mask 1 0)))) (is (= 65535 (mask 16 0) (bit-or (mask 5 11) (mask 11 0))))) (testing "bit-mask width computation..." (is (= 3 (mask-width (mask 3 7)))) (is (= 3 (mask-width (mask 3 0)))) (is (= 12 (mask-width (mask 12 13)))) (is (= 30 (mask-width (mask 30 32)))) (is (= 31 (mask-width (mask 31 32)))) (is (= 32 (mask-width (mask 32 0)))) (is (= 31 (mask-width (mask 31 32)))) (is (= 62 (mask-width (mask 62 0)))) (is (= 48 (mask-width (mask 48 15)))) (is (= 64 (mask-width (mask 64 0)))) (is (= 60 (mask-width (mask 60 4)))) (is (= 31 (mask-width (mask 31 33)))) (is (= 1 (mask-width (mask 1 63))))) (testing "bit-mask offset computation..." (is (= 7 (mask-offset (mask 3 7)))) (is (= 0 (mask-offset (mask 3 0)))) (is (= 32 (mask-offset (mask 3 32)))) (is (= 0 (mask-offset (mask 0 0)))) (is (= 0 (mask-offset (mask 64 0)))) (is (= 63 (mask-offset (mask 1 63)))) (is (= 1 (mask-offset (mask 63 1)))))) (deftest check-bitwise-primitives (testing "ldb..." (is (= 15 (ldb (mask 4 0) (mask 32 0)))) (is (= 15 (ldb (mask 4 8) (mask 32 0)))) (is (= 12 (ldb (mask 4 0) (mask 32 2)))) (is (= -1 (ldb (mask 64 0) (mask 64 0)))) (is (= 0x7fffffffffffffff (ldb (mask 63 1) (mask 64 0)))) (is (= 0x0fffffffffffffff (ldb (mask 60 4) (mask 64 0)))) (is (= 1 (ldb (mask 1 63) (mask 64 0)))) (is (= 15 (ldb (mask 4 60) (mask 64 0)))) (is (= 7 (ldb (mask 4 60) (mask 63 0)))) (for [i (range 0 64)] (is (= 1 (ldb (mask 1 i) (mask 64 0))))) (for [i (range 0 61)] (is (= 15 (ldb (mask 4 i) (mask 64 0)))))) (testing "dpb..." (map #(is (= 0x3 %)) (for [i (range 8)] (ldb (mask 4 (* i 4)) (dpb (mask 4 (* i 4)) (mask 64 0) 0x3)))) (map #(is (= %1 %2)) (into [] (map expt2 (range 7))) (for [i (range 7)] (ldb (mask 8 (* i 8)) (dpb (mask 8 (* i 8)) (mask 64 0) (bit-shift-left 0x1 i)))))) (testing "bit-count..." (is (= (bit-count 0x01010101) 4)) (is (= (bit-count 0x77773333) 20)) (is (= (bit-count (mask 17 6)) 17)) (is (= (bit-count (mask 32 8)) 32)) (is (= (bit-count (mask 56 0)) 56)) (is (= (bit-count (mask 64 0)) 64)) (is (= (bit-count (mask 63 1)) 63)) (is (= (bit-count -1) 64)) (is (= (bit-count -0xffffffff) 33)) (is (= (bit-count 0xffffffff) 32)) (is (= (bit-count 0) 0)) (is (= (bit-count (- (mask 62 0))) 3)))) (deftest check-byte-cast-operators (testing "byte-cast ops..." (is (= (sb8 255) -1)) (is (= (sb8 127) 127)) (is (= (sb8 -128) -128)) (is (= (sb8 -254) 2)) (is (= (sb64 (mask 63 1)) -2)) (is (= (sb64 (mask 62 1)) 9223372036854775806)) (is (= (sb64 (mask 62 2)) -4)) (is (= (ub4 -1) 15)) (is (= (ub4 16) 0)) (is (= (ub4 15) 15)) (is (= (ub4 7) 7)) (is (= (ub56 0x80) 128)) (is (= (class (ub56 0x80)) Long)))) (deftest check-byte-reassembly-roundtrip (testing "disassemble/reassemble-bytes..." (dotimes [_ 256] (let [bytes (for [i (range 8)] (sb8 (rand-int (mask 8 0))))] (is (= (seq (long->bytes (assemble-bytes bytes))) bytes)))))) (deftest check-simple-octet-hex-mapping (testing "octet-hex mapping..." (is (= (octet-hex 0xff) "FF")) (is (= (octet-hex 0x00) "00")) (is (= (octet-hex 0x7a) "7A")) (is (= (octet-hex 15) "0F")) (is (= (octet-hex 45) "2D")) (is (= (octet-hex 250) "FA")) (is (= (octet-hex 0x11) "11")))) (deftest check-hex-string-conversion (testing "hex string conversion..." (is (= (hex 0xff) "00000000000000FF")) (is (= (hex 0xfff) "0000000000000FFF")) (is (= (hex 0xefef) "000000000000EFEF")) (is (= (hex 0xf0e0a01003) "000000F0E0A01003")) (is (= (hex -1) "FFFFFFFFFFFFFFFF")) (is (= (hex 256) "0000000000000100")) (is (= (hex 255) "00000000000000FF")) (is (= (hex 65536) "0000000000010000")) (is (= (hex -1) "FFFFFFFFFFFFFFFF")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 3. ByteBuffer-Specific Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-buffer-creation (testing "buffer 0-arity creates zeroed 16-byte buffer..." (let [buf (buffer)] (is (instance? ByteBuffer buf)) (is (= 16 (.capacity buf))) (is (= 0 (.getLong buf 0))) (is (= 0 (.getLong buf 8))))) (testing "buffer 2-arity stores MSB and LSB..." (let [msb 0x0123456789ABCDEF lsb (unchecked-long 0xFEDCBA9876543210) ^ByteBuffer buf (buffer msb lsb)] (is (= msb (.getLong buf 0))) (is (= lsb (.getLong buf 8))))) (testing "buffer-from-bytes copies byte array contents..." (let [arr (byte-array 16) msb (unchecked-long 0xDEADBEEF01020304) lsb 0x0506070809101112] (doto (ByteBuffer/wrap arr) (.putLong 0 msb) (.putLong 8 lsb)) (let [buf (buffer-from-bytes arr)] (is (= msb (.getLong buf 0))) (is (= lsb (.getLong buf 8))) ;; verify it's independent (not sharing backing) (aset-byte arr 0 0) (is (= msb (.getLong buf 0))))))) (deftest check-buffer-typed-access (testing "put-byte / get-byte roundtrip..." (let [buf (buffer)] (doseq [i (range 16)] (put-byte buf i (* i 17)) (is (= (* i 17) (get-byte buf i)) (format "byte at offset %d" i))))) (testing "put-short / get-short roundtrip..." (let [buf (buffer)] (put-short buf 0 0x0102) (put-short buf 2 0xABCD) (put-short buf 14 0xFFFF) (is (= 0x0102 (get-short buf 0))) (is (= 0xABCD (get-short buf 2))) (is (= 0xFFFF (get-short buf 14))))) (testing "put-int / get-int roundtrip..." (let [buf (buffer)] (put-int buf 0 0x01020304) (put-int buf 4 0xDEADBEEF) (put-int buf 12 0xFFFFFFFF) (is (= 0x01020304 (get-int buf 0))) (is (= 0xDEADBEEF (get-int buf 4))) (is (= 0xFFFFFFFF (get-int buf 12))))) (testing "put-long / get-long roundtrip..." (let [buf (buffer)] (put-long buf 0 0x0123456789ABCDEF) (put-long buf 8 (unchecked-long 0xFEDCBA9876543210)) (is (= 0x0123456789ABCDEF (get-long buf 0))) (is (= (unchecked-long 0xFEDCBA9876543210) (get-long buf 8))))) (testing "typed access reads byte-level data correctly..." ;; Write known bytes and verify typed reads (let [buf (buffer 0x0102030405060708 0x090A0B0C0D0E0F10)] (is (= 0x01 (get-byte buf 0))) (is (= 0x08 (get-byte buf 7))) (is (= 0x10 (get-byte buf 15))) (is (= 0x0102 (get-short buf 0))) (is (= 0x0506 (get-short buf 4))) (is (= 0x01020304 (get-int buf 0))) (is (= 0x05060708 (get-int buf 4)))))) (deftest check-buffer-word-access (testing "get-msb / get-lsb..." (let [msb 0x0123456789ABCDEF lsb (unchecked-long 0xFEDCBA9876543210) buf (buffer msb lsb)] (is (= msb (get-msb buf))) (is (= lsb (get-lsb buf))))) (testing "set-msb / set-lsb..." (let [buf (buffer)] (set-msb buf (unchecked-long 0xAAAAAAAAAAAAAAAA)) (set-lsb buf 0x5555555555555555) (is (= (unchecked-long 0xAAAAAAAAAAAAAAAA) (get-msb buf))) (is (= 0x5555555555555555 (get-lsb buf))))) (testing "set-msb / set-lsb are independent..." (let [buf (buffer 0x1111111111111111 0x2222222222222222)] (set-msb buf (unchecked-long 0xFFFFFFFFFFFFFFFF)) (is (= -1 (get-msb buf))) (is (= 0x2222222222222222 (get-lsb buf))) (set-lsb buf 0) (is (= -1 (get-msb buf))) (is (= 0 (get-lsb buf)))))) (deftest check-buffer-bit-field-operations (testing "ldb-buf extracts fields from buffer words..." (let [buf (buffer 0x0123456789ABCDEF (unchecked-long 0xFEDCBA9876543210))] ;; Extract from MSB (word offset 0) (is (= (b/ldb (b/mask 32 0) 0x0123456789ABCDEF) (ldb-buf buf 0 (b/mask 32 0)))) (is (= (b/ldb (b/mask 16 48) 0x0123456789ABCDEF) (ldb-buf buf 0 (b/mask 16 48)))) (is (= (b/ldb (b/mask 4 12) 0x0123456789ABCDEF) (ldb-buf buf 0 (b/mask 4 12)))) ;; Extract from LSB (word offset 8) (is (= (b/ldb (b/mask 48 0) (unchecked-long 0xFEDCBA9876543210)) (ldb-buf buf 8 (b/mask 48 0)))) (is (= (b/ldb (b/mask 2 62) (unchecked-long 0xFEDCBA9876543210)) (ldb-buf buf 8 (b/mask 2 62)))))) (testing "dpb-buf deposits fields into buffer words..." (let [buf (buffer -1 -1)] ;; Deposit version bits into MSB (dpb-buf buf 0 (b/mask 4 12) 0x7) (is (= 0x7 (ldb-buf buf 0 (b/mask 4 12)))) ;; Deposit variant bits into LSB (dpb-buf buf 8 (b/mask 2 62) 0x2) (is (= 0x2 (ldb-buf buf 8 (b/mask 2 62)))) ;; Non-targeted bits are preserved (is (= (b/ldb (b/mask 12 0) -1) (ldb-buf buf 0 (b/mask 12 0)))) (is (= (b/ldb (b/mask 48 0) (b/dpb (b/mask 2 62) -1 0x2)) (ldb-buf buf 8 (b/mask 48 0)))))) (testing "dpb-buf returns the buffer for chaining..." (let [buf (buffer)] (is (identical? buf (dpb-buf buf 0 (b/mask 4 12) 0x1)))))) (deftest check-buffer-conversion (testing "buf->bytes produces correct 16-byte array..." (let [buf (buffer 0x0123456789ABCDEF (unchecked-long 0xFEDCBA9876543210)) arr (buf->bytes buf)] (is (= 16 (alength arr))) (is (= 0x0123456789ABCDEF (b/bytes->long arr 0))) (is (= (unchecked-long 0xFEDCBA9876543210) (b/bytes->long arr 8))))) (testing "buffer-from-bytes -> buf->bytes roundtrip..." (dotimes [_ 100] (let [arr (byte-array (repeatedly 16 #(unchecked-byte (rand-int 256)))) buf (buffer-from-bytes arr) out (buf->bytes buf)] (is (= (seq arr) (seq out)))))) (testing "buf->uuid produces correct UUID..." (let [msb 0x0123456789ABCDEF lsb (unchecked-long 0xFEDCBA9876543210) buf (buffer msb lsb) uuid (buf->uuid buf)] (is (instance? UUID uuid)) (is (= msb (.getMostSignificantBits uuid))) (is (= lsb (.getLeastSignificantBits uuid))))) (testing "uuid->buf produces correct buffer..." (let [uuid (UUID/randomUUID) buf (uuid->buf uuid)] (is (= (.getMostSignificantBits uuid) (get-msb buf))) (is (= (.getLeastSignificantBits uuid) (get-lsb buf))))) (testing "uuid->buf -> buf->uuid roundtrip..." (dotimes [_ 100] (let [uuid (UUID/randomUUID)] (is (= uuid (buf->uuid (uuid->buf uuid))))))) (testing "duplicate creates an independent copy..." (let [buf (buffer (unchecked-long 0xAAAAAAAAAAAAAAAA) 0x5555555555555555) copy (duplicate buf)] (is (= (get-msb buf) (get-msb copy))) (is (= (get-lsb buf) (get-lsb copy))) ;; mutating copy doesn't affect original (set-msb copy 0) (is (= (unchecked-long 0xAAAAAAAAAAAAAAAA) (get-msb buf))) (is (= 0 (get-msb copy)))))) (deftest check-buffer-hex-and-string (testing "buf-hex produces correct 32-char hex string..." (let [buf (buffer 0x0123456789ABCDEF (unchecked-long 0xFEDCBA9876543210))] (is (= "0123456789ABCDEFFEDCBA9876543210" (buf-hex buf)))) (let [buf (buffer 0 0)] (is (= "00000000000000000000000000000000" (buf-hex buf)))) (let [buf (buffer -1 -1)] (is (= "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" (buf-hex buf))))) (testing "buf-str produces correct canonical UUID string..." (let [buf (buffer 0x0123456789ABCDEF (unchecked-long 0xFEDCBA9876543210))] (is (= "01234567-89AB-CDEF-FEDC-BA9876543210" (buf-str buf)))) (let [buf (buffer 0 0)] (is (= "00000000-0000-0000-0000-000000000000" (buf-str buf))))) (testing "buf-str matches UUID.toString for random UUIDs..." (dotimes [_ 100] (let [uuid (UUID/randomUUID) buf (uuid->buf uuid)] (is (= (.toUpperCase (.toString uuid)) (buf-str buf)))))) (testing "hex->buf parses hex string correctly..." (let [hex-str "0123456789ABCDEFFEDCBA9876543210" buf (hex->buf hex-str)] (is (= 0x0123456789ABCDEF (get-msb buf))) (is (= (unchecked-long 0xFEDCBA9876543210) (get-lsb buf))))) (testing "hex->buf -> buf-hex roundtrip..." (dotimes [_ 100] (let [uuid (UUID/randomUUID) buf (uuid->buf uuid) hex1 (buf-hex buf) buf2 (hex->buf hex1) hex2 (buf-hex buf2)] (is (= hex1 hex2))))) (testing "buf-hex matches bitmop/hex for MSB/LSB..." (dotimes [_ 50] (let [uuid (UUID/randomUUID) buf (uuid->buf uuid) msb (.getMostSignificantBits uuid) lsb (.getLeastSignificantBits uuid)] (is (= (str (b/hex msb) (b/hex lsb)) (buf-hex buf))))))) (deftest check-buffer-comparison (testing "buf-compare: equal buffers..." (is (= 0 (buf-compare (buffer 0 0) (buffer 0 0)))) (is (= 0 (buf-compare (buffer -1 -1) (buffer -1 -1)))) (is (= 0 (buf-compare (buffer 0x0123456789ABCDEF 0) (buffer 0x0123456789ABCDEF 0))))) (testing "buf-compare: MSB determines order..." (is (neg? (buf-compare (buffer 0 0) (buffer 1 0)))) (is (pos? (buf-compare (buffer 1 0) (buffer 0 0)))) ;; unsigned comparison: 0x8... is greater than 0x7... as unsigned (is (pos? (buf-compare (buffer (unchecked-long 0x8000000000000000) 0) (buffer 0x7FFFFFFFFFFFFFFF 0))))) (testing "buf-compare: LSB breaks ties..." (is (neg? (buf-compare (buffer 1 0) (buffer 1 1)))) (is (pos? (buf-compare (buffer 1 1) (buffer 1 0)))) ;; unsigned LSB comparison (is (pos? (buf-compare (buffer 0 (unchecked-long 0x8000000000000000)) (buffer 0 0x7FFFFFFFFFFFFFFF))))) (testing "buf-compare: ordering matches UUID string ordering..." (dotimes [_ 100] (let [u1 (UUID/randomUUID) u2 (UUID/randomUUID) b1 (uuid->buf u1) b2 (uuid->buf u2) ;; compare canonical uppercase strings lexicographically str-cmp (compare (.toUpperCase (.toString u1)) (.toUpperCase (.toString u2))) buf-cmp (buf-compare b1 b2)] (is (= (Long/signum str-cmp) (Long/signum buf-cmp)) (format "ordering mismatch for %s vs %s" u1 u2)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 4. Performance -- bitmop Primitives ;; ;; Benchmarks the core bitmop operations: ;; - long->bytes / bytes->long (ByteBuffer putLong/getLong) ;; - assemble-bytes (shift-accumulation loop) ;; - hex (StringBuilder direct append) ;; - UUID buffer roundtrip (uuid->buf / buf->uuid) ;; - buf-str vs UUID.toString (StringBuilder vs JVM native) ;; ;; Results are printed to stdout. No hard timing assertions are made ;; (too fragile for CI), but correctness under load is verified. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private +bench-iterations+ 100000) (defn- bench-ns "Run `f` for `n` iterations after warmup. Returns average ns/op." ^double [^long n f] ;; warmup (dotimes [_ (min n 10000)] (f)) ;; timed (let [start (System/nanoTime)] (dotimes [_ n] (f)) (double (/ (- (System/nanoTime) start) n)))) (defn- bench-print "Benchmark `f` and print a formatted result line. Returns ns/op." [label n f] (let [ns-val (bench-ns n f)] (println (format " %-30s %8.1f ns" label ns-val)) ns-val)) (deftest check-performance-long-bytes-roundtrip (testing "long->bytes / bytes->long performance..." (println) (println "--- Performance: long<->bytes ---") (let [test-val (unchecked-long 0xDEADBEEFCAFEBABE)] (bench-print "long->bytes" +bench-iterations+ #(b/long->bytes test-val)) (let [arr (b/long->bytes test-val)] (bench-print "bytes->long" +bench-iterations+ #(b/bytes->long arr 0))) ;; roundtrip correctness under load (dotimes [_ 10000] (let [v (unchecked-long (long (* (Math/random) Long/MAX_VALUE)))] (is (= v (b/bytes->long (b/long->bytes v) 0)))))))) (deftest check-performance-assemble-bytes (testing "assemble-bytes performance..." (println) (println "--- Performance: assemble-bytes ---") (let [test-bytes (list (byte 0x01) (byte 0x02) (byte 0x03) (byte 0x04) (byte 0x05) (byte 0x06) (byte 0x07) (byte 0x08))] (bench-print "assemble-bytes" +bench-iterations+ #(b/assemble-bytes test-bytes)) ;; correctness under load (dotimes [_ 10000] (let [bytes (for [_ (range 8)] (b/sb8 (rand-int 256))) result (b/assemble-bytes bytes)] (is (= result (b/bytes->long (byte-array (map unchecked-byte bytes)) 0)))))))) (deftest check-performance-hex (testing "hex conversion performance..." (println) (println "--- Performance: hex ---") (let [test-val (unchecked-long 0xDEADBEEFCAFEBABE)] (bench-print "hex (long)" +bench-iterations+ #(b/hex test-val)) ;; correctness under load (dotimes [_ 10000] (let [v (unchecked-long (long (* (Math/random) Long/MAX_VALUE)))] (is (= 16 (count (b/hex v))))))))) (deftest check-performance-uuid-buffer-roundtrip (testing "UUID buffer roundtrip performance..." (println) (println "--- Performance: UUID buffer roundtrip ---") (let [msb 0x0123456789ABCDEF lsb (unchecked-long 0xFEDCBA9876543210) uuid (UUID. msb lsb)] (bench-print "UUID -> byte[] -> UUID" +bench-iterations+ (fn [] (let [arr (byte-array 16)] (b/long->bytes (.getMostSignificantBits uuid) arr 0) (b/long->bytes (.getLeastSignificantBits uuid) arr 8) (UUID. (b/bytes->long arr 0) (b/bytes->long arr 8))))) (bench-print "UUID -> buf -> UUID" +bench-iterations+ #(buf->uuid (uuid->buf uuid))) ;; correctness under load (dotimes [_ 10000] (let [u (UUID/randomUUID)] (is (= u (buf->uuid (uuid->buf u))))))))) (deftest check-performance-buf-str (testing "buf-str performance..." (println) (println "--- Performance: UUID string rendering ---") (let [uuid (UUID. 0x0123456789ABCDEF (unchecked-long 0xFEDCBA9876543210)) buf (uuid->buf uuid)] (bench-print "UUID.toString" +bench-iterations+ #(.toString uuid)) (bench-print "buf-str" +bench-iterations+ #(buf-str buf)) ;; correctness under load (dotimes [_ 1000] (let [u (UUID/randomUUID) b (uuid->buf u)] (is (= (.toUpperCase (.toString u)) (buf-str b)))))))) ================================================ FILE: test/clj_uuid/clock_test.clj ================================================ (ns clj-uuid.clock-test (:require [clojure.test :refer :all] [clojure.set] [clj-uuid.clock :refer :all]) (:import [clj_uuid.clock State])) (deftest check-single-threaded (let [iterations 1000000 groups 10 check #(mapv (fn [_] (%)) (range iterations))] (testing "monotonic-time..." (dotimes [_ groups] (let [result (check monotonic-time)] (is (= (count result) (count (set result))))))) (testing "monotonic-unix-time-and-random-counter..." (dotimes [_ groups] (let [result (check monotonic-unix-time-and-random-counter) pairs (mapv #(vector (.millis ^State %) (.seqid ^State %)) result)] (is (= (count pairs) (count (set pairs))))))))) (deftest check-multi-threaded-monotonic-time (doseq [concur (range 0 9)] (let [extent 1000000 agents (mapv agent (repeat concur nil)) working (mapv #(send-off % (fn [state] (repeatedly extent monotonic-time))) agents) _ (apply await working) answers (mapv deref working)] (testing (str "concurrent timestamp uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map set answers)))))) (testing (str "concurrent monotonic increasing (" concur " threads)...") (is (every? identity (map #(apply < %) answers))))))) (deftest check-multi-threaded-monotonic-unix-time-and-random-counter (doseq [concur (range 0 9)] (let [extent 1000000 agents (mapv agent (repeat concur nil)) working (mapv #(send-off % (fn [state] (repeatedly extent monotonic-unix-time-and-random-counter))) agents) _ (apply await working) answers (mapv deref working)] (testing (str "concurrent timestamp uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map #(set (map (fn [^State s] [(.millis s) (.seqid s)]) %)) answers)))))) (testing (str "concurrent monotonic increasing (" concur " threads)...") (doseq [answer answers] (let [^State first-state (first answer)] (loop [time (.millis first-state) counter (.seqid first-state) more (rest answer)] (when-let [^State next-state (first more)] (let [next-time (.millis next-state) next-counter (.seqid next-state)] (cond (< next-time time) (is false "time must be increasing") (and (= next-time time) (<= next-counter counter)) (is false "counter must be increasing") :else (recur next-time next-counter (rest more)))))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Time Conversion Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-posix-time-zero-arity (testing "posix-time with no args returns a number" (is (number? (posix-time))))) (deftest check-posix-time-with-arg (testing "posix-time converts gregorian to POSIX" (let [greg 131059232331511828 pt (posix-time greg)] (is (number? pt)) (is (= (- (quot 131059232331511828 10000) 12219292800000) pt))))) (deftest check-universal-time-zero-arity (testing "universal-time returns a number" (is (number? (universal-time))))) (deftest check-universal-time-with-arg (testing "universal-time converts gregorian to universal time" (let [greg 131059232331511828 ut (universal-time greg) pt (posix-time greg)] (is (= (+ pt 2208988800) ut))))) (deftest check-time-conversion-consistency (testing "posix-time and universal-time differ by epoch offset" (let [greg (monotonic-time)] (is (= 2208988800 (- (universal-time greg) (posix-time greg))))))) ================================================ FILE: test/clj_uuid/compare_bench.clj ================================================ (ns clj-uuid.compare-bench "Apples-to-apples benchmarks: clj-uuid vs JUG vs uuid-creator vs JDK. Run: lein test :only clj-uuid.compare-bench" (:require [clojure.test :refer :all] [clj-uuid :as uuid]) (:import [java.util UUID] [com.fasterxml.uuid Generators EthernetAddress] [com.fasterxml.uuid.impl TimeBasedGenerator TimeBasedReorderedGenerator TimeBasedEpochGenerator NameBasedGenerator RandomBasedGenerator] [com.github.f4b6a3.uuid UuidCreator])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Infrastructure ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private ^:const +n+ 500000) (def ^:private ^:const +warmup+ 50000) (defn- bench-ns "Run f for n iterations after warmup. Returns average ns/op." ^double [^long n f] (dotimes [_ +warmup+] (f)) (let [start (System/nanoTime)] (dotimes [_ n] (f)) (double (/ (- (System/nanoTime) start) n)))) (defn- bench-row "Benchmark f, return [label ns/op]." [label f] (let [ns-val (bench-ns +n+ f)] [label ns-val])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Generators — pre-initialize outside the timing loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:private ^TimeBasedGenerator jug-v1 (Generators/timeBasedGenerator)) (def ^:private ^RandomBasedGenerator jug-v4 (Generators/randomBasedGenerator)) (def ^:private ^NameBasedGenerator jug-v5 (Generators/nameBasedGenerator)) (def ^:private ^TimeBasedReorderedGenerator jug-v6 (Generators/timeBasedReorderedGenerator)) (def ^:private ^TimeBasedEpochGenerator jug-v7 (Generators/timeBasedEpochGenerator)) (def ^:private ^UUID jug-ns-dns (UUID/fromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Print helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- print-table-header [] (println) (println (format "| %-20s | %14s | %14s | %14s | %14s |" "Operation" "clj-uuid" "JUG 5.2" "uuid-creator" "JDK")) (println (format "|%s|%s:|%s:|%s:|%s:|" (apply str (repeat 22 "-")) (apply str (repeat 15 "-")) (apply str (repeat 15 "-")) (apply str (repeat 15 "-")) (apply str (repeat 15 "-"))))) (defn- fmt-ns [v] (if v (format "%10.1f ns" v) (format "%14s" "--"))) (defn- print-row [label clj-ns jug-ns uc-ns jdk-ns] (println (format "| %-20s | %14s | %14s | %14s | %14s |" label (fmt-ns clj-ns) (fmt-ns jug-ns) (fmt-ns uc-ns) (fmt-ns jdk-ns)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Benchmarks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest bench-comparison (testing "Apples-to-apples UUID library comparison" (print-table-header) ;; v1 (let [[_ clj] (bench-row "v1" #(uuid/v1)) [_ jug] (bench-row "v1" #(.generate jug-v1)) [_ uc] (bench-row "v1" #(UuidCreator/getTimeBased))] (print-row "v1 (time-based)" clj jug uc nil)) ;; v4 (let [[_ clj] (bench-row "v4" #(uuid/v4)) [_ jug] (bench-row "v4" #(.generate jug-v4)) [_ uc] (bench-row "v4" #(UuidCreator/getRandomBased)) [_ jdk] (bench-row "v4" #(UUID/randomUUID))] (print-row "v4 (random)" clj jug uc jdk)) ;; v5 (let [[_ clj] (bench-row "v5" #(uuid/v5 uuid/+namespace-dns+ "www.example.com")) [_ jug] (bench-row "v5" #(.generate jug-v5 "www.example.com")) [_ uc] (bench-row "v5" #(UuidCreator/getNameBasedSha1 jug-ns-dns "www.example.com"))] (print-row "v5 (SHA1)" clj jug uc nil)) ;; v6 (let [[_ clj] (bench-row "v6" #(uuid/v6)) [_ jug] (bench-row "v6" #(.generate jug-v6)) [_ uc] (bench-row "v6" #(UuidCreator/getTimeOrdered))] (print-row "v6 (time-ordered)" clj jug uc nil)) ;; v7 (let [[_ clj] (bench-row "v7" #(uuid/v7)) [_ jug] (bench-row "v7" #(.generate jug-v7)) [_ uc] (bench-row "v7" #(UuidCreator/getTimeOrderedEpoch))] (print-row "v7 (unix epoch)" clj jug uc nil)) ;; v7nc (let [[_ clj] (bench-row "v7nc" #(uuid/v7nc)) [_ jug] (bench-row "v7nc" #(.generate jug-v7))] (print-row "v7nc (fast epoch)" clj jug nil nil)) ;; to-string (let [u (uuid/v4)] (let [[_ clj] (bench-row "str" #(uuid/to-string u)) [_ jdk] (bench-row "str" #(.toString u))] (print-row "to-string" clj nil nil jdk))) ;; to-byte-array (let [u (uuid/v4)] (let [[_ clj] (bench-row "bytes" #(uuid/to-byte-array u))] (print-row "to-byte-array" clj nil nil nil))) (println) ;; Correctness checks (is (= 1 (.version (.generate jug-v1)))) (is (= 4 (.version (.generate jug-v4)))) (is (= 5 (.version (.generate jug-v5 "test")))) (is (= 6 (.version (.generate jug-v6)))) (is (= 7 (.version (.generate jug-v7)))) (is (= 1 (.version (UuidCreator/getTimeBased)))) (is (= 4 (.version (UuidCreator/getRandomBased)))) (is (= 6 (.version (UuidCreator/getTimeOrdered)))) (is (= 7 (.version (UuidCreator/getTimeOrderedEpoch)))))) ================================================ FILE: test/clj_uuid/core_test.clj ================================================ (ns clj-uuid.core-test "Tests that exercise the clj-uuid.core namespace directly. Mirrors api-test but requires from clj-uuid.core instead of clj-uuid." (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clojure.string :as str] [clj-uuid.core :refer :all :exclude [= > <]] [clj-uuid.core :as core] [clj-uuid.clock :as clock]) (:import (java.lang IllegalArgumentException) (java.net URI URL) (java.util Date UUID))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Protocol Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-null-uuid-protocol (let [tmpid +null+] (is (= (get-word-high tmpid) 0)) (is (= (get-word-low tmpid) 0)) (is (= (null? tmpid) true)) (is (= (seq (to-byte-array tmpid)) [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0])) (is (= (hash-code tmpid) 0)) (is (= (get-version tmpid) 0)) (is (= (to-string tmpid) "00000000-0000-0000-0000-000000000000")) (is (= (to-urn-string tmpid) "urn:uuid:00000000-0000-0000-0000-000000000000")) (is (= (get-time-low tmpid) 0)) (is (= (get-time-mid tmpid) 0)) (is (= (get-time-high tmpid) 0)) (is (= (get-clk-low tmpid) 0)) (is (= (get-clk-high tmpid) 0)) (is (= (get-node-id tmpid) 0)) (is (= (get-timestamp tmpid) nil)) (is (= (get-unix-time tmpid) nil)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Constructor Tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-v0-null-constructor (is (= +null+ (null))) (is (= +null+ (v0)))) (deftest check-max-constructor (is (= +max+ (max))) (is (not= (null) (max)))) (deftest check-v1-constructor (testing "v1 produces a valid version-1 UUID" (let [u (v1)] (is (instance? UUID u)) (is (= 1 (get-version u))) (is (= 2 (get-variant u)))))) (deftest check-v1-uniqueness (testing "v1 UUIDs are unique" (let [uuids (repeatedly 10000 v1)] (is (= 10000 (count (set uuids))))))) (deftest check-v3-constructor (testing "v3 produces deterministic UUID" (let [u1 (v3 +namespace-dns+ "example.com") u2 (v3 +namespace-dns+ "example.com")] (is (= u1 u2)) (is (= 3 (get-version u1))) (is (= 2 (get-variant u1))))) (testing "different inputs produce different UUIDs" (is (not= (v3 +namespace-dns+ "a") (v3 +namespace-dns+ "b"))))) (deftest check-v4-constructor (testing "v4 zero-arity produces random UUID with correct bits" (dotimes [_ 1000] (let [u (v4)] (is (= 4 (get-version u))) (is (= 2 (get-variant u)))))) (testing "v4 two-arity stamps version and variant" (is (= 4 (get-version (v4 0 0)))) (is (= 2 (get-variant (v4 0 0)))) (is (= 4 (get-version (v4 -1 -1)))) (is (= 2 (get-variant (v4 -1 -1)))))) (deftest check-v5-constructor (testing "v5 produces deterministic UUID" (let [u1 (v5 +namespace-dns+ "example.com") u2 (v5 +namespace-dns+ "example.com")] (is (= u1 u2)) (is (= 5 (get-version u1))) (is (= 2 (get-variant u1))))) (testing "v3 and v5 produce different results for same input" (is (not= (v3 +namespace-dns+ "example.com") (v5 +namespace-dns+ "example.com"))))) (deftest check-v6-constructor (testing "v6 produces a valid version-6 UUID" (let [u (v6)] (is (instance? UUID u)) (is (= 6 (get-version u))) (is (= 2 (get-variant u)))))) (deftest check-v6-uniqueness (testing "v6 UUIDs are unique" (let [uuids (repeatedly 10000 v6)] (is (= 10000 (count (set uuids))))))) (deftest check-v7-constructor (testing "v7 produces valid version-7 UUID" (let [u (v7)] (is (instance? UUID u)) (is (= 7 (get-version u))) (is (= 2 (get-variant u)))))) (deftest check-v7-uniqueness (testing "v7 UUIDs are unique" (let [uuids (repeatedly 10000 v7)] (is (= 10000 (count (set uuids))))))) (deftest check-v8-constructor (testing "v8 stamps version=8 and variant=2" (is (= 8 (get-version (v8 0 0)))) (is (= 2 (get-variant (v8 0 0)))) (is (= 8 (get-version (v8 -1 -1)))) (is (= 2 (get-variant (v8 -1 -1)))))) (deftest check-squuid-constructor (testing "squuid returns a v4 UUID" (let [s (squuid)] (is (uuid? s)) (is (= 4 (get-version s))) (is (= 2 (get-variant s))))) (testing "squuids are unique" (let [uuids (repeatedly 1000 squuid)] (is (= 1000 (count (set uuids))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Predicates and Coercion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-uuid? (is (true? (uuid? (v4)))) (is (true? (uuid? +null+))) (is (true? (uuid? +max+))) (is (false? (uuid? "not-a-uuid"))) (is (false? (uuid? 42))) (is (false? (uuid? nil)))) (deftest check-null?-max? (is (true? (null? +null+))) (is (false? (null? +max+))) (is (true? (max? +max+))) (is (false? (max? +null+)))) (deftest check-string-predicates (is (uuid-string? (to-string (v4)))) (is (uuid-urn-string? (to-urn-string (v4)))) (is (not (uuid-string? "garbage"))) (is (not (uuid-urn-string? "garbage"))) (is (not (uuid-string? nil))) (is (not (uuid-urn-string? nil))) (is (not (uuid-vec? nil)))) (deftest check-uuidable? (is (true? (uuidable? (v4)))) (is (true? (uuidable? "6ba7b810-9dad-11d1-80b4-00c04fd430c8"))) (is (true? (uuidable? "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"))) (is (true? (uuidable? (byte-array 16)))) (is (false? (uuidable? (byte-array 15)))) (is (false? (uuidable? "not-a-uuid"))) (is (false? (uuidable? nil))) (is (false? (uuidable? 42)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; as-uuid coercion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-as-uuid-string (let [u (as-uuid "6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ u)))) (deftest check-as-uuid-urn-string (let [u (as-uuid "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ u)))) (deftest check-as-uuid-byte-array (let [u (v4) ba (to-byte-array u)] (is (= u (as-uuid ba))))) (deftest check-as-uuid-uri (let [uri (URI/create "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")] (is (= +namespace-dns+ (as-uuid uri))))) (deftest check-as-uuid-identity (let [u (v4)] (is (identical? u (as-uuid u))))) (deftest check-as-uuid-errors (is (thrown? IllegalArgumentException (as-uuid nil))) (is (thrown? IllegalArgumentException (as-uuid "bad"))) (is (thrown? IllegalArgumentException (as-uuid "")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; as-byte-array ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-as-byte-array-types (testing "byte array passthrough" (let [ba (byte-array [1 2 3])] (is (identical? ba (as-byte-array ba))))) (testing "String produces UTF-8 bytes" (let [ba (as-byte-array "hello")] (is (= (seq (.getBytes "hello" "UTF-8")) (seq ba))))) (testing "URL produces bytes" (let [ba (as-byte-array (URL. "http://example.com"))] (is (pos? (alength ^bytes ba))))) (testing "Object produces serialized bytes" (let [ba (as-byte-array (Long. 42))] (is (pos? (alength ^bytes ba))))) (testing "nil throws" (is (thrown? IllegalArgumentException (as-byte-array nil))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Serialization ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-to-string (is (= "00000000-0000-0000-0000-000000000000" (to-string +null+))) (is (= "ffffffff-ffff-ffff-ffff-ffffffffffff" (to-string +max+)))) (deftest check-to-urn-string (is (= "urn:uuid:00000000-0000-0000-0000-000000000000" (to-urn-string +null+))) (is (= "urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff" (to-urn-string +max+)))) (deftest check-to-hex-string (let [u (v4) h (to-hex-string u)] (is (= 32 (count h))) (is (re-matches #"[0-9A-Fa-f]{32}" h))) (is (= "00000000000000000000000000000000" (to-hex-string +null+))) (is (= "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" (to-hex-string +max+)))) (deftest check-to-uri (let [u (v4) uri (to-uri u)] (is (instance? URI uri)) (is (= u (as-uuid uri))))) (deftest check-to-byte-array-roundtrip (let [u (v4) ba (to-byte-array u)] (is (= 16 (alength ^bytes ba))) (is (= u (as-uuid ba))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Time accessors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-get-timestamp (testing "v1 has a timestamp" (is (some? (get-timestamp (v1))))) (testing "v6 has a timestamp" (is (some? (get-timestamp (v6))))) (testing "v7 has a timestamp" (is (some? (get-timestamp (v7))))) (testing "non-time-based return nil" (is (nil? (get-timestamp (v3 +namespace-dns+ "x")))) (is (nil? (get-timestamp (v4)))) (is (nil? (get-timestamp (v5 +namespace-dns+ "x")))) (is (nil? (get-timestamp (v8 0 0)))))) (deftest check-get-unix-time (testing "v1 returns current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v1)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "v6 returns current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v6)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "v7 returns current POSIX millis" (let [before (System/currentTimeMillis) ut (get-unix-time (v7)) after (System/currentTimeMillis)] (is (some? ut)) (is (<= before ut after)))) (testing "non-time-based return nil" (is (nil? (get-unix-time (v3 +namespace-dns+ "x")))) (is (nil? (get-unix-time (v4)))) (is (nil? (get-unix-time (v5 +namespace-dns+ "x")))) (is (nil? (get-unix-time (v8 0 0)))))) (deftest check-get-instant (testing "v1 returns a Date" (let [inst (get-instant (v1))] (is (instance? Date inst)))) (testing "v6 returns a Date" (let [inst (get-instant (v6))] (is (instance? Date inst)))) (testing "v7 returns a Date" (let [inst (get-instant (v7))] (is (instance? Date inst)))) (testing "non-time-based return nil" (is (nil? (get-instant (v4)))) (is (nil? (get-instant +null+))) (is (nil? (get-instant +max+))))) (deftest check-get-clk-seq (testing "v1 returns a clock sequence" (let [cs (get-clk-seq (v1))] (is (some? cs)) (is (integer? cs)))) (testing "v6 returns a clock sequence" (let [cs (get-clk-seq (v6))] (is (some? cs)) (is (<= 0 cs 0x3FFF)))) (testing "non-gregorian return nil" (is (nil? (get-clk-seq (v4)))) (is (nil? (get-clk-seq (v7)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Comparison operators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-multi-arity-comparisons (testing "single-arg always returns true" (let [u (v4)] (is (core/= u)) (is (core/< u)) (is (core/> u)))) (testing "two-arg = works" (let [u (v3 +namespace-dns+ "same")] (is (core/= u u)) (is (not (core/= u (v4)))))) (testing "three-arg =" (let [u (v3 +namespace-dns+ "same")] (is (core/= u u u)) (is (not (core/= u u (v4)))))) (testing "multi-arg < with ordered UUIDs" (let [a (v4 0 0) b (v4 0x3000000000000000 0) c (v4 0x5000000000000000 0)] (is (core/< a b c)) (is (not (core/< a c b))))) (testing "multi-arg > with ordered UUIDs" (let [a (v4 0 0) b (v4 0x3000000000000000 0) c (v4 0x5000000000000000 0)] (is (core/> c b a)) (is (not (core/> c a b))))) (testing "< and > with equal elements returns false" (let [u (v4)] (is (not (core/< u u))) (is (not (core/> u u)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Field extraction ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-field-extraction (let [u (v1)] (is (integer? (get-word-high u))) (is (integer? (get-word-low u))) (is (integer? (get-time-low u))) (is (integer? (get-time-mid u))) (is (integer? (get-time-high u))) (is (integer? (get-clk-low u))) (is (integer? (get-clk-high u))) (is (integer? (get-node-id u))) (is (integer? (hash-code u))) (is (integer? (get-variant u))))) (deftest check-get-version-all-types (is (= 0 (get-version +null+))) (is (= 1 (get-version (v1)))) (is (= 3 (get-version (v3 +namespace-dns+ "x")))) (is (= 4 (get-version (v4)))) (is (= 5 (get-version (v5 +namespace-dns+ "x")))) (is (= 6 (get-version (v6)))) (is (= 7 (get-version (v7)))) (is (= 8 (get-version (v8 0 0)))) (is (= 15 (get-version +max+)))) (deftest check-get-variant-all-types (is (= 0 (get-variant +null+))) (is (= 2 (get-variant (v1)))) (is (= 2 (get-variant (v3 +namespace-dns+ "x")))) (is (= 2 (get-variant (v4)))) (is (= 2 (get-variant (v5 +namespace-dns+ "x")))) (is (= 2 (get-variant (v6)))) (is (= 2 (get-variant (v7)))) (is (= 2 (get-variant (v8 0 0)))) (is (= 7 (get-variant +max+)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Backward compat alias ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftest check-rfc4122-alias (is (= (:on-interface UUIDRfc4122) (:on-interface UUIDRfc9562)))) ================================================ FILE: test/clj_uuid/node_test.clj ================================================ (ns clj-uuid.node-test (:require [clojure.test :refer :all] [clj-uuid.node :refer :all])) (deftest check-node-id (testing "existance and type of node id...") (is (= (node-id) (node-id))) (is (coll? (node-id))) (is (= 6 (count (node-id)))) (is (every? number? (node-id))) (is (= 1 (bit-and 0x01 @+node-id+))) (is (instance? Long @+node-id+))) ================================================ FILE: test/clj_uuid/random_test.clj ================================================ (ns clj-uuid.random-test (:refer-clojure :exclude [bytes long]) (:require [clojure.test :refer :all] [clj-uuid.random :refer :all])) (deftest check-bytes (testing "returns byte array of requested length" (doseq [n [1 4 8 16 32]] (let [bs (bytes n)] (is (= (Class/forName "[B") (class bs))) (is (= n (alength ^bytes bs)))))) (testing "two calls return different bytes" (let [a (seq (bytes 16)) b (seq (bytes 16))] (is (not= a b) "extremely unlikely to produce identical 128-bit sequences")))) (deftest check-long-zero-arity (testing "returns a long" (is (instance? Long (long)))) (testing "two calls return different values" (let [vals (repeatedly 100 long)] (is (< 90 (count (set vals))) "should produce mostly distinct values")))) (deftest check-long-n-bytes (testing "single byte produces value in signed byte range [-128, 127]" ;; bytes() returns signed Java bytes; the reduce uses (+ (bit-shift-left n 8) b) ;; with initial accumulator 0, so a single byte gives [-128, 127] (dotimes [_ 1000] (let [v (long 1)] (is (<= -128 v 127))))) (testing "two bytes produces value in signed 2-byte range" ;; reduce folds: b0*256 + b1 where b0,b1 in [-128,127] ;; min: -128*256 + (-128) = -32896 ;; max: 127*256 + 127 = 32639 (dotimes [_ 1000] (let [v (long 2)] (is (<= -32896 v 32639)))))) (deftest check-eight-bits (testing "returns values in [0, 255]" (dotimes [_ 10000] (let [v (eight-bits)] (is (<= 0 v 255)))))) (deftest check-ten-bits (testing "returns values in [0, 1023]" (dotimes [_ 10000] (let [v (ten-bits)] (is (<= 0 v 1023)))))) (deftest check-eleven-bits (testing "returns values in [0, 2047]" (dotimes [_ 10000] (let [v (eleven-bits)] (is (<= 0 v 2047)))))) (deftest check-twelve-bits (testing "returns values in [0, 4095]" (dotimes [_ 10000] (let [v (twelve-bits)] (is (<= 0 v 4095)))))) (deftest check-bit-functions-distribution (testing "N-bit functions use full range (statistical)" ;; Generate enough samples to statistically confirm the high bit is used. ;; With 10000 samples, P(all < half-range) is vanishingly small. (let [n 10000 eights (repeatedly n eight-bits) tens (repeatedly n ten-bits) elevens (repeatedly n eleven-bits) twelves (repeatedly n twelve-bits)] (is (some #(> % 127) eights) "eight-bits should use bit 7") (is (some #(> % 511) tens) "ten-bits should use bit 9") (is (some #(> % 1023) elevens) "eleven-bits should use bit 10") (is (some #(> % 2047) twelves) "twelve-bits should use bit 11")))) ================================================ FILE: test/clj_uuid/util_test.clj ================================================ (ns clj-uuid.util-test (:require [clojure.test :refer :all] [clj-uuid.util :refer :all])) (deftest check-returning (testing "returns first value, executes side effects" (let [side-effect (atom nil)] (is (= 42 (returning 42 (reset! side-effect :done)))) (is (= :done @side-effect)))) (testing "returns first value with no side effects" (is (= :hello (returning :hello)))) (testing "multiple side effects" (let [log (atom [])] (is (= "result" (returning "result" (swap! log conj :a) (swap! log conj :b)))) (is (= [:a :b] @log))))) (deftest check-java6? (testing "current JVM is not Java 6" ;; We're running on JDK 25, so java6? should be false (is (false? (java6?))))) (deftest check-compile-if (testing "compile-if with truthy expression" (is (= :then (compile-if true :then :else)))) (testing "compile-if with falsy expression" (is (= :else (compile-if false :then :else)))) (testing "compile-if with exception-throwing expression" (is (= :else (compile-if (throw (Exception. "boom")) :then :else))))) (deftest check-with-timing (testing "returns [result elapsed-ms] pair" (let [[result ms] (with-timing (+ 1 2))] (is (= 3 result)) (is (number? ms)) (is (>= ms 0.0)))) (testing "measures non-trivial time" (let [[result ms] (with-timing (Thread/sleep 10) :done)] (is (= :done result)) (is (>= ms 5.0) "sleep 10ms should measure at least 5ms")))) (deftest check-with-temp-file (testing "creates and deletes temp file" (let [path (atom nil)] (with-temp-file f (reset! path (.getAbsolutePath f)) (is (.exists f)) (spit f "test content") (is (= "test content" (slurp f)))) (is (not (.exists (java.io.File. @path))) "temp file should be deleted"))) (testing "cleans up on exception" (let [path (atom nil)] (try (with-temp-file f (reset! path (.getAbsolutePath f)) (throw (Exception. "test"))) (catch Exception _)) (is (not (.exists (java.io.File. @path))) "temp file should be deleted even on exception")))) (defn ^:dynamic test-fn-for-wrap [x] (* x 2)) (deftest check-wrap-fn (testing "wrap-fn wraps a function" (let [original (var-get #'test-fn-for-wrap)] (wrap-fn test-fn-for-wrap [a] {:wrapped a}) (is (= {:wrapped 5} (test-fn-for-wrap 5))) ;; Restore original (alter-var-root #'test-fn-for-wrap (constantly original))))) (deftest check-lines-of-file (testing "reads lines from a temp file" (with-temp-file f (spit f "line1\nline2\nline3") (let [lines (doall (lines-of-file (.getAbsolutePath f)))] (is (= ["line1" "line2" "line3"] lines)))))) ================================================ FILE: test/clj_uuid/v1_test.clj ================================================ (ns clj-uuid.v1-test "Time based UUIDs tests" (:require [clojure.test :refer :all] [clojure.set] [clj-uuid :as uuid :refer [v1 get-timestamp]] [clj-uuid.clock :as clock])) (deftest check-v1-single-threaded (let [iterations 1000000 groups 10] (testing "single-thread v1 uuid uniqueness..." (dotimes [_ groups] (let [result (repeatedly iterations v1)] (is (= (count result) (count (set result))))))))) (deftest check-v1-concurrency (doseq [concur (range 2 9)] (let [extent 1000000 agents (map agent (repeat concur nil)) working (map #(send-off % (fn [state] (repeatedly extent v1))) agents) _ (apply await working) answers (map deref working)] (testing (str "concurrent v1 uuid uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map set answers)))))) (testing (str "concurrent v1 monotonic increasing (" concur " threads)...") (is (every? identity (map #(apply < (map get-timestamp %)) answers))))))) (deftest check-get-timestamp (testing "timestamp round-trip through v1 UUID" (dotimes [_ 100000] (let [before (clock/monotonic-time) u (v1) after (clock/monotonic-time)] (is (<= before (uuid/get-timestamp u) after)))))) ================================================ FILE: test/clj_uuid/v3_test.clj ================================================ (ns clj-uuid.v3-test (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clj-uuid :refer :all :exclude [> < =]])) (deftest check-v3-special-cases (testing "v3 special case correctness..." (is (= (v3 +null+ "") #uuid "4ae71336-e44b-39bf-b9d2-752e234818a5")) (is (= (v3 +namespace-x500+ "") #uuid "7AAF118C-F174-3EBA-9EC5-680CD791A020")) (is (= (v3 +namespace-oid+ "") #uuid "596B79DC-00DD-3991-A72F-D3696C38C64F")) (is (= (v3 +namespace-dns+ "") #uuid "C87EE674-4DDC-3EFE-A74E-DFE25DA5D7B3")) (is (= (v3 +namespace-url+ "") #uuid "14CDB9B4-DE01-3FAA-AFF5-65BC2F771745")))) (def +v3-null-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "84527A03-63CA-381A-8AFB-CF4244EF61FE") ("!\"#$%&'()*+,-./0123456789:" #uuid "50D816D1-EEBA-3CA5-9C84-2C1C81EA53EF") ("\"#$%&'()*+,-./0123456789:;" #uuid "A52D645C-B81C-3DD9-8AC7-E3B9E27B7399") ("#$%&'()*+,-./0123456789:;<" #uuid "2F73C64B-D58B-3714-93A0-5208599BDD84") ("$%&'()*+,-./0123456789:;<=" #uuid "3624A1E8-15D2-35CD-90E3-A482CF6ED426") ("%&'()*+,-./0123456789:;<=>" #uuid "B75CCFA3-6E9E-301A-A7E7-80DF0EFB5E11") ("&'()*+,-./0123456789:;<=>?" #uuid "CC1CA3BD-9FEA-348E-8133-857306B90520") ("'()*+,-./0123456789:;<=>?@" #uuid "A7C27CC9-4F2D-3155-88AF-2E10A000A929") ("()*+,-./0123456789:;<=>?@A" #uuid "A9B01A6B-6ECB-3754-A397-7C92BCB34233") (")*+,-./0123456789:;<=>?@AB" #uuid "31FB5B5C-0CAC-3C90-B6CD-9443645F4FD7") ("*+,-./0123456789:;<=>?@ABC" #uuid "47F741D2-E7E8-3D96-86D7-C955CDE87E0D") ("+,-./0123456789:;<=>?@ABCD" #uuid "8EFFE6B8-1B60-35C7-BE1E-6D67A3FF5D55") (",-./0123456789:;<=>?@ABCDE" #uuid "6FE5CED9-30CF-3D2A-92A0-9D0AFB1A857C") ("-./0123456789:;<=>?@ABCDEF" #uuid "F9AF2E93-0DF6-3C2E-BEE9-137ED731385D") ("./0123456789:;<=>?@ABCDEFG" #uuid "F5053601-F75F-39AB-8FB7-AD3A4919953A") ("/0123456789:;<=>?@ABCDEFGH" #uuid "E3219C41-1396-32E5-9A03-8BED1F9F3BCC") ("0123456789:;<=>?@ABCDEFGHI" #uuid "94486C0C-5882-3490-9B5C-FDA8823BCD9C") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "ABCB5CCC-7BCA-3388-B8A9-CA8888672F1B") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "96C62C75-1B95-3A99-A68B-C0A98AF6652F") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "F93BE46D-0C6F-34E0-94EA-E7390A3BFAAD") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "8F558F64-472C-341E-B49E-A6743F54CC16") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "2F4B6958-3FB4-3FD4-952A-861A7919A25C") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "FC83A94A-75AF-3E47-A633-10E784D5E311") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "9DA1321F-1A84-3FAB-A494-307C0E37938D") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "FC9CED61-35EF-3386-BF8E-31F9DA844323") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "80E91EB9-0298-30C8-B582-750262D42D4B") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "2862A3CE-0115-39F9-A08F-4F26445C7C07") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "94CDB337-4C2D-3AAD-BDE4-A895060CC460") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "55F086BC-9EB0-3F2E-B0BC-F0BF637475D4") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "9F6FC7D5-7353-306D-BCDA-37B166A48755") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "E5DA63AF-8285-300E-A518-2F553A2BBC7D") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "54962C09-F4CC-3EAC-B2A8-F4CF6E53C86D") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "D0CA9015-8176-3490-A119-1FBBA7833F7C") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "1F1723EB-AEB7-32C6-9221-B43CF93434AE") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "990A8C1A-01A9-3088-B275-A0EDCF171118") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "E54DC0F1-8DAC-3372-8938-4C77BBF4A795") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "DACA530A-D409-3DB5-AC3E-16FAC891EB8B") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "EC762B89-AE25-34DD-B267-77BA166D4C39") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "D99667DF-7B0A-335D-8889-31D54CC1D0C0") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "E1BD4B67-B324-3D93-B3AD-F2F7F5BC346C") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "9BA6966F-5425-3816-B53A-CAEDBB979028") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "E25019D1-332E-376B-BA06-5EA4EBF8FC8A") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "B3BC1638-4A04-3B4C-9191-82D1D70356CF") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "2F528BEC-A02E-3844-9267-B45FE9E7A860") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "6B13CD75-382B-334C-B511-797C9305DB8C") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "CFE5D94D-6634-3011-B616-9D634DCC7533") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "7D2E3761-8E9C-3828-96E6-854F4F678C81") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "FE71897C-B88F-36CA-9765-757A82E24AE4") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "6101A664-0DB3-30DD-B04D-F8B6BA0EA255") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "EB769B0A-F62A-3EA3-BA7D-191471D4A016") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "628CB7F4-CC8E-34C1-A905-44D3558C247B") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "1AE38A2F-EDF5-3BC4-AD63-E005EF35077E") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "24C25E2E-CF08-3216-8BBC-521C4BD61AFC") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "E023C41B-3A85-3AB3-A18C-A87FFC84CF53") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "27B13D02-E89E-3E30-8ABB-A43F8A8858FD") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "5DC9BA7C-30FC-3476-BBC3-FD45898C24AF") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "BF707BBE-53B0-3E29-BF53-BFE88A1BAE5D") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "B8837DD6-6BF0-3D9F-BDE4-D2D6AFF011F0") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "A88482D3-07EB-3FFF-B030-7D35B4E372F1") ("[\\]^_`abcdefghijklmnopqrst" #uuid "06FA4DAE-2EC9-3E9F-A01C-1DEC8F009F47") ("\\]^_`abcdefghijklmnopqrstu" #uuid "33B53814-8A07-3AAB-85BB-0897254CF7D3") ("]^_`abcdefghijklmnopqrstuv" #uuid "75543166-1164-3C52-9E18-688690CF9750") ("^_`abcdefghijklmnopqrstuvw" #uuid "F5D74A6C-A216-310B-9568-652A0640E277") ("_`abcdefghijklmnopqrstuvwx" #uuid "21D2CD4B-5CEC-3C0C-ABF4-87A256F37745") ("`abcdefghijklmnopqrstuvwxy" #uuid "C520DDB7-E9E5-3D05-9157-F2157DEB08CE") ("abcdefghijklmnopqrstuvwxyz" #uuid "6B95E2CC-5E76-365C-9C53-5DDAD84E612A") ("bcdefghijklmnopqrstuvwxyz{" #uuid "DEDE6550-14B9-3213-9660-661C46879AC1") ("cdefghijklmnopqrstuvwxyz{|" #uuid "4DC61324-36A9-3305-97FA-04F8461A8656") ("defghijklmnopqrstuvwxyz{|}" #uuid "ED828E85-BB8D-327B-92AD-366F29E9C804") ("efghijklmnopqrstuvwxyz{|}~" #uuid "925484B7-F24E-35FA-B607-6625301C99E9") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "3EDFBE37-E082-3C35-97EB-9547B53E8E49") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "8A18A7D7-3C2B-3594-8C1C-9DE815BD26AD") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "C1834810-8486-32CD-9EEC-90FFDCF84D28") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "42CFD52A-8177-3F5C-899A-586FB8B86BB4") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "7535FABD-FA47-3569-B74D-D1FE338503FB") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "2EEA92DB-5A9F-3469-9CA9-AC7391EBD520") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "8A13DB2B-ACC9-3A40-AF19-B283691239D9") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "AFF4E386-6231-3E2A-88F1-3FE63D467059") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "E81B7969-464A-3B48-AC6A-C780732B94B3") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "316E4D11-F765-32E4-97B9-D3704000BD57") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "3BF51E5F-6940-3591-96AC-0322900FB4DE") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "8DB58107-A4C3-3560-A914-7547B57A0565") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "037874F2-EFCA-3DAB-9AD3-411B46D4C185") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "B3F2E765-46DE-3082-ABFF-5B65801BF1E7") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "4FD026E5-5478-3DD5-AF5A-EAE806EEF62D") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "3142B451-58AC-3055-9D0D-9948A1F3706C") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "741E5817-5E0F-31D8-8FAE-3AF54EB27365") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "A983541C-66E6-33E6-99C0-9D7F7750899B") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "56F776F7-7F8C-3A05-84B8-E6FD416C4940") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "4B0029B8-A2CE-30D8-9147-99DC8AC92BAC") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "91EB3CE3-9F35-36CB-AA35-F40E96960E4E") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "B89FD471-22CA-3D4A-B255-14479084F964") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "4E07B5E6-66EB-370B-A710-97064AE6D845") ("}~ !\"#$%&'()*+,-./01234567" #uuid "F23EFB3D-C9BF-3198-ACC6-38C646818FFF") ("~ !\"#$%&'()*+,-./012345678" #uuid "8099A277-2EE2-3A97-AAF1-7D186DEEBBDC") )) (deftest check-v3-null-ns-cases (testing "v3 null-ns case-based correctness..." (doseq [case +v3-null-ns-cases+] (is (= (second case) (v3 +null+ (first case))))))) (def +v3-dns-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "8502CBB1-B406-39A3-AC93-00AE0DC70F89") ("!\"#$%&'()*+,-./0123456789:" #uuid "5CD1F6D6-BDE7-3951-B13A-A09645DC80B4") ("\"#$%&'()*+,-./0123456789:;" #uuid "3E5E99E8-0EFB-3F3F-B8A6-873B8EF8F95B") ("#$%&'()*+,-./0123456789:;<" #uuid "C75A5D34-955A-3D4F-B8F6-ED670CEFD20E") ("$%&'()*+,-./0123456789:;<=" #uuid "9062C80A-709E-3890-BA62-E01A7A9F5BE6") ("%&'()*+,-./0123456789:;<=>" #uuid "92168DE8-02D8-3E43-BF5F-BB96F9081EEF") ("&'()*+,-./0123456789:;<=>?" #uuid "5AE75650-AA07-3116-94B5-651E17E890B7") ("'()*+,-./0123456789:;<=>?@" #uuid "60F0E242-6A03-3710-BC32-E673CD132FB8") ("()*+,-./0123456789:;<=>?@A" #uuid "6B04570A-F602-3669-AC1D-1FE799A3B5F6") (")*+,-./0123456789:;<=>?@AB" #uuid "FA527A0F-C8F0-3608-8351-4D006D8E4F62") ("*+,-./0123456789:;<=>?@ABC" #uuid "72375C7F-1B61-3851-A4C6-D65839931A36") ("+,-./0123456789:;<=>?@ABCD" #uuid "9315034D-29BB-39C9-B29C-2AA2C4921EC8") (",-./0123456789:;<=>?@ABCDE" #uuid "C61EE942-70D4-3F61-B411-5D71667081C4") ("-./0123456789:;<=>?@ABCDEF" #uuid "929650E9-EAFB-3AC2-9945-95B254699094") ("./0123456789:;<=>?@ABCDEFG" #uuid "3751476F-2EAE-3CE1-954E-7CBFA8B46061") ("/0123456789:;<=>?@ABCDEFGH" #uuid "2910586F-51A0-353E-A5C3-C40F0C14F410") ("0123456789:;<=>?@ABCDEFGHI" #uuid "B91DF409-A8CB-351A-AC0B-24E4A6A7FD28") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "2A9784FF-AA2E-3974-86BF-B37F0E60F50B") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "A91A7B75-6674-30AB-94FF-ED6F93728BEA") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "932F664F-3289-3527-9D52-0EA2C0A205AB") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "FF6A35C9-0BD7-314C-BF4C-DABF76F8BBF9") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "AA5AD585-6B5D-3303-BBCD-EB493EC61DE6") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "E88EC15E-AC84-3295-868F-9CAB7FC1D15B") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "36AD17D3-9EB5-3222-B944-DF80AD71CE8C") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "75477B3F-47CC-3EC8-ABD2-5D8C2B700C00") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "B76D9FDC-C337-34C2-9EA2-95BF83BB4FF3") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "481DD83F-9686-39F9-AA46-FE893E69EF78") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "763F4C35-D29D-3223-98EC-1ED581A3D4B9") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "DC2C9ECD-B365-3D76-A989-2964B7AB10E0") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "20BC5EB3-B6BD-322D-97C2-FCE1F65271BB") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "8A6E735D-D1EB-33E1-88BB-F0E30A369318") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "0EB2F01D-560A-3616-BCC3-2C0E10612EA5") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "C3B2FCB2-1CE3-33AF-98DC-824DAA7A5327") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "DBAE940B-391B-3655-B21F-2AE528FAD04C") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "08B4ED9D-791A-369F-BBF7-F4FCEBE803F5") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "907267C2-5EB6-36DC-9104-E9B036075927") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "F9F89EBA-AA31-3353-8016-BE7E24D5FEA5") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "A8A80596-3613-39DF-82D1-D5B8C1789ED3") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "0DAFC143-7019-30AA-999B-21C8EFDE44C8") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "BDE827C3-2DB7-39D2-8361-17C5177C3A9B") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "D47F238E-AEC0-3D16-A68F-557FE161D6F4") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "737E81D8-B110-3333-8BAA-9F215028E353") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "26C803EF-6B0D-387E-BB11-DFC748546F92") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "BBAA20C9-5428-31AC-99C7-4C5959E77F5F") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "C7655287-1D3A-392A-B29D-DECB40F34D60") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "0295F24F-EE5E-3D05-BCA3-56BD9960CFD2") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "911D25F2-D390-31CA-ACFF-6E6FBA5B7DDD") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "528A84FD-9E97-303A-92E7-51645F93525F") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "AB4FB6D1-4F74-3331-98EF-C27E87008D07") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "23DA9F4B-5218-3C3E-AE38-1A4F760E5C93") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "98A14203-0B09-3095-83BC-0FFB9A4684C4") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "CA10A029-E06A-364E-A0ED-B7AA4F24D207") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "2C95761B-FBD7-33E2-936A-13D8D0C48F58") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "E3C6A320-23A7-353E-8E06-C363EC783A86") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "32EFCA32-AD04-3CB2-BA66-E8509FC1C743") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "9203D568-7AE8-3F1E-BD83-AA72C1F47495") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "6860513A-1DCF-3B63-AC85-E684406A2F61") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "564B821A-DEAC-3A72-89BC-5659B8883FB8") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "6988DFAF-7A52-3F4B-AE03-BFE348977A37") ("[\\]^_`abcdefghijklmnopqrst" #uuid "CF62DC30-B276-39F5-9FFD-AA571936C5FD") ("\\]^_`abcdefghijklmnopqrstu" #uuid "8715D8EC-89BB-395F-A393-95EFF3396E25") ("]^_`abcdefghijklmnopqrstuv" #uuid "C66A5CB0-3695-31F8-8C41-D061DB8B76FD") ("^_`abcdefghijklmnopqrstuvw" #uuid "0045436E-446F-3A24-86A2-5D3547DFFA18") ("_`abcdefghijklmnopqrstuvwx" #uuid "5E24D6DF-AF96-3F19-8A71-89EEF9F19083") ("`abcdefghijklmnopqrstuvwxy" #uuid "4D0B435F-C0F9-3329-BDCB-F3FC3AABEDD4") ("abcdefghijklmnopqrstuvwxyz" #uuid "E7684C6A-B70E-3531-9426-3BC6E033B0FE") ("bcdefghijklmnopqrstuvwxyz{" #uuid "486F569D-7C28-33C2-8B16-4227BD39E3D3") ("cdefghijklmnopqrstuvwxyz{|" #uuid "DAE05C81-A7FD-3BE3-824C-789FD9F57A64") ("defghijklmnopqrstuvwxyz{|}" #uuid "15F225A9-BF75-352A-BEBA-03BDEDCA0C56") ("efghijklmnopqrstuvwxyz{|}~" #uuid "F27B144E-D8A9-3D01-923D-E81296D994D4") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "040C3AF4-3765-3383-BD52-9258CE747EE4") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "1751D35C-113B-399C-B8EC-B40D0ADD30BE") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "CD19EAB7-86F6-3DEB-8BDB-A382B8939580") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "C6AAFEEA-0794-3BA2-BBB0-C019DEBEA642") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "E566FDE3-FB60-347F-8A61-CE4E8314C7D8") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "3E44EBAC-F311-3801-B576-3D570247E085") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "7C3E1262-380A-3970-ACC2-8C67C95AB416") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "8C802F36-4223-3A16-A002-7CF4333CA2BC") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "68E9670E-DDB7-3235-9E22-BDE736C58160") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "619A2D6F-8DE7-389E-92C9-DCE56B0CF5F4") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "F5D251CF-76C2-3C0F-BBEB-13AE71B90EDD") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "71AE2AA0-47C1-307A-9BE7-73B6BFEE0AE6") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "0BB601E4-94D6-3AD0-8BFC-28CDA8AAD2CD") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "CABDBD7F-54AE-37F5-B7C8-DFE9219133CE") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "FEE30B3E-FAF2-3465-AC90-17D6BEC041FF") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "D37DD9FB-6DD9-362B-9BAA-E3A67E7C3A15") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "365BED8E-5E7C-3704-AD7B-4A5B91767D2E") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "0C99C105-BAC3-3D4B-B4F5-04251CF48B7B") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "E06D9F72-813B-3F3E-A096-E4C7FC05BD84") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "16DF5C3B-9E1D-3CE9-A8F4-165B7067756C") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "D3196C68-D1A0-3CE5-9DA7-4F360C0E528B") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "F9EA7DC9-F790-3EBC-B970-FE944FC25078") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "786245E9-B956-3774-BD77-3DD581271B30") ("}~ !\"#$%&'()*+,-./01234567" #uuid "3BAC4AC0-3FD1-371F-AC81-5BA1AB950E18") ("~ !\"#$%&'()*+,-./012345678" #uuid "6EC555CD-9DFF-3783-B556-2D8DD55A5CD0") )) (deftest check-v3-dns-ns-cases (testing "v3 dns-ns case-based correctness..." (doseq [case +v3-dns-ns-cases+] (is (= (second case) (v3 +namespace-dns+ (first case))))))) (def +v3-oid-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "43D9B705-E75C-31EB-A5AE-89D03DEA6C8A") ("!\"#$%&'()*+,-./0123456789:" #uuid "D425D486-816F-310F-9675-D9F0F5568AAD") ("\"#$%&'()*+,-./0123456789:;" #uuid "411A971C-E960-3B93-B02D-40E492EAD382") ("#$%&'()*+,-./0123456789:;<" #uuid "4DF246BF-DBE6-337E-BC7F-A2C18B8F9264") ("$%&'()*+,-./0123456789:;<=" #uuid "D72384E9-DF60-3B90-A18B-CA71ADBBEFA1") ("%&'()*+,-./0123456789:;<=>" #uuid "6CB6E4D1-F590-329D-9CB3-11B985B81333") ("&'()*+,-./0123456789:;<=>?" #uuid "C2C9252E-6018-3704-A0EB-786BEE2C949B") ("'()*+,-./0123456789:;<=>?@" #uuid "F162EFDE-3FDC-31A0-8BBD-281E3911DB83") ("()*+,-./0123456789:;<=>?@A" #uuid "4A4327AA-B253-3A36-9D51-FD149EF61FFC") (")*+,-./0123456789:;<=>?@AB" #uuid "E6D5600D-6965-3AFD-946F-BC3E1E3E51D3") ("*+,-./0123456789:;<=>?@ABC" #uuid "36A30022-28CD-3AFE-87F1-B84974ECF06D") ("+,-./0123456789:;<=>?@ABCD" #uuid "B9E15BA0-1574-3AA2-8B85-AD7D264F861F") (",-./0123456789:;<=>?@ABCDE" #uuid "4047ABD8-DAB4-318A-90F7-423759DC796F") ("-./0123456789:;<=>?@ABCDEF" #uuid "86E64E13-7028-39BD-80FB-912D77416CC3") ("./0123456789:;<=>?@ABCDEFG" #uuid "75FDCE21-970F-3046-8D8A-693FB255746B") ("/0123456789:;<=>?@ABCDEFGH" #uuid "52A2192D-BD14-334D-B9B4-2756FADC303B") ("0123456789:;<=>?@ABCDEFGHI" #uuid "3AC1FF81-E7F2-3F2B-A4BB-0CF77AC9878B") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "0AE96CC9-7371-3ACF-8D78-ACF5FD1E42F6") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "223AC51D-323A-3889-B3AB-5733CC79479A") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "A9288FC0-599A-3C05-9EFC-96DB4D59451B") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "EE795374-5D2F-38C4-B0DC-BD2E72E03FEB") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "80E415B6-32DF-350B-8AF2-388EEB0AE788") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "BD6755C9-5565-3ADD-8B37-F53284B1DBD5") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "30CFFF63-A342-30E4-9692-846588A5702B") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "B419E181-2D73-3619-B809-FCC0B518EE68") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "C795BEAB-5B07-3D8C-ACC2-374CBE19669F") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "21FAADF5-3A76-34B4-B835-A80D9E269A91") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "EF0A7F62-8C69-30B2-B66B-E480C7BC71FE") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "159FC9E2-5A10-3C8E-8DC1-D8D9C0CB7706") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "F6882739-0246-3AF0-AB7C-267644CEA4DD") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "C004A348-6266-3D52-B3BB-ADE1F48A9D49") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "681545F9-D596-34EF-9F10-C6D8F87087F1") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "9C78D8DD-918F-331A-A9C4-69CE5C2505F0") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "1AE24819-DE5A-327D-89FE-2745E611B9F3") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "E6ACEB11-DEEB-36D7-A281-1E3C9F653C2B") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "71300878-7997-3AB1-BE58-4A1879169575") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "F3E984D7-CACF-302F-8E30-616634DEC171") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "9DABDA54-3905-3443-BFE4-3680CB5D4CB6") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "BB9F5584-4772-3D83-9AA2-7808B78E346E") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "B9160282-CEBB-3AD3-A52F-4DC60393798D") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "89BB630C-4C3C-317F-BA91-D3E12DF00FF7") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "4A9C2A72-F9D6-3650-8721-FC8E0EF635FB") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "F3DABB7C-2C7F-3AE5-9214-D85BF57910CB") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "671A4498-CE67-314C-B50F-06212DCF5AC4") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "3FC4800A-A5C3-3454-A407-2D874993D0A8") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "862A6E34-653C-3D90-9BC4-DF5F875F4F49") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "13423AE6-E6C4-3F4A-A94D-E9A5B5DD7707") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "24F418E9-C5BA-3A82-8492-334ADFD63CD1") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "A1B3F1E1-E25F-3AE9-88C8-1BF76FB82CA7") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "D84E1825-DD1A-3147-8275-1A6CF04E60EC") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "9B293E2C-6A27-3A79-82FF-C225846296CD") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "F17A6EC8-F42E-3523-ADDC-51AB5F08EF2B") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "7C17D37D-49D1-3815-8F3D-4F2950B3D863") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "71E14392-3F2A-3EC9-9476-027B4D7F9EDA") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "7103789B-4358-3BF3-B7E9-0B3C203E7552") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "7C95A201-3F6F-36CF-9DBB-E1BCCD9D9FD2") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "A05E1B2E-63BC-38D9-9333-56E6337C14ED") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "34515391-27CF-3202-9AF8-624095EC7D7F") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "FA32549C-6874-3DAC-B69B-AE22C3EF2411") ("[\\]^_`abcdefghijklmnopqrst" #uuid "E6C8FC6A-240B-382C-9FDD-7856BCFF4EA1") ("\\]^_`abcdefghijklmnopqrstu" #uuid "2C6677D4-BDD9-3A9C-9755-5F6F06D53A54") ("]^_`abcdefghijklmnopqrstuv" #uuid "EA5349C6-5861-3C7F-9A6F-ED4F5FE37AE9") ("^_`abcdefghijklmnopqrstuvw" #uuid "F93457EF-D9AD-3151-91A4-57BE63AEAC4A") ("_`abcdefghijklmnopqrstuvwx" #uuid "A29821DC-23A0-3CB9-88FC-2F5C4B4EDE24") ("`abcdefghijklmnopqrstuvwxy" #uuid "AB8D5F5D-D180-34B3-B320-DE0FA700664F") ("abcdefghijklmnopqrstuvwxyz" #uuid "0C491E79-D2B7-3A30-B9C4-BE9515E6E0E2") ("bcdefghijklmnopqrstuvwxyz{" #uuid "E2D1EFD6-229C-3868-BBF1-5143CF5E3649") ("cdefghijklmnopqrstuvwxyz{|" #uuid "F8BAE8BC-E1A4-3D42-8015-AF897E3AA1E4") ("defghijklmnopqrstuvwxyz{|}" #uuid "AF0061CD-2B25-3ECC-BFD4-8707FC33E3F4") ("efghijklmnopqrstuvwxyz{|}~" #uuid "AE4EBA8B-95F9-32CF-A37F-0A202D4D746E") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "AF3A5813-EB8C-3DA2-9BDA-866C2D255DB6") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "D547E557-7542-3241-A56D-D5B9A44D4BFF") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "FA78A373-D9B9-3EBF-8699-642816516351") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "57D1E4B6-BE59-3692-BD09-07C62A4570B6") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "B7946A08-1F5A-3EA4-8040-422179089B5E") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "BB50DA00-95FE-37D8-A9B4-847555CFA047") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "54DEC298-B336-3690-BA29-182C6B358BE7") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "1181FADE-BC28-3BA1-9594-61848F97CD74") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "8BB47B9F-237D-313D-9B71-A897646ACF43") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "2E548DFD-1A9D-3EBF-AB39-41DC1530F39D") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "4082F972-90D3-3011-A16E-D3ADAB93A1C9") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "181BACA8-4476-377E-AC33-739E1B49CDFA") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "FB6E8638-AA96-3C2A-A621-2319E9604BA4") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "C30943D9-FD02-3922-AD5E-3911F0B460F8") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "80F5F58A-4C81-3E25-BC67-681F523F5A47") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "1656D4B3-F6C4-3774-AF81-7BEEE3C64A8A") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "05DA3CB1-6634-3BEE-A275-E5D672F2B303") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "6CA9378F-0A4F-3F79-9D07-76D37D080F4C") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "795D525B-B8AB-36EC-9069-C0B108434526") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "55DD78E1-5ECB-3E9E-9D52-90C0D0D4F9B0") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "DF4A937F-5E7C-372B-8CAD-96778829C4D0") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "5AC9C9D3-446A-3D44-81C0-B3BA2D5F72D9") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "D5A24A2C-A19F-314F-A2D1-4DEAFB6DF09A") ("}~ !\"#$%&'()*+,-./01234567" #uuid "0BE200A8-CCF3-39E6-B784-28ABCF933676") ("~ !\"#$%&'()*+,-./012345678" #uuid "B1BD83C9-B045-3416-A411-2091009AA351") )) (deftest check-v3-oid-ns-cases (testing "v3 oid-ns case-based correctness..." (doseq [case +v3-oid-ns-cases+] (is (= (second case) (v3 +namespace-oid+ (first case))))))) ================================================ FILE: test/clj_uuid/v4_test.clj ================================================ (ns clj-uuid.v4-test "Random UUIDs tests" (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clj-uuid :refer :all :exclude [> < =]])) (deftest check-v4-special-cases (testing "v4 special case correctness..." (is (= (v4 0 0) #uuid "00000000-0000-4000-8000-000000000000")) (is (= (v4 0 1) #uuid "00000000-0000-4000-8000-000000000001")) (is (= (v4 0 -1) #uuid "00000000-0000-4000-bfff-ffffffffffff")) (is (= (v4 -1 0) #uuid "ffffffff-ffff-4fff-8000-000000000000")) (is (= (v4 -1 -1) #uuid "ffffffff-ffff-4fff-bfff-ffffffffffff")))) ================================================ FILE: test/clj_uuid/v5_test.clj ================================================ (ns clj-uuid.v5-test (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clj-uuid :refer :all :exclude [> < =]])) (deftest check-v5-special-cases (testing "v5 special case correctness..." (is (= (v5 +null+ "") #uuid "E129F27C-5103-5C5C-844B-CDF0A15E160D")) (is (= (v5 +namespace-x500+ "") #uuid "B4BDF874-8C03-5BD8-8FD7-5E409DFD82C0")) (is (= (v5 +namespace-oid+ "") #uuid "0A68EB57-C88A-5F34-9E9D-27F85E68AF4F")) (is (= (v5 +namespace-dns+ "") #uuid "4EBD0208-8328-5D69-8C44-EC50939C0967")) (is (= (v5 +namespace-url+ "") #uuid "1B4DB7EB-4057-5DDF-91E0-36DEC72071F5")))) (def +v5-null-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "242E6E8E-7545-5A07-A02F-326EC30CB6B6") ("!\"#$%&'()*+,-./0123456789:" #uuid "41F22F8C-6251-523B-B46D-E13DB8EA92AB") ("\"#$%&'()*+,-./0123456789:;" #uuid "D8F25381-4007-57D5-85ED-34F8F5C2879B") ("#$%&'()*+,-./0123456789:;<" #uuid "45846189-7511-535D-9F94-8E0BD4E32383") ("$%&'()*+,-./0123456789:;<=" #uuid "5E0E00BE-97CA-5270-BAE1-826A1528126E") ("%&'()*+,-./0123456789:;<=>" #uuid "00715EE8-3D82-5605-B403-CC58B2F4C71A") ("&'()*+,-./0123456789:;<=>?" #uuid "15D19A8D-76E1-58D2-B5DE-1E4A5CC3B515") ("'()*+,-./0123456789:;<=>?@" #uuid "474DBC21-26B2-5B08-A76B-F06E331B93DD") ("()*+,-./0123456789:;<=>?@A" #uuid "65B54688-43AD-54DC-B052-B612E09B8AD6") (")*+,-./0123456789:;<=>?@AB" #uuid "20CCA61F-4F55-5420-9CD0-30B1D166FE5B") ("*+,-./0123456789:;<=>?@ABC" #uuid "D9C06A76-8189-583B-B3E9-FFC143531914") ("+,-./0123456789:;<=>?@ABCD" #uuid "014FAF6F-5246-5007-B5CB-E0EFC331D3B0") (",-./0123456789:;<=>?@ABCDE" #uuid "D87E9933-09B4-5DA5-B980-7CC65E6B471B") ("-./0123456789:;<=>?@ABCDEF" #uuid "31E0B629-9781-5E59-B7AF-04D2D88660AF") ("./0123456789:;<=>?@ABCDEFG" #uuid "CCED497C-35AF-5FE4-A6C2-61A0572FDA59") ("/0123456789:;<=>?@ABCDEFGH" #uuid "C993482E-33E9-5CC8-9E51-8EE46B3FE152") ("0123456789:;<=>?@ABCDEFGHI" #uuid "0F6BDD65-B698-5762-AD0E-AB35B5D73885") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "AA1D4298-9A26-5AB9-B0F2-E1D7AF6CF813") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "27F508C3-7402-57F8-B17B-0D81C0D7F062") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "6EC8BBC6-987E-52E3-A948-EE3DBF3D0D4A") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "25DFBD80-01B3-5540-AEBA-22E627A48899") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "FFF7A94C-F5F2-51FF-9D5B-55B388600BE6") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "B4FD5C57-0AE3-5D8F-ADAC-1E8E8CB60C27") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "0E1880FB-CB2E-588B-9CFD-6B451E447E50") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "5040D88C-1881-599D-A954-A2CBCAFFC758") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "77EEC4CC-5AD9-54AA-91D6-952336D771FE") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "30310096-BD69-5B19-AF68-FFBC854A06F6") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "D9A83C73-1B93-5E59-9BF3-7FD6707085B8") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "5BC33DA2-F6B3-5CF7-822C-1289C81DE1A1") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "31D4E146-67ED-50C4-BCC6-BB3C74CDDAAC") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "F3939252-7456-5360-B34D-E327B823957F") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "D518FB94-0083-5933-BEE1-4D21694188F2") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "6852364E-017E-5D10-B4F9-63FE7DCF6D85") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "009442DE-7929-5001-9022-0D340553A9D6") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "58860D2D-09C9-5B46-B330-2E0867C7EB7A") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "3D6024D9-75E7-5BBF-90DB-097939462E63") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "F400AE6B-B384-5DD3-8D3C-BCEC42E64F9F") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "7C466264-9005-558E-8F8B-844868A27041") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "96F0E71A-AEC5-5F2D-82BD-A38CB763589B") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "B2133431-BC91-5C9B-868D-7F182215CF59") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "70572482-055E-5013-8755-EF77D56E31CF") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "F4A88F92-5D12-5495-A4A3-0933441B535A") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "F5FC6436-BB13-5C2E-8BBC-349F5BBCF126") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "C77E9DA8-0B0C-5DBE-8B1C-85E7BA4C631E") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "2C3EF189-369C-5FE6-ABE8-D071246957B6") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "BF42EC1C-0234-5226-9618-9538F89A98FF") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "39056E38-A0B0-5194-B16E-D6F75AFA93AE") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "17C8A690-3261-5EA5-9DE1-FF05B956E56B") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "763D44CE-E4EF-5B63-A5EC-6BD6ACB860B8") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "880D1D2E-3912-51C8-A7EF-A2954B96D6D8") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "DDB08918-D45E-52D2-9999-B4C5925C67B7") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "C9770255-BAFC-5974-9F23-B17C119F7FF3") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "58128319-128B-54FF-B202-33A2A271B380") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "7D1628F8-615E-593E-B56C-DDC5BCDDC45C") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "1359497D-5D7F-5288-A021-88F071979ACC") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "097BB556-5D10-5915-AB34-136DFBEDA005") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "85AB2C59-868D-52B2-8D28-A04ADF32C456") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "430C7305-D1F8-592D-BCB8-7345F0577B51") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "AD411859-3F9D-5CEF-8891-2D91453BF02B") ("[\\]^_`abcdefghijklmnopqrst" #uuid "AE01E30C-B5DF-5A40-AD98-C2E2E5F00505") ("\\]^_`abcdefghijklmnopqrstu" #uuid "D306C4AD-7215-5331-895C-D3B123AB07F8") ("]^_`abcdefghijklmnopqrstuv" #uuid "9670F138-C67F-5A3B-8421-3DB6D3E1E271") ("^_`abcdefghijklmnopqrstuvw" #uuid "E246E515-EE87-5E71-9766-FE5CC691F2B6") ("_`abcdefghijklmnopqrstuvwx" #uuid "CA8B9A8A-C953-58C2-8CBC-B9D5C08B7082") ("`abcdefghijklmnopqrstuvwxy" #uuid "72A55C81-7771-573C-A968-D9C2E99349F6") ("abcdefghijklmnopqrstuvwxyz" #uuid "15EF97D2-2E8B-5705-B040-13073A8403C6") ("bcdefghijklmnopqrstuvwxyz{" #uuid "2FBC6E63-876F-5B40-9740-0194EAFF582A") ("cdefghijklmnopqrstuvwxyz{|" #uuid "DA8F3757-7272-5E61-AB62-AA925CA23FEE") ("defghijklmnopqrstuvwxyz{|}" #uuid "0D3D2034-3451-5F68-90AD-1C6C278DEC4D") ("efghijklmnopqrstuvwxyz{|}~" #uuid "5E81B26E-6EF6-57ED-9BD7-21B6BC153AED") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "083EE855-E5E0-5ED3-8E76-93BA065FF370") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "8B2746D3-EDCB-5AC5-9A06-6DA96E4D135C") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "EC9932CD-6FBA-53C9-B3F5-6D41312DA6A3") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "2D587B17-4476-5EFF-BF0E-1B3D567DC402") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "D1682F44-2F08-58D6-BD7D-210C91B87111") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "1EE16A30-3B2B-5AB7-8E1D-EFF3A5E4C7E8") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "CD66FFE0-37D2-55F6-8049-81F3AD4A881E") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "63638AF4-3E7C-5A7D-82E7-DC7FE152C0B7") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "1CBCA560-CE4A-52F6-B86C-F25CA1EAB1EC") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "ED541D15-90DE-5790-AEAC-AE92696FE339") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "4DB93134-5B4E-537C-A178-3CE6E2EAB2F6") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "6402FA45-D31A-5E79-8CDA-F0B5F5BD88DD") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "063A63DF-A01A-5A67-9BB9-B3BF4E42131B") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "61DD21C6-2DB9-5C41-9602-577CCF4B7C8B") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "6799784F-3507-5450-9274-D9BA10AFC752") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "30A6284C-6269-5D2E-98E3-7A87D84F51F1") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "18777242-8DF3-53AE-B695-FDCAF7E1E458") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "ED8BA679-1BC9-5EB4-9EED-DD717809FEC2") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "CE96C5BE-70EE-5594-9A5B-8C96A456A6D9") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "5C884C16-E0B7-5A26-A32A-6DB49B501A1A") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "7C0EBA71-82F0-5904-B745-75541AE65312") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "37CDD9D8-A94F-5BB5-AA57-97A92ACA22FC") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "BB23D8C2-29F0-5EC5-BF50-B7092FA62204") ("}~ !\"#$%&'()*+,-./01234567" #uuid "AD3AD027-A2ED-5F09-B581-78AD87D86A7C") ("~ !\"#$%&'()*+,-./012345678" #uuid "093B7461-98EF-55DC-8616-890210247499") )) (deftest check-v5-null-ns-cases (testing "v5 null-ns case-based correctness..." (doseq [case +v5-null-ns-cases+] (is (= (second case) (v5 +null+ (first case))))))) (def +v5-dns-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "20936B0A-AB47-5EBD-9788-7F379130910E") ("!\"#$%&'()*+,-./0123456789:" #uuid "06454276-F2D4-5F4C-9A62-E9471E2D1EA4") ("\"#$%&'()*+,-./0123456789:;" #uuid "AD979D13-7728-5FB1-8799-52D2B973DC7E") ("#$%&'()*+,-./0123456789:;<" #uuid "E0A54D87-993D-510D-BB56-6660B266330F") ("$%&'()*+,-./0123456789:;<=" #uuid "7F9C48C3-4145-5EDE-BE28-03EDD13C2FAB") ("%&'()*+,-./0123456789:;<=>" #uuid "A9AAE61F-D1A3-5F61-BE26-FED829E8AB6D") ("&'()*+,-./0123456789:;<=>?" #uuid "CF2DBC66-F8A6-50ED-B00A-9A6A6206CA8D") ("'()*+,-./0123456789:;<=>?@" #uuid "84107B8C-C5EB-54B4-9ACE-B607F24BBB0F") ("()*+,-./0123456789:;<=>?@A" #uuid "1D5E2031-445A-53F7-B088-4E20C41249ED") (")*+,-./0123456789:;<=>?@AB" #uuid "CAB0515F-1641-56BA-9042-967CC0243D7E") ("*+,-./0123456789:;<=>?@ABC" #uuid "7548EB9D-7A15-5C6A-BE64-D51BD5FD9EC8") ("+,-./0123456789:;<=>?@ABCD" #uuid "73DF2DFA-3F14-557A-84FB-3C3011EC902E") (",-./0123456789:;<=>?@ABCDE" #uuid "49DEDE7F-121B-5697-B478-16EBC77A2C1F") ("-./0123456789:;<=>?@ABCDEF" #uuid "D4089686-20D4-5A75-9058-95C3289C9A91") ("./0123456789:;<=>?@ABCDEFG" #uuid "E6312674-9157-5749-9A6E-760667886FD6") ("/0123456789:;<=>?@ABCDEFGH" #uuid "E4C7BB57-9085-56DA-ADCA-4A742650EEDA") ("0123456789:;<=>?@ABCDEFGHI" #uuid "C4863E91-D99C-5BFE-B44B-5E0D58EBE9B1") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "7A635616-E871-5962-821C-7F7FDA74627A") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "3F9F12F2-520F-522A-801C-C0EFA4078A1C") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "820886F4-5CA7-595B-B484-FF40FC94D5CD") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "436BA120-F191-54FE-9532-176DDBFFBA20") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "4054BBB1-FD3A-5D38-BA77-C6F5620904D9") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "F9E7C39E-802E-53B3-B7CE-F84C02BDB3A8") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "E620E967-540D-5D2A-A361-6E7F5B4DB83B") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "2FCD4A7A-E98E-5D97-90CC-03DB0575EF80") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "2442A8C4-DC2A-5A15-A65F-E3101E98E5CC") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "7C9CD2DF-338A-5F77-B9C2-4346854E55AF") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "C8B122B0-C58A-5F38-84F7-4D5CDCE21CAA") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "59176B9E-7ADC-5CC3-BC48-033A4D5DF013") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "8BC00B22-EEF0-57A3-85D1-4ED9B3B28B8C") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "6E712F40-0361-53E0-9045-AD363F9C4333") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "55AC4EE7-4E09-5257-9847-7F2DBE57C45B") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "430C2AE3-8037-5CD7-A6C6-66A4E8AFC2AF") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "8C31F75B-F02A-555C-9235-21C2CF0BA6FF") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "360A5E98-3C9D-5D1E-ACC9-65B7CE3CE70D") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "4BC8DE4D-B04D-5D09-B6C3-93E69D774A34") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "95990338-4A46-5875-AA9F-127F3F78B97E") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "5079A2EE-3786-5E24-8253-3A045B856228") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "C9C5363D-6470-5A55-8A58-6B3ADA8F61AC") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "A0D07537-22DC-5DC1-A1FE-35F892AA7A9A") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "A8FE82DA-8CE2-5266-970D-9D593A59606C") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "05C970D7-9C81-5CE7-87C3-27008C8862D1") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "3F32768E-31FF-5E45-9DF4-61F4961144E0") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "927713A3-5D2C-562E-B288-B78ABDFAC527") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "7FBF943F-6E9D-5B07-A2DA-B8A68FB256F7") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "19D04CF8-537E-562C-8ED5-F9568C444F33") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "2EAB5AE8-B9E2-5F3D-B66B-D45C929B4BBC") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "4A489F5C-1E9F-5441-B49B-8018442346CC") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "B8DA5FD0-F067-57D0-A3CB-7BC7168BFDAB") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "BF6C0099-7DBA-5198-88BF-B2A8E540A3CE") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "49F92AD5-E5BD-5DE2-B1C2-167774882BC0") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "2862D585-D6E6-5994-98C7-736E5A0B8D77") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "BA036CCB-4051-5ED7-9602-76134092DAA5") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "AB6C1DE0-5BD7-53E3-B507-2F19972F8CA7") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "3962BFC1-ADF7-57EF-AA6F-ECD29F1BE434") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "93515E40-497D-583D-A4EC-2BE08089533A") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "194AD9B4-04CF-51A3-8D2D-3AEAED62B84D") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "C19D95E9-08C0-5E8D-93C3-FEE78F781408") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "462415ED-8D55-5894-AEE0-A3F38A985FB1") ("[\\]^_`abcdefghijklmnopqrst" #uuid "F0EF3EAA-3B12-5AE7-87E1-F2BA988B59CF") ("\\]^_`abcdefghijklmnopqrstu" #uuid "859212C2-6DE4-5593-8986-5163D3D7851D") ("]^_`abcdefghijklmnopqrstuv" #uuid "BFCFB5EC-0CE3-58FC-9BB3-1B3EB3B4FD98") ("^_`abcdefghijklmnopqrstuvw" #uuid "01724DA7-DCAE-569B-8D14-D81F9A37295E") ("_`abcdefghijklmnopqrstuvwx" #uuid "52443FBF-2C97-5DB2-BF13-AE9526B617D1") ("`abcdefghijklmnopqrstuvwxy" #uuid "FF0EC0B6-CECB-517F-87A0-194759C2DFC4") ("abcdefghijklmnopqrstuvwxyz" #uuid "E1CDA567-F00A-578F-B4EC-F248BD2743B2") ("bcdefghijklmnopqrstuvwxyz{" #uuid "1B9A7485-0653-5110-8557-7EB635C0FD62") ("cdefghijklmnopqrstuvwxyz{|" #uuid "EFFF805A-741C-5B3E-A5BC-5E0FCBBAC60A") ("defghijklmnopqrstuvwxyz{|}" #uuid "A1F0CDFB-6C7F-5F8F-BBFC-8ACAB78F287F") ("efghijklmnopqrstuvwxyz{|}~" #uuid "2153125C-E5C1-5805-A4A5-51923A40D57B") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "AE92DD4A-6B17-591C-A404-33548F8E0D08") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "7D9703EA-F876-51AF-869A-ED3485EF40FA") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "00AA4363-CDAA-5A3F-8F00-C685EDFE1455") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "19E4C188-E0B5-5770-B878-86227B76B26E") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "E539E727-3605-5DEC-96CF-2F4FC633F57D") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "EA16CBB8-B495-5113-8DC1-11ACBA333C2C") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "A2A8234B-874E-5C87-846A-0558FCF8C898") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "E9B76262-6370-5C36-BD09-627F0EC78E7E") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "6505BF7B-C096-5DDE-83D1-68BE3D450CC8") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "2DAD12B6-17EA-5556-AEDF-B34FC0418BBD") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "797F9A21-4F7C-556C-8BB8-95337F4F47EF") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "D3674C30-0DA6-5F4D-9F36-C19C07E5B707") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "1F7BB665-4D61-59CF-8D4D-F3E203C99464") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "C3DC70CC-6B5E-53FE-B0D6-54131AD96063") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "DE2F3C19-9075-5873-873C-94965089CFA7") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "1308A9AE-0A07-5DBD-8F78-EB5C6C1BB7DE") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "90A57D98-EB6A-52E9-92AA-6F1377741632") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "A9216C3B-DFDD-561C-96B0-60EC57DF2176") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "7DCDFFF0-A0E0-5CE5-989D-61A73D67C4E8") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "7EA67B17-A917-5E1B-8807-818DC9B4A9E6") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "E7BC2E14-9029-5BBF-8367-10437778EE67") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "2EB1112A-6E8D-55A7-B712-93B109E53CAF") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "5783E9F5-4723-503F-8800-9BF782817DC4") ("}~ !\"#$%&'()*+,-./01234567" #uuid "CA765530-6B64-5791-AFD7-8138A5D1AB00") ("~ !\"#$%&'()*+,-./012345678" #uuid "59B8A01C-BE09-50A6-802F-C93B1D6A5C8E") )) (deftest check-v5-dns-ns-cases (testing "v5 dns-ns case-based correctness..." (doseq [case +v5-dns-ns-cases+] (is (= (second case) (v5 +namespace-dns+ (first case))))))) (def +v5-oid-ns-cases+ '((" !\"#$%&'()*+,-./0123456789" #uuid "71D9EA92-356D-5940-B0A9-951BD32DDBAE") ("!\"#$%&'()*+,-./0123456789:" #uuid "F3DDBCEB-D3AB-5B85-83E4-8B89B504FBED") ("\"#$%&'()*+,-./0123456789:;" #uuid "C0C2E013-BE36-558E-9E9B-6D19BDEC4147") ("#$%&'()*+,-./0123456789:;<" #uuid "8AE9B168-73AC-5788-AA22-7002D8D39921") ("$%&'()*+,-./0123456789:;<=" #uuid "28A1EA4F-2FEE-5A36-9460-6DAEDB3FF36B") ("%&'()*+,-./0123456789:;<=>" #uuid "C26A66CB-E2C0-59AE-A65A-A027DA13677D") ("&'()*+,-./0123456789:;<=>?" #uuid "AE2B437A-0DA2-5988-883B-BEF432C63A43") ("'()*+,-./0123456789:;<=>?@" #uuid "49AF28B5-F7F7-5720-840D-94A052236422") ("()*+,-./0123456789:;<=>?@A" #uuid "390456C5-FB1B-52CB-B922-D9EE75F94F80") (")*+,-./0123456789:;<=>?@AB" #uuid "104297AF-C263-5E38-8792-C2CE39168482") ("*+,-./0123456789:;<=>?@ABC" #uuid "E6AA9B91-409E-58E1-8BFC-B3A9907F2749") ("+,-./0123456789:;<=>?@ABCD" #uuid "B4CEB85C-FEB9-564A-AE1D-90273F89CC2B") (",-./0123456789:;<=>?@ABCDE" #uuid "F245626A-71F8-5F73-A3B7-5DC122E0A768") ("-./0123456789:;<=>?@ABCDEF" #uuid "85B90BEE-EE31-53BD-804B-61014E55DDE8") ("./0123456789:;<=>?@ABCDEFG" #uuid "51A2EE70-605E-540D-819C-8F0231764259") ("/0123456789:;<=>?@ABCDEFGH" #uuid "3A31EB3A-DE8C-520D-ADC9-2048C2B9123F") ("0123456789:;<=>?@ABCDEFGHI" #uuid "4EB6C362-7709-5F58-AF4B-69BFB0D4D3E6") ("123456789:;<=>?@ABCDEFGHIJ" #uuid "8EA7FFEC-7357-5073-9E90-9B6A6091BE38") ("23456789:;<=>?@ABCDEFGHIJK" #uuid "4FEF89F0-0921-5118-A456-EFA5EB9FCFFB") ("3456789:;<=>?@ABCDEFGHIJKL" #uuid "73C117FF-9E97-5950-8334-6A4777365851") ("456789:;<=>?@ABCDEFGHIJKLM" #uuid "F3391045-7646-5944-9710-467598E6E3B4") ("56789:;<=>?@ABCDEFGHIJKLMN" #uuid "0CE3D8DB-7C5E-5CA8-85B1-54C4C4B6D755") ("6789:;<=>?@ABCDEFGHIJKLMNO" #uuid "8E34E08C-256B-5F08-B7DF-710AF259AE72") ("789:;<=>?@ABCDEFGHIJKLMNOP" #uuid "DC721C82-30DC-51B8-890B-BA46CEFDAF22") ("89:;<=>?@ABCDEFGHIJKLMNOPQ" #uuid "A303B3EC-12F5-5E20-B9BD-3AEA01124BB4") ("9:;<=>?@ABCDEFGHIJKLMNOPQR" #uuid "75FC219A-ABE7-56C2-AD7A-9E91BB0E0504") (":;<=>?@ABCDEFGHIJKLMNOPQRS" #uuid "F6183650-657C-5BB0-94CE-F9348D8EF981") (";<=>?@ABCDEFGHIJKLMNOPQRST" #uuid "7B0B3E18-8370-55FD-AC8D-189697A6D008") ("<=>?@ABCDEFGHIJKLMNOPQRSTU" #uuid "524D3B31-14EC-5906-BB3C-339F40F4B5C6") ("=>?@ABCDEFGHIJKLMNOPQRSTUV" #uuid "9B3073B9-2786-566D-892F-8B5D0BB0BE2C") (">?@ABCDEFGHIJKLMNOPQRSTUVW" #uuid "CBE55728-B4B1-5C5A-8A51-395B5236B73E") ("?@ABCDEFGHIJKLMNOPQRSTUVWX" #uuid "474BDBD0-EEA8-57D8-A0BC-85AC730D2E83") ("@ABCDEFGHIJKLMNOPQRSTUVWXY" #uuid "F031E945-A302-50ED-BEA1-625535DF0708") ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" #uuid "D4D4B4F7-CA21-5A38-9A1A-B5952F780839") ("BCDEFGHIJKLMNOPQRSTUVWXYZ[" #uuid "698B1CC6-4490-5AA6-8685-C4CA9A70B99C") ("CDEFGHIJKLMNOPQRSTUVWXYZ[\\" #uuid "4F5CB0D5-5171-5AF3-86DB-C149DDA86CA0") ("DEFGHIJKLMNOPQRSTUVWXYZ[\\]" #uuid "B1AEA275-F005-5B74-9545-5AD3AE481DA8") ("EFGHIJKLMNOPQRSTUVWXYZ[\\]^" #uuid "3D891BBB-55FE-53EA-AA37-381DBE4B5B8F") ("FGHIJKLMNOPQRSTUVWXYZ[\\]^_" #uuid "DD1DE872-0AE7-5A76-80A8-983D3B8B002D") ("GHIJKLMNOPQRSTUVWXYZ[\\]^_`" #uuid "EE601373-012B-5CDD-9985-E1C1955AEBB1") ("HIJKLMNOPQRSTUVWXYZ[\\]^_`a" #uuid "BE7276B7-7371-5208-AC0D-397C0D89DD84") ("IJKLMNOPQRSTUVWXYZ[\\]^_`ab" #uuid "9D584707-CE26-5AEF-9CCC-E462EA568EE3") ("JKLMNOPQRSTUVWXYZ[\\]^_`abc" #uuid "7B925C14-44FD-553A-A75F-B97369B97AF4") ("KLMNOPQRSTUVWXYZ[\\]^_`abcd" #uuid "C445B204-6E1B-5D47-915F-8651014BD350") ("LMNOPQRSTUVWXYZ[\\]^_`abcde" #uuid "DD7C53C8-D447-58DB-85F9-F8C37165F1B6") ("MNOPQRSTUVWXYZ[\\]^_`abcdef" #uuid "B565C336-E751-5DCB-ABDD-D44C4D3E5576") ("NOPQRSTUVWXYZ[\\]^_`abcdefg" #uuid "D4657BED-4DB4-579C-AF46-91442BEBCD03") ("OPQRSTUVWXYZ[\\]^_`abcdefgh" #uuid "ECD09D93-66BB-590A-81A0-CE998E5A1C4E") ("PQRSTUVWXYZ[\\]^_`abcdefghi" #uuid "665BBED6-C93C-5706-9A53-97346A5A675C") ("QRSTUVWXYZ[\\]^_`abcdefghij" #uuid "9AB63221-3159-59F3-8B50-BCAD5FC4AD22") ("RSTUVWXYZ[\\]^_`abcdefghijk" #uuid "41ACDB12-B0CE-5ED6-966C-35C85612106C") ("STUVWXYZ[\\]^_`abcdefghijkl" #uuid "6F22A8AB-E584-59CC-9FFA-B662E614B29F") ("TUVWXYZ[\\]^_`abcdefghijklm" #uuid "BAC2AFC2-362E-5339-B1EF-D19915309B3E") ("UVWXYZ[\\]^_`abcdefghijklmn" #uuid "1C3CD630-FD8B-5D26-95C8-8C84B2314C63") ("VWXYZ[\\]^_`abcdefghijklmno" #uuid "2453EBB1-5045-5C90-88B6-7B49AFD689F8") ("WXYZ[\\]^_`abcdefghijklmnop" #uuid "DA7E4BB1-8A1B-5E4C-B394-AE369DCABCAF") ("XYZ[\\]^_`abcdefghijklmnopq" #uuid "A2398C09-E200-502F-B0A3-EA9FA95D13E6") ("YZ[\\]^_`abcdefghijklmnopqr" #uuid "9E505244-5804-5938-88C8-8FAF526F39BF") ("Z[\\]^_`abcdefghijklmnopqrs" #uuid "D53E3FA0-EB27-56E9-9FAD-6D62EFEB433C") ("[\\]^_`abcdefghijklmnopqrst" #uuid "E9BFA441-02D4-54B0-AC02-810B5EFD6D6C") ("\\]^_`abcdefghijklmnopqrstu" #uuid "F31AF79E-228C-5194-B845-511FC0E806EA") ("]^_`abcdefghijklmnopqrstuv" #uuid "45FF0AFD-5D35-5C0D-A7E7-51BD79186BA8") ("^_`abcdefghijklmnopqrstuvw" #uuid "D0D588D4-7C0D-54B4-8286-7C7B15D62013") ("_`abcdefghijklmnopqrstuvwx" #uuid "D9593422-69B8-590C-B207-F35C0974D60B") ("`abcdefghijklmnopqrstuvwxy" #uuid "294448B1-C82C-5FF0-BB48-E97D4F9C88AF") ("abcdefghijklmnopqrstuvwxyz" #uuid "6C7D893E-A11C-5274-B839-CA68CD47E627") ("bcdefghijklmnopqrstuvwxyz{" #uuid "23F2DC57-D808-5892-90FD-C7453C8AF511") ("cdefghijklmnopqrstuvwxyz{|" #uuid "43C0C792-0CDC-5D2E-A332-21243A3BF277") ("defghijklmnopqrstuvwxyz{|}" #uuid "2CBF26D6-D0B0-5CE8-8CB8-D2E41C45A693") ("efghijklmnopqrstuvwxyz{|}~" #uuid "5A43F434-1389-567B-8CF2-C6DC2CE12584") ("fghijklmnopqrstuvwxyz{|}~ " #uuid "53014805-B9A6-5AEE-BCE7-8B625B191B21") ("ghijklmnopqrstuvwxyz{|}~ !" #uuid "671E3E93-1193-527E-9036-BE959A40D657") ("hijklmnopqrstuvwxyz{|}~ !\"" #uuid "F89DAAC8-2905-5ED9-A4B1-A32FD588C9CC") ("ijklmnopqrstuvwxyz{|}~ !\"#" #uuid "0F0BE36D-B240-5915-8A63-826AE4CC1C60") ("jklmnopqrstuvwxyz{|}~ !\"#$" #uuid "F8C0C1FE-D6D3-531E-B822-DD94B0F3B3D5") ("klmnopqrstuvwxyz{|}~ !\"#$%" #uuid "24581D7C-0AE4-5BE4-9013-71CBC582E25D") ("lmnopqrstuvwxyz{|}~ !\"#$%&" #uuid "DBE82A12-620C-548C-8D24-C432995DD0D6") ("mnopqrstuvwxyz{|}~ !\"#$%&'" #uuid "83480C50-F2EE-5CF1-943E-2C5C4753D39F") ("nopqrstuvwxyz{|}~ !\"#$%&'(" #uuid "61E3F2AE-7EA0-5D75-90BF-C3FB14C5B764") ("opqrstuvwxyz{|}~ !\"#$%&'()" #uuid "95E8012A-8401-5AEE-8C55-C038233AA12D") ("pqrstuvwxyz{|}~ !\"#$%&'()*" #uuid "0A76BB26-CB8F-5B74-A25C-7B14AA1231E3") ("qrstuvwxyz{|}~ !\"#$%&'()*+" #uuid "D7C6DBD5-F976-5535-B447-E9887E9305B4") ("rstuvwxyz{|}~ !\"#$%&'()*+," #uuid "CFF90D62-C9AE-5D27-B71D-C26937F45B86") ("stuvwxyz{|}~ !\"#$%&'()*+,-" #uuid "FA856CEE-2A0A-5FEF-A58E-E19998333A73") ("tuvwxyz{|}~ !\"#$%&'()*+,-." #uuid "EA8CC78B-3FED-5926-94CB-C07978EE18DD") ("uvwxyz{|}~ !\"#$%&'()*+,-./" #uuid "8DD82AD7-11EC-520E-A620-42D7F71CDAE2") ("vwxyz{|}~ !\"#$%&'()*+,-./0" #uuid "5835D993-AA8B-57A5-8128-84C57D100CB5") ("wxyz{|}~ !\"#$%&'()*+,-./01" #uuid "B66C29F2-F34A-519F-B459-3EBF5301679F") ("xyz{|}~ !\"#$%&'()*+,-./012" #uuid "9573FC13-FAAD-5BBE-8F3F-801A9B713DB2") ("yz{|}~ !\"#$%&'()*+,-./0123" #uuid "5190E6CA-05CD-5970-86FA-9571C2CBC21D") ("z{|}~ !\"#$%&'()*+,-./01234" #uuid "75A67394-441A-5BC5-AEC9-ADD6DC77DDBA") ("{|}~ !\"#$%&'()*+,-./012345" #uuid "A2F570E1-A297-5F7E-BD15-AC9C76C8B07D") ("|}~ !\"#$%&'()*+,-./0123456" #uuid "46610A2C-7628-5F1B-8C5A-155639D29120") ("}~ !\"#$%&'()*+,-./01234567" #uuid "2127C846-ACF4-5418-8454-F8B7D309E338") ("~ !\"#$%&'()*+,-./012345678" #uuid "9D321B3F-E7E8-53A3-9EA5-466DF23617A9") )) (deftest check-v5-oid-ns-cases (testing "v5 oid-ns case-based correctness..." (doseq [case +v5-oid-ns-cases+] (is (= (second case) (v5 +namespace-oid+ (first case))))))) ================================================ FILE: test/clj_uuid/v6_test.clj ================================================ (ns clj-uuid.v6-test "Time based UUIDs tests" (:require [clojure.test :refer :all] [clojure.set] [clj-uuid :as uuid :refer [v6 get-timestamp]] [clj-uuid.clock :as clock])) (deftest check-v6-single-threaded (let [iterations 1000000 groups 10] (testing "single-thread v6 uuid uniqueness..." (dotimes [_ groups] (let [result (repeatedly iterations v6)] (is (= (count result) (count (set result))))))))) (deftest check-v6-concurrency (doseq [concur (range 2 9)] (let [extent 1000000 agents (map agent (repeat concur nil)) working (map #(send-off % (fn [state] (repeatedly extent v6))) agents) _ (apply await working) answers (map deref working)] (testing (str "concurrent v6 uuid uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map set answers)))))) (testing (str "concurrent v6 monotonic increasing (" concur " threads)...") (is (every? identity (map (partial apply uuid/<) answers))))))) (deftest check-get-timestamp (testing "timestamp round-trip through v6 UUID" (dotimes [_ 100000] (let [before (clock/monotonic-time) u (v6) after (clock/monotonic-time)] (is (<= before (uuid/get-timestamp u) after)))))) ================================================ FILE: test/clj_uuid/v7_test.clj ================================================ (ns clj-uuid.v7-test (:require [clojure.test :refer :all] [clojure.set] [clj-uuid :as uuid :refer [v7]] [clj-uuid.clock :as clock]) (:import [clj_uuid.clock State])) (deftest check-v7-single-threaded (let [iterations 1000000 groups 10] (testing "single-thread v7 uuid uniqueness..." (dotimes [_ groups] (let [result (repeatedly iterations v7)] (is (= (count result) (count (set result))))))))) (deftest check-v7-concurrency (doseq [concur (range 2 9)] (let [extent 1000000 agents (map agent (repeat concur nil)) working (map #(send-off % (fn [state] (repeatedly extent v7))) agents) _ (apply await working) answers (map deref working)] (testing (str "concurrent v7 uuid uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map set answers)))))) (testing (str "concurrent v7 monotonic increasing (" concur " threads)...") (is (every? identity (map (partial apply uuid/<) answers))))))) (deftest check-get-timestamp (dotimes [_ 1000000] (let [time (.millis ^State (clock/monotonic-unix-time-and-random-counter))] (with-redefs [clock/monotonic-unix-time-and-random-counter (fn [] (clock/->State (rand-int 4095) time))] (is (= time (uuid/get-timestamp (v7))) "Timestamp should be retrievable from v7 UUID"))))) ================================================ FILE: test/clj_uuid/v7nc_test.clj ================================================ (ns clj-uuid.v7nc-test (:require [clojure.test :refer :all] [clojure.set] [clj-uuid :as uuid :refer [v7nc]])) (deftest check-v7nc-version (testing "v7nc produces version 7 UUIDs" (dotimes [_ 100] (is (= 7 (uuid/get-version (v7nc))))))) (deftest check-v7nc-variant (testing "v7nc produces variant 2 (RFC 4122) UUIDs" (dotimes [_ 100] (is (= 2 (.variant (v7nc))))))) (deftest check-v7nc-single-threaded (let [iterations 1000000 groups 10] (testing "single-thread v7nc uuid uniqueness..." (dotimes [_ groups] (let [result (repeatedly iterations v7nc)] (is (= (count result) (count (set result))))))))) (deftest check-v7nc-concurrency (doseq [concur (range 2 9)] (let [extent 1000000 agents (map agent (repeat concur nil)) working (map #(send-off % (fn [state] (repeatedly extent v7nc))) agents) _ (apply await working) answers (map deref working)] (testing (str "concurrent v7nc uuid uniqueness (" concur " threads)...") (is (= (* concur extent) (count (apply clojure.set/union (map set answers)))))) (testing (str "concurrent v7nc monotonic increasing (" concur " threads)...") (is (every? identity (map (partial apply uuid/<) answers))))))) (deftest check-v7nc-timestamp (testing "v7nc timestamp round-trip" (dotimes [_ 100000] (let [before (System/currentTimeMillis) u (v7nc) after (System/currentTimeMillis)] (is (<= before (uuid/get-unix-time u) after)))))) ================================================ FILE: test/clj_uuid/v8_test.clj ================================================ (ns clj-uuid.v8-test "Custom UUIDs tests" (:refer-clojure :exclude [uuid? max]) (:require [clojure.test :refer :all] [clj-uuid :refer :all :exclude [> < =]])) (deftest check-v8-special-cases (testing "v8 custom UUID" (is (= (v8 0 0) #uuid "00000000-0000-8000-8000-000000000000")) (is (= (v8 0 1) #uuid "00000000-0000-8000-8000-000000000001")) (is (= (v8 0 -1) #uuid "00000000-0000-8000-bfff-ffffffffffff")) (is (= (v8 -1 0) #uuid "ffffffff-ffff-8fff-8000-000000000000")) (is (= (v8 -1 -1) #uuid "ffffffff-ffff-8fff-bfff-ffffffffffff"))))