# modern-uuid

![Language][badge-language]
![Standard][badge-cpp-standard]
[![License][badge-license]][license]
[![Tests][badge-tests]][tests]

A modern, no-dependencies, portable C++ library for manipulating [UUIDs][wiki-uuid], 
[ULIDs][ulid-spec], [NanoIDs][nanoid] and [Cuid2s][cuid2].

<!-- TOC depthfrom:2 -->

- [Features](#features)
- [Usage](#usage)
    - [UUID](#uuid)
    - [ULID](#ulid)
    - [NanoID](#nanoid)
    - [Cuid2](#cuid2)
- [Building/Integrating](#buildingintegrating)

<!-- /TOC -->

## Features

* UUID: Implements the newer [RFC 9562][rfc9562] (which supersedes the older [RFC 4122][rfc4122]). 
  Supports generation of UUID variants 1, 3, 5, 6 and 7.

* ULID: Implements the canonical [spec][ulid-spec].

* NanoID: Since there is no formal spec, this library implements an external textual format identical 
  to the JavaScript library and a generation algorithm fully equivalent to it. It also supports custom 
  alphabets and sizes with the same semantics. The implementation is done from first principles - 
  it is not a port of the JavaScript library. 
  * In particular, NanoID objects are internally stored and manipulated in a packed binary format rather 
    than strings, resulting in much better memory efficiency.

* Self-contained with no dependencies beyond the C++ standard library.

* Works on Mac, Linux, Windows, BSD, Wasm, Illumos and even Haiku OS. Might even work on some embedded 
  systems given a suitable compiler and standard library support.

* Requires C++20 but does not require a very recent compiler (GCC is supported from version 10 and 
  Clang from version 13).

* Most operations (with the obvious exceptions of XXID generation and iostream I/O) are `constexpr` 
  and can be done at compile time. Notably, this enables:
  * Natural syntax for compile-time XXID literals.
  * Using XXIDs as template parameters and in other compile-time contexts.

* Supports `std::format` (if available) for formatting and parsing in addition to iostreams.

* Does not rely on C++ exceptions and can be used with C++ exceptions disabled.

* Uses only "safe" constructs in the public interface (no raw pointers and such).

* Properly handles `fork` with no `exec` on Unix systems. XXIDs generated by the child process will not 
  collide with the parent's.

See also [differences from other UUID libraries](/doc/other-uuid-libraries.md).

## Usage

A quick intro to the library is given below. For more details, see:

* [UUID Usage Guide](/doc/uuid-usage.md)
* [ULID Usage Guide](/doc/ulid-usage.md)
* [NanoID Usage Guide](/doc/nanoid-usage.md)
* [CUID2 Usage Guide](/doc/cuid2-usage.md)

### UUID

```cpp
#include <modern-uuid/uuid.h>

using namespace muuid;

//this is a compile-time UUID literal
constexpr uuid u1("e53d37db-e4e0-484f-996f-3ab1d4701abc");

//if you want to, you can use uuid as a template parameter
template<uuid U> class some_class {...};
some_class<uuid("bc961bfb-b006-42f4-93ae-206f02658810")> some_object;

//you can generate all non-proprietary versions of UUID from RFC 9562:
uuid u_v1 = uuid::generate_time_based();
uuid u_v3 = uuid::generate_md5(uuid::namespaces::dns, "www.widgets.com");
uuid u_v4 = uuid::generate_random();
uuid u_v5 = uuid::generate_sha1(uuid::namespaces::dns, "www.widgets.com");
uuid u_v6 = uuid::generate_reordered_time_based();
uuid u_v7 = uuid::generate_unix_time_based();

//for non-literal strings, you can parse uuids from strings using uuid::from_chars
//the argument to from_chars can be anything convertible to std::span<char>
//the call is constexpr
std::string some_uuid_str = "7D444840-9DC0-11D1-B245-5FFDCE74FAD2";
std::optional<uuid> maybe_uuid = uuid::from_chars(some_uuid_str);
if (maybe_uuid) {
    uuid parsed = *maybe_uuid;
}

//uuid objects can be compared in every possible way
assert(u_v1 > uuid());
assert(u_v1 != u_v3);
std::strong_ordering res = (u_v6 <=> u_v7);
//etc.

//uuid objects can be hashed
std::unordered_map<uuid, transaction> transaction_map;

//they can be formatted. u and l stand for uppercase and lowercase

std::string str = std::format("{}", u1);
assert(str == "e53d37db-e4e0-484f-996f-3ab1d4701abc");

str = std::format("{:u}", u1);
assert(str == "E53D37DB-E4E0-484F-996F-3AB1D4701ABC");

str = std::format("{:l}", u1);
assert(str == "e53d37db-e4e0-484f-996f-3ab1d4701abc");

//uuids can be read/written from/to iostreams

//when reading, case doesn't matter
std::istringstream istr("bc961bfb-b006-42f4-93ae-206f02658810");
uuid uuidr;
istr >> uuidr;
assert(uuidr == uuid("bc961bfb-b006-42f4-93ae-206f02658810"));

std::ostringstream ostr;
ostr << uuid("bc961bfb-b006-42f4-93ae-206f02658810");
assert(ostr.str() == "bc961bfb-b006-42f4-93ae-206f02658810");
ostr.str("");

//writing respects the std::ios_base::uppercase stream flag
ostr << std::uppercase << uuid("7d444840-9dc0-11d1-b245-5ffdce74fad2");
assert(ostr.str() == "7D444840-9DC0-11D1-B245-5FFDCE74FAD2");
```

### ULID

```cpp
#include <modern-uuid/ulid.h> //note the 'l'

using namespace muuid;

//this is a compile-time ULID literal
constexpr ulid u1("01BX5ZZKBKACTAV9WEVGEMMVRY");

//if you want to, you can use ulid as a template parameter
template<ulid U1> class some_class {...};
some_class<ulid("01BX5ZZKBKACTAV9WEVGEMMVRY")> some_object;

ulid u2 = ulid::generate();

//for non-literal strings, you can parse ulids from strings using ulid::from_chars
//the argument to from_chars can be anything convertible to std::span<char>
//the call is constexpr
std::string some_ulid_str = "01bx5zzkbkactav9wevgemmvry";
std::optional<ulid> maybe_ulid = ulid::from_chars(some_ulid_str);
if (maybe_ulid) {
    ulid parsed = *maybe_ulid;
}

//ulid objects can be compared in every possible way
assert(u2 > ulid());
assert(u2 != u1);
std::strong_ordering res = (u2 <=> u1);
//etc.

//ulid objects can be hashed
std::unordered_map<ulid, transaction> transaction_map;

//they can be formatted. u and l stand for uppercase and lowercase
std::string str = std::format("{}", u1);
assert(str == "01bx5zzkbkactav9wevgemmvry");

str = std::format("{:u}", u1);
assert(str == "01BX5ZZKBKACTAV9WEVGEMMVRY");

str = std::format("{:l}", u1);
assert(str == "01bx5zzkbkactav9wevgemmvry");

//ulids can be read/written from/to iostreams

//when reading, case doesn't matter
std::istringstream istr("01bx5zzkbkactav9wevgemmvry");
ulid ur;
istr >> ur;
assert(ur == ulid("01bx5zzkbkactav9wevgemmvry"));

std::ostringstream ostr;
ostr << ulid("01bx5zzkbkactav9wevgemmvry");
assert(ostr.str() == "01bx5zzkbkactav9wevgemmvry");
ostr.str("");

//writing respects the std::ios_base::uppercase stream flag
ostr << std::uppercase << ulid("01bx5zzkbkactav9wevgemmvry");
assert(ostr.str() == "01BX5ZZKBKACTAV9WEVGEMMVRY");
```

### NanoID

```cpp
#include <modern-uuid/nanoid.h>

using namespace muuid;

//this is a compile-time NanoID literal
constexpr nanoid n1("V1StGXR8_Z5jdHi6B-myT");

//if you want to, you can use nanoid as a template parameter
template<nanoid N> class some_class {...};
some_class<nanoid("V1StGXR8_Z5jdHi6B-myT")> some_object;

//generate a NanoID:
nanoid ng = nanoid::generate();

//for non-literal strings, you can parse nanoids from strings using nanoid::from_chars
//the argument to from_chars can be anything convertible to std::span<char, any extent>
//the call is constexpr
std::string some_nanoid_str = "Uakgb_J5m9g-0JDMbcJqL";
std::optional<nanoid> maybe_nanoid = nanoid::from_chars(some_nanoid_str);
if (maybe_nanoid) {
    nanoid parsed = *maybe_nanoid;
}

//nanoid objects can be compared in every possible way
assert(ng > nanoid());
assert(ng != n1);
std::strong_ordering res = (ng <=> n1);
//etc.

//nanoid objects can be hashed
std::unordered_map<nanoid, transaction> transaction_map;

//they can be formatted. 

std::string str = std::format("{}", n1);
assert(str == "V1StGXR8_Z5jdHi6B-myT");

//nanoids can be read/written from/to iostreams

std::istringstream istr("V1StGXR8_Z5jdHi6B-myT");
nanoid nr;
istr >> nr;
assert(nr == nanoid("V1StGXR8_Z5jdHi6B-myT"));

std::ostringstream ostr;
ostr << nanoid("V1StGXR8_Z5jdHi6B-myT");
assert(ostr.str() == "V1StGXR8_Z5jdHi6B-myT");

//You can define different nanoid types that use custom alphabets and sizes
MUUID_DECLARE_NANOID_ALPHABET(my_alphabet, "1234567890abcdef");

using my_id = basic_nanoid<my_alphabet, 10>;

//Note that the size of such an ID might be different from the default nanoid
static_assert(sizeof(my_id) == 5);

constexpr my_id mid1("4f90d13a42");
my_id mid2 = my_id::generate();
//etc.
//all other nanoid operations are the same (with different string/buffer sizes)
//as for the default nanoid
```


### Cuid2

```cpp
#include <modern-uuid/cuid2.h>

using namespace muuid;

//this is a compile-time Cuid2 literal
constexpr cuid2 c1("kgiupd0rsg67b97553xdrg2f");

//if you want to, you can use cuid2 as a template parameter
template<cuid2 C> class some_class {...};
some_class<cuid2("kgiupd0rsg67b97553xdrg2f")> some_object;

//generate a Cuid2:
cuid2 cg = cuid2::generate();

//for non-literal strings, you can parse cuid2s from strings using cuid2::from_chars
//the argument to from_chars can be anything convertible to std::span<char, any extent>
//the call is constexpr
std::string some_cuid2_str = "a96dz76vcu55q6aorin51oqo";
std::optional<cuid2> maybe_cuid2 = cuid2::from_chars(some_cuid2_str);
if (maybe_cuid2) {
    cuid2 parsed = *maybe_cuid2;
}

//cuid2 objects can be compared in every possible way
assert(cg > cuid2());
assert(cg != c1);
std::strong_ordering res = (cg <=> c1);
//etc.

//cuid2 objects can be hashed
std::unordered_map<cuid2, transaction> transaction_map;

//they can be formatted. u and l stand for uppercase and lowercase
std::string str = std::format("{}", c1);
assert(str == "kgiupd0rsg67b97553xdrg2f");

str = std::format("{:u}", c1);
assert(str == "KGIUPD0RSG67B97553XDRG2F");

str = std::format("{:l}", c1);
assert(str == "kgiupd0rsg67b97553xdrg2f");

//cuid2s can be read/written from/to iostreams

//when reading, case doesn't matter
std::istringstream istr("kgiupd0rsg67b97553xdrg2f");
cuid2 cr;
istr >> cr;
assert(cr == cuid2("kgiupd0rsg67b97553xdrg2f"));

std::ostringstream ostr;
ostr << cuid2("kgiupd0rsg67b97553xdrg2f");
assert(ostr.str() == "kgiupd0rsg67b97553xdrg2f");
ostr.str("");

//writing respects the std::ios_base::uppercase stream flag
ostr << std::uppercase << cuid2("kgiupd0rsg67b97553xdrg2f");
assert(ostr.str() == "KGIUPD0RSG67B97553XDRG2F");
```

## Building/Integrating

The quickest CMake method is given below. For more details and other methods, 
see [Integration Guide](doc/building.md).

```cmake
include(FetchContent)
FetchContent_Declare(modern-uuid
    GIT_REPOSITORY git@github.com:gershnik/modern-uuid.git
    GIT_TAG        <desired tag like v1.2 or a sha>
    GIT_SHALLOW    TRUE
)
FetchContent_MakeAvailable(modern-uuid)
...
target_link_libraries(mytarget
PRIVATE
  modern-uuid::modern-uuid
)
```

<!-- Links -->

[wiki-uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
[ulid-spec]: https://github.com/ulid/spec
[nanoid]: https://github.com/ai/nanoid
[cuid2]: https://github.com/paralleldrive/cuid2
[rfc9562]: https://www.rfc-editor.org/rfc/rfc9562.html
[rfc4122]: https://www.rfc-editor.org/rfc/rfc4122.html

[badge-language]: https://img.shields.io/badge/language-C++-blue.svg
[badge-cpp-standard]: https://img.shields.io/badge/C%2B%2B-20-blue.svg
[badge-license]: https://img.shields.io/badge/license-BSD-brightgreen.svg
[license]: https://opensource.org/licenses/BSD-3-Clause
[badge-tests]: https://github.com/gershnik/modern-uuid/actions/workflows/test.yml/badge.svg
[tests]: https://github.com/gershnik/modern-uuid/actions/workflows/test.yml

<!-- End Links -->
