{"id":24503975,"url":"https://github.com/gershnik/modern-uuid","last_synced_at":"2025-07-22T04:04:39.375Z","repository":{"id":273467232,"uuid":"918609104","full_name":"gershnik/modern-uuid","owner":"gershnik","description":"A modern, no-dependencies, portable C++ library for manipulating UUIDs. Fully supports RFC 9562 and RFC 4122.","archived":false,"fork":false,"pushed_at":"2025-06-27T16:23:58.000Z","size":168,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-01T13:55:20.214Z","etag":null,"topics":["c-plus-plus-20","constexpr","cplusplus","cplusplus-20","cpp","cpp-library","cpp20","no-dependencies","rfc-4122","rfc-9562","uuid"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gershnik.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-18T11:42:03.000Z","updated_at":"2025-06-27T16:24:02.000Z","dependencies_parsed_at":"2025-01-21T05:19:42.113Z","dependency_job_id":"649791e3-e5cd-48fa-b2d6-103895d60b8b","html_url":"https://github.com/gershnik/modern-uuid","commit_stats":null,"previous_names":["gershnik/modern-uuid"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/gershnik/modern-uuid","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gershnik%2Fmodern-uuid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gershnik%2Fmodern-uuid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gershnik%2Fmodern-uuid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gershnik%2Fmodern-uuid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gershnik","download_url":"https://codeload.github.com/gershnik/modern-uuid/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gershnik%2Fmodern-uuid/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266424024,"owners_count":23926124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-plus-plus-20","constexpr","cplusplus","cplusplus-20","cpp","cpp-library","cpp20","no-dependencies","rfc-4122","rfc-9562","uuid"],"created_at":"2025-01-21T23:19:19.712Z","updated_at":"2025-07-22T04:04:39.340Z","avatar_url":"https://github.com/gershnik.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"## modern-uuid\n\n[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/)\n[![Standard](https://img.shields.io/badge/C%2B%2B-20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)\n[![License](https://img.shields.io/badge/license-BSD-brightgreen.svg)](https://opensource.org/licenses/BSD-3-Clause)\n[![Tests](https://github.com/gershnik/modern-uuid/actions/workflows/test.yml/badge.svg)](https://github.com/gershnik/modern-uuid/actions/workflows/test.yml)\n\nA modern, no-dependencies, portable C++ library for manipulating UUIDs.\n\n## Features\n\n* Implements newer [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562.html) (which supersedes older [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122.html)). Supports generation of UUID variants 1, 3, 5, 6 and 7.\n* Self-contained with no dependencies beyond C++ standard library.\n* Works on Mac, Linux, Windows, BSD, Wasm, and even Illumos. Might even work on some embedded systems given a suitable compiler and standard library support.\n* Requires C++20 but does not require a very recent compiler (GCC is supported from version 10 and clang from version 13).\n* Most operations (with an obvious exception of UUID generation and iostream I/O) are `constexpr` and can be done at compile time. Notably this enables:\n  * Natural syntax for compile-time UUID literals\n  * Using UUIDs as template parameters and in other compile-time contexts\n* Supports `std::format` (if available) for formatting and parsing in addition to iostreams.\n* Does not rely on C++ exceptions and can be used with C++ exceptions disabled.\n* Uses \"safe\" constructs only in public interface (no raw pointers and such).\n* Properly handles `fork` with no `exec` on Unix systems. UUIDs generated by the child process will not collide with parent's.\n\nSee also [differences from other libraries](#differences-from-other-libraries) below.\n\n## Usage\n\nA quick intro to the library is given below. For more details see [Usage Guide](/doc/Usage.md)\n\n```cpp\n#include \u003cmodern-uuid/uuid.h\u003e\n\nusing namespace uuid;\n\n//this is a compile time UUID literal\nconstexpr uuid u1(\"e53d37db-e4e0-484f-996f-3ab1d4701abc\");\n\n//default constructor creates Nil UUID 00000000-0000-0000-0000-000000000000\nconstexpr uuid nil_uuid;\n\n//there is also uuid::max() to get Max UUID: FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF\nconstexpr uuid max_nil_uuid = uuid::max();\n\n//if you want to you can use uuid as a template parameter\ntemplate\u003cuuid U1\u003e class some_class {...};\nsome_class\u003cuuid(\"bc961bfb-b006-42f4-93ae-206f02658810\")\u003e some_object;\n\n//you can generate all non-proprietary versions of UUID from RFC 9562:\nuuid u_v1 = uuid::generate_time_based();\nuuid u_v3 = uuid::generate_md5(uuid::namespaces::dns, \"www.widgets.com\");\nuuid u_v4 = uuid::generate_random();\nuuid u_v5 = uuid::generate_sha1(uuid::namespaces::dns, \"www.widgets.com\");\nuuid u_v6 = uuid::generate_reordered_time_based();\nuuid u_v7 = uuid::generate_unix_time_based();\n\n//for non-literal strings you can parse uuids from strings using uuid::from_chars\n//the argument to from_chars can be anything convertible to std::span\u003cchar, any extent\u003e\n//the call is constexpr\nstd::string some_uuid_str = \"7D444840-9DC0-11D1-B245-5FFDCE74FAD2\";\nstd::optional\u003cuuid\u003e maybe_uuid = uuid::from_chars(some_uuid_str);\nif (maybe_uuid) {\n    uuid parsed = *maybe_uuid;\n}\n\n//uuid objects can be compared in every possible way\nassert(u_v1 \u003e mil_uuid);\nassert(u_v1 != u_v2);\nstd::strong_ordering res = (u_v6 \u003c=\u003e u_v7);\n//etc.\n\n//uuid objects can be hashed\nstd::unordered_map\u003cuuid, transaction\u003e transaction_map;\n\n//they can be formatted. u and l stand for uppercase and lowercase\n\nstd::string str = std::format(\"{}\", u1);\nassert(str == \"e53d37db-e4e0-484f-996f-3ab1d4701abc\");\n\nstr = std::format(\"{:u}\", u1);\nassert(str == \"E53D37DB-E4E0-484F-996F-3AB1D4701ABC\")\n\nstr = std::format(\"{:l}\", u1);\nassert(str == \"e53d37db-e4e0-484f-996f-3ab1d4701abc\")\n\n//uuids can be read/written from/to iostream \n\n//when reading case doesn't matter\nstd::istringstream istr(\"bc961bfb-b006-42f4-93ae-206f02658810\");\nuuid uuidr;\nistr \u003e\u003e uuidr;\nassert(uuidr = uuid(\"bc961bfb-b006-42f4-93ae-206f02658810\"));\n\nstd::ostringstream ostr;\nostr \u003c\u003c uuid(\"bc961bfb-b006-42f4-93ae-206f02658810\");\nassert(ostr.str() == \"bc961bfb-b006-42f4-93ae-206f02658810\");\nostr.str(\"\");\n\n//writing respects std::ios_base::uppercase stream flag\nostr \u003c\u003c std::uppercase \u003c\u003c uuid(\"7d444840-9dc0-11d1-b245-5ffdce74fad2\");\nassert(ostr.str() == \"7D444840-9DC0-11D1-B245-5FFDCE74FAD2\");\n\n//uuid objects can be created from raw bytes\n//you need an std::span\u003canything byte-like, 16\u003e or anything convertible to \n//such a span\nstd::array\u003cstd::byte, 16\u003e arr1 = {...};\nuuid u_from_std_array(arr1);\n\nuint8_t arr2[16] = {...};\nuuid u_from_c_array(arr2);\n\nstd::vector\u003cuint8_t\u003e vec = {...};\nuuid u_from_bytes(std::span{vec}.subspan\u003c3, 19\u003e());\n\n//finally you can access raw uuid bytes via bytes public member\nconstexpr uuid ua(\"7d444840-9dc0-11d1-b245-5ffdce74fad2\");\nassert(ua.bytes[3] == 0x48);\n\n//bytes is an std::array\u003cuint8_t, 16\u003e so you can use all std::array\n//functionality\nfor(auto b: ua.bytes) {\n    ...use the byte...\n}\n```\n\n## Building/Integrating\n\nQuickest CMake method is given below. For more details and other method see [Integration Guide](doc/Building.md)\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(modern-uuid\n    GIT_REPOSITORY git@github.com:gershnik/modern-uuid.git\n    GIT_TAG        \u003cdesired tag like v1.2 or a sha\u003e\n    GIT_SHALLOW    TRUE\n)\nFetchContent_MakeAvailable(modern-uuid)\n...\ntarget_link_libraries(mytarget\nPRIVATE\n  modern-uuid::modern-uuid\n)\n```\n\n## Differences from other libraries\n\nThere are two well-known libraries commonly used to handle UUIDs: `libuuid` from [util-linux](https://github.com/util-linux/util-linux) and\n[Boost.Uuid](http://boost.org/libs/uuid). Both are very good libraries, but have, \u003cins\u003e**at the time of this writing**\u003c/ins\u003e (03-2025), limitations \nand/or trade-offs that I found inconvenient or annoying and which `modern-uuid` was created to address. In particular:\n\n### libuuid\n\nPortability. `libuuid` is only really portable to Linux and BSD. It doesn't work on Windows. It hasn't even been [working on Mac](https://github.com/util-linux/util-linux/issues/2873) for almost a year now without patches. That's two major platforms out there. \n\nSecond, while its time-based generation algorithms are very robust and correct they assume that the system clock ticks once a microsecond.\nThis assumption is not necessarily true. For example on WASM the clock ticks with a millisecond precision. This results in UUID generation \nbeing very slow on that platform and the results very predictable.\n\nLastly, `libuuid` is a C library. It doesn't really help C++ code to actually manipulate UUIDs as first class objects. For that one \nneeds to either write a custom UUID class or use a different library.\n\n### Boost.Uuid\n\nBoost.Uuid makes two design trade-offs that might not be the right ones for many users\n\nIt prioritizes speed above RFC compliance and, in some cases, correctness. For example it allows the sequential UUID counters to wrap around without\nwaiting for the clock to change. This makes UUID v7 (and possibly v6 too) non always monotonically increasing on platforms with a slower clock \n(e.g. WASM again). The ways it populates v7 fields also contradicts RFC recommendations. Whether this results in some increased guessability or not is hard to tell. On the flip side this results in much faster UUID generation so YMMV.\n\nIt pushes management of UUID generator objects to the library user. Unfortunately, managing the generators is not a trivial task for most of them. You need to be aware of various intricacies that a casual user without deep understanding of how UUIDs work will likely miss. For example, you need to be aware that you must reset any inherited parent process generator in a forked child process or risk duplicate UUIDs being generated.\n\nThis approach makes the library simpler and synthetic benchmarks faster. But it also makes it easy for the user to misuse the library and the speed gains would be negated by external generator management anyway.\n\nBoost.Uuid is a header only library, which is great, but to actually use it you still need to get the entire 130+MB Boost-zilla download. If your\nproject already contains Boost this is not a problem. But, for things that don't, it is a big annoyance.\n\n`modern-uuid` tries to address these perceived shortcomings:\n\n- It strives to be widely portable to any reasonable system out there.\n- It does not require you to know how to manage generators. Just call generate_xxx and it will do the right thing.\n- It handles slow clocks correctly (hopefully)\n- It is standalone with no dependencies. \n\nOn the negative side:\n- It is slower than Boost.Uuid for UUID generation (but is faster than `libuuid`!).\n- It currently is not header-only. (This might, or might not, be addressed in future releases)\n\n\n## Implementation details\n\nThere are many implementation choices for generating time-based UUIDs of versions 1, 6 and 7. This section documents some of them but these are\nnot contractual and can change in future releases\n\nFor UUID version 7 the `rand_a` field is used to store additional clock precision using Method 3 of the [section 6.2](https://www.rfc-editor.org/rfc/rfc9562.html#name-monotonicity-and-counters) of the RFC. This extends the precision of distinct representable times to 1µs. The first 14 bit of `rand_b` field are filled with a randomly seeded counter using Method 1 of the same section.\n\nFor UUID version 6 the `node` field is populated with a random value on each generation. The `clock_seq` field is filled with a randomly seeded counter using Method 1 of the [section 6.2](https://www.rfc-editor.org/rfc/rfc9562.html#name-monotonicity-and-counters) of the RFC. \n\nThe actual granularity of the system clock is detected at runtime (unfortunately you cannot trust `time_point::period` on many systems). If the clock ticks slower than the maximum available precision for the desired UUID version then the unfilled rightmost decimal digits of the timestamp are filled by incrementing a counter for each generation. The counter is in the range `[0, max-1)` where `max` is the ratio of clock tick period to the desired precision (e.g. if the clock period is 1ms and desired precision is 1µs then `max` is 1,000). \nWhen the counter reaches `max`:\n- for version 1 the generation waits until the clock changes\n- for versions 6 and 7 the `clock_seq`/`rand_b` field is used to provide further monotonicity as described above. When the these counters are exhausted the generation also waits for a clock change.\n\nIf the system clock goes backwards:\n- for version 1 the `clock_seq` is incremented by 1 modulo 2\u003csup\u003e14\u003c/sup\u003e\n- for versions 6 and 7 the monotonicity has been lost - there is nothing that can be done about it - so the `clock_seq`/first 14 bits of `rand_b` are initialized to a random number.\n\nOn Unix-like systems upon `fork()` without `exec()` in the child process all the \"static\" state\nfor all generators is reinitialized anew as-if on a new process start. Specifically this affects:\n- random number generator(s)\n- all `clock_seq`/first 14 bits of `rand_b` fields\n\nIn a multithreaded environment generation of UUIDs is completely independent on different threads.\nThat is, different threads behave similar to how different processes would with regards to the UUIDs they generate. If full monotonicity for UUID versions 6 or 7 across different threads is desired the generation and clock usage can be made synchronous by providing custom `clock_persistence` callback implementation. \n\nBy default, if available, one of the system's network cards MAC addresses is used for version 1 UUIDs. If not available it is replaced by a random number (initialized once per process) as described in RFC 9562. You can change this behavior via `set_node_id` APIs. Alternatively, you can simply use UUID versions 6, 7 or 4.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgershnik%2Fmodern-uuid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgershnik%2Fmodern-uuid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgershnik%2Fmodern-uuid/lists"}