RFC 9562 UUID implementation in Zig sites.wisp.place/jcollie.dev/uuid.zig/
zig uuid
1

Configure Feed

Select the types of activity you want to include in your feed.

update licensing, add tangled workflow

Jeffrey C. Ollie (May 19, 2026, 10:20 PM -0500) a934d8d3 cacf7485

+374 -356
-6
.github/dependabot.yaml
··· 1 - version: 2 2 - updates: 3 - - package-ecosystem: "github-actions" 4 - directory: "/" 5 - schedule: 6 - interval: "daily"
-61
.github/workflows/test.yaml
··· 1 - on: 2 - push: {} 3 - pull_request: {} 4 - workflow_dispatch: {} 5 - 6 - jobs: 7 - pinact: 8 - runs-on: ubuntu-latest 9 - name: Check Github Actions Pins 10 - steps: 11 - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 12 - - uses: cachix/install-nix-action@c134e4c9e34bac6cab09cf239815f9339aaaf84e # v31.5.1 13 - with: 14 - nix_path: nixpkgs=channel:nixos-unstable 15 - - uses: cachix/cachix-action@18cf96c7c98e048e10a83abd92116114cd8504be # v14 16 - with: 17 - name: jcollie 18 - signingKey: "${{ secrets.CACHIX_SIGNING_KEY }}" 19 - authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 20 - - name: pinact check 21 - run: nix develop -c pinact run --check 22 - 23 - zig-fmt: 24 - runs-on: ubuntu-latest 25 - name: Check Zig formatting 26 - needs: 27 - - pinact 28 - steps: 29 - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 30 - - uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5 31 - with: 32 - version: master 33 - - run: zig fmt --check . 34 - 35 - test_0_14: 36 - runs-on: ubuntu-latest 37 - name: Test with Zig 0.14 38 - needs: 39 - - pinact 40 - - zig-fmt 41 - 42 - steps: 43 - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 44 - - uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5 45 - with: 46 - version: 0.14.1 47 - - run: zig build test 48 - 49 - test_0_15: 50 - runs-on: ubuntu-latest 51 - name: Test with Zig 0.15 52 - needs: 53 - - pinact 54 - - zig-fmt 55 - 56 - steps: 57 - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 58 - - uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5 59 - with: 60 - version: master 61 - - run: zig build test
+6 -2
.gitignore
··· 1 - /.zig-cache 2 - /zig-out 1 + # SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + # SPDX-License-Identifier: MIT 3 3 4 + .zig-cache/ 5 + zig-out/ 6 + zig-pkg/ 7 + result*
+44
.tangled/workflows/ci.yml
··· 1 + # SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + # SPDX-License-Identifier: MIT 3 + 4 + when: 5 + - event: 6 + - push 7 + branch: 8 + - main 9 + - event: 10 + - manual 11 + 12 + engine: nixery 13 + 14 + dependencies: 15 + nixpkgs: 16 + - curl 17 + - glibc 18 + 19 + github:NixOS/nixpkgs/nixpkgs-unstable: 20 + - zig_0_16 21 + 22 + environment: 23 + TERM: dumb 24 + NO_COLOR: "true" 25 + 26 + steps: 27 + - name: Check formatting 28 + command: zig fmt --check . 29 + 30 + - name: Run tests 31 + command: zig build test 32 + 33 + - name: Build API docs 34 + command: zig build docs 35 + 36 + - name: Deploy API docs to Wisp 37 + command: | 38 + curl -s -o ./wispcli https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-x86_64-linux 39 + chmod a+x ./wispcli 40 + ./wispcli \ 41 + "$WISP_HANDLE" \ 42 + --path ./zig-out/docs \ 43 + --site "$WISP_SITE" \ 44 + --password "$WISP_APP_PASSWORD"
-21
LICENSE
··· 1 - MIT License 2 - 3 - Copyright (c) 2025 Jeffrey C. Ollie 4 - 5 - Permission is hereby granted, free of charge, to any person obtaining a copy 6 - of this software and associated documentation files (the "Software"), to deal 7 - in the Software without restriction, including without limitation the rights 8 - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 - copies of the Software, and to permit persons to whom the Software is 10 - furnished to do so, subject to the following conditions: 11 - 12 - The above copyright notice and this permission notice shall be included in all 13 - copies or substantial portions of the Software. 14 - 15 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 - SOFTWARE.
+121
LICENSES/CC0-1.0.txt
··· 1 + Creative Commons Legal Code 2 + 3 + CC0 1.0 Universal 4 + 5 + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 + HEREUNDER. 13 + 14 + Statement of Purpose 15 + 16 + The laws of most jurisdictions throughout the world automatically confer 17 + exclusive Copyright and Related Rights (defined below) upon the creator 18 + and subsequent owner(s) (each and all, an "owner") of an original work of 19 + authorship and/or a database (each, a "Work"). 20 + 21 + Certain owners wish to permanently relinquish those rights to a Work for 22 + the purpose of contributing to a commons of creative, cultural and 23 + scientific works ("Commons") that the public can reliably and without fear 24 + of later claims of infringement build upon, modify, incorporate in other 25 + works, reuse and redistribute as freely as possible in any form whatsoever 26 + and for any purposes, including without limitation commercial purposes. 27 + These owners may contribute to the Commons to promote the ideal of a free 28 + culture and the further production of creative, cultural and scientific 29 + works, or to gain reputation or greater distribution for their Work in 30 + part through the use and efforts of others. 31 + 32 + For these and/or other purposes and motivations, and without any 33 + expectation of additional consideration or compensation, the person 34 + associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 + is an owner of Copyright and Related Rights in the Work, voluntarily 36 + elects to apply CC0 to the Work and publicly distribute the Work under its 37 + terms, with knowledge of his or her Copyright and Related Rights in the 38 + Work and the meaning and intended legal effect of CC0 on those rights. 39 + 40 + 1. Copyright and Related Rights. A Work made available under CC0 may be 41 + protected by copyright and related or neighboring rights ("Copyright and 42 + Related Rights"). Copyright and Related Rights include, but are not 43 + limited to, the following: 44 + 45 + i. the right to reproduce, adapt, distribute, perform, display, 46 + communicate, and translate a Work; 47 + ii. moral rights retained by the original author(s) and/or performer(s); 48 + iii. publicity and privacy rights pertaining to a person's image or 49 + likeness depicted in a Work; 50 + iv. rights protecting against unfair competition in regards to a Work, 51 + subject to the limitations in paragraph 4(a), below; 52 + v. rights protecting the extraction, dissemination, use and reuse of data 53 + in a Work; 54 + vi. database rights (such as those arising under Directive 96/9/EC of the 55 + European Parliament and of the Council of 11 March 1996 on the legal 56 + protection of databases, and under any national implementation 57 + thereof, including any amended or successor version of such 58 + directive); and 59 + vii. other similar, equivalent or corresponding rights throughout the 60 + world based on applicable law or treaty, and any national 61 + implementations thereof. 62 + 63 + 2. Waiver. To the greatest extent permitted by, but not in contravention 64 + of, applicable law, Affirmer hereby overtly, fully, permanently, 65 + irrevocably and unconditionally waives, abandons, and surrenders all of 66 + Affirmer's Copyright and Related Rights and associated claims and causes 67 + of action, whether now known or unknown (including existing as well as 68 + future claims and causes of action), in the Work (i) in all territories 69 + worldwide, (ii) for the maximum duration provided by applicable law or 70 + treaty (including future time extensions), (iii) in any current or future 71 + medium and for any number of copies, and (iv) for any purpose whatsoever, 72 + including without limitation commercial, advertising or promotional 73 + purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 + member of the public at large and to the detriment of Affirmer's heirs and 75 + successors, fully intending that such Waiver shall not be subject to 76 + revocation, rescission, cancellation, termination, or any other legal or 77 + equitable action to disrupt the quiet enjoyment of the Work by the public 78 + as contemplated by Affirmer's express Statement of Purpose. 79 + 80 + 3. Public License Fallback. Should any part of the Waiver for any reason 81 + be judged legally invalid or ineffective under applicable law, then the 82 + Waiver shall be preserved to the maximum extent permitted taking into 83 + account Affirmer's express Statement of Purpose. In addition, to the 84 + extent the Waiver is so judged Affirmer hereby grants to each affected 85 + person a royalty-free, non transferable, non sublicensable, non exclusive, 86 + irrevocable and unconditional license to exercise Affirmer's Copyright and 87 + Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 + maximum duration provided by applicable law or treaty (including future 89 + time extensions), (iii) in any current or future medium and for any number 90 + of copies, and (iv) for any purpose whatsoever, including without 91 + limitation commercial, advertising or promotional purposes (the 92 + "License"). The License shall be deemed effective as of the date CC0 was 93 + applied by Affirmer to the Work. Should any part of the License for any 94 + reason be judged legally invalid or ineffective under applicable law, such 95 + partial invalidity or ineffectiveness shall not invalidate the remainder 96 + of the License, and in such case Affirmer hereby affirms that he or she 97 + will not (i) exercise any of his or her remaining Copyright and Related 98 + Rights in the Work or (ii) assert any associated claims and causes of 99 + action with respect to the Work, in either case contrary to Affirmer's 100 + express Statement of Purpose. 101 + 102 + 4. Limitations and Disclaimers. 103 + 104 + a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 + surrendered, licensed or otherwise affected by this document. 106 + b. Affirmer offers the Work as-is and makes no representations or 107 + warranties of any kind concerning the Work, express, implied, 108 + statutory or otherwise, including without limitation warranties of 109 + title, merchantability, fitness for a particular purpose, non 110 + infringement, or the absence of latent or other defects, accuracy, or 111 + the present or absence of errors, whether or not discoverable, all to 112 + the greatest extent permissible under applicable law. 113 + c. Affirmer disclaims responsibility for clearing rights of other persons 114 + that may apply to the Work or any use thereof, including without 115 + limitation any person's Copyright and Related Rights in the Work. 116 + Further, Affirmer disclaims responsibility for obtaining any necessary 117 + consents, permissions or other rights required for any use of the 118 + Work. 119 + d. Affirmer understands and acknowledges that Creative Commons is not a 120 + party to this document and has no duty or obligation with respect to 121 + this CC0 or use of the Work.
+18
LICENSES/MIT.txt
··· 1 + MIT License 2 + 3 + Copyright (c) <year> <copyright holders> 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 6 + associated documentation files (the "Software"), to deal in the Software without restriction, including 7 + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 + copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 9 + following conditions: 10 + 11 + The above copyright notice and this permission notice shall be included in all copies or substantial 12 + portions of the Software. 13 + 14 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 15 + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 16 + EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 18 + USE OR OTHER DEALINGS IN THE SOFTWARE.
+9
REUSE.toml
··· 1 + # SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie 2 + # SPDX-License-Identifier: GPL-3.0-or-later 3 + 4 + version = 1 5 + 6 + [[annotations]] 7 + path = "flake.lock" 8 + SPDX-FileCopyrightText = "NONE" 9 + SPDX-License-Identifier = "CC0-1.0"
+17
build.zig
··· 1 + // SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + // SPDX-License-Identifier: MIT 3 + 1 4 const std = @import("std"); 2 5 3 6 pub fn build(b: *std.Build) void { ··· 18 21 19 22 const test_step = b.step("test", "Run tests"); 20 23 test_step.dependOn(&run_mod_tests.step); 24 + 25 + const lib = b.addLibrary(.{ 26 + .name = "uuid", 27 + .root_module = mod, 28 + }); 29 + 30 + const install_docs = b.addInstallDirectory(.{ 31 + .source_dir = lib.getEmittedDocs(), 32 + .install_dir = .prefix, 33 + .install_subdir = "docs", 34 + }); 35 + 36 + const docs_step = b.step("docs", "Build the API docs"); 37 + docs_step.dependOn(&install_docs.step); 21 38 }
+8 -72
build.zig.zon
··· 1 + // SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + // SPDX-License-Identifier: MIT 3 + 1 4 .{ 2 - // This is the default name used by packages depending on this one. For 3 - // example, when a user runs `zig fetch --save <url>`, this field is used 4 - // as the key in the `dependencies` table. Although the user can choose a 5 - // different name, most users will stick with this provided value. 6 - // 7 - // It is redundant to include "zig" in this name because it is already 8 - // within the Zig package namespace. 9 - .name = .zig_uuid, 10 - // This is a [Semantic Version](https://semver.org/). 11 - // In a future version of Zig it will be used for package deduplication. 5 + .name = .uuid, 12 6 .version = "0.0.0", 13 - // Together with name, this represents a globally unique package 14 - // identifier. This field is generated by the Zig toolchain when the 15 - // package is first created, and then *never changes*. This allows 16 - // unambiguous detection of one package being an updated version of 17 - // another. 18 - // 19 - // When forking a Zig project, this id should be regenerated (delete the 20 - // field and run `zig build`) if the upstream project is still maintained. 21 - // Otherwise, the fork is *hostile*, attempting to take control over the 22 - // original project's identity. Thus it is recommended to leave the comment 23 - // on the following line intact, so that it shows up in code reviews that 24 - // modify the field. 25 - .fingerprint = 0x1915b3454b88a934, // Changing this has security and trust implications. 26 - // Tracks the earliest Zig version that the package considers to be a 27 - // supported use case. 28 - .minimum_zig_version = "0.15.0-dev.1474+3ea015db9", 29 - // This field is optional. 30 - // Each dependency must either provide a `url` and `hash`, or a `path`. 31 - // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. 32 - // Once all dependencies are fetched, `zig build` no longer requires 33 - // internet connectivity. 34 - .dependencies = .{ 35 - // See `zig fetch --save <url>` for a command-line interface for adding dependencies. 36 - //.example = .{ 37 - // // When updating this field to a new URL, be sure to delete the corresponding 38 - // // `hash`, otherwise you are communicating that you expect to find the old hash at 39 - // // the new URL. If the contents of a URL change this will result in a hash mismatch 40 - // // which will prevent zig from using it. 41 - // .url = "https://example.com/foo.tar.gz", 42 - // 43 - // // This is computed from the file contents of the directory of files that is 44 - // // obtained after fetching `url` and applying the inclusion rules given by 45 - // // `paths`. 46 - // // 47 - // // This field is the source of truth; packages do not come from a `url`; they 48 - // // come from a `hash`. `url` is just one of many possible mirrors for how to 49 - // // obtain a package matching this `hash`. 50 - // // 51 - // // Uses the [multihash](https://multiformats.io/multihash/) format. 52 - // .hash = "...", 53 - // 54 - // // When this is provided, the package is found in a directory relative to the 55 - // // build root. In this case the package's hash is irrelevant and therefore not 56 - // // computed. This field and `url` are mutually exclusive. 57 - // .path = "foo", 58 - // 59 - // // When this is set to `true`, a package is declared to be lazily 60 - // // fetched. This makes the dependency only get fetched if it is 61 - // // actually used. 62 - // .lazy = false, 63 - //}, 64 - }, 65 - // Specifies the set of files and directories that are included in this package. 66 - // Only files and directories listed here are included in the `hash` that 67 - // is computed for this package. Only files listed here will remain on disk 68 - // when using the zig package manager. As a rule of thumb, one should list 69 - // files required for compilation plus any license(s). 70 - // Paths are relative to the build root. Use the empty string (`""`) to refer to 71 - // the build root itself. 72 - // A directory listed here means that all files within, recursively, are included. 7 + .fingerprint = 0xd17f50a64fdbdc53, // Changing this has security and trust implications. 8 + .minimum_zig_version = "0.16.0", 9 + .dependencies = .{}, 73 10 .paths = .{ 74 11 "build.zig", 75 12 "build.zig.zon", 76 13 "src", 77 - // For example... 78 - //"LICENSE", 14 + "LICENSES", 79 15 //"README.md", 80 16 }, 81 17 }
+4 -82
flake.lock
··· 1 1 { 2 2 "nodes": { 3 - "flake-compat": { 4 - "flake": false, 5 - "locked": { 6 - "lastModified": 1747046372, 7 - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", 8 - "owner": "edolstra", 9 - "repo": "flake-compat", 10 - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", 11 - "type": "github" 12 - }, 13 - "original": { 14 - "owner": "edolstra", 15 - "repo": "flake-compat", 16 - "type": "github" 17 - } 18 - }, 19 - "flake-utils": { 20 - "inputs": { 21 - "systems": "systems" 22 - }, 23 - "locked": { 24 - "lastModified": 1731533236, 25 - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 26 - "owner": "numtide", 27 - "repo": "flake-utils", 28 - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 29 - "type": "github" 30 - }, 31 - "original": { 32 - "owner": "numtide", 33 - "repo": "flake-utils", 34 - "type": "github" 35 - } 36 - }, 37 3 "nixpkgs": { 38 4 "locked": { 39 - "lastModified": 1754725699, 40 - "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", 5 + "lastModified": 1778869304, 6 + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", 41 7 "owner": "NixOS", 42 8 "repo": "nixpkgs", 43 - "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", 9 + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", 44 10 "type": "github" 45 11 }, 46 12 "original": { ··· 51 17 }, 52 18 "root": { 53 19 "inputs": { 54 - "flake-compat": "flake-compat", 55 - "flake-utils": "flake-utils", 56 - "nixpkgs": "nixpkgs", 57 - "zig": "zig" 58 - } 59 - }, 60 - "systems": { 61 - "locked": { 62 - "lastModified": 1681028828, 63 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 64 - "owner": "nix-systems", 65 - "repo": "default", 66 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 67 - "type": "github" 68 - }, 69 - "original": { 70 - "owner": "nix-systems", 71 - "repo": "default", 72 - "type": "github" 73 - } 74 - }, 75 - "zig": { 76 - "inputs": { 77 - "flake-compat": [ 78 - "flake-compat" 79 - ], 80 - "flake-utils": [ 81 - "flake-utils" 82 - ], 83 - "nixpkgs": [ 84 - "nixpkgs" 85 - ] 86 - }, 87 - "locked": { 88 - "lastModified": 1754914485, 89 - "narHash": "sha256-/oGhrnR5ZOYGpwsUOLcnIu4z4hkh6x/NkB+PSRMjnuQ=", 90 - "owner": "mitchellh", 91 - "repo": "zig-overlay", 92 - "rev": "ffcc8c880dffb4a8198f2a2b07d0d4d65ba93f53", 93 - "type": "github" 94 - }, 95 - "original": { 96 - "owner": "mitchellh", 97 - "repo": "zig-overlay", 98 - "type": "github" 20 + "nixpkgs": "nixpkgs" 99 21 } 100 22 } 101 23 },
+31 -45
flake.nix
··· 1 + # SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + # SPDX-License-Identifier: MIT 3 + 1 4 { 2 5 description = "zig-uuid"; 3 6 ··· 5 8 nixpkgs = { 6 9 url = "nixpkgs/nixos-unstable"; 7 10 }; 8 - flake-utils = { 9 - url = "github:numtide/flake-utils"; 10 - }; 11 - flake-compat = { 12 - url = "github:edolstra/flake-compat"; 13 - flake = false; 14 - }; 15 - zig = { 16 - url = "github:mitchellh/zig-overlay"; 17 - inputs = { 18 - nixpkgs.follows = "nixpkgs"; 19 - flake-utils.follows = "flake-utils"; 20 - flake-compat.follows = "flake-compat"; 21 - }; 22 - }; 23 11 }; 24 12 25 - outputs = { 26 - self, 27 - nixpkgs, 28 - flake-utils, 29 - zig, 30 - ... 31 - }: let 32 - in 33 - flake-utils.lib.eachDefaultSystem ( 34 - system: let 35 - pkgs = import nixpkgs { 13 + outputs = 14 + { 15 + nixpkgs, 16 + ... 17 + }: 18 + let 19 + lib = nixpkgs.lib; 20 + platforms = [ 21 + "aarch64-darwin" 22 + "aarch64-linux" 23 + "x86_64-linux" 24 + ]; 25 + makePackages = 26 + system: 27 + import nixpkgs { 36 28 inherit system; 29 + overlays = [ ]; 37 30 }; 38 - in { 39 - devShells = { 40 - zig_0_14 = pkgs.mkShell { 41 - name = "zig-uuid-0.14"; 42 - nativeBuildInputs = [ 43 - pkgs.zig_0_14 44 - pkgs.pinact 45 - ]; 46 - }; 47 - zig_0_15 = pkgs.mkShell { 48 - name = "zig-uuid-0.15"; 49 - nativeBuildInputs = [ 50 - zig.packages.${system}.master 51 - pkgs.pinact 52 - ]; 53 - }; 54 - default = self.devShells.${system}.zig_0_15; 31 + forAllSystems = (function: lib.genAttrs platforms (system: function (makePackages system))); 32 + in 33 + { 34 + devShells = forAllSystems (pkgs: { 35 + default = pkgs.mkShell { 36 + nativeBuildInputs = [ 37 + pkgs.zig_0_16 38 + pkgs.pinact 39 + pkgs.reuse 40 + ]; 55 41 }; 56 - } 57 - ); 42 + }); 43 + }; 58 44 }
+116 -67
src/root.zig
··· 1 - //! © 2025 Jeffrey C. Ollie 2 - //! 1 + // SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie 2 + // SPDX-License-Identifier: MIT 3 + 3 4 const std = @import("std"); 4 5 5 6 const assert = std.debug.assert; ··· 92 93 const uuid_epoch_offset = 0x01b21dd213814000; 93 94 94 95 /// Return the current UUID epoch timestamp. 95 - pub fn now() v1v6Time { 96 - return .{ .raw = @intCast(@divFloor(std.time.nanoTimestamp(), 100) + uuid_epoch_offset) }; 96 + pub fn now(io: std.Io) v1v6Time { 97 + const ts: std.Io.Timestamp = .now(io, .real); 98 + return .{ .raw = @intCast(@divFloor(ts.toNanoseconds(), 100) + uuid_epoch_offset) }; 97 99 } 98 100 99 101 /// Return the UUID epoch timestamp that corresponds to the given Unix ··· 126 128 /// The time in 100-nanosecond intervals since the UUID epoch, which 127 129 /// is 1582-10-15 00:00:00. If this is left null the current time 128 130 /// will be used. 129 - time: ?v1v6Time = null, 130 - clock_seq: ?u14 = null, 131 - node: ?u48 = null, 131 + time: v1v6Time, 132 + clock_seq: union(enum) { 133 + raw: u14, 134 + random: std.Random, 135 + }, 136 + node: union(enum) { 137 + raw: u48, 138 + random: std.Random, 139 + }, 132 140 }, 133 141 v2, 134 142 v3: struct { ··· 144 152 return @bitCast(std.mem.readInt(u128, self.hash, .big)); 145 153 } 146 154 }, 147 - v4, 155 + v4: struct { 156 + rng: std.Random, 157 + }, 148 158 v5: struct { 149 159 hash: *const [std.crypto.hash.Sha1.digest_length]u8, 150 160 ··· 161 171 }, 162 172 v6: struct { 163 173 /// The time in 100-nanosecond intervals since the UUID epoch, which 164 - /// is 1582-10-15 00:00:00. If this is left null the current time 165 - /// will be used. 166 - time: ?v1v6Time = null, 167 - clock_seq: ?u14 = null, 168 - node: ?u48 = null, 174 + /// is 1582-10-15 00:00:00. 175 + time: v1v6Time, 176 + clock_seq: union(enum) { 177 + raw: u14, 178 + random: std.Random, 179 + }, 180 + node: union(enum) { 181 + raw: u48, 182 + random: std.Random, 183 + }, 169 184 }, 170 185 v7: struct { 171 - unix_ts_ms: ?u48 = null, 186 + unix_ts_ms: union(enum) { 187 + raw: u48, 188 + timestamp: std.Io.Timestamp, 189 + }, 190 + rng: std.Random, 172 191 }, 173 192 v8: packed union { 174 193 custom: u122, 175 - part: packed struct(u122) { 194 + parts: packed struct(u122) { 176 195 a: u48, 177 196 b: u12, 178 197 c: u62, ··· 297 316 pub fn new(options: NewOptions) UUID { 298 317 switch (options) { 299 318 .v1 => |v| { 300 - const time = v.time orelse v1v6Time.now(); 301 - const clock_seq = v.clock_seq orelse std.crypto.random.int(u14); 302 - const node = v.node orelse (std.crypto.random.int(u48) | 0x1000_0000_0000); 303 - 319 + const time = v.time; 320 + const clock_seq = switch (v.clock_seq) { 321 + .raw => |raw| raw, 322 + .random => |rng| rng.int(u14), 323 + }; 324 + const node = switch (v.node) { 325 + .raw => |raw| raw, 326 + .random => |rng| rng.int(u48) | 0x1000_0000_0000, 327 + }; 304 328 return .{ 305 329 .v1 = .{ 306 330 .time_low = time.v1.low, ··· 325 349 }; 326 350 }, 327 351 328 - .v4 => { 329 - var id: UUID = .{ .id = std.crypto.random.int(u128) }; 352 + .v4 => |v| { 353 + var id: UUID = .{ .id = v.rng.int(u128) }; 330 354 id.v4.version = .v4; 331 355 id.v4.variant = 0b10; 332 356 return id; ··· 344 368 }, 345 369 346 370 .v6 => |v| { 347 - const time = v.time orelse v1v6Time.now(); 348 - const clock_seq = v.clock_seq orelse std.crypto.random.int(u14); 371 + const time = v.time; 372 + const clock_seq = switch (v.clock_seq) { 373 + .raw => |raw| raw, 374 + .random => |rng| rng.int(u14), 375 + }; 349 376 // if we didn't get a node, create a random one and set the multicast bit 350 - const node = v.node orelse (std.crypto.random.int(u48) | 0x100000000000); 351 - 377 + const node = switch (v.node) { 378 + .raw => |raw| raw, 379 + .random => |rng| rng.int(u48) | 0x100000000000, 380 + }; 352 381 return .{ 353 382 .v6 = .{ 354 383 .time_low = time.v6.low, ··· 361 390 }, 362 391 363 392 .v7 => |v| { 364 - var id: UUID = .{ .id = std.crypto.random.int(u128) }; 365 - id.v7.unix_ts_ms = v.unix_ts_ms orelse @intCast(std.time.milliTimestamp()); 393 + var id: UUID = .{ .id = v.rng.int(u128) }; 394 + id.v7.unix_ts_ms = switch (v.unix_ts_ms) { 395 + .raw => |raw| raw, 396 + .timestamp => |ts| @intCast(ts.toMilliseconds()), 397 + }; 366 398 id.v7.version = .v7; 367 399 id.v7.variant = 0b10; 368 400 return id; ··· 371 403 .v8 => |v| { 372 404 return .{ 373 405 .v8 = .{ 374 - .custom_a = v.part.a, 375 - .custom_b = v.part.b, 376 - .custom_c = v.part.c, 406 + .custom_a = v.parts.a, 407 + .custom_b = v.parts.b, 408 + .custom_c = v.parts.c, 377 409 }, 378 410 }; 379 411 }, ··· 396 428 return buf; 397 429 } 398 430 399 - pub fn serializeZ(self: UUID) [36:0]u8 { 400 - var buf: [36:0]u8 = undefined; 431 + pub fn serializeSentinel(self: UUID, comptime sentinel: u8) [36:sentinel]u8 { 432 + var buf: [36:sentinel]u8 = undefined; 401 433 _ = std.fmt.bufPrint( 402 434 &buf, 403 435 "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>4}-{x:0>12}", ··· 409 441 self.serializable.e, 410 442 }, 411 443 ) catch unreachable; 412 - buf[buf.len] = 0; 444 + buf[buf.len] = sentinel; 413 445 return buf; 414 446 } 415 447 ··· 444 476 return buf; 445 477 } 446 478 447 - pub fn serializeUrnZ(self: UUID) [45:0]u8 { 448 - var buf: [45:0]u8 = undefined; 479 + pub fn serializeUrnSentinel(self: UUID, comptime sentinel: u8) [45:sentinel]u8 { 480 + var buf: [45:sentinel]u8 = undefined; 449 481 @memcpy(buf[0..9], "urn:uuid:"); 450 482 _ = std.fmt.bufPrint( 451 483 buf[9..], ··· 458 490 self.serializable.e, 459 491 }, 460 492 ) catch unreachable; 461 - buf[buf.len] = 0; 493 + buf[buf.len] = sentinel; 462 494 return buf; 463 495 } 464 496 ··· 470 502 }; 471 503 472 504 test "uuid test 1" { 473 - const id1: UUID = .new(.v4); 505 + const rng_impl: std.Random.IoSource = .{ .io = std.testing.io }; 506 + const rng = rng_impl.interface(); 507 + const id1: UUID = .new(.{ .v4 = .{ .rng = rng } }); 474 508 const str = id1.serialize(); 475 509 const id2 = try UUID.deserialize(&str); 476 510 try std.testing.expectEqual(id2.id, id1.id); 477 511 } 478 512 479 513 test "uuid test 2" { 480 - const id1: UUID = .new(.{ .v7 = .{} }); 514 + const rng_impl: std.Random.IoSource = .{ .io = std.testing.io }; 515 + const rng = rng_impl.interface(); 516 + const id1: UUID = .new( 517 + .{ 518 + .v7 = .{ 519 + .unix_ts_ms = .{ 520 + .timestamp = .now(std.testing.io, .real), 521 + }, 522 + .rng = rng, 523 + }, 524 + }, 525 + ); 481 526 const str = id1.serialize(); 482 527 const id2 = try UUID.deserialize(&str); 483 528 try std.testing.expectEqual(id2.id, id1.id); ··· 488 533 // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv1-value 489 534 const id: UUID = .{ 490 535 .v1 = .{ 491 - .time_low = 0xC232AB00, 536 + .time_low = 0xc232ab00, 492 537 .time_mid = 0x9414, 493 - .time_high = 0x1EC, 494 - .clock_seq = 0x33C8, 495 - .node = 0x9F6BDECED846, 538 + .time_high = 0x1ec, 539 + .clock_seq = 0x33c8, 540 + .node = 0x9f6bdeced846, 496 541 }, 497 542 }; 498 543 ··· 501 546 try std.testing.expectEqualStrings("c232ab00-9414-11ec-b3c8-9f6bdeced846", &str); 502 547 } 503 548 { 504 - const str = id.serializeZ(); 549 + const str = id.serializeSentinel(0); 505 550 try std.testing.expectEqualSentinel(u8, 0, "c232ab00-9414-11ec-b3c8-9f6bdeced846", &str); 506 551 } 507 552 } ··· 512 557 .{ 513 558 .v1 = .{ 514 559 .time = .{ 515 - .raw = 0x1EC9414C232AB00, 560 + .raw = 0x1ec9414c232ab00, 561 + }, 562 + .clock_seq = .{ 563 + .raw = 0x33C8, 564 + }, 565 + .node = .{ 566 + .raw = 0x9f6bdeced846, 516 567 }, 517 - .clock_seq = 0x33C8, 518 - .node = 0x9F6BDECED846, 519 568 }, 520 569 }, 521 570 ); ··· 528 577 try std.testing.expectEqualStrings("c232ab00-9414-11ec-b3c8-9f6bdeced846", &str); 529 578 } 530 579 { 531 - const str = id.serializeZ(); 580 + const str = id.serializeSentinel(0); 532 581 try std.testing.expectEqualSentinel(u8, 0, "c232ab00-9414-11ec-b3c8-9f6bdeced846", &str); 533 582 } 534 583 } ··· 552 601 try std.testing.expectEqualStrings("5df41881-3aed-3515-88a7-2f4a814cf09e", &str); 553 602 } 554 603 { 555 - const str = id.serializeZ(); 604 + const str = id.serializeSentinel(0); 556 605 try std.testing.expectEqualSentinel(u8, 0, "5df41881-3aed-3515-88a7-2f4a814cf09e", &str); 557 606 } 558 607 } ··· 574 623 try std.testing.expectEqualStrings("5df41881-3aed-3515-88a7-2f4a814cf09e", &str); 575 624 } 576 625 { 577 - const str = id.serializeZ(); 626 + const str = id.serializeSentinel(0); 578 627 try std.testing.expectEqualSentinel(u8, 0, "5df41881-3aed-3515-88a7-2f4a814cf09e", &str); 579 628 } 580 629 } ··· 597 646 try std.testing.expectEqualStrings("919108f7-52d1-4320-9bac-f847db4148a8", &str); 598 647 } 599 648 { 600 - const str = id.serializeZ(); 649 + const str = id.serializeSentinel(0); 601 650 try std.testing.expectEqualSentinel(u8, 0, "919108f7-52d1-4320-9bac-f847db4148a8", &str); 602 651 } 603 652 } ··· 620 669 try std.testing.expectEqualStrings("2ed6657d-e927-568b-95e1-2665a8aea6a2", &str); 621 670 } 622 671 { 623 - const str = id.serializeZ(); 672 + const str = id.serializeSentinel(0); 624 673 try std.testing.expectEqualSentinel(u8, 0, "2ed6657d-e927-568b-95e1-2665a8aea6a2", &str); 625 674 } 626 675 } ··· 643 692 try std.testing.expectEqualStrings("2ed6657d-e927-568b-95e1-2665a8aea6a2", &str); 644 693 } 645 694 { 646 - const str = id.serializeZ(); 695 + const str = id.serializeSentinel(0); 647 696 try std.testing.expectEqualSentinel(u8, 0, "2ed6657d-e927-568b-95e1-2665a8aea6a2", &str); 648 697 } 649 698 } ··· 652 701 // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv6-value 653 702 const id: UUID = .{ 654 703 .v6 = .{ 655 - .time_high = 0x1EC9414C, 656 - .time_mid = 0x232A, 657 - .time_low = 0xB00, 658 - .clock_seq = 0x33C8, 659 - .node = 0x9F6BDECED846, 704 + .time_high = 0x1ec9414c, 705 + .time_mid = 0x232a, 706 + .time_low = 0xb00, 707 + .clock_seq = 0x33c8, 708 + .node = 0x9f6bdeced846, 660 709 }, 661 710 }; 662 711 ··· 668 717 try std.testing.expectEqualStrings("1ec9414c-232a-6b00-b3c8-9f6bdeced846", &str); 669 718 } 670 719 { 671 - const str = id.serializeZ(); 720 + const str = id.serializeSentinel(0); 672 721 try std.testing.expectEqualSentinel(u8, 0, "1ec9414c-232a-6b00-b3c8-9f6bdeced846", &str); 673 722 } 674 723 } ··· 677 726 // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv7-value 678 727 const id: UUID = .{ 679 728 .v7 = .{ 680 - .unix_ts_ms = 0x017F22E279B0, 681 - .rand_a = 0xCC3, 682 - .rand_b = 0x18C4DC0C0C07398F, 729 + .unix_ts_ms = 0x017f22e279b0, 730 + .rand_a = 0xcc3, 731 + .rand_b = 0x18c4dc0c0c07398f, 683 732 }, 684 733 }; 685 734 ··· 691 740 try std.testing.expectEqualStrings("017f22e2-79b0-7cc3-98c4-dc0c0c07398f", &str); 692 741 } 693 742 { 694 - const str = id.serializeZ(); 743 + const str = id.serializeSentinel(0); 695 744 try std.testing.expectEqualSentinel(u8, 0, "017f22e2-79b0-7cc3-98c4-dc0c0c07398f", &str); 696 745 } 697 746 } ··· 700 749 // https://www.rfc-editor.org/rfc/rfc9562.html#name-example-of-a-uuidv8-value-t 701 750 const id: UUID = .{ 702 751 .v8 = .{ 703 - .custom_a = 0x2489E9AD2EE2, 704 - .custom_b = 0xE00, 705 - .custom_c = 0x0EC932D5F69181C0, 752 + .custom_a = 0x2489e9ad2ee2, 753 + .custom_b = 0xe00, 754 + .custom_c = 0x0ec932d5f69181c0, 706 755 }, 707 756 }; 708 757 ··· 714 763 try std.testing.expectEqualStrings("2489e9ad-2ee2-8e00-8ec9-32d5f69181c0", &str); 715 764 } 716 765 { 717 - const str = id.serializeZ(); 766 + const str = id.serializeSentinel(0); 718 767 try std.testing.expectEqualSentinel(u8, 0, "2489e9ad-2ee2-8e00-8ec9-32d5f69181c0", &str); 719 768 } 720 769 } ··· 738 787 try std.testing.expectEqualStrings("5c146b14-3c52-8afd-938a-375d0df1fbf6", &str); 739 788 } 740 789 { 741 - const str = id.serializeZ(); 790 + const str = id.serializeSentinel(0); 742 791 try std.testing.expectEqualSentinel(u8, 0, "5c146b14-3c52-8afd-938a-375d0df1fbf6", &str); 743 792 } 744 793 }