aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.util
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-07-02 04:00:06 +0100
committerLexi Winter <lexi@le-fay.org>2025-07-02 04:00:06 +0100
commit5adeb648f74c1771164c0686d6e0fc584cf36d9e (patch)
tree060cd918d3dd9e931a1541a43c9edff1a404ff47 /nihil.util
parent06fafff8e9e9c096cc39bde0306caa53ad3a2351 (diff)
downloadnihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.gz
nihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.bz2
move everything from util to core
Diffstat (limited to 'nihil.util')
-rw-r--r--nihil.util/CMakeLists.txt59
-rw-r--r--nihil.util/capture_stream.ccm55
-rw-r--r--nihil.util/capture_stream.test.cc44
-rw-r--r--nihil.util/construct.ccm22
-rw-r--r--nihil.util/ctype.ccm72
-rw-r--r--nihil.util/ctype.test.cc376
-rw-r--r--nihil.util/error.ccm321
-rw-r--r--nihil.util/error.test.cc273
-rw-r--r--nihil.util/flagset.ccm200
-rw-r--r--nihil.util/flagset.test.cc144
-rw-r--r--nihil.util/guard.ccm41
-rw-r--r--nihil.util/guard.test.cc16
-rw-r--r--nihil.util/match.ccm18
-rw-r--r--nihil.util/match.test.cc32
-rw-r--r--nihil.util/monad.ccm282
-rw-r--r--nihil.util/monad.test.cc65
-rw-r--r--nihil.util/next_word.ccm32
-rw-r--r--nihil.util/next_word.test.cc65
-rw-r--r--nihil.util/nihil.util.ccm18
-rw-r--r--nihil.util/parse_size.ccm90
-rw-r--r--nihil.util/parse_size.test.cc166
-rw-r--r--nihil.util/save_errno.ccm35
-rw-r--r--nihil.util/skipws.ccm25
-rw-r--r--nihil.util/skipws.test.cc46
-rw-r--r--nihil.util/sys_error.ccm18
-rw-r--r--nihil.util/tabulate.ccm298
-rw-r--r--nihil.util/tabulate.test.cc70
-rw-r--r--nihil.util/uuid.ccm768
-rw-r--r--nihil.util/uuid.test.cc923
29 files changed, 0 insertions, 4574 deletions
diff --git a/nihil.util/CMakeLists.txt b/nihil.util/CMakeLists.txt
deleted file mode 100644
index 30a33b3..0000000
--- a/nihil.util/CMakeLists.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-# This source code is released into the public domain.
-
-add_library(nihil.util STATIC)
-
-target_link_libraries(nihil.util PRIVATE
- nihil.std
- nihil.core
-)
-
-target_sources(nihil.util
- PUBLIC FILE_SET modules TYPE CXX_MODULES FILES
- nihil.util.ccm
-
- capture_stream.ccm
- construct.ccm
- ctype.ccm
- error.ccm
- flagset.ccm
- guard.ccm
- match.ccm
- monad.ccm
- parse_size.ccm
- next_word.ccm
- save_errno.ccm
- skipws.ccm
- sys_error.ccm
- tabulate.ccm
- uuid.ccm
-)
-
-if(NIHIL_TESTS)
- enable_testing()
-
- add_executable(nihil.util.test
- capture_stream.test.cc
- ctype.test.cc
- error.test.cc
- flagset.test.cc
- guard.test.cc
- match.test.cc
- monad.test.cc
- parse_size.test.cc
- next_word.test.cc
- skipws.test.cc
- tabulate.test.cc
- uuid.test.cc
- )
-
- target_link_libraries(nihil.util.test PRIVATE
- nihil.std
- nihil.core
- nihil.util
- Catch2::Catch2WithMain
- )
-
- include(CTest)
- include(Catch)
- catch_discover_tests(nihil.util.test)
-endif()
diff --git a/nihil.util/capture_stream.ccm b/nihil.util/capture_stream.ccm
deleted file mode 100644
index f061558..0000000
--- a/nihil.util/capture_stream.ccm
+++ /dev/null
@@ -1,55 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:capture_stream;
-
-import nihil.std;
-
-namespace nihil {
-
-// Capture output written to a stream and redirect it to an internal string
-// buffer. Call .str() to get the data written. Call .release() to stop
-// capturing (or simply delete the capture_stream object).
-export template<typename Char, typename Traits>
-struct capture_stream {
- capture_stream(std::basic_ostream<Char, Traits> &stream)
- : m_stream(&stream)
- {
- m_old_streambuf = m_stream->rdbuf();
- m_stream->rdbuf(m_buffer.rdbuf());
- }
-
- ~capture_stream()
- {
- if (m_old_streambuf == nullptr)
- return;
- m_stream->rdbuf(m_old_streambuf);
- }
-
- /*
- * Release this capture, returning the stream to its previous state.
- */
- auto release(this capture_stream &self) -> void
- {
- if (self.m_old_streambuf == nullptr)
- throw std::logic_error(
- "release() called on empty capture_stream");
-
- self.m_stream->rdbuf(self.m_old_streambuf);
- self.m_old_streambuf = nullptr;
- }
-
- /*
- * Get the data which has been written to the stream.
- */
- [[nodiscard]] auto str(this capture_stream const &self)
- -> std::basic_string_view<Char, Traits>
- {
- return self.m_buffer.view();
- }
-
-private:
- std::basic_ostringstream<Char, Traits> m_buffer;
- std::basic_ostream<Char, Traits> *m_stream;
- std::streambuf *m_old_streambuf;
-};
-
-} // namespace nihil
diff --git a/nihil.util/capture_stream.test.cc b/nihil.util/capture_stream.test.cc
deleted file mode 100644
index a4821b7..0000000
--- a/nihil.util/capture_stream.test.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-TEST_CASE("nihil.util: capture", "[nihil][nihil.util]")
-{
- SECTION("std::cout with release()") {
- auto cap = nihil::capture_stream(std::cout);
-
- std::cout << 1 << '+' << 1 << '=' << (1 + 1) << '\n';
- REQUIRE(cap.str() == "1+1=2\n");
-
- cap.release();
- REQUIRE(cap.str() == "1+1=2\n");
- }
-
- SECTION("std::cout with dtor") {
- auto cap = nihil::capture_stream(std::cout);
- std::cout << 1 << '+' << 1 << '=' << (1 + 1) << '\n';
- REQUIRE(cap.str() == "1+1=2\n");
- }
-
- SECTION("std::cerr with release()") {
- auto cap = nihil::capture_stream(std::cerr);
-
- std::cerr << 1 << '+' << 1 << '=' << (1 + 1) << '\n';
- REQUIRE(cap.str() == "1+1=2\n");
-
- cap.release();
- REQUIRE(cap.str() == "1+1=2\n");
- }
-
- SECTION("std::cerr with dtor") {
- auto cap = nihil::capture_stream(std::cerr);
- std::cerr << 1 << '+' << 1 << '=' << (1 + 1) << '\n';
- REQUIRE(cap.str() == "1+1=2\n");
- }
-}
-
-} // anonymous namespace
diff --git a/nihil.util/construct.ccm b/nihil.util/construct.ccm
deleted file mode 100644
index 894a446..0000000
--- a/nihil.util/construct.ccm
+++ /dev/null
@@ -1,22 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:construct;
-
-import nihil.std;
-
-namespace nihil {
-
-// A functor that constructs objects of an arbitrary type.
-// Useful for std::views::transform.
-template <typename T>
-struct construct_fn final
-{
- [[nodiscard]] auto operator()(this construct_fn const &, auto &&...args) -> T
- {
- return T(std::forward<decltype(args)>(args)...);
- }
-};
-
-export template <typename T>
-inline constexpr auto construct = construct_fn<T>{};
-
-} // namespace nihil
diff --git a/nihil.util/ctype.ccm b/nihil.util/ctype.ccm
deleted file mode 100644
index 8f5de27..0000000
--- a/nihil.util/ctype.ccm
+++ /dev/null
@@ -1,72 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:ctype;
-
-import nihil.std;
-
-namespace nihil {
-
-/*
- * ctype_is: wrap std::ctype<T>::is() in a form suitable for use as an algorithm
- * predicate, i.e., ctype_is(m) will return a functor object that takes any char
- * type as an argument and returns bool.
- *
- * If the locale is not specified, the current global locale is used by default.
- *
- * ctype_is copies the locale, so passing a temporary is fine.
- */
-
-export struct ctype_is final
-{
- explicit ctype_is(std::ctype_base::mask mask_,
- std::locale const &locale_ = std::locale()) noexcept
- : m_mask(mask_)
- , m_locale(locale_)
- {
- }
-
- [[nodiscard]] auto operator()(this ctype_is const &self, std::integral auto c)
- {
- using ctype = std::ctype<decltype(c)>;
- auto &facet = std::use_facet<ctype>(self.m_locale);
- return facet.is(self.m_mask, c);
- }
-
-private:
- std::ctype_base::mask m_mask;
- std::locale m_locale;
-};
-
-// Predefined tests for the current global locale.
-
-export inline auto is_space = ctype_is(std::ctype_base::space);
-export inline auto is_print = ctype_is(std::ctype_base::print);
-export inline auto is_cntrl = ctype_is(std::ctype_base::cntrl);
-export inline auto is_upper = ctype_is(std::ctype_base::upper);
-export inline auto is_lower = ctype_is(std::ctype_base::lower);
-export inline auto is_alpha = ctype_is(std::ctype_base::alpha);
-export inline auto is_digit = ctype_is(std::ctype_base::digit);
-export inline auto is_punct = ctype_is(std::ctype_base::punct);
-export inline auto is_xdigit = ctype_is(std::ctype_base::xdigit);
-export inline auto is_blank = ctype_is(std::ctype_base::blank);
-export inline auto is_alnum = ctype_is(std::ctype_base::alnum);
-export inline auto is_graph = ctype_is(std::ctype_base::graph);
-
-// Predefined tests for the C locale. The C locale is guaranteed to always be
-// available, so this doesn't create lifetime issues.
-
-//NOLINTBEGIN: Technically, std::locale::classic() can throw. Assume it doesn't.
-export inline auto is_c_space = ctype_is(std::ctype_base::space, std::locale::classic());
-export inline auto is_c_print = ctype_is(std::ctype_base::print, std::locale::classic());
-export inline auto is_c_cntrl = ctype_is(std::ctype_base::cntrl, std::locale::classic());
-export inline auto is_c_upper = ctype_is(std::ctype_base::upper, std::locale::classic());
-export inline auto is_c_lower = ctype_is(std::ctype_base::lower, std::locale::classic());
-export inline auto is_c_alpha = ctype_is(std::ctype_base::alpha, std::locale::classic());
-export inline auto is_c_digit = ctype_is(std::ctype_base::digit, std::locale::classic());
-export inline auto is_c_punct = ctype_is(std::ctype_base::punct, std::locale::classic());
-export inline auto is_c_xdigit = ctype_is(std::ctype_base::xdigit, std::locale::classic());
-export inline auto is_c_blank = ctype_is(std::ctype_base::blank, std::locale::classic());
-export inline auto is_c_alnum = ctype_is(std::ctype_base::alnum, std::locale::classic());
-export inline auto is_c_graph = ctype_is(std::ctype_base::graph, std::locale::classic());
-//NOLINTEND
-
-} // namespace nihil
diff --git a/nihil.util/ctype.test.cc b/nihil.util/ctype.test.cc
deleted file mode 100644
index d000b45..0000000
--- a/nihil.util/ctype.test.cc
+++ /dev/null
@@ -1,376 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-
-TEST_CASE("ctype: space", "[ctype]") {
- auto is_utf8_space =
- nihil::ctype_is(std::ctype_base::space,
- std::locale("C.UTF-8"));
-
- // '\v' (vertical tab) is a space
- REQUIRE(nihil::is_space('\v') == true);
- REQUIRE(nihil::is_space(L'\v') == true);
-
- REQUIRE(nihil::is_c_space('\v') == true);
- REQUIRE(nihil::is_c_space(L'\v') == true);
-
- REQUIRE(is_utf8_space('\v') == true);
- REQUIRE(is_utf8_space(L'\v') == true);
-
- // 'x' is not a space
- REQUIRE(nihil::is_space('x') == false);
- REQUIRE(nihil::is_space(L'x') == false);
-
- REQUIRE(nihil::is_c_space('x') == false);
- REQUIRE(nihil::is_c_space(L'x') == false);
-
- REQUIRE(is_utf8_space('x') == false);
- REQUIRE(is_utf8_space(L'x') == false);
-
- // U+2003 EM SPACE is a space
- REQUIRE(nihil::is_space(L'\u2003') == false);
- REQUIRE(nihil::is_c_space(L'\u2003') == false);
- REQUIRE(is_utf8_space(L'\u2003') == true);
-}
-
-TEST_CASE("ctype: print", "[ctype]") {
- auto is_utf8_print =
- nihil::ctype_is(std::ctype_base::print,
- std::locale("C.UTF-8"));
-
- // 'x' is printable
- REQUIRE(nihil::is_print('x') == true);
- REQUIRE(nihil::is_print(L'x') == true);
-
- REQUIRE(nihil::is_c_print('x') == true);
- REQUIRE(nihil::is_c_print(L'x') == true);
-
- REQUIRE(is_utf8_print('x') == true);
- REQUIRE(is_utf8_print(L'x') == true);
-
- // '\003' is not printable
- REQUIRE(nihil::is_print('\003') == false);
- REQUIRE(nihil::is_print(L'\003') == false);
-
- REQUIRE(nihil::is_c_print('\003') == false);
- REQUIRE(nihil::is_c_print(L'\003') == false);
-
- REQUIRE(is_utf8_print('\003') == false);
- REQUIRE(is_utf8_print(L'\003') == false);
-
- // U+0410 CYRILLIC CAPITAL LETTER A is printable
- REQUIRE(nihil::is_print(L'\u0410') == false);
- REQUIRE(nihil::is_c_print(L'\u0410') == false);
- REQUIRE(is_utf8_print(L'\u0410') == true);
-}
-
-TEST_CASE("ctype: cntrl", "[ctype]") {
- auto is_utf8_cntrl =
- nihil::ctype_is(std::ctype_base::cntrl,
- std::locale("C.UTF-8"));
-
- // '\003' is a control character
- REQUIRE(nihil::is_cntrl('\003') == true);
- REQUIRE(nihil::is_cntrl(L'\003') == true);
-
- REQUIRE(nihil::is_c_cntrl('\003') == true);
- REQUIRE(nihil::is_c_cntrl(L'\003') == true);
-
- REQUIRE(is_utf8_cntrl('\003') == true);
- REQUIRE(is_utf8_cntrl(L'\003') == true);
-
-
- // 'x' is not a control character
- REQUIRE(nihil::is_cntrl('x') == false);
- REQUIRE(nihil::is_cntrl(L'x') == false);
-
- REQUIRE(nihil::is_c_cntrl('x') == false);
- REQUIRE(nihil::is_c_cntrl(L'x') == false);
-
- REQUIRE(is_utf8_cntrl('x') == false);
- REQUIRE(is_utf8_cntrl(L'x') == false);
-
- // U+00AD SOFT HYPHEN is a control character.
- REQUIRE(nihil::is_cntrl(L'\u00ad') == false);
- REQUIRE(nihil::is_c_cntrl(L'\u00ad') == false);
- REQUIRE(is_utf8_cntrl(L'\u00ad') == true);
-}
-
-TEST_CASE("ctype: upper", "[ctype]") {
- auto is_utf8_upper =
- nihil::ctype_is(std::ctype_base::upper,
- std::locale("C.UTF-8"));
-
- // 'A' is upper case
- REQUIRE(nihil::is_upper('A') == true);
- REQUIRE(nihil::is_upper(L'A') == true);
-
- REQUIRE(nihil::is_c_upper('A') == true);
- REQUIRE(nihil::is_c_upper(L'A') == true);
-
- REQUIRE(is_utf8_upper('A') == true);
- REQUIRE(is_utf8_upper(L'A') == true);
-
- // 'a' is not upper case
- REQUIRE(nihil::is_upper('a') == false);
- REQUIRE(nihil::is_upper(L'a') == false);
-
- REQUIRE(nihil::is_c_upper('a') == false);
- REQUIRE(nihil::is_c_upper(L'a') == false);
-
- REQUIRE(is_utf8_upper('a') == false);
- REQUIRE(is_utf8_upper(L'a') == false);
-
- // U+0410 CYRILLIC CAPITAL LETTER A is upper case
- REQUIRE(nihil::is_upper(L'\u0410') == false);
- REQUIRE(nihil::is_c_upper(L'\u0410') == false);
- REQUIRE(is_utf8_upper(L'\u0410') == true);
-}
-
-TEST_CASE("ctype: lower", "[ctype]") {
- auto is_utf8_lower =
- nihil::ctype_is(std::ctype_base::lower,
- std::locale("C.UTF-8"));
-
- // 'a' is lower case
- REQUIRE(nihil::is_lower('a') == true);
- REQUIRE(nihil::is_lower(L'a') == true);
-
- REQUIRE(nihil::is_c_lower('a') == true);
- REQUIRE(nihil::is_c_lower(L'a') == true);
-
- REQUIRE(is_utf8_lower('a') == true);
- REQUIRE(is_utf8_lower(L'a') == true);
-
- // 'A' is not lower case
- REQUIRE(nihil::is_lower('A') == false);
- REQUIRE(nihil::is_lower(L'A') == false);
-
- REQUIRE(nihil::is_c_lower('A') == false);
- REQUIRE(nihil::is_c_lower(L'A') == false);
-
- REQUIRE(is_utf8_lower('A') == false);
- REQUIRE(is_utf8_lower(L'A') == false);
-
- // U+0430 CYRILLIC SMALL LETTER A
- REQUIRE(nihil::is_lower(L'\u0430') == false);
- REQUIRE(nihil::is_c_lower(L'\u0430') == false);
- REQUIRE(is_utf8_lower(L'\u0430') == true);
-}
-
-TEST_CASE("ctype: alpha", "[ctype]") {
- auto is_utf8_alpha =
- nihil::ctype_is(std::ctype_base::alpha,
- std::locale("C.UTF-8"));
-
- // 'a' is alphabetical
- REQUIRE(nihil::is_alpha('a') == true);
- REQUIRE(nihil::is_alpha(L'a') == true);
-
- REQUIRE(nihil::is_c_alpha('a') == true);
- REQUIRE(nihil::is_c_alpha(L'a') == true);
-
- REQUIRE(is_utf8_alpha('a') == true);
- REQUIRE(is_utf8_alpha(L'a') == true);
-
- // '1' is not alphabetical
- REQUIRE(nihil::is_alpha('1') == false);
- REQUIRE(nihil::is_alpha(L'1') == false);
-
- REQUIRE(nihil::is_c_alpha('1') == false);
- REQUIRE(nihil::is_c_alpha(L'1') == false);
-
- REQUIRE(is_utf8_alpha('1') == false);
- REQUIRE(is_utf8_alpha(L'1') == false);
-
- // U+0430 CYRILLIC SMALL LETTER A
- REQUIRE(nihil::is_alpha(L'\u0430') == false);
- REQUIRE(nihil::is_c_alpha(L'\u0430') == false);
- REQUIRE(is_utf8_alpha(L'\u0430') == true);
-}
-
-TEST_CASE("ctype: digit", "[ctype]") {
- auto is_utf8_digit =
- nihil::ctype_is(std::ctype_base::digit,
- std::locale("C.UTF-8"));
-
- // '1' is a digit
- REQUIRE(nihil::is_digit('1') == true);
- REQUIRE(nihil::is_digit(L'1') == true);
-
- REQUIRE(nihil::is_c_digit('1') == true);
- REQUIRE(nihil::is_c_digit(L'1') == true);
-
- REQUIRE(is_utf8_digit('1') == true);
- REQUIRE(is_utf8_digit(L'1') == true);
-
- // 'a' is not a digit
- REQUIRE(nihil::is_digit('a') == false);
- REQUIRE(nihil::is_digit(L'a') == false);
-
- REQUIRE(nihil::is_c_digit('a') == false);
- REQUIRE(nihil::is_c_digit(L'a') == false);
-
- REQUIRE(is_utf8_digit('a') == false);
- REQUIRE(is_utf8_digit(L'a') == false);
-
- // U+0660 ARABIC-INDIC DIGIT ZERO
- REQUIRE(nihil::is_digit(L'\u0660') == false);
- REQUIRE(nihil::is_c_digit(L'\u0660') == false);
- REQUIRE(is_utf8_digit(L'\u0660') == true);
-}
-
-TEST_CASE("ctype: punct", "[ctype]") {
- auto is_utf8_punct =
- nihil::ctype_is(std::ctype_base::punct,
- std::locale("C.UTF-8"));
-
- // ';' is punctuation
- REQUIRE(nihil::is_punct(';') == true);
- REQUIRE(nihil::is_punct(L';') == true);
-
- REQUIRE(nihil::is_c_punct(';') == true);
- REQUIRE(nihil::is_c_punct(L';') == true);
-
- REQUIRE(is_utf8_punct(';') == true);
- REQUIRE(is_utf8_punct(L';') == true);
-
- // 'a' is not punctuation
- REQUIRE(nihil::is_punct('a') == false);
- REQUIRE(nihil::is_punct(L'a') == false);
-
- REQUIRE(nihil::is_c_punct('a') == false);
- REQUIRE(nihil::is_c_punct(L'a') == false);
-
- REQUIRE(is_utf8_punct('a') == false);
- REQUIRE(is_utf8_punct(L'a') == false);
-
- // U+00A1 INVERTED EXCLAMATION MARK
- REQUIRE(nihil::is_punct(L'\u00A1') == false);
- REQUIRE(nihil::is_c_punct(L'\u00A1') == false);
- REQUIRE(is_utf8_punct(L'\u00A1') == true);
-}
-
-TEST_CASE("ctype: xdigit", "[ctype]") {
- auto is_utf8_xdigit =
- nihil::ctype_is(std::ctype_base::xdigit,
- std::locale("C.UTF-8"));
-
- // 'f' is an xdigit
- REQUIRE(nihil::is_xdigit('f') == true);
- REQUIRE(nihil::is_xdigit(L'f') == true);
-
- REQUIRE(nihil::is_c_xdigit('f') == true);
- REQUIRE(nihil::is_c_xdigit(L'f') == true);
-
- REQUIRE(is_utf8_xdigit('f') == true);
- REQUIRE(is_utf8_xdigit(L'f') == true);
-
- // 'g' is not an xdigit
- REQUIRE(nihil::is_xdigit('g') == false);
- REQUIRE(nihil::is_xdigit(L'g') == false);
-
- REQUIRE(nihil::is_c_xdigit('g') == false);
- REQUIRE(nihil::is_c_xdigit(L'g') == false);
-
- REQUIRE(is_utf8_xdigit('g') == false);
- REQUIRE(is_utf8_xdigit(L'g') == false);
-}
-
-TEST_CASE("ctype: blank", "[ctype]") {
- auto is_utf8_blank =
- nihil::ctype_is(std::ctype_base::blank,
- std::locale("C.UTF-8"));
-
- // '\t' is a blank
- REQUIRE(nihil::is_blank('\t') == true);
- REQUIRE(nihil::is_blank(L'\t') == true);
-
- REQUIRE(nihil::is_c_blank('\t') == true);
- REQUIRE(nihil::is_c_blank(L'\t') == true);
-
- REQUIRE(is_utf8_blank('\t') == true);
- REQUIRE(is_utf8_blank(L'\t') == true);
-
- // '\v' is not a blank
- REQUIRE(nihil::is_blank('\v') == false);
- REQUIRE(nihil::is_blank(L'\v') == false);
-
- REQUIRE(nihil::is_c_blank('\v') == false);
- REQUIRE(nihil::is_c_blank(L'\v') == false);
-
- REQUIRE(is_utf8_blank('\v') == false);
- REQUIRE(is_utf8_blank(L'\v') == false);
-
- // There don't seem to be any UTF-8 blank characters, at least
- // in FreeBSD libc.
-}
-
-TEST_CASE("ctype: alnum", "[ctype]") {
- auto is_utf8_alnum =
- nihil::ctype_is(std::ctype_base::alnum,
- std::locale("C.UTF-8"));
-
- // 'a' is alphanumeric
- REQUIRE(nihil::is_alnum('a') == true);
- REQUIRE(nihil::is_alnum(L'a') == true);
-
- REQUIRE(nihil::is_c_alnum('a') == true);
- REQUIRE(nihil::is_c_alnum(L'a') == true);
-
- REQUIRE(is_utf8_alnum('a') == true);
- REQUIRE(is_utf8_alnum(L'a') == true);
-
- // '\t' is not a alnum
- REQUIRE(nihil::is_alnum('\t') == false);
- REQUIRE(nihil::is_alnum(L'\t') == false);
-
- REQUIRE(nihil::is_c_alnum('\t') == false);
- REQUIRE(nihil::is_c_alnum(L'\t') == false);
-
- REQUIRE(is_utf8_alnum('\t') == false);
- REQUIRE(is_utf8_alnum(L'\t') == false);
-
- // U+0430 CYRILLIC SMALL LETTER A
- REQUIRE(nihil::is_alnum(L'\u0430') == false);
- REQUIRE(nihil::is_c_alnum(L'\u0430') == false);
- REQUIRE(is_utf8_alnum(L'\u0430') == true);
-}
-
-TEST_CASE("ctype: graph", "[ctype]") {
- auto is_utf8_graph =
- nihil::ctype_is(std::ctype_base::graph,
- std::locale("C.UTF-8"));
-
- // 'a' is graphical
- REQUIRE(nihil::is_graph('a') == true);
- REQUIRE(nihil::is_graph(L'a') == true);
-
- REQUIRE(nihil::is_c_graph('a') == true);
- REQUIRE(nihil::is_c_graph(L'a') == true);
-
- REQUIRE(is_utf8_graph('a') == true);
- REQUIRE(is_utf8_graph(L'a') == true);
-
- // '\t' is not graphical
- REQUIRE(nihil::is_graph('\t') == false);
- REQUIRE(nihil::is_graph(L'\t') == false);
-
- REQUIRE(nihil::is_c_graph('\t') == false);
- REQUIRE(nihil::is_c_graph(L'\t') == false);
-
- REQUIRE(is_utf8_graph('\t') == false);
- REQUIRE(is_utf8_graph(L'\t') == false);
-
- // U+0430 CYRILLIC SMALL LETTER A
- REQUIRE(nihil::is_graph(L'\u0430') == false);
- REQUIRE(nihil::is_c_graph(L'\u0430') == false);
- REQUIRE(is_utf8_graph(L'\u0430') == true);
-}
-
-} // anonymous namespace
diff --git a/nihil.util/error.ccm b/nihil.util/error.ccm
deleted file mode 100644
index fd3aac9..0000000
--- a/nihil.util/error.ccm
+++ /dev/null
@@ -1,321 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:error;
-
-// error: a type representing an error.
-//
-// An error consists of an immediate cause, which may be a string or
-// std:error_code, and an optional proximate cause, which is another error
-// object. Any number of error objects may be stacked.
-//
-// For example, a failure to open a file might be a stack of two errors:
-//
-// - string, "failed to open /etc/somefile",
-// - std::error_code, "No such file or directory".
-//
-// Calling .str() will format the entire stack starting at that error,
-// for example: "failed to open /etc/somefile: No such file or directory".
-//
-// Errors may be moved and (relatively) cheaply copied, since the cause
-// chain is refcounted.
-//
-// error derives from std::exception, so it may be thrown and caught and
-// provides a useful what(). When throwing errors, creating a derived
-// error will make it easier to distinguish errors when catching them.
-
-import nihil.std;
-
-import :match;
-
-namespace nihil {
-
-// Things which can be errors.
-using error_t = std::variant<std::monostate, std::string, std::error_code, std::error_condition>;
-
-// A proxy class used when constructing errors. This has implicit constructors from various types,
-// which means we don't have to handle every possible combination of types in error itself.
-export struct error_proxy
-{
- // Construct from...
-
- // ... a string_view
- error_proxy(std::string_view const what) // NOLINT
- : m_error(std::string(what))
- {
- }
-
- // ... an std::string; so we can move the string into place if it's an rvalue.
- error_proxy(auto &&what) // NOLINT
- requires(std::same_as<std::remove_cvref_t<decltype(what)>, std::string>)
- : m_error(std::forward<decltype(what)>(what))
- {
- }
-
- // ... a C string
- error_proxy(char const *what) // NOLINT
- : m_error(std::string(what))
- {
- }
-
- // ... an std::error_code
- error_proxy(std::error_code const what) // NOLINT
- : m_error(what)
- {
- }
-
- // ... an std::error_condition
- error_proxy(std::error_condition const what) // NOLINT
- : m_error(what)
- {
- }
-
- // ... an error code enum
- template <typename T>
- requires(std::is_error_code_enum<T>::value)
- error_proxy(T what) // NOLINT
- : m_error(make_error_code(what))
- {
- }
-
- // ... an error condition enum
- template <typename T>
- requires(std::is_error_condition_enum<T>::value)
- error_proxy(T what) // NOLINT
- : m_error(make_error_condition(what))
- {
- }
-
- // Not copyable.
- error_proxy(error_proxy const &) = delete;
- auto operator=(error_proxy const &) -> error_proxy & = delete;
-
- // Not movable.
- error_proxy(error_proxy &&) = delete;
- auto operator=(error_proxy &&) -> error_proxy & = delete;
-
- ~error_proxy() = default;
-
- // Let error extract the error_t.
- [[nodiscard]] auto error() && -> error_t &&
- {
- return std::move(m_error);
- }
-
-private:
- // The error.
- error_t m_error;
-};
-
-// The error class.
-export struct error : std::exception
-{
- // Create an empty error, representing success.
- error() = default;
-
- // Destroy an error.
- ~error() override = default;
-
- // Create an error from an error proxy.
- explicit error(error_proxy &&proxy)
- : m_error(std::move(std::move(proxy).error()))
- {
- }
-
- // Create an error from an error proxy and an error cause.
- error(error_proxy &&proxy, error cause)
- : m_error(std::move(std::move(proxy).error()))
- , m_cause(std::make_shared<error>(std::move(cause)))
- {
- }
-
- // Create an error from an error proxy and an error_proxy cause.
- // For example, error("cannot open file", std::errc::permission_denied).
- error(error_proxy &&proxy, error_proxy &&cause)
- : m_error(std::move(std::move(proxy).error()))
- , m_cause(std::make_shared<error>(std::move(cause)))
- {
- }
-
- // Copyable.
- error(error const &) = default;
- auto operator=(error const &) -> error & = default;
-
- // Movable.
- error(error &&) noexcept = default;
- auto operator=(error &&) noexcept -> error & = default;
-
- // Return the cause of this error.
- [[nodiscard]] auto cause(this error const &self) -> std::shared_ptr<error> const &
- {
- return self.m_cause;
- }
-
- // Return the root cause of this error, which may be this object.
- // For errors caused by an OS error, this will typically be the
- // error_code error.
- [[nodiscard]] auto root_cause(this error const &self) -> error const &
- {
- auto const *cause = &self;
- while (cause->m_cause)
- cause = cause->m_cause.get();
- return *cause;
- }
-
- // Format this error without its cause as a string.
- [[nodiscard]] auto this_str(this error const &self) -> std::string
- {
- return self.m_error | match {
- [] (std::monostate) -> std::string {
- return "No error";
- },
- [] (std::error_code const &m) {
- return m.message();
- },
- [] (std::error_condition const &m) {
- return m.message();
- },
- [] (std::string const &m) {
- return m;
- }
- };
- }
-
- // Format this error and its cause as a string.
- [[nodiscard]] auto full_str(this error const &self) -> std::string
- {
- auto str = self.this_str();
-
- auto cause = self.cause();
- while (cause) {
- str += ": " + cause->this_str();
- cause = cause->cause();
- }
-
- return str;
- }
-
- // Return this error's error_code, if any.
- [[nodiscard]] auto code(this error const &self) -> std::optional<std::error_code>
- {
- auto const *code = std::get_if<std::error_code>(&self.m_error);
- if (code != nullptr)
- return {*code};
- return {};
- }
-
- // Return this error's error_condition, if any.
- [[nodiscard]] auto condition(this error const &self) -> std::optional<std::error_condition>
- {
- auto const *condition = std::get_if<std::error_condition>(&self.m_error);
- if (condition != nullptr)
- return {*condition};
- return {};
- }
-
- // Format this error and its cause as a C string and return it. This is for
- // compatibility with std::exception. The lifetime of the returned string
- // is the same as the error object.
- [[nodiscard]] auto what() const noexcept -> char const * final
- {
- if (!m_what)
- m_what = this->full_str();
- return m_what->c_str();
- }
-
- // Allow error to be implicitly converted to std::expected and std::unexpected, to make
- // using it with std::expected easier.
-
- template <typename T>
- operator std::expected<T, error> () && // NOLINT
- {
- return std::unexpected{std::move(*this)};
- }
-
- template<typename T>
- operator std::expected<T, error> () const // NOLINT
- {
- return std::unexpected{*this};
- }
-
- operator std::unexpected<error> () && // NOLINT
- {
- return std::unexpected{std::move(*this)};
- }
-
- operator std::unexpected<error> () const // NOLINT
- {
- return std::unexpected{*this};
- }
-
-private:
- // This error.
- error_t m_error = make_error_code(std::errc());
-
- // The cause of this error, if any.
- std::shared_ptr<error> m_cause;
-
- // For std::exception::what(), we need to keep the string valid
- // until we're destroyed.
- mutable std::optional<std::string> m_what;
-
- // Equality comparison.
- [[nodiscard]] friend auto operator==(error const &lhs, error const &rhs) -> bool
- {
- return lhs.m_error == rhs.m_error;
- }
-
- [[nodiscard]] friend auto operator<=>(error const &lhs, error const &rhs) -> std::strong_ordering
- {
- return lhs.m_error <=> rhs.m_error;
- }
-
- // Compare an error with an std::error_code.
- [[nodiscard]] friend auto operator==(error const &lhs, std::error_code const &rhs) -> bool
- {
- return lhs.code() == rhs;
- }
-
- // Compare an error to an std::error_condition.
- [[nodiscard]] friend auto operator==(error const &lhs, std::error_condition const &rhs) -> bool
- {
- return lhs.condition() == rhs;
- }
-
- // Compare an error to an std::error_code enum.
- [[nodiscard]] friend auto operator==(error const &lhs, auto rhs) -> bool
- requires(std::is_error_code_enum<decltype(rhs)>::value)
- {
- return lhs.code() == rhs;
- }
-
- // Compare an error to an std::error_condition enum.
- [[nodiscard]] friend auto operator==(error const &lhs, auto rhs) -> bool
- requires(std::is_error_condition_enum<decltype(rhs)>::value)
- {
- return lhs.condition() == rhs;
- }
-
- // Print an error to a stream.
- friend auto operator<<(std::ostream &strm, error const &e) -> std::ostream &
- {
- return strm << e.full_str();
- }
-};
-
-} // namespace nihil
-
-// Make error formattable.
-export template <>
-struct std::formatter<nihil::error, char>
-{
- template <typename ParseContext>
- constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
- {
- return ctx.begin();
- }
-
- template <typename FormatContext>
- auto format(nihil::error const &e, FormatContext &ctx) const -> FormatContext::iterator
- {
- return std::ranges::copy(e.full_str(), ctx.out()).out;
- }
-};
diff --git a/nihil.util/error.test.cc b/nihil.util/error.test.cc
deleted file mode 100644
index f4ec1ee..0000000
--- a/nihil.util/error.test.cc
+++ /dev/null
@@ -1,273 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.core;
-import nihil.util;
-
-namespace {
-inline constexpr auto *test_tags = "[nihil][nihil.error]";
-
-TEST_CASE("error: invariants", test_tags)
-{
- static_assert(std::destructible<nihil::error>);
- static_assert(std::default_initializable<nihil::error>);
- static_assert(std::move_constructible<nihil::error>);
- static_assert(std::copy_constructible<nihil::error>);
- static_assert(std::equality_comparable<nihil::error>);
- static_assert(std::totally_ordered<nihil::error>);
- static_assert(std::swappable<nihil::error>);
- static_assert(std::regular<nihil::error>);
-}
-
-SCENARIO("A nihil::error can be constructed from a C string", test_tags)
-{
- GIVEN ("An error object constructed from a string") {
- auto e = nihil::error("an error");
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error");
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::string lvalue", test_tags)
-{
- GIVEN ("An error object constructed from an std::string lvalue") {
- auto s = std::string("an error");
- auto e = nihil::error(s);
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error");
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::string rvalue", test_tags)
-{
- GIVEN ("An error object constructed from an std::string rvalue") {
- auto e = nihil::error(std::string("an error"));
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error");
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::string_view lvalue", test_tags)
-{
- GIVEN ("An error object constructed from an std::string_view lvalue") {
- auto s = std::string_view("an error");
- auto e = nihil::error(s);
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error");
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::string_view rvalue", test_tags)
-{
- GIVEN ("An error object constructed from an std::string_view rvalue") {
- auto e = nihil::error(std::string_view("an error"));
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error");
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::error_condition", test_tags)
-{
- GIVEN ("An error object constructed from std::errc::invalid_argument") {
- auto e = nihil::error(std::error_condition(std::errc::invalid_argument));
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "Invalid argument");
- }
-
- AND_THEN ("condition() should return the error code") {
- REQUIRE(e.condition().has_value());
- REQUIRE(*e.condition() == std::errc::invalid_argument);
- }
-
- AND_THEN ("The error should be comparable to the error code") {
- REQUIRE(e == std::errc::invalid_argument);
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from an std::errc", test_tags)
-{
- GIVEN ("An error object constructed from std::errc::invalid_argument") {
- auto e = nihil::error(std::errc::invalid_argument);
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "Invalid argument");
- }
-
- AND_THEN ("condition() should return the error code") {
- REQUIRE(e.condition().has_value());
- REQUIRE(*e.condition() == std::errc::invalid_argument);
- }
-
- AND_THEN ("The error should be comparable to the error code") {
- REQUIRE(e == std::errc::invalid_argument);
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed from a nihil::errc", test_tags)
-{
- GIVEN ("An error object constructed from std::errc::invalid_argument") {
- auto e = nihil::error(nihil::errc::incomplete_command);
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "Incomplete command");
- }
-
- AND_THEN ("condition() should return the error code") {
- REQUIRE(e.condition().has_value());
- REQUIRE(*e.condition() == nihil::errc::incomplete_command);
- }
-
- AND_THEN ("The error should be comparable to the error code") {
- REQUIRE(e == nihil::errc::incomplete_command);
- }
- }
-}
-
-SCENARIO("A nihil::error can be constructed with a cause", test_tags)
-{
- GIVEN ("An error object constructed with a cause") {
- auto e = nihil::error("an error", std::errc::invalid_argument);
-
- THEN ("full_str() should return the string") {
- REQUIRE(e.full_str() == "an error: Invalid argument");
- }
-
- AND_THEN ("cause() should return the cause") {
- REQUIRE(e.cause());
- REQUIRE(*e.cause() == std::errc::invalid_argument);
- }
- }
-}
-
-SCENARIO("std::format with a nihil::error", test_tags)
-{
- GIVEN ("A nihil::error with no cause") {
- auto e = nihil::error("an error");
-
- THEN ("std::format should return the string") {
- REQUIRE(std::format("{}", e) == "an error");
- }
-
- AND_THEN ("std::format should return the same as full_str()") {
- REQUIRE(std::format("{}", e) == e.full_str());
- }
- }
-
- GIVEN ("A nihil::error with a cause") {
- auto e = nihil::error("an error", std::errc::invalid_argument);
-
- THEN ("std::format should return the string") {
- REQUIRE(std::format("{}", e) == "an error: Invalid argument");
- }
-
- AND_THEN ("std::format should return the same as full_str()") {
- REQUIRE(std::format("{}", e) == e.full_str());
- }
- }
-}
-
-SCENARIO("Print a nihil::error to an std::ostream", test_tags)
-{
- GIVEN ("A nihil::error with no cause") {
- auto e = nihil::error("an error");
-
- THEN ("The error should be printed to the stream") {
- auto ss = std::stringstream();
- ss << e;
- REQUIRE(ss.str() == "an error");
- }
- }
-
- GIVEN ("A nihil::error with a cause") {
- auto e = nihil::error("an error", std::errc::invalid_argument);
-
- THEN ("The error should be printed to the stream") {
- auto ss = std::stringstream();
- ss << e;
- REQUIRE(ss.str() == "an error: Invalid argument");
- }
- }
-}
-
-SCENARIO("Comparison of nihil::error with operator==", test_tags)
-{
- GIVEN ("Two nihil::error objects constructed from the same string") {
- auto e1 = nihil::error("an error");
- auto e2 = nihil::error("an error");
-
- THEN ("The two objects should be equal") {
- REQUIRE(e1 == e2);
- }
- }
-
- GIVEN ("Two nihil::error objects constructed from different strings") {
- auto e1 = nihil::error("an error");
- auto e2 = nihil::error("another error");
-
- THEN ("The two objects should not be equal") {
- REQUIRE(e1 != e2);
- }
- }
-
- GIVEN ("Two nihil::error objects constructed from the same error code") {
- auto e1 = nihil::error(std::errc::invalid_argument);
- auto e2 = nihil::error(std::errc::invalid_argument);
-
- THEN ("The two objects should be equal") {
- REQUIRE(e1 == e2);
- }
- }
-
- GIVEN ("Two nihil::error objects constructed from different error codes") {
- auto e1 = nihil::error(std::errc::invalid_argument);
- auto e2 = nihil::error(std::errc::permission_denied);
-
- THEN ("The two objects should not be equal") {
- REQUIRE(e1 != e2);
- }
- }
-}
-
-SCENARIO("Comparison of nihil::error with operator<", test_tags)
-{
- GIVEN ("Two nihil::error objects constructed from the same string") {
- auto e1 = nihil::error("aaa");
- auto e2 = nihil::error("zzz");
-
- THEN ("aaa should be less than zzz") {
- REQUIRE(e1 < e2);
- }
- }
-}
-
-SCENARIO("Throwing and catching a nihil::error object", test_tags)
-{
- GIVEN ("A nihil::error object") {
- THEN ("We should be able to throw and catch the error") {
- REQUIRE_THROWS_AS(throw nihil::error("an error"), nihil::error);
-
- try {
- throw nihil::error("an error");
- } catch (nihil::error const &e) {
- REQUIRE(e.full_str() == "an error");
- };
- }
- }
-}
-
-} // anonymous namespace
diff --git a/nihil.util/flagset.ccm b/nihil.util/flagset.ccm
deleted file mode 100644
index 4c42223..0000000
--- a/nihil.util/flagset.ccm
+++ /dev/null
@@ -1,200 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:flagset;
-
-/*
- * flagset: a type-safe flags type.
- */
-
-import nihil.std;
-
-namespace nihil {
-
-export template <std::integral base_type, typename Tag>
-struct flagset final
-{
- using underlying_type = base_type;
-
- /*
- * Create an empty flags.
- */
- flagset() noexcept = default;
-
- /*
- * Copyable.
- */
- flagset(flagset const &other) noexcept
- : m_value(other.m_value)
- {
- }
-
- /*
- * Create flags from an integer mask.
- */
- template <base_type flag>
- [[nodiscard]] static constexpr auto mask() noexcept -> flagset
- {
- return flagset(flag);
- }
-
- /*
- * Create flags for a specific bit.
- */
- template <unsigned bitnr>
- [[nodiscard]] static constexpr auto bit() noexcept -> flagset
- {
- static_assert(bitnr < std::numeric_limits<base_type>::digits);
- return flagset(static_cast<base_type>(1) << bitnr);
- }
-
- /*
- * Create flags from a runtime value.
- */
- [[nodiscard]] static auto from_int(base_type value) noexcept -> flagset
- {
- return flagset(value);
- }
-
- /*
- * Assign this flagset.
- */
- auto operator=(this flagset &lhs, flagset rhs) noexcept -> flagset &
- {
- if (&lhs != &rhs)
- lhs.m_value = rhs.m_value;
- return lhs;
- }
-
- /*
- * The integer value of this flagset.
- */
- [[nodiscard]] constexpr auto value(this flagset self) noexcept -> base_type
- {
- return self.m_value;
- }
-
- /*
- * True if this flagset has any bits set.
- */
- [[nodiscard]] explicit constexpr operator bool(this flagset self) noexcept
- {
- return self.m_value != 0;
- }
-
- /*
- * Set bits.
- */
- constexpr auto operator|=(this flagset &lhs, flagset rhs) noexcept -> flagset &
- {
- lhs.m_value |= rhs.value();
- return lhs;
- }
-
- /*
- * Mask bits.
- */
- constexpr auto operator&=(this flagset &lhs, flagset rhs) noexcept -> flagset &
- {
- lhs.m_value &= rhs.value();
- return lhs;
- }
-
- /*
- * Invert bits.
- */
- [[nodiscard]] constexpr auto operator~(this flagset self) noexcept -> flagset
- {
- return flagset(~self.m_value);
- }
-
- /*
- * xor bits.
- */
- constexpr auto operator^=(this flagset &lhs, flagset rhs) noexcept -> flagset
- {
- lhs.m_value ^= rhs.value();
- return lhs;
- }
-
-private:
- base_type m_value = 0;
-
- explicit constexpr flagset(base_type mask) noexcept
- : m_value(mask)
- {
- }
-
- [[nodiscard]] friend auto operator|(flagset lhs, flagset rhs) noexcept -> flagset
- {
- return (lhs |= rhs);
- }
-
- [[nodiscard]] friend auto operator&(flagset lhs, flagset rhs) noexcept -> flagset
- {
- return (lhs &= rhs);
- }
-
- [[nodiscard]] friend auto operator^(flagset lhs, flagset rhs) noexcept -> flagset
- {
- return (lhs ^= rhs);
- }
-
- [[nodiscard]] friend auto
- operator==(flagset lhs, flagset rhs) noexcept -> bool
- {
- return lhs.value() == rhs.value();
- }
-
- friend auto operator<<(std::ostream &strm, flagset<base_type, Tag> flags) -> std::ostream &
- {
- std::print(strm, "{}", flags);
- return strm;
- }
-};
-
-} // namespace nihil
-
-/*
- * Formatting for flagset.
- */
-export template <std::integral base_type, typename Tag, typename Char>
-struct std::formatter<nihil::flagset<base_type, Tag>, Char>
-{
- using flags_t = nihil::flagset<base_type, Tag>;
-
- template <typename ParseContext>
- constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
- {
- return ctx.begin();
- }
-
- template <typename FmtContext>
- auto format(flags_t flags, FmtContext &ctx) const -> FmtContext::iterator
- {
- auto constexpr digits = std::numeric_limits<base_type>::digits;
- auto value = flags.value();
- auto it = ctx.out();
- *it++ = Char{'<'};
-
- auto printed_any = false;
-
- for (unsigned i = 0; i < digits; ++i) {
- unsigned bit = (digits - 1) - i;
-
- auto this_bit = static_cast<base_type>(1) << bit;
- if ((value & this_bit) == 0)
- continue;
-
- if (printed_any)
- *it++ = Char{','};
-
- if (bit > 10)
- *it++ = Char{'0'} + (bit / 10);
- *it++ = Char{'0'} + (bit % 10);
-
- printed_any = true;
- }
-
- *it++ = Char{'>'};
- return it;
- }
-};
diff --git a/nihil.util/flagset.test.cc b/nihil.util/flagset.test.cc
deleted file mode 100644
index 85cd0d3..0000000
--- a/nihil.util/flagset.test.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-struct test_tag
-{
-};
-using testflags = nihil::flagset<unsigned, test_tag>;
-
-constexpr auto zero = testflags::bit<0>();
-constexpr auto one = testflags::bit<1>();
-constexpr auto two = testflags::bit<2>();
-constexpr auto twelve = testflags::bit<12>();
-
-TEST_CASE("flagset: invariant", "[nihil]")
-{
- static_assert(std::regular<testflags>);
- static_assert(sizeof(testflags) == sizeof(testflags::underlying_type));
-}
-
-TEST_CASE("flagset: bit<>", "[nihil]")
-{
- REQUIRE(zero.value() == 0x1);
- REQUIRE(one.value() == 0x2);
-}
-
-TEST_CASE("flagset: mask<>", "[nihil]")
-{
- auto zero_ = testflags::mask<0x1>();
- auto one_ = testflags::mask<0x2>();
-
- REQUIRE(zero_ == zero);
- REQUIRE(one_ == one);
-}
-
-TEST_CASE("flagset: constructor", "[nihil]")
-{
- SECTION ("default construct") {
- auto flags = testflags();
- REQUIRE(flags.value() == 0);
- }
-
- SECTION ("construct from int") {
- auto flags = testflags::from_int(one.value() | zero.value());
- REQUIRE(flags == (one | zero));
- }
-
- SECTION ("copy construct") {
- auto flags = one;
- auto flags2(flags);
- REQUIRE(flags == flags2);
- }
-}
-
-TEST_CASE("flagset: operators", "[nihil]")
-{
- SECTION ("operator|") {
- REQUIRE((zero | one).value() == 0x3);
- }
-
- SECTION ("operator|=") {
- auto flags = zero;
- flags |= one;
- REQUIRE(flags.value() == 0x3);
- }
-
- SECTION ("operator&") {
- auto flags = zero | one;
-
- REQUIRE((flags & zero) == zero);
- }
-
- SECTION ("operator&=") {
- auto flags = zero | one | two;
- REQUIRE(flags.value() == 0x7);
- flags &= (zero | one);
- REQUIRE(flags.value() == 0x3);
- }
-
- SECTION ("operator^") {
- auto flags = zero | one;
- REQUIRE((flags ^ (one | two)) == (zero | two));
- }
-
- SECTION ("operator^=") {
- auto flags = zero | one;
- flags ^= (one | two);
- REQUIRE(flags == (zero | two));
- }
-
- SECTION ("operator~") {
- auto flags = ~zero;
- REQUIRE(flags.value() == ~static_cast<unsigned>(1));
- }
-
- SECTION ("operator==") {
- auto flags = zero;
- REQUIRE(flags == zero);
- REQUIRE(flags != one);
- }
-}
-
-TEST_CASE("flagset: assignment", "[nihil]")
-{
- auto flags = zero;
- REQUIRE(flags == zero);
-
- flags = one;
- REQUIRE(flags == one);
- REQUIRE(flags != zero);
-}
-
-TEST_CASE("flagset: format", "[nihil]")
-{
- REQUIRE(std::format("{}", testflags()) == "<>");
- REQUIRE(std::format("{}", zero) == "<0>");
- REQUIRE(std::format("{}", one) == "<1>");
- REQUIRE(std::format("{}", zero | one) == "<1,0>");
-
- REQUIRE(std::format("{}", twelve) == "<12>");
- REQUIRE(std::format("{}", twelve | one) == "<12,1>");
-}
-
-TEST_CASE("flagset: ostream operator<<", "[nihil]")
-{
- auto write = [](testflags flags) -> std::string {
- auto strm = std::ostringstream();
- strm << flags;
- return strm.str();
- };
-
- REQUIRE(write(testflags()) == "<>");
- REQUIRE(write(zero) == "<0>");
- REQUIRE(write(one) == "<1>");
- REQUIRE(write(zero | one) == "<1,0>");
-
- REQUIRE(write(twelve) == "<12>");
- REQUIRE(write(twelve | one) == "<12,1>");
-}
-} // anonymous namespace
diff --git a/nihil.util/guard.ccm b/nihil.util/guard.ccm
deleted file mode 100644
index 77394fc..0000000
--- a/nihil.util/guard.ccm
+++ /dev/null
@@ -1,41 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:guard;
-
-import nihil.std;
-
-namespace nihil {
-
-// guard: invoke a callable when this object is destroyed; this is similar to
-// scope_exit from the library fundamentals TS, which LLVM doesn't implement.
-export template<std::invocable F>
-struct guard final {
- // Initialise the guard with a callable we will invoke later.
- explicit guard(F func) : m_func(std::move(func)) {}
-
- // We are being destroyed, so call the callable.
- // If the callable throws, std::terminate() will be called.
- ~guard()
- {
- if (m_func)
- std::invoke(*m_func);
- }
-
- // Release the guard. This turns the destructor into a no-op.
- auto release(this guard &self) noexcept -> void
- {
- self.m_func.reset();
- }
-
- // Not default-constructible, movable or copyable.
- guard() = delete;
- guard(guard const &) = delete;
- guard(guard &&) noexcept = delete;
- auto operator=(this guard &, guard const &) -> guard & = delete;
- auto operator=(this guard &, guard &&) noexcept -> guard & = delete;
-
-private:
- // The callable to be invoked when we are destroyed.
- std::optional<F> m_func;
-};
-
-} // namespace nihil
diff --git a/nihil.util/guard.test.cc b/nihil.util/guard.test.cc
deleted file mode 100644
index 2c3ac2f..0000000
--- a/nihil.util/guard.test.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.util;
-
-TEST_CASE("guard: basic", "[guard]") {
- int n = 0;
-
- {
- auto guard = nihil::guard([&] { n = 1; });
- REQUIRE(n == 0);
- }
-
- REQUIRE(n == 1);
-}
diff --git a/nihil.util/match.ccm b/nihil.util/match.ccm
deleted file mode 100644
index b72416a..0000000
--- a/nihil.util/match.ccm
+++ /dev/null
@@ -1,18 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:match;
-
-import nihil.std;
-
-namespace nihil {
-
-export template<class... Ts>
-struct match : Ts... { using Ts::operator()...; };
-
-export template<typename... Ts, typename... Fs>
-[[nodiscard]] constexpr decltype(auto) operator|
- (std::variant<Ts...> const &v, match<Fs...> const &match)
-{
- return std::visit(match, v);
-}
-
-} // namespace nihil
diff --git a/nihil.util/match.test.cc b/nihil.util/match.test.cc
deleted file mode 100644
index 6cc0e27..0000000
--- a/nihil.util/match.test.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-TEST_CASE("match", "[nihil]")
-{
- using namespace nihil;
- using namespace std::literals;
-
- auto v = std::variant<int, std::string>(42);
-
- auto s = v | match {
- [](int) { return "int"s; },
- [](std::string const &) { return "string"s; }
- };
-
- REQUIRE(s == "int");
-
- v = "test"s;
-
- s = v | match {
- [](int) { return "int"s; },
- [](std::string const &) { return "string"s; }
- };
-
- REQUIRE(s == "string");
-}
-} // anonymous namespace
diff --git a/nihil.util/monad.ccm b/nihil.util/monad.ccm
deleted file mode 100644
index eefa1fe..0000000
--- a/nihil.util/monad.ccm
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * From https://github.com/toby-allsopp/coroutine_monad
- *
- * Copyright (c) 2017 Toby Allsopp
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-export module nihil.util:monad;
-
-import nihil.std;
-
-namespace nihil {
-
-/**********************************************************************
- * return_object_holder
- */
-
-// An object that starts out unitialized. Initialized by a call to emplace.
-export template <typename T>
-using deferred = std::optional<T>;
-
-export template <typename T>
-struct return_object_holder {
- // The staging object that is returned (by copy/move) to the caller of
- // the coroutine.
- deferred<T> stage;
- return_object_holder*& p;
-
- // When constructed, we assign a pointer to ourselves to the supplied
- // reference to pointer.
- return_object_holder(return_object_holder*& p)
- : stage{}
- , p(p)
- {
- p = this;
- }
-
- // Copying doesn't make any sense (which copy should the pointer refer
- // to?).
- return_object_holder(return_object_holder const&) = delete;
-
- // To move, we just update the pointer to point at the new object.
- return_object_holder(return_object_holder&& other)
- : stage(std::move(other.stage))
- , p(other.p)
- {
- p = this;
- }
-
- // Assignment doesn't make sense.
- void operator=(return_object_holder const&) = delete;
- void operator=(return_object_holder&&) = delete;
-
- // A non-trivial destructor is required until
- // https://bugs.llvm.org//show_bug.cgi?id=28593 is fixed.
- ~return_object_holder() {}
-
- // Construct the staging value; arguments are perfect forwarded to T's
- // constructor.
- template <typename... Args>
- void emplace(Args&&... args)
- {
- stage.emplace(std::forward<Args>(args)...);
- }
-
- // We assume that we will be converted only once, so we can move from
- // the staging object. We also assume that `emplace` has been called
- // at least once.
- operator T()
- {
- return std::move(*stage);
- }
-};
-
-export template <typename T>
-auto make_return_object_holder(return_object_holder<T>*& p)
-{
- return return_object_holder<T>{p};
-}
-
-/**********************************************************************
- * std::optional
- */
-
-template <typename T>
-struct optional_promise {
- return_object_holder<std::optional<T>>* data;
-
- auto get_return_object()
- {
- return make_return_object_holder(data);
- }
-
- auto initial_suspend() noexcept -> std::suspend_never
- {
- return {};
- }
-
- auto final_suspend() noexcept -> std::suspend_never
- {
- return {};
- }
-
- void return_value(T x)
- {
- data->emplace(std::move(x));
- }
-
- void unhandled_exception()
- {
- std::rethrow_exception(std::current_exception());
- }
-};
-
-} // namespace nihil
-
-export template <typename T, typename... Args>
-struct std::coroutine_traits<std::optional<T>, Args...> {
- using promise_type = nihil::optional_promise<T>;
-};
-
-namespace nihil {
-
-template <typename T>
-struct optional_awaitable {
- std::optional<T> o;
-
- auto await_ready()
- {
- return o.has_value();
- }
-
- auto await_resume()
- {
- return *o;
- }
-
- template <typename U>
- void await_suspend(std::coroutine_handle<optional_promise<U>> h)
- {
- h.promise().data->emplace(std::nullopt);
- h.destroy();
- }
-};
-
-} // namespace nihil
-
-namespace std {
-
-export template <typename T>
-auto operator co_await(std::optional<T> o) {
- return nihil::optional_awaitable<T>{std::move(o)};
-}
-
-} // namespace std
-
-/**********************************************************************
- * std::expected
- */
-
-namespace nihil {
-
-export template <typename T, typename E>
-struct expected_promise_base {
- return_object_holder<std::expected<T, E>>* data;
-
- auto get_return_object()
- {
- return make_return_object_holder(data);
- }
-
- auto initial_suspend() noexcept -> std::suspend_never
- {
- return {};
- }
-
- auto final_suspend() noexcept -> std::suspend_never
- {
- return {};
- }
-
- void unhandled_exception()
- {
- std::rethrow_exception(std::current_exception());
- }
-};
-
-export template <typename T, typename E>
-struct expected_promise : expected_promise_base<T, E> {
- void return_value(this expected_promise &self, std::unexpected<E> err)
- {
- self.data->emplace(std::move(err));
- }
-
- void return_value(this expected_promise &self, T o)
- {
- self.data->emplace(std::move(o));
- }
-};
-
-export template <typename E>
-struct expected_promise<void, E> : expected_promise_base<void, E> {
- void return_value(this expected_promise &self, std::unexpected<E> err)
- {
- self.data->emplace(std::move(err));
- }
-
- void return_value(this expected_promise &self, std::expected<void, E> o)
- {
- self.data->emplace(std::move(o));
- }
-};
-
-} // namespace nihil
-
-export template <typename T, typename E, typename... Args>
-struct std::coroutine_traits<std::expected<T, E>, Args...> {
- using promise_type = nihil::expected_promise<T, E>;
-};
-
-namespace nihil {
-
-export template<typename T, typename E>
-struct expected_awaitable_base {
- std::expected<T, E> o;
-
- auto await_ready()
- {
- return o.has_value();
- }
-
- template <typename P>
- void await_suspend(std::coroutine_handle<P> h)
- {
- h.promise().data->emplace(std::unexpected(o.error()));
- h.destroy();
- }
-};
-
-export template <typename T, typename E>
-struct expected_awaitable : expected_awaitable_base<T, E> {
- auto await_resume(this expected_awaitable &self)
- {
- return std::move(*self.o);
- }
-};
-
-export template <typename E>
-struct expected_awaitable<void, E> : expected_awaitable_base<void, E> {
- auto await_resume(this expected_awaitable &)
- {
- return std::expected<void, E>();
- }
-};
-
-} // namespace nihil
-
-namespace std {
-
-export template <typename T, typename E>
-auto operator co_await(std::expected<T, E> o) {
- return nihil::expected_awaitable<T, E>{std::move(o)};
-}
-
-} // namespace std
diff --git a/nihil.util/monad.test.cc b/nihil.util/monad.test.cc
deleted file mode 100644
index 7f39fca..0000000
--- a/nihil.util/monad.test.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-TEST_CASE("monad: co_await std::optional<> with value", "[nihil]")
-{
- auto get_value = [] -> std::optional<int> {
- return 42;
- };
-
- auto try_get_value = [&get_value] -> std::optional<int> {
- co_return co_await get_value();
- };
-
- auto o = try_get_value();
- REQUIRE(o == 42);
-}
-
-TEST_CASE("monad: co_await std::optional<> without value", "[nihil]")
-{
- auto get_value = [] -> std::optional<int> {
- return {};
- };
-
- auto try_get_value = [&get_value] -> std::optional<int> {
- co_return co_await get_value();
- };
-
- auto o = try_get_value();
- REQUIRE(!o.has_value());
-}
-
-TEST_CASE("monad: co_await std::expected<> with value", "[nihil]")
-{
- auto get_value = [] -> std::expected<int, std::string> {
- return 42;
- };
-
- auto try_get_value = [&get_value] -> std::expected<int, std::string> {
- co_return co_await get_value();
- };
-
- auto o = try_get_value();
- REQUIRE(o == 42);
-}
-
-TEST_CASE("monad: co_await std::expected<> with error", "[nihil]")
-{
- auto get_value = [] -> std::expected<int, std::string> {
- return std::unexpected("error");
- };
-
- auto try_get_value = [&get_value] -> std::expected<int, std::string> {
- co_return co_await get_value();
- };
-
- auto o = try_get_value();
- REQUIRE(!o);
- REQUIRE(o.error() == "error");
-}
-} // anonymous namespace
diff --git a/nihil.util/next_word.ccm b/nihil.util/next_word.ccm
deleted file mode 100644
index 89eeaee..0000000
--- a/nihil.util/next_word.ccm
+++ /dev/null
@@ -1,32 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:next_word;
-
-import :skipws;
-
-namespace nihil {
-
-// Return the next word from a string_view. Skips leading whitespace, so
-// calling this repeatedly will return each word from the string.
-export template <typename Char>
-[[nodiscard]]
-auto next_word(std::basic_string_view<Char> text, std::locale const &locale = std::locale())
- -> std::pair<std::basic_string_view<Char>, std::basic_string_view<Char>>
-{
- text = skipws(text, locale);
-
- auto is_space = ctype_is(std::ctype_base::space, locale);
- auto split_pos = std::ranges::find_if(text, is_space);
-
- return {{std::ranges::begin(text), split_pos}, {split_pos, std::ranges::end(text)}};
-}
-
-export template <typename Char>
-auto next_word(std::basic_string_view<Char> *text, std::locale const &locale = std::locale())
- -> std::basic_string_view<Char>
-{
- auto [word, rest] = next_word(*text, locale);
- *text = rest;
- return word;
-}
-
-} // namespace nihil
diff --git a/nihil.util/next_word.test.cc b/nihil.util/next_word.test.cc
deleted file mode 100644
index 87d491a..0000000
--- a/nihil.util/next_word.test.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-namespace {
-
-TEST_CASE("next_word: basic", "[next_word]")
-{
- using namespace std::literals;
- auto s = "foo bar baz"sv;
-
- auto words = nihil::next_word(s);
- REQUIRE(words.first == "foo");
- REQUIRE(words.second == " bar baz");
-
- auto word = nihil::next_word(&s);
- REQUIRE(word == "foo");
- REQUIRE(s == " bar baz");
-}
-
-TEST_CASE("next_word: multiple spaces", "[next_word]")
-{
- using namespace std::literals;
- auto s = "foo bar baz"sv;
-
- auto words = nihil::next_word(s);
- REQUIRE(words.first == "foo");
- REQUIRE(words.second == " bar baz");
-
- auto word = nihil::next_word(&s);
- REQUIRE(word == "foo");
- REQUIRE(s == " bar baz");
-}
-
-TEST_CASE("next_word: leading spaces", "[next_word]")
-{
- using namespace std::literals;
- auto s = " \tfoo bar baz"sv;
-
- auto words = nihil::next_word(s);
- REQUIRE(words.first == "foo");
- REQUIRE(words.second == " bar baz");
-
- auto word = nihil::next_word(&s);
- REQUIRE(word == "foo");
- REQUIRE(s == " bar baz");
-}
-
-TEST_CASE("next_word: locale", "[next_word]")
-{
- using namespace std::literals;
- auto s = L"\u2003foo\u2003bar\u2003baz"sv;
-
- auto words = nihil::next_word(s);
- REQUIRE(words.first == s);
-
- words = nihil::next_word(s, std::locale("C.UTF-8"));
- REQUIRE(words.first == L"foo");
- REQUIRE(words.second == L"\u2003bar\u2003baz");
-}
-
-} // anonymous namespace
diff --git a/nihil.util/nihil.util.ccm b/nihil.util/nihil.util.ccm
deleted file mode 100644
index 49e5687..0000000
--- a/nihil.util/nihil.util.ccm
+++ /dev/null
@@ -1,18 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util;
-
-export import :capture_stream;
-export import :construct;
-export import :ctype;
-export import :error;
-export import :flagset;
-export import :guard;
-export import :match;
-export import :monad;
-export import :parse_size;
-export import :next_word;
-export import :save_errno;
-export import :skipws;
-export import :sys_error;
-export import :tabulate;
-export import :uuid;
diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm
deleted file mode 100644
index 51bfb4f..0000000
--- a/nihil.util/parse_size.ccm
+++ /dev/null
@@ -1,90 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:parse_size;
-
-import nihil.std;
-import nihil.core;
-
-import :ctype;
-import :error;
-import :monad;
-
-namespace nihil {
-
-export template <typename Char>
-auto get_multiplier(Char c) -> std::expected<std::uint64_t, error>
-{
- auto ret = std::uint64_t{1};
-
- // clang-format off
- switch (c) {
- case 'p': case 'P': ret *= 1024; // NOLINT
- case 't': case 'T': ret *= 1024; // NOLINT
- case 'g': case 'G': ret *= 1024; // NOLINT
- case 'm': case 'M': ret *= 1024; // NOLINT
- case 'k': case 'K': ret *= 1024; // NOLINT
- return ret;
-
- default:
- return error(errc::invalid_unit);
- }
- // clang-format on
-}
-
-// Parse a string containing a human-formatted size, such as "1024"
-// or "4g". Parsing is always done in the "C" locale and does not
-// recognise thousands separators or negative numbers.
-export template <typename T, typename Char>
-[[nodiscard]]
-auto parse_size(std::basic_string_view<Char> str) -> std::expected<T, error>
-{
- // Extract the numeric part of the string.
- auto it = std::ranges::find_if_not(str, is_c_digit);
- auto num_str = std::basic_string_view<Char>(std::ranges::begin(str), it);
-
- if (num_str.empty())
- co_return error(errc::empty_string);
-
- auto ret = T{0};
-
- for (auto c : num_str) {
- if (ret > (std::numeric_limits<T>::max() / 10))
- co_return error(std::errc::result_out_of_range);
- ret *= 10;
-
- auto digit = static_cast<T>(c - '0');
- if ((std::numeric_limits<T>::max() - digit) < ret)
- co_return error(std::errc::result_out_of_range);
- ret += digit;
- }
-
- if (it == str.end())
- // No multiplier.
- co_return ret;
-
- auto mchar = *it++;
-
- if (it != str.end())
- // Multiplier is more than one character.
- co_return error(errc::invalid_unit);
-
- auto mult = co_await get_multiplier(mchar);
-
- if (std::cmp_greater(ret, std::numeric_limits<T>::max() / mult))
- co_return error(std::errc::result_out_of_range);
-
- co_return ret *mult;
-}
-
-export template <typename T>
-[[nodiscard]] auto parse_size(char const *s)
-{
- return parse_size<T>(std::string_view(s));
-}
-
-export template <typename T>
-[[nodiscard]] auto parse_size(wchar_t const *s)
-{
- return parse_size<T>(std::wstring_view(s));
-}
-
-} // namespace nihil
diff --git a/nihil.util/parse_size.test.cc b/nihil.util/parse_size.test.cc
deleted file mode 100644
index ee97996..0000000
--- a/nihil.util/parse_size.test.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.core;
-import nihil.util;
-
-namespace {
-TEST_CASE("parse_size: empty value", "[nihil]")
-{
- using namespace nihil;
-
- auto n = parse_size<std::uint64_t>("");
- REQUIRE(!n);
- REQUIRE(n.error() == nihil::errc::empty_string);
-}
-
-TEST_CASE("parse_size: basic", "[nihil]")
-{
- using namespace nihil;
-
- SECTION ("bare number") {
- auto n = parse_size<std::uint64_t>("1024").value();
- REQUIRE(n == 1024);
- }
-
- SECTION ("max value, unsigned") {
- auto n = parse_size<std::uint16_t>("65535").value();
- REQUIRE(n == 65535);
- }
-
- SECTION ("max value, signed") {
- auto n = parse_size<std::uint16_t>("32767").value();
- REQUIRE(n == 32767);
- }
-
- SECTION ("overflow by 1, unsigned") {
- auto n = parse_size<std::uint16_t>("65536");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-
- SECTION ("overflow by 1, signed") {
- auto n = parse_size<std::int16_t>("32768");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-
- SECTION ("overflow by many, unsigned") {
- auto n = parse_size<std::uint16_t>("100000");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-
- SECTION ("overflow by many, signed") {
- auto n = parse_size<std::int16_t>("100000");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-}
-
-TEST_CASE("parse_size: invalid multiplier", "[nihil]")
-{
- using namespace nihil;
-
- auto n = parse_size<std::uint64_t>("4z");
- REQUIRE(!n);
- REQUIRE(n.error() == nihil::errc::invalid_unit);
-
- n = parse_size<std::uint64_t>("4kz");
- REQUIRE(!n);
- REQUIRE(n.error() == nihil::errc::invalid_unit);
-}
-
-TEST_CASE("parse_size: multipliers", "[nihil]")
-{
- using namespace nihil;
-
- auto sf = static_cast<std::uint64_t>(4);
-
- SECTION ("k") {
- auto n = parse_size<std::uint64_t>("4k").value();
- REQUIRE(n == sf * 1024);
- }
-
- SECTION ("m") {
- auto n = parse_size<std::uint64_t>("4m").value();
- REQUIRE(n == sf * 1024 * 1024);
- }
-
- SECTION ("g") {
- auto n = parse_size<std::uint64_t>("4g").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024);
- }
-
- SECTION ("t") {
- auto n = parse_size<std::uint64_t>("4t").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024);
- }
-
- SECTION ("p") {
- auto n = parse_size<std::uint64_t>("4p").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024 * 1024);
- }
-}
-
-TEST_CASE("parse_size: multiplier overflow", "[nihil]")
-{
- using namespace nihil;
-
- SECTION ("signed") {
- auto n = parse_size<std::uint16_t>("64k");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-
- SECTION ("unsigned") {
- auto n = parse_size<std::int16_t>("32k");
- REQUIRE(!n);
- REQUIRE(n.error() == std::errc::result_out_of_range);
- }
-}
-
-TEST_CASE("parse_size: wide", "[nihil]")
-{
- using namespace nihil;
-
- SECTION ("bare number") {
- auto n = parse_size<std::uint64_t>(L"1024").value();
- REQUIRE(n == 1024);
- }
-}
-
-TEST_CASE("parse_size: wide multipliers", "[nihil]")
-{
- using namespace nihil;
-
- auto sf = static_cast<std::uint64_t>(4);
-
- SECTION ("k") {
- auto n = parse_size<std::uint64_t>(L"4k").value();
- REQUIRE(n == sf * 1024);
- }
-
- SECTION ("m") {
- auto n = parse_size<std::uint64_t>(L"4m").value();
- REQUIRE(n == sf * 1024 * 1024);
- }
-
- SECTION ("g") {
- auto n = parse_size<std::uint64_t>(L"4g").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024);
- }
-
- SECTION ("t") {
- auto n = parse_size<std::uint64_t>(L"4t").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024);
- }
-
- SECTION ("p") {
- auto n = parse_size<std::uint64_t>(L"4p").value();
- REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024 * 1024);
- }
-}
-} // anonymous namespace
diff --git a/nihil.util/save_errno.ccm b/nihil.util/save_errno.ccm
deleted file mode 100644
index 27567f8..0000000
--- a/nihil.util/save_errno.ccm
+++ /dev/null
@@ -1,35 +0,0 @@
-// This source code is released into the public domain.
-module;
-
-#include <cerrno>
-
-export module nihil.util:save_errno;
-
-// save_errno: save the current value of errno and restore it when we're destroyed.
-// this allows wrappers around C functions that use errno to preserve the caller's
-// errno value.
-
-namespace nihil {
-
-export struct save_errno final
-{
- save_errno() : m_errno(errno) {}
-
- ~save_errno()
- {
- errno = m_errno;
- }
-
- // Not copyable
- save_errno(const save_errno&) = delete;
- auto operator=(const save_errno&) -> save_errno & = delete;
-
- // Not movable
- save_errno(save_errno&&) = delete;
- auto operator=(save_errno&&) -> save_errno & = delete;
-
-private:
- int m_errno;
-};
-
-} // namespace nihil
diff --git a/nihil.util/skipws.ccm b/nihil.util/skipws.ccm
deleted file mode 100644
index 0a15775..0000000
--- a/nihil.util/skipws.ccm
+++ /dev/null
@@ -1,25 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:skipws;
-
-import :ctype;
-
-namespace nihil {
-
-// Remove leading whitespace from a string.
-export template <typename Char>
-[[nodiscard]]
-auto skipws(std::basic_string_view<Char> text, std::locale const &locale = std::locale())
- -> std::basic_string_view<Char>
-{
- auto is_space = ctype_is(std::ctype_base::space, locale);
- auto nonws = std::ranges::find_if_not(text, is_space);
- return {nonws, std::ranges::end(text)};
-}
-
-export template <typename Char>
-auto skipws(std::basic_string_view<Char> *text, std::locale const &locale = std::locale()) -> void
-{
- *text = skipws(*text, locale);
-}
-
-} // namespace nihil
diff --git a/nihil.util/skipws.test.cc b/nihil.util/skipws.test.cc
deleted file mode 100644
index 0cb741c..0000000
--- a/nihil.util/skipws.test.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-TEST_CASE("skipws: basic", "[skipws]")
-{
- using namespace std::literals;
-
- REQUIRE(nihil::skipws("foo"sv) == "foo");
- REQUIRE(nihil::skipws(" foo"sv) == "foo");
- REQUIRE(nihil::skipws("foo "sv) == "foo ");
- REQUIRE(nihil::skipws("foo bar"sv) == "foo bar");
-}
-
-TEST_CASE("skipws: pointer", "[skipws]")
-{
- using namespace std::literals;
-
- auto s = "foo"sv;
- nihil::skipws(&s);
- REQUIRE(s == "foo");
-
- s = " foo"sv;
- nihil::skipws(&s);
- REQUIRE(s == "foo");
-
- s = "foo "sv;
- nihil::skipws(&s);
- REQUIRE(s == "foo ");
-
- s = "foo bar"sv;
- nihil::skipws(&s);
- REQUIRE(s == "foo bar");
-}
-
-TEST_CASE("skipws: locale", "[skipws]")
-{
- using namespace std::literals;
-
- // Assume the default locale is C.
- REQUIRE(nihil::skipws(L"\u2003foo"sv) == L"\u2003foo");
- REQUIRE(nihil::skipws(L"\u2003foo"sv, std::locale("C.UTF-8")) == L"foo");
-}
diff --git a/nihil.util/sys_error.ccm b/nihil.util/sys_error.ccm
deleted file mode 100644
index a39552e..0000000
--- a/nihil.util/sys_error.ccm
+++ /dev/null
@@ -1,18 +0,0 @@
-// This source code is released into the public domain.
-module;
-
-#include <cerrno>
-
-export module nihil.util:sys_error;
-
-import nihil.std;
-
-namespace nihil {
-
-// Allow access to errno without having to include <cerrno>.
-export [[nodiscard]] auto sys_error() -> std::errc
-{
- return static_cast<std::errc>(errno); // NOLINT
-}
-
-} // namespace nihil
diff --git a/nihil.util/tabulate.ccm b/nihil.util/tabulate.ccm
deleted file mode 100644
index 762e2a3..0000000
--- a/nihil.util/tabulate.ccm
+++ /dev/null
@@ -1,298 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.util:tabulate;
-
-import nihil.std;
-
-import :ctype;
-import :error;
-
-namespace nihil {
-
-// tabulate: format the given range in an ASCII table and write the output
-// to the given output iterator. The range's values will be converted to
-// strings as if by std::format.
-//
-// tabulate is implemented by copying the range; this allows it to work on
-// input/forward ranges at the cost of slightly increased memory use.
-//
-// The table spec is a string consisting of zero or more field formats,
-// formatted as {flags:fieldname}; both flags and fieldname are optional.
-// If there are fewer field formats than fields, the remaining fields
-// are formatted as if by {:}.
-//
-// The following flags are supported:
-//
-// < left-align this column (default)
-// > right-align this column
-
-// Exception thrown when a table spec is invalid.
-export struct table_spec_error : error {
- explicit table_spec_error(std::string_view what)
- : error(what)
- {
- }
-};
-
-// The specification for a single field.
-template<typename Char>
-struct field_spec {
- enum align_t { left, right };
-
- // Get the name of this field.
- auto name(this field_spec const &self)
- -> std::basic_string_view<Char>
- {
- return self.m_name;
- }
-
- // Set the name of this field.
- auto name(this field_spec &self,
- std::basic_string_view<Char> new_name)
- -> void
- {
- self.m_name = new_name;
- }
-
- // Set this field's alignment.
- auto align(this field_spec &self, align_t new_align) -> void
- {
- self.m_align = new_align;
- }
-
- // Ensure the length of this field is at least the given width.
- auto ensure_width(this field_spec &self, std::size_t newwidth)
- -> void
- {
- self.m_width = std::max(self.m_width, newwidth);
- }
-
- // Format an object to a string based on our field spec.
- [[nodiscard]] auto format(this field_spec const &, auto &&obj)
- -> std::basic_string<Char>
- {
- auto format_string = std::basic_string<Char>{'{', '}'};
- return std::format(std::runtime_format(format_string), obj);
- }
-
- // Print a column value to an output iterator according to our field
- // spec. If is_last is true, this is the last field on the line, so
- // we won't output any trailling padding.
- auto print(this field_spec const &self,
- std::basic_string_view<Char> value,
- std::output_iterator<Char> auto &out,
- bool is_last)
- -> void
- {
- auto padding = self.m_width - value.size();
-
- if (self.m_align == right)
- for (std::size_t i = 0; i < padding; ++i)
- *out++ = ' ';
-
- std::ranges::copy(value, out);
-
- if (!is_last && self.m_align == left)
- for (std::size_t i = 0; i < padding; ++i)
- *out++ = ' ';
- }
-
-private:
- std::basic_string_view<Char> m_name;
- std::size_t m_width = 0;
- align_t m_align = left;
-};
-
-/*
- * The specification for an entire table.
- */
-template<typename Char>
-struct table_spec {
- // Add a new field spec to this table.
- auto add(this table_spec &self, field_spec<Char> field) -> void
- {
- self.m_fields.emplace_back(std::move(field));
- }
-
- // Return the field spec for a given field. If the field doesn't
- // exist, this field and any intermediate fields will be created.
- [[nodiscard]] auto field(this table_spec &self, std::size_t fieldnr)
- -> field_spec<Char> &
- {
- if (fieldnr >= self.m_fields.size())
- self.m_fields.resize(fieldnr + 1);
- return self.m_fields.at(fieldnr);
- }
-
- // The number of columns in this table.
- [[nodiscard]] auto columns(this table_spec const &self) -> std::size_t
- {
- return self.m_fields.size();
- }
-
- // Return all the fields in this table.
- [[nodiscard]] auto fields(this table_spec const &self)
- -> std::vector<field_spec<Char>> const &
- {
- return self.m_fields;
- }
-
-private:
- std::vector<field_spec<Char>> m_fields;
-};
-
-// Parse the field flags, e.g. '<'.
-template<typename Char,
- std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
-auto parse_field_flags(field_spec<Char> &field, Iterator &pos, Sentinel end)
- -> void
-{
- while (pos < end) {
- switch (*pos) {
- case '<':
- field.align(field_spec<Char>::left);
- break;
- case '>':
- field.align(field_spec<Char>::right);
- break;
- case ':':
- ++pos;
- /*FALLTHROUGH*/
- case '}':
- return;
- default:
- throw table_spec_error("Invalid table spec: "
- "unknown flag character");
- }
-
- if (++pos == end)
- throw table_spec_error("Invalid table spec: "
- "unterminated field");
- }
-}
-
-// Parse a complete field spec, e.g. "{<:NAME}".
-template<typename Char,
- std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
-[[nodiscard]] auto parse_field(Iterator &pos, Sentinel end)
- -> field_spec<Char>
-{
- auto field = field_spec<Char>{};
-
- if (pos == end)
- throw table_spec_error("Invalid table spec: empty field");
-
- // The field spec should start with a '{'.
- if (*pos != '{')
- throw table_spec_error("Invalid table spec: expected '{'");
-
- if (++pos == end)
- throw table_spec_error("Invalid table spec: unterminated field");
-
- // This consumes 'pos' up to and including the ':'.
- parse_field_flags(field, pos, end);
-
- auto brace = std::ranges::find(pos, end, '}');
- if (brace == end)
- throw table_spec_error("Invalid table spec: expected '}'");
-
- field.name(std::basic_string_view<Char>(pos, brace));
- pos = std::next(brace);
-
- // The field must be at least as wide as its header.
- field.ensure_width(field.name().size());
-
- return field;
-}
-
-template<typename Char>
-[[nodiscard]] auto parse_table_spec(std::basic_string_view<Char> spec)
- -> table_spec<Char>
-{
- auto table = table_spec<Char>();
-
- auto pos = std::ranges::begin(spec);
- auto end = std::ranges::end(spec);
-
- for (;;) {
- // Skip leading whitespace
- while (pos < end && is_c_space(*pos))
- ++pos;
-
- if (pos == end)
- break;
-
- table.add(parse_field<Char>(pos, end));
- }
-
- return table;
-}
-
-export template<typename Char,
- std::ranges::range Range,
- std::output_iterator<Char> Iterator>
-auto basic_tabulate(std::basic_string_view<Char> table_spec,
- Range &&range,
- Iterator &&out)
- -> void
-{
- // Parse the table spec.
- auto table = parse_table_spec(table_spec);
-
- // Create our copy of the input data.
- auto data = std::vector<std::vector<std::basic_string<Char>>>();
- // Reserve the first row for the header.
- data.resize(1);
-
- // Find the required length of each field.
- for (auto &&row : range) {
- // LLVM doesn't have std::enumerate_view yet
- auto i = std::size_t{0};
- auto &this_row = data.emplace_back();
-
- for (auto &&column : row) {
- auto &field = table.field(i);
- auto &str = this_row.emplace_back(field.format(column));
- field.ensure_width(str.size());
- ++i;
- }
- }
-
- // Add the header row.
- for (auto &&field : table.fields())
- data.at(0).emplace_back(std::from_range, field.name());
-
- // Print the values.
- for (auto &&row : data) {
- for (std::size_t i = 0; i < row.size(); ++i) {
- auto &field = table.field(i);
- bool is_last = (i == row.size() - 1);
-
- field.print(row[i], out, is_last);
-
- if (!is_last)
- *out++ = ' ';
- }
-
- *out++ = '\n';
- }
-}
-
-export auto tabulate(std::string_view table_spec,
- std::ranges::range auto &&range,
- std::output_iterator<char> auto &&out)
-{
- return basic_tabulate<char>(table_spec,
- std::forward<decltype(range)>(range),
- std::forward<decltype(out)>(out));
-}
-
-export auto wtabulate(std::wstring_view table_spec,
- std::ranges::range auto &&range,
- std::output_iterator<wchar_t> auto &&out)
-{
- return basic_tabulate<wchar_t>(table_spec,
- std::forward<decltype(range)>(range),
- std::forward<decltype(out)>(out));
-}
-
-} // namespace nihil
diff --git a/nihil.util/tabulate.test.cc b/nihil.util/tabulate.test.cc
deleted file mode 100644
index 408cc18..0000000
--- a/nihil.util/tabulate.test.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// This source code is released into the public domain.
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-using namespace std::literals;
-using namespace nihil;
-
-TEST_CASE("tabulate: basic", "[tabulate]")
-{
- auto input = std::vector{
- std::vector{"a", "foo", "b"},
- std::vector{"bar", "c", "baz"},
- };
-
- auto result = std::string();
- tabulate("{:1} {:2} {:3}", input, std::back_inserter(result));
- REQUIRE(result ==
-"1 2 3\n"
-"a foo b\n"
-"bar c baz\n");
-}
-
-TEST_CASE("tabulate: basic wide", "[tabulate]")
-{
- auto input = std::vector{
- std::vector{L"a", L"foo", L"b"},
- std::vector{L"bar", L"c", L"baz"},
- };
-
- auto result = std::wstring();
- wtabulate(L"{:1} {:2} {:3}", input, std::back_inserter(result));
-
- REQUIRE(result ==
-L"1 2 3\n"
-"a foo b\n"
-"bar c baz\n");
-}
-
-TEST_CASE("tabulate: jagged", "[tabulate]")
-{
- auto input = std::vector{
- std::vector{"a", "foo", "b"},
- std::vector{"bar", "baz"},
- };
-
- auto result = std::string();
- tabulate("{:1} {:2} {:3}", input, std::back_inserter(result));
- REQUIRE(result ==
-"1 2 3\n"
-"a foo b\n"
-"bar baz\n");
-}
-
-TEST_CASE("tabulate: align", "[tabulate]")
-{
- auto input = std::vector{
- std::vector{"a", "longvalue", "s"},
- std::vector{"a", "s", "longvalue"},
- };
-
- auto result = std::string();
- tabulate("{:1} {<:2} {>:3}", input, std::back_inserter(result));
- REQUIRE(result ==
-"1 2 3\n"
-"a longvalue s\n"
-"a s longvalue\n");
-}
diff --git a/nihil.util/uuid.ccm b/nihil.util/uuid.ccm
deleted file mode 100644
index 7b5727c..0000000
--- a/nihil.util/uuid.ccm
+++ /dev/null
@@ -1,768 +0,0 @@
-// From https://github.com/mariusbancila/stduuid
-//
-// Copyright (c) 2017
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-
-export module nihil.util:uuid;
-
-import nihil.std;
-
-namespace nihil {
-
-template <typename TChar>
-[[nodiscard]] constexpr auto hex2char(TChar const ch) noexcept -> unsigned char
-{
- if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9'))
- return static_cast<unsigned char>(ch - static_cast<TChar>('0'));
-
- if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f'))
- return static_cast<unsigned char>(10 + ch - static_cast<TChar>('a'));
-
- if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F'))
- return static_cast<unsigned char>(10 + ch - static_cast<TChar>('A'));
-
- return 0;
-}
-
-template <typename TChar>
-[[nodiscard]] constexpr auto is_hex(TChar const ch) noexcept -> bool
-{
- return (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) ||
- (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) ||
- (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F'));
-}
-
-template <typename TChar>
-[[nodiscard]] constexpr auto
-to_string_view(TChar const *str) noexcept -> std::basic_string_view<TChar>
-{
- if (str)
- return str;
- return {};
-}
-
-template <typename StringType>
-[[nodiscard]] constexpr auto to_string_view(StringType const &str) noexcept
- -> std::basic_string_view<typename StringType::value_type, typename StringType::traits_type>
-{
- return str;
-}
-
-struct sha1
-{
- using digest32_t = std::array<std::uint32_t, 5>;
- using digest8_t = std::array<std::uint8_t, 20>;
-
- static constexpr unsigned int block_bytes = 64;
-
- sha1()
- {
- reset();
- }
-
- [[nodiscard]] static auto
- left_rotate(std::uint32_t value, std::size_t const count) noexcept -> std::uint32_t
- {
- return (value << count) ^ (value >> (32 - count));
- }
-
- auto reset(this sha1 &self) noexcept -> void
- {
- self.m_digest[0] = 0x67452301;
- self.m_digest[1] = 0xEFCDAB89;
- self.m_digest[2] = 0x98BADCFE;
- self.m_digest[3] = 0x10325476;
- self.m_digest[4] = 0xC3D2E1F0;
- self.m_blockByteIndex = 0;
- self.m_byteCount = 0;
- }
-
- auto process_byte(this sha1 &self, std::uint8_t octet) -> void
- {
- self.m_block.at(self.m_blockByteIndex++) = octet;
- ++self.m_byteCount;
-
- if (self.m_blockByteIndex == block_bytes) {
- self.m_blockByteIndex = 0;
- self.process_block();
- }
- }
-
- auto process_block(this sha1 &self, void const *const start, void const *const end) -> void
- {
- auto const *first = static_cast<std::uint8_t const *>(start);
- auto const *last = static_cast<std::uint8_t const *>(end);
-
- while (first != last) {
- self.process_byte(*first);
- first++;
- }
- }
-
- auto process_bytes(this sha1 &self, void const *const data, std::size_t const len) -> void
- {
- auto *block = static_cast<std::uint8_t const *>(data);
- self.process_block(block, block + len);
- }
-
- auto get_digest(this sha1 &self) -> digest32_t
- {
- auto const bit_count = self.m_byteCount * 8;
-
- self.process_byte(0x80);
- if (self.m_blockByteIndex > 56) {
- while (self.m_blockByteIndex != 0)
- self.process_byte(0);
-
- while (self.m_blockByteIndex < 56)
- self.process_byte(0);
- } else {
- while (self.m_blockByteIndex < 56)
- self.process_byte(0);
- }
-
- self.process_byte(0);
- self.process_byte(0);
- self.process_byte(0);
- self.process_byte(0);
- self.process_byte(static_cast<unsigned char>((bit_count >> 24U) & 0xFFU));
- self.process_byte(static_cast<unsigned char>((bit_count >> 16U) & 0xFFU));
- self.process_byte(static_cast<unsigned char>((bit_count >> 8U) & 0xFFU));
- self.process_byte(static_cast<unsigned char>((bit_count) & 0xFFU));
-
- return self.m_digest;
- }
-
- auto get_digest_bytes(this sha1 &self) -> digest8_t
- {
- auto d32 = self.get_digest();
-
- return {
- static_cast<std::uint8_t>(d32[0] >> 24U),
- static_cast<std::uint8_t>(d32[0] >> 16U),
- static_cast<std::uint8_t>(d32[0] >> 8U),
- static_cast<std::uint8_t>(d32[0] >> 0U),
-
- static_cast<std::uint8_t>(d32[1] >> 24U),
- static_cast<std::uint8_t>(d32[1] >> 16U),
- static_cast<std::uint8_t>(d32[1] >> 8U),
- static_cast<std::uint8_t>(d32[1] >> 0U),
-
- static_cast<std::uint8_t>(d32[2] >> 24U),
- static_cast<std::uint8_t>(d32[2] >> 16U),
- static_cast<std::uint8_t>(d32[2] >> 8U),
- static_cast<std::uint8_t>(d32[2] >> 0U),
-
- static_cast<std::uint8_t>(d32[3] >> 24U),
- static_cast<std::uint8_t>(d32[3] >> 16U),
- static_cast<std::uint8_t>(d32[3] >> 8U),
- static_cast<std::uint8_t>(d32[3] >> 0U),
-
- static_cast<std::uint8_t>(d32[4] >> 24U),
- static_cast<std::uint8_t>(d32[4] >> 16U),
- static_cast<std::uint8_t>(d32[4] >> 8U),
- static_cast<std::uint8_t>(d32[4] >> 0U),
- };
- }
-
-private:
- auto process_block(this sha1 &self) -> void
- {
- auto w = std::array<std::uint32_t, 80>{};
-
- for (std::size_t i = 0; i < 16; i++) {
- w.at(i) = static_cast<std::uint32_t>(self.m_block.at((i * 4) + 0)) << 24U;
- w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 1)) << 16U;
- w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 2)) << 8U;
- w.at(i) |= static_cast<std::uint32_t>(self.m_block.at((i * 4) + 3));
- }
-
- for (std::size_t i = 16; i < 80; i++) {
- w.at(i) = left_rotate(
- (w.at(i - 3) ^ w.at(i - 8) ^ w.at(i - 14) ^ w.at(i - 16)), 1);
- }
-
- auto a = self.m_digest[0];
- auto b = self.m_digest[1];
- auto c = self.m_digest[2];
- auto d = self.m_digest[3];
- auto e = self.m_digest[4];
-
- for (std::size_t i = 0; i < 80; ++i) {
- auto f = std::uint32_t{0};
- auto k = std::uint32_t{0};
-
- if (i < 20) {
- f = (b & c) | (~b & d);
- k = 0x5A827999;
- } else if (i < 40) {
- f = b ^ c ^ d;
- k = 0x6ED9EBA1;
- } else if (i < 60) {
- f = (b & c) | (b & d) | (c & d);
- k = 0x8F1BBCDC;
- } else {
- f = b ^ c ^ d;
- k = 0xCA62C1D6;
- }
-
- auto temp = std::uint32_t{left_rotate(a, 5) + f + e + k + w.at(i)};
- e = d;
- d = c;
- c = left_rotate(b, 30);
- b = a;
- a = temp;
- }
-
- self.m_digest[0] += a;
- self.m_digest[1] += b;
- self.m_digest[2] += c;
- self.m_digest[3] += d;
- self.m_digest[4] += e;
- }
-
- digest32_t m_digest{};
- std::array<std::uint8_t, 64> m_block{};
- std::size_t m_blockByteIndex{};
- std::size_t m_byteCount{};
-};
-
-template <typename CharT>
-inline constexpr std::string_view empty_guid = "00000000-0000-0000-0000-000000000000";
-
-template <>
-inline constexpr std::wstring_view empty_guid<wchar_t> = L"00000000-0000-0000-0000-000000000000";
-
-template <typename CharT>
-inline constexpr std::string_view guid_encoder = "0123456789abcdef";
-
-template <>
-inline constexpr std::wstring_view guid_encoder<wchar_t> = L"0123456789abcdef";
-
-// ---------------------------------------------------------------------
-// UUID format https://tools.ietf.org/html/rfc4122
-// ---------------------------------------------------------------------
-
-// ---------------------------------------------------------------------
-// Field NDR Data Type Octet # Note
-// Note
-// ---------------------------------------------------------------------
-// time_low unsigned long 0 - 3
-// The low field of the timestamp.
-// time_mid unsigned short 4 - 5
-// The middle field of the timestamp.
-// time_hi_and_version unsigned short 6 - 7
-// The high field of the timestamp multiplexed with the version number.
-// clock_seq_hi_and_reserved unsigned small 8
-// The high field of the clock sequence multiplexed with the variant.
-// clock_seq_low unsigned small 9
-// The low field of the clock sequence.
-// node character 10 - 15
-// The spatially unique node identifier.
-// ---------------------------------------------------------------------
-// 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) |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-// ---------------------------------------------------------------------
-// enumerations
-// ---------------------------------------------------------------------
-
-// indicated by a bit pattern in octet 8, marked with N in
-// xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx
-export enum struct uuid_variant : std::uint8_t {
- // NCS backward compatibility (with the obsolete Apollo Network
- // Computing System 1.5 UUID format).
- // N bit pattern: 0xxx
- // > the first 6 octets of the UUID are a 48-bit timestamp (the number
- // of 4 microsecond units of time since 1 Jan 1980 UTC);
- // > the next 2 octets are reserved;
- // > the next octet is the "address family";
- // > the final 7 octets are a 56-bit host ID in the form specified by
- // the address family
- ncs,
-
- // RFC 4122/DCE 1.1
- // N bit pattern: 10xx
- // > big-endian byte order
- rfc,
-
- // Microsoft Corporation backward compatibility
- // N bit pattern: 110x
- // > little endian byte order
- // > formely used in the Component Object Model (COM) library
- microsoft,
-
- // reserved for possible future definition
- // N bit pattern: 111x
- reserved
-};
-
-// indicated by a bit pattern in octet 6, marked with M in
-// xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx
-export enum struct uuid_version : std::uint8_t {
- // only possible for nil or invalid uuids
- none = 0,
- // The time-based version specified in RFC 4122
- time_based = 1,
- // DCE Security version, with embedded POSIX UIDs.
- dce_security = 2,
- // The name-based version specified in RFS 4122 with MD5 hashing
- name_based_md5 = 3,
- // The randomly or pseudo-randomly generated version specified in RFS 4122
- random_number_based = 4,
- // The name-based version specified in RFS 4122 with SHA1 hashing
- name_based_sha1 = 5
-};
-
-// Forward declare uuid and to_string so that we can declare to_string as a
-// friend later.
-export struct uuid;
-export template <typename CharT = char, typename Traits = std::char_traits<CharT>,
- typename Allocator = std::allocator<CharT>>
-auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>;
-
-// --------------------------------------------------------------------------------------------------------------------------
-// uuid class
-// --------------------------------------------------------------------------------------------------------------------------
-export struct uuid
-{
- using value_type = std::uint8_t;
-
- constexpr uuid() noexcept = default;
-
- uuid(value_type (&arr)[16]) noexcept // NOLINT
- {
- std::ranges::copy(arr, std::ranges::begin(data));
- }
-
- explicit constexpr uuid(std::array<value_type, 16> const &arr) noexcept
- : data{arr}
- {
- }
-
- explicit uuid(std::span<value_type, 16> bytes)
- {
- std::ranges::copy(bytes, std::ranges::begin(data));
- }
-
- explicit uuid(std::span<value_type> bytes)
- {
- if (bytes.size() != 16)
- throw std::logic_error("wrong size for uuid");
- std::ranges::copy(bytes, std::ranges::begin(data));
- }
-
- template <typename ForwardIterator>
- explicit uuid(ForwardIterator first, ForwardIterator last)
- {
- if (std::distance(first, last) != 16)
- throw std::logic_error("wrong size for uuid");
-
- std::copy(first, last, std::begin(data));
- }
-
- [[nodiscard]] constexpr auto variant() const noexcept -> uuid_variant
- {
- if ((data[8] & 0x80U) == 0x00U)
- return uuid_variant::ncs;
- else if ((data[8] & 0xC0U) == 0x80U)
- return uuid_variant::rfc;
- else if ((data[8] & 0xE0U) == 0xC0U)
- return uuid_variant::microsoft;
- else
- return uuid_variant::reserved;
- }
-
- [[nodiscard]] constexpr auto version() const noexcept -> uuid_version
- {
- if ((data[6] & 0xF0U) == 0x10U)
- return uuid_version::time_based;
- else if ((data[6] & 0xF0U) == 0x20U)
- return uuid_version::dce_security;
- else if ((data[6] & 0xF0U) == 0x30U)
- return uuid_version::name_based_md5;
- else if ((data[6] & 0xF0U) == 0x40U)
- return uuid_version::random_number_based;
- else if ((data[6] & 0xF0U) == 0x50U)
- return uuid_version::name_based_sha1;
- else
- return uuid_version::none;
- }
-
- [[nodiscard]] constexpr auto is_nil() const noexcept -> bool
- {
- return std::ranges::all_of(data, [](auto i) { return i == 0; });
- }
-
- auto swap(uuid &other) noexcept -> void
- {
- data.swap(other.data);
- }
-
- [[nodiscard]] auto as_bytes() const -> std::span<std::byte const, 16>
- {
- return std::span<std::byte const, 16>(
- reinterpret_cast<std::byte const *>(data.data()), 16);
- }
-
- template <typename StringType>
- [[nodiscard]] constexpr static auto is_valid_uuid(StringType const &in_str) noexcept -> bool
- {
- auto str = to_string_view(in_str);
- auto firstDigit = true;
- auto hasBraces = std::size_t{0};
- auto index = std::size_t{0};
-
- if (str.empty())
- return false;
-
- if (str.front() == '{')
- hasBraces = 1;
-
- if (hasBraces && str.back() != '}')
- return false;
-
- for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) {
- if (str[i] == '-')
- continue;
-
- if (index >= 16 || !is_hex(str[i]))
- return false;
-
- if (firstDigit) {
- firstDigit = false;
- } else {
- index++;
- firstDigit = true;
- }
- }
-
- if (index < 16)
- return false;
-
- return true;
- }
-
- template <typename StringType>
- [[nodiscard]] constexpr static auto
- from_string(StringType const &in_str) -> std::optional<uuid>
- {
- auto str = to_string_view(in_str);
- bool firstDigit = true;
- auto hasBraces = std::size_t{0};
- auto index = std::size_t{0};
-
- auto data = std::array<std::uint8_t, 16>{};
-
- if (str.empty())
- return {};
-
- if (str.front() == '{')
- hasBraces = 1;
- if (hasBraces && str.back() != '}')
- return {};
-
- for (std::size_t i = hasBraces; i < str.size() - hasBraces; ++i) {
- if (str[i] == '-')
- continue;
-
- if (index >= 16 || !is_hex(str[i])) {
- return {};
- }
-
- if (firstDigit) {
- data.at(index) = static_cast<std::uint8_t>(hex2char(str[i]) << 4);
- firstDigit = false;
- } else {
- data.at(index) =
- static_cast<std::uint8_t>(data.at(index) | hex2char(str[i]));
- index++;
- firstDigit = true;
- }
- }
-
- if (index < 16) {
- return {};
- }
-
- return uuid{data};
- }
-
-private:
- std::array<value_type, 16> data{{0}};
-
- friend auto operator==(uuid const &, uuid const &) noexcept -> bool;
- friend auto operator<(uuid const &, uuid const &) noexcept -> bool;
-
- template <class Elem, class Traits>
- friend auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id)
- -> std::basic_ostream<Elem, Traits> &;
-
- template <class CharT, class Traits, class Allocator>
- friend auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>;
-
- friend std::hash<uuid>;
-};
-
-// --------------------------------------------------------------------------------------------------------------------------
-// operators and non-member functions
-// --------------------------------------------------------------------------------------------------------------------------
-
-export [[nodiscard]]
-auto operator==(uuid const &lhs, uuid const &rhs) noexcept -> bool
-{
- return lhs.data == rhs.data;
-}
-
-export [[nodiscard]]
-auto operator!=(uuid const &lhs, uuid const &rhs) noexcept -> bool
-{
- return !(lhs == rhs);
-}
-
-export [[nodiscard]]
-auto operator<(uuid const &lhs, uuid const &rhs) noexcept -> bool
-{
- return lhs.data < rhs.data;
-}
-
-export template <typename CharT, typename Traits, typename Allocator>
-[[nodiscard]] auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>
-{
- auto uustr =
- std::basic_string<CharT, Traits, Allocator>(std::from_range, empty_guid<CharT>);
-
- for (std::size_t i = 0, index = 0; i < 36; ++i) {
- if (i == 8 || i == 13 || i == 18 || i == 23)
- continue;
-
- uustr[i] = guid_encoder<CharT>[id.data.at(index) >> 4U & 0x0FU];
- uustr[++i] = guid_encoder<CharT>[id.data.at(index) & 0x0FU];
- index++;
- }
-
- return uustr;
-}
-
-export template <class Elem, class Traits>
-auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id)
- -> std::basic_ostream<Elem, Traits> &
-{
- return s << to_string(id);
-}
-
-export auto swap(uuid &lhs, uuid &rhs) noexcept -> void
-{
- lhs.swap(rhs);
-}
-
-/***********************************************************************
- * namespace IDs that could be used for generating name-based uuids
- */
-
-// Name string is a fully-qualified domain name
-export uuid uuid_namespace_dns{
- {0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
- 0xc8}
-};
-
-// Name string is a URL
-export uuid uuid_namespace_url{
- {0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
- 0xc8}
-};
-
-// Name string is an ISO OID (See https://oidref.com/,
-// https://en.wikipedia.org/wiki/Object_identifier)
-export uuid uuid_namespace_oid{
- {0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
- 0xc8}
-};
-
-// Name string is an X.500 DN, in DER or a text output format (See
-// https://en.wikipedia.org/wiki/X.500,
-// https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One)
-export uuid uuid_namespace_x500{
- {0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
- 0xc8}
-};
-
-/***********************************************************************
- * uuid generators
- */
-
-export template <typename UniformRandomNumberGenerator>
-struct basic_uuid_random_generator
-{
- using engine_type = UniformRandomNumberGenerator;
-
- explicit basic_uuid_random_generator(engine_type &gen)
- : generator(&gen, [](auto) {})
- {
- }
-
- explicit basic_uuid_random_generator(engine_type *gen)
- : generator(gen, [](auto) {})
- {
- }
-
- [[nodiscard]] auto operator()() -> uuid
- {
- auto bytes = std::array<std::uint8_t, 16>{};
- std::ranges::generate(bytes, [&] { return distribution(*generator); });
-
- // variant must be 10xxxxxx
- bytes[8] &= 0xBFU;
- bytes[8] |= 0x80U;
-
- // version must be 0100xxxx
- bytes[6] &= 0x4FU;
- bytes[6] |= 0x40U;
-
- return uuid{std::begin(bytes), std::end(bytes)};
- }
-
-private:
- std::uniform_int_distribution<std::uint32_t> distribution;
- std::shared_ptr<UniformRandomNumberGenerator> generator;
-};
-
-export using uuid_random_generator = basic_uuid_random_generator<std::mt19937>;
-
-export struct uuid_name_generator
-{
- explicit uuid_name_generator(uuid const &namespace_uuid) noexcept
- : nsuuid(namespace_uuid)
- {
- }
-
- template <typename StringType>
- [[nodiscard]] auto operator()(StringType const &name) -> uuid
- {
- reset();
- process_characters(to_string_view(name));
- return make_uuid();
- }
-
-private:
- auto reset() -> void
- {
- hasher.reset();
-
- auto nsbytes = nsuuid.as_bytes();
-
- auto bytes = std::array<std::byte, 16>();
- std::ranges::copy(nsbytes, std::ranges::begin(bytes));
-
- hasher.process_bytes(bytes.data(), bytes.size());
- }
-
- template <typename CharT, typename Traits>
- auto process_characters(std::basic_string_view<CharT, Traits> const str) -> void
- {
- for (std::uint32_t c : str) {
- hasher.process_byte(static_cast<std::uint8_t>(c & 0xFFU));
- if constexpr (!std::is_same_v<CharT, char>) {
- hasher.process_byte(static_cast<std::uint8_t>((c >> 8U) & 0xFFU));
- hasher.process_byte(static_cast<std::uint8_t>((c >> 16U) & 0xFFU));
- hasher.process_byte(static_cast<std::uint8_t>((c >> 24U) & 0xFFU));
- }
- }
- }
-
- [[nodiscard]] auto make_uuid() -> uuid
- {
- auto digest = hasher.get_digest_bytes();
-
- // variant must be 0b10xxxxxx
- digest[8] &= 0xBFU;
- digest[8] |= 0x80U;
-
- // version must be 0b0101xxxx
- digest[6] &= 0x5FU;
- digest[6] |= 0x50U;
-
- return uuid(std::span(digest).subspan(0, 16));
- }
-
- uuid nsuuid;
- sha1 hasher;
-};
-
-/*
- * Create a random UUID.
- */
-export auto random_uuid() -> uuid
-{
- auto rd = std::random_device();
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
-
- auto seq = std::seed_seq(std::ranges::begin(seed_data), std::ranges::end(seed_data));
- auto generator = std::mt19937(seq);
- auto gen = uuid_random_generator{generator};
-
- return gen();
-}
-
-} // namespace nihil
-
-namespace std {
-
-export template <>
-struct hash<nihil::uuid>
-{
- using argument_type = nihil::uuid;
- using result_type = std::size_t;
-
- [[nodiscard]] auto operator()(argument_type const &uuid) const noexcept -> result_type
- {
- auto const l = static_cast<uint64_t>(uuid.data[0]) << 56U |
- static_cast<uint64_t>(uuid.data[1]) << 48U |
- static_cast<uint64_t>(uuid.data[2]) << 40U |
- static_cast<uint64_t>(uuid.data[3]) << 32U |
- static_cast<uint64_t>(uuid.data[4]) << 24U |
- static_cast<uint64_t>(uuid.data[5]) << 16U |
- static_cast<uint64_t>(uuid.data[6]) << 8U |
- static_cast<uint64_t>(uuid.data[7]);
-
- auto const h = static_cast<uint64_t>(uuid.data[8]) << 56U |
- static_cast<uint64_t>(uuid.data[9]) << 48U |
- static_cast<uint64_t>(uuid.data[10]) << 40U |
- static_cast<uint64_t>(uuid.data[11]) << 32U |
- static_cast<uint64_t>(uuid.data[12]) << 24U |
- static_cast<uint64_t>(uuid.data[13]) << 16U |
- static_cast<uint64_t>(uuid.data[14]) << 8U |
- static_cast<uint64_t>(uuid.data[15]);
-
- return std::hash<std::uint64_t>{}(l ^ h);
- }
-};
-
-} // namespace std
diff --git a/nihil.util/uuid.test.cc b/nihil.util/uuid.test.cc
deleted file mode 100644
index eca94d1..0000000
--- a/nihil.util/uuid.test.cc
+++ /dev/null
@@ -1,923 +0,0 @@
-/*
- * From https://github.com/mariusbancila/stduuid
- *
- * Copyright (c) 2017
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <catch2/catch_test_macros.hpp>
-
-import nihil.std;
-import nihil.util;
-
-// NOLINTBEGIN(bugprone-unchecked-optional-access)
-
-namespace {
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html
-template <typename EngineT, std::size_t StateSize = EngineT::state_size>
-void seed_rng(EngineT &engine)
-{
- using engine_type = typename EngineT::result_type;
- using device_type = std::random_device::result_type;
- using seedseq_type = std::seed_seq::result_type;
-
- constexpr auto bytes_needed = StateSize * sizeof(engine_type);
- constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type))
- ? (bytes_needed / sizeof(device_type))
- : (bytes_needed / sizeof(seedseq_type));
-
- auto numbers = std::array<device_type, numbers_needed>{};
- auto rnddev = std::random_device{};
- std::ranges::generate(numbers, std::ref(rnddev));
-
- auto seedseq = std::seed_seq(std::cbegin(numbers), std::cend(numbers));
- engine.seed(seedseq);
-}
-
-using namespace nihil;
-
-TEST_CASE("uuid: Test multiple default generators", "[uuid]")
-{
- uuid id1;
- uuid id2;
-
- {
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::mt19937 generator(seq);
-
- id1 = uuid_random_generator{generator}();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
- }
-
- {
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::mt19937 generator(seq);
-
- id2 = uuid_random_generator{generator}();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
- }
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test default generator", "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::mt19937 generator(seq);
-
- uuid const guid = uuid_random_generator{generator}();
- REQUIRE(!guid.is_nil());
- REQUIRE(guid.version() == uuid_version::random_number_based);
- REQUIRE(guid.variant() == uuid_variant::rfc);
-}
-
-TEST_CASE("uuid: Test random generator (conversion ctor w/ smart ptr)", "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::mt19937 generator(seq);
-
- uuid_random_generator dgen(&generator);
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test random generator (conversion ctor w/ ptr)", "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- auto generator = std::make_unique<std::mt19937>(seq);
-
- uuid_random_generator dgen(generator.get());
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test random generator (conversion ctor w/ ref)", "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, std::mt19937::state_size>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::mt19937 generator(seq);
-
- uuid_random_generator dgen(generator);
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ptr) "
- "w/ ranlux48_base",
- "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, 6>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::ranlux48_base generator(seq);
-
- basic_uuid_random_generator<std::ranlux48_base> dgen(&generator);
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test basic random generator (conversion ctor w/ smart ptr) "
- "w/ ranlux48_base",
- "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, 6>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- auto generator = std::make_unique<std::ranlux48_base>(seq);
-
- basic_uuid_random_generator<std::ranlux48_base> dgen(generator.get());
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ref) "
- "w/ ranlux48_base",
- "[uuid]")
-{
- std::random_device rd;
- auto seed_data = std::array<int, 6>{};
- std::ranges::generate(seed_data, std::ref(rd));
- std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
- std::ranlux48_base generator(seq);
-
- basic_uuid_random_generator<std::ranlux48_base> dgen(generator);
- auto id1 = dgen();
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::random_number_based);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen();
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::random_number_based);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
-}
-
-TEST_CASE("uuid: Test namespaces", "[uuid]")
-{
- REQUIRE(uuid_namespace_dns == uuid::from_string("6ba7b810-9dad-11d1-80b4-00c04fd430c8"));
- REQUIRE(uuid_namespace_url == uuid::from_string("6ba7b811-9dad-11d1-80b4-00c04fd430c8"));
- REQUIRE(uuid_namespace_oid == uuid::from_string("6ba7b812-9dad-11d1-80b4-00c04fd430c8"));
- REQUIRE(uuid_namespace_x500 == uuid::from_string("6ba7b814-9dad-11d1-80b4-00c04fd430c8"));
-}
-
-TEST_CASE("uuid: Test name generator (char*)", "[uuid]")
-{
- uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
-
- auto id1 = dgen("john");
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::name_based_sha1);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen("jane");
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::name_based_sha1);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- auto id3 = dgen("jane");
- REQUIRE(!id3.is_nil());
- REQUIRE(id3.version() == uuid_version::name_based_sha1);
- REQUIRE(id3.variant() == uuid_variant::rfc);
-
- auto id4 = dgen(L"jane");
- REQUIRE(!id4.is_nil());
- REQUIRE(id4.version() == uuid_version::name_based_sha1);
- REQUIRE(id4.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
- REQUIRE(id2 == id3);
- REQUIRE(id3 != id4);
-}
-
-TEST_CASE("uuid: Test name generator (std::string)", "[uuid]")
-{
- using namespace std::string_literals;
-
- uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
- auto id1 = dgen("john"s);
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::name_based_sha1);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen("jane"s);
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::name_based_sha1);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- auto id3 = dgen("jane"s);
- REQUIRE(!id3.is_nil());
- REQUIRE(id3.version() == uuid_version::name_based_sha1);
- REQUIRE(id3.variant() == uuid_variant::rfc);
-
- auto id4 = dgen(L"jane"s);
- REQUIRE(!id4.is_nil());
- REQUIRE(id4.version() == uuid_version::name_based_sha1);
- REQUIRE(id4.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
- REQUIRE(id2 == id3);
- REQUIRE(id3 != id4);
-}
-
-TEST_CASE("uuid: Test name generator (std::string_view)", "[uuid]")
-{
- using namespace std::string_view_literals;
-
- uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
- auto id1 = dgen("john"sv);
- REQUIRE(!id1.is_nil());
- REQUIRE(id1.version() == uuid_version::name_based_sha1);
- REQUIRE(id1.variant() == uuid_variant::rfc);
-
- auto id2 = dgen("jane"sv);
- REQUIRE(!id2.is_nil());
- REQUIRE(id2.version() == uuid_version::name_based_sha1);
- REQUIRE(id2.variant() == uuid_variant::rfc);
-
- auto id3 = dgen("jane"sv);
- REQUIRE(!id3.is_nil());
- REQUIRE(id3.version() == uuid_version::name_based_sha1);
- REQUIRE(id3.variant() == uuid_variant::rfc);
-
- auto id4 = dgen(L"jane"sv);
- REQUIRE(!id4.is_nil());
- REQUIRE(id4.version() == uuid_version::name_based_sha1);
- REQUIRE(id4.variant() == uuid_variant::rfc);
-
- REQUIRE(id1 != id2);
- REQUIRE(id2 == id3);
- REQUIRE(id3 != id4);
-}
-
-TEST_CASE("uuid: Test name generator equality (char const*, std::string, "
- "std::string_view)",
- "[uuid]")
-{
- using namespace std::literals;
-
- auto dgen = uuid_name_generator(
- uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value());
- auto id1 = dgen("john");
- auto id2 = dgen("john"s);
- auto id3 = dgen("john"sv);
-
- REQUIRE(id1 == id2);
- REQUIRE(id2 == id3);
-}
-
-TEST_CASE("uuid: Test default constructor", "[uuid]")
-{
- auto empty = uuid();
- REQUIRE(empty.is_nil());
-}
-
-TEST_CASE("uuid: Test string conversion", "[uuid]")
-{
- auto empty = uuid();
- REQUIRE(to_string(empty) == "00000000-0000-0000-0000-000000000000");
- REQUIRE(to_string<wchar_t>(empty) == L"00000000-0000-0000-0000-000000000000");
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(char*)", "[uuid]")
-{
- REQUIRE(uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"));
- REQUIRE(uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"));
- REQUIRE(uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"));
- REQUIRE(uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"));
- REQUIRE(uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"));
- REQUIRE(uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"));
- REQUIRE(uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"));
- REQUIRE(uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"));
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(basic_string)", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "00000000-0000-0000-0000-000000000000"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "{00000000-0000-0000-0000-000000000000}"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = L"00000000-0000-0000-0000-000000000000"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-
- {
- auto str = L"{00000000-0000-0000-0000-000000000000}"s;
- REQUIRE(uuid::is_valid_uuid(str));
- }
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(basic_string_view)", "[uuid]")
-{
- using namespace std::string_view_literals;
-
- REQUIRE(uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43"sv));
- REQUIRE(uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43}"sv));
- REQUIRE(uuid::is_valid_uuid(L"47183823-2574-4bfd-b411-99ed177d3e43"sv));
- REQUIRE(uuid::is_valid_uuid(L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv));
- REQUIRE(uuid::is_valid_uuid("00000000-0000-0000-0000-000000000000"sv));
- REQUIRE(uuid::is_valid_uuid("{00000000-0000-0000-0000-000000000000}"sv));
- REQUIRE(uuid::is_valid_uuid(L"00000000-0000-0000-0000-000000000000"sv));
- REQUIRE(uuid::is_valid_uuid(L"{00000000-0000-0000-0000-000000000000}"sv));
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(char*) invalid format", "[uuid]")
-{
- REQUIRE(!uuid::is_valid_uuid(""));
- REQUIRE(!uuid::is_valid_uuid("{}"));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"));
- REQUIRE(!uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"));
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(basic_string) invalid format", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- auto str = ""s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "{}"s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
- REQUIRE(!uuid::is_valid_uuid(str));
- }
-}
-
-TEST_CASE("uuid: Test is_valid_uuid(basic_string_view) invalid format", "[uuid]")
-{
- using namespace std::string_view_literals;
-
- REQUIRE(!uuid::is_valid_uuid(""sv));
- REQUIRE(!uuid::is_valid_uuid("{}"sv));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e4"sv));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e430"sv));
- REQUIRE(!uuid::is_valid_uuid("{47183823-2574-4bfd-b411-99ed177d3e43"sv));
- REQUIRE(!uuid::is_valid_uuid("47183823-2574-4bfd-b411-99ed177d3e43}"sv));
-}
-
-TEST_CASE("uuid: Test from_string(char*)", "[uuid]")
-{
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43";
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == str);
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}";
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto str = L"47183823-2574-4bfd-b411-99ed177d3e43";
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string<wchar_t>(guid) == str);
- }
-
- {
- auto str = "4718382325744bfdb41199ed177d3e43";
- REQUIRE_NOTHROW(uuid::from_string(str));
- REQUIRE(uuid::from_string(str).has_value());
- }
-
- {
- auto str = "00000000-0000-0000-0000-000000000000";
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = "{00000000-0000-0000-0000-000000000000}";
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"00000000-0000-0000-0000-000000000000";
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"{00000000-0000-0000-0000-000000000000}";
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-}
-
-TEST_CASE("uuid: Test from_string(basic_string)", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == str);
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string<wchar_t>(guid) == str);
- }
-
- {
- auto str = "4718382325744bfdb41199ed177d3e43"s;
- REQUIRE_NOTHROW(uuid::from_string(str));
- REQUIRE(uuid::from_string(str).has_value());
- }
-
- {
- auto str = "00000000-0000-0000-0000-000000000000"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = "{00000000-0000-0000-0000-000000000000}"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"00000000-0000-0000-0000-000000000000"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"{00000000-0000-0000-0000-000000000000}"s;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-}
-
-TEST_CASE("uuid: Test from_string(basic_string_view)", "[uuid]")
-{
- using namespace std::string_view_literals;
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == str);
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value();
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43");
- REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43");
- }
-
- {
- auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(to_string<wchar_t>(guid) == str);
- }
-
- {
- auto str = "4718382325744bfdb41199ed177d3e43"sv;
- REQUIRE_NOTHROW(uuid::from_string(str));
- REQUIRE(uuid::from_string(str).has_value());
- }
-
- {
- auto str = "00000000-0000-0000-0000-000000000000"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = "{00000000-0000-0000-0000-000000000000}"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"00000000-0000-0000-0000-000000000000"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-
- {
- auto str = L"{00000000-0000-0000-0000-000000000000}"sv;
- auto guid = uuid::from_string(str).value();
- REQUIRE(guid.is_nil());
- }
-}
-
-TEST_CASE("uuid: Test constexpr from_string", "[uuid]")
-{
- constexpr uuid value = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
- static_assert(!value.is_nil());
- static_assert(value.variant() == uuid_variant::rfc);
- static_assert(value.version() != uuid_version::none);
-}
-
-TEST_CASE("uuid: Test from_string(char*) invalid format", "[uuid]")
-{
- REQUIRE(!uuid::from_string("").has_value());
- REQUIRE(!uuid::from_string("{}").has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4").has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430").has_value());
- REQUIRE(!uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43").has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}").has_value());
-}
-
-TEST_CASE("uuid: Test from_string(basic_string) invalid format", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- auto str = ""s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-
- {
- auto str = "{}"s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-
- {
- auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-
- {
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s;
- REQUIRE(!uuid::from_string(str).has_value());
- }
-}
-
-TEST_CASE("uuid: Test from_string(basic_string_view) invalid format", "[uuid]")
-{
- using namespace std::string_view_literals;
-
- REQUIRE(!uuid::from_string(""sv).has_value());
- REQUIRE(!uuid::from_string("{}"sv).has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value());
- REQUIRE(!uuid::from_string("{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value());
- REQUIRE(!uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value());
-}
-
-TEST_CASE("uuid: Test iterators constructor", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- std::array<uuid::value_type, 16> arr{
- {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed,
- 0x17, 0x7d, 0x3e, 0x43}
- };
-
- auto const guid = uuid(std::begin(arr), std::end(arr));
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s);
- }
-
- {
- uuid::value_type arr[16] = {// NOLINT
- 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd,
- 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43};
-
- auto const guid = uuid(std::begin(arr), std::end(arr));
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s);
- }
-}
-
-TEST_CASE("uuid: Test array constructors", "[uuid]")
-{
- using namespace std::string_literals;
-
- {
- auto const guid = uuid {
- {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed,
- 0x17, 0x7d, 0x3e, 0x43}
- };
-
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s);
- }
-
- {
- auto arr = std::array<uuid::value_type, 16>{
- {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed,
- 0x17, 0x7d, 0x3e, 0x43}
- };
-
- auto const guid = uuid(arr);
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s);
- }
-
- {
- uuid::value_type arr[16]{// NOLINT
- 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd,
- 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43};
-
- auto const guid = uuid(arr);
- REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s);
- }
-}
-
-TEST_CASE("uuid: Test equality", "[uuid]")
-{
- uuid empty;
-
- auto engine = uuid_random_generator::engine_type{};
- seed_rng(engine);
- uuid guid = uuid_random_generator{engine}();
-
- REQUIRE(empty == empty);
- REQUIRE(guid == guid);
- REQUIRE(empty != guid);
-}
-
-TEST_CASE("Test comparison", "[uuid]")
-{
- auto empty = uuid{};
-
- auto engine = uuid_random_generator::engine_type{};
- seed_rng(engine);
-
- auto gen = uuid_random_generator{engine};
- auto id = gen();
-
- REQUIRE(empty < id);
-
- auto ids = std::set{uuid{}, gen(), gen(), gen(), gen()};
-
- REQUIRE(ids.size() == 5);
- REQUIRE(ids.contains(uuid{}) == true);
-}
-
-TEST_CASE("uuid: Test hashing", "[uuid]")
-{
- using namespace std::string_literals;
-
- auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s;
- auto guid = uuid::from_string(str).value();
-
- auto h1 = std::hash<std::string>{};
- auto h2 = std::hash<uuid>{};
- REQUIRE(h1(str) != h2(guid));
-
- auto engine = uuid_random_generator::engine_type{};
- seed_rng(engine);
- uuid_random_generator gen{engine};
-
- std::unordered_set<uuid> ids{uuid{}, gen(), gen(), gen(), gen()};
-
- REQUIRE(ids.size() == 5);
- REQUIRE(ids.find(uuid{}) != ids.end());
-}
-
-TEST_CASE("uuid: Test swap", "[uuid]")
-{
- uuid empty;
-
- auto engine = uuid_random_generator::engine_type{};
- seed_rng(engine);
- uuid guid = uuid_random_generator{engine}();
-
- REQUIRE(empty.is_nil());
- REQUIRE(!guid.is_nil());
-
- std::swap(empty, guid);
-
- REQUIRE(!empty.is_nil());
- REQUIRE(guid.is_nil());
-
- empty.swap(guid);
-
- REQUIRE(empty.is_nil());
- REQUIRE(!guid.is_nil());
-}
-
-TEST_CASE("uuid: Test constexpr", "[uuid]")
-{
- constexpr uuid empty;
- static_assert(empty.is_nil());
- static_assert(empty.variant() == uuid_variant::ncs);
- static_assert(empty.version() == uuid_version::none);
-}
-
-TEST_CASE("uuid: Test size", "[uuid]")
-{
- REQUIRE(sizeof(uuid) == 16);
-}
-
-TEST_CASE("uuid: Test assignment", "[uuid]")
-{
- auto id1 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value();
- auto id2 = id1;
- REQUIRE(id1 == id2);
-
- id1 = uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value();
- REQUIRE(id1 != id2);
-
- auto id3 = std::move(id2);
- REQUIRE(to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43");
-}
-
-TEST_CASE("uuid: Test trivial", "[uuid]")
-{
- REQUIRE(std::is_trivially_copyable_v<uuid>);
-}
-
-TEST_CASE("uuid: Test as_bytes", "[uuid]")
-{
- std::array<uuid::value_type, 16> arr{
- {0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, 0xb4, 0x11, 0x99, 0xed, 0x17, 0x7d,
- 0x3e, 0x43}
- };
-
- {
- uuid id{arr};
- REQUIRE(!id.is_nil());
-
- auto view = id.as_bytes();
- REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0);
- }
-
- {
- const uuid id{arr};
- REQUIRE(!id.is_nil());
-
- auto view = id.as_bytes();
- REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0);
- }
-}
-} // anonymous namespace
-
-// NOLINTEND(bugprone-unchecked-optional-access)