From 4fa6821e0645ff61a9380cd090abff472205c630 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 29 Jun 2025 17:16:22 +0100 Subject: add clang-tidy support --- .gitignore | 1 - CMakeLists.txt | 23 ++ clang-tidy.conf | 8 + nihil.cli/command.cc | 1 + nihil.cli/command_node.cc | 9 +- nihil.cli/command_path.ccm | 16 -- nihil.cli/test.cc | 12 +- nihil.config/option.cc | 1 + nihil.core/CMakeLists.txt | 11 + nihil.core/errc.cc | 51 ++++ nihil.core/errc.ccm | 44 ++++ nihil.core/nihil.core.ccm | 9 + nihil.error/error.cc | 2 +- nihil.error/error.ccm | 2 +- nihil.generator/CMakeLists.txt | 6 + nihil.generator/elements_of.ccm | 69 +++++ nihil.generator/generator.ccm | 209 +-------------- nihil.generator/manual_lifetime.ccm | 117 +++++++++ nihil.generator/nihil.generator.ccm | 32 +++ nihil.generator/promise_base_alloc.ccm | 94 +++++++ nihil.generator/test.cc | 2 +- nihil.generator/util.ccm | 37 +++ nihil.guard/guard.ccm | 12 +- nihil.posix/posix.spawn.ccm | 7 +- nihil.posix/posix.tempfile.cc | 17 +- nihil.posix/posix.tempfile.ccm | 8 +- nihil.posix/test.fd.cc | 6 +- nihil.ucl/integer.ccm | 2 +- nihil.ucl/map.ccm | 3 +- nihil.ucl/object.cc | 21 +- nihil.ucl/object.ccm | 2 +- nihil.ucl/parser.ccm | 2 +- nihil.ucl/real.ccm | 2 +- nihil.ucl/tests/map.cc | 16 +- nihil.util/capture_stream.ccm | 5 +- nihil.util/parse_size.ccm | 10 +- nihil.uuid/test.cc | 28 +- nihil.uuid/uuid.ccm | 458 +++++++++++++++++++-------------- 38 files changed, 867 insertions(+), 488 deletions(-) create mode 100644 clang-tidy.conf delete mode 100644 nihil.cli/command_path.ccm create mode 100644 nihil.core/CMakeLists.txt create mode 100644 nihil.core/errc.cc create mode 100644 nihil.core/errc.ccm create mode 100644 nihil.core/nihil.core.ccm create mode 100644 nihil.generator/elements_of.ccm create mode 100644 nihil.generator/manual_lifetime.ccm create mode 100644 nihil.generator/nihil.generator.ccm create mode 100644 nihil.generator/promise_base_alloc.ccm create mode 100644 nihil.generator/util.ccm diff --git a/.gitignore b/.gitignore index 4cb47c2..ad0c823 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /build /dist *.sw? -*.core diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bf5a42..8e8b131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,34 @@ project(nihil) option(NIHIL_CONFIG "Build the nihil.config library" ON) option(NIHIL_UCL "Build the nihil.ucl library" ON) option(NIHIL_TESTS "Build nihil's unit tests" ON) +option(NIHIL_TIDY "Run clang-tidy during build" ON) set(CMAKE_CXX_STANDARD 26) find_package(PkgConfig REQUIRED) +# clang-tidy support +find_program(CLANG_TIDY clang-tidy) + +if(NOT (CLANG_TIDY STREQUAL "")) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + file(GLOB_RECURSE NIHIL_SOURCES "*.cc" "*.ccm") + list(FILTER NIHIL_SOURCES EXCLUDE REGEX ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_target(tidy COMMAND + ${CLANG_TIDY} -config-file=${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy.conf + -p=${CMAKE_CURRENT_BINARY_DIR} + ${NIHIL_SOURCES}) + + if(NIHIL_TIDY) + set(CMAKE_CXX_CLANG_TIDY + ${CLANG_TIDY}; + -config-file=${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy.conf; + ) + endif() +endif() + add_compile_options(-W) add_compile_options(-Wall) add_compile_options(-Wextra) diff --git a/clang-tidy.conf b/clang-tidy.conf new file mode 100644 index 0000000..92a59bc --- /dev/null +++ b/clang-tidy.conf @@ -0,0 +1,8 @@ +--- +WarningsAsErrors: '*' +Checks: > + -*, + bugprone-*, + -bugprone-reserved-identifier, + -bugprone-easily-swappable-parameters, + modernize-*, diff --git a/nihil.cli/command.cc b/nihil.cli/command.cc index 475cad0..725b4eb 100644 --- a/nihil.cli/command.cc +++ b/nihil.cli/command.cc @@ -22,6 +22,7 @@ import :registry; namespace nihil { +//NOLINTNEXTLINE(bugprone-easily-swappable-parameters) command::command(std::string_view path, std::string_view usage, command_function_t handler) : command_node(path) diff --git a/nihil.cli/command_node.cc b/nihil.cli/command_node.cc index 98aeac1..dd18716 100644 --- a/nihil.cli/command_node.cc +++ b/nihil.cli/command_node.cc @@ -9,6 +9,8 @@ module; #include #include +#include + module nihil.cli; import nihil.core; @@ -16,9 +18,14 @@ import nihil.error; namespace nihil { +//NOLINTNEXTLINE(bugprone-exception-escape) command_node::command_node(std::string_view path) noexcept - : m_path(path) +try : m_path(path) { +} catch (std::exception const &exc) { + std::fprintf(stderr, "%s\n", exc.what()); + _exit(1); + /*NOTREACHED*/ } command_node::~command_node() diff --git a/nihil.cli/command_path.ccm b/nihil.cli/command_path.ccm deleted file mode 100644 index a5bd5e3..0000000 --- a/nihil.cli/command_path.ccm +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -module; - -/* - * command_path represents a command split into its component parts. - */ - -export module nihil.cli:command_path; - -namespace nihil { - - -} // namespace nihil diff --git a/nihil.cli/test.cc b/nihil.cli/test.cc index c265700..c82281e 100644 --- a/nihil.cli/test.cc +++ b/nihil.cli/test.cc @@ -36,7 +36,8 @@ TEST_CASE("nihil.cli: dispatch_command: basic", "[nihil.cli]") }; auto argv = const_cast(args.data()); - int ret = nihil::dispatch_command(args.size() - 1, argv); + int ret = nihil::dispatch_command( + static_cast(args.size()) - 1, argv); REQUIRE(ret == 0); REQUIRE(cmd_sub1_called == true); REQUIRE(cmd_sub2_called == false); @@ -48,7 +49,8 @@ TEST_CASE("nihil.cli: dispatch_command: basic", "[nihil.cli]") }; auto argv = const_cast(args.data()); - int ret = nihil::dispatch_command(args.size() - 1, argv); + int ret = nihil::dispatch_command( + static_cast(args.size()) - 1, argv); REQUIRE(ret == 0); REQUIRE(cmd_sub2_called == true); } @@ -65,7 +67,8 @@ TEST_CASE("nihil.cli: dispatch_command: unknown command", "[nihil.cli]") auto ret = int{}; { auto capture = nihil::capture_stream(std::cerr); - ret = nihil::dispatch_command(args.size() - 1, argv); + ret = nihil::dispatch_command( + static_cast(args.size()) - 1, argv); std::cerr.flush(); output = capture.str(); } @@ -87,7 +90,8 @@ TEST_CASE("nihil.cli: dispatch_command: incomplete command", "[nihil.cli]") auto ret = int{}; { auto capture = nihil::capture_stream(std::cerr); - ret = nihil::dispatch_command(args.size() - 1, argv); + ret = nihil::dispatch_command( + static_cast(args.size()) - 1, argv); std::cerr.flush(); output = capture.str(); } diff --git a/nihil.config/option.cc b/nihil.config/option.cc index bcaf167..886f4b6 100644 --- a/nihil.config/option.cc +++ b/nihil.config/option.cc @@ -17,6 +17,7 @@ import nihil.ucl; namespace nihil::config { +//NOLINTNEXTLINE(bugprone-easily-swappable-parameters) option::option(std::string_view name, std::string_view description) : m_name(name) , m_description(description) diff --git a/nihil.core/CMakeLists.txt b/nihil.core/CMakeLists.txt new file mode 100644 index 0000000..4564640 --- /dev/null +++ b/nihil.core/CMakeLists.txt @@ -0,0 +1,11 @@ +# This source code is released into the public domain. + +add_library(nihil.core STATIC) +target_sources(nihil.core + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.core.ccm + errc.ccm + + PRIVATE + errc.cc +) diff --git a/nihil.core/errc.cc b/nihil.core/errc.cc new file mode 100644 index 0000000..35c9d8f --- /dev/null +++ b/nihil.core/errc.cc @@ -0,0 +1,51 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include +#include + +module nihil.core; + +namespace nihil { + +struct nihil_error_category final : std::error_category { + [[nodiscard]] auto name() const noexcept -> char const * override; + [[nodiscard]] auto message(int err) const -> std::string override; +}; + +[[nodiscard]] auto nihil_category() noexcept -> std::error_category & +{ + static auto category = nihil_error_category(); + return category; +} + +auto make_error_condition(errc ec) -> std::error_condition +{ + return {static_cast(ec), nihil_category()}; +} + +auto nihil_error_category::name() const noexcept -> char const * +{ + return "nihil"; +} + +auto nihil_error_category::message(int err) const -> std::string +{ + switch (static_cast(err)) { + case errc::no_error: + return "No error"; + case errc::incomplete_command: + return "Incomplete command"; + case errc::empty_string: + return "Empty string is not permitted"; + case errc::invalid_unit: + return "Invalid unit specifier"; + default: + return "Undefined error"; + } +} + +} // namespace nihil diff --git a/nihil.core/errc.ccm b/nihil.core/errc.ccm new file mode 100644 index 0000000..c597faf --- /dev/null +++ b/nihil.core/errc.ccm @@ -0,0 +1,44 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include +#include + +export module nihil.core:errc; + +namespace nihil { + +export enum struct errc { + no_error = 0, + + /* + * nihil.command + */ + + incomplete_command, + + /* + * nihil.util + */ + + // Empty string is not allowed. + empty_string, + + // Invalid unit, e.g. in parse_size() + invalid_unit, +}; + +export [[nodiscard]] auto nihil_category() noexcept -> std::error_category &; +export [[nodiscard]] auto make_error_condition(errc ec) -> std::error_condition; + +} // namespace nihil + +namespace std { + +export template<> +struct is_error_condition_enum : true_type {}; + +} // namespace std diff --git a/nihil.core/nihil.core.ccm b/nihil.core/nihil.core.ccm new file mode 100644 index 0000000..a7a4100 --- /dev/null +++ b/nihil.core/nihil.core.ccm @@ -0,0 +1,9 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.core; + +export import :errc; diff --git a/nihil.error/error.cc b/nihil.error/error.cc index 15805a3..e4023f9 100644 --- a/nihil.error/error.cc +++ b/nihil.error/error.cc @@ -85,7 +85,7 @@ auto error::root_cause(this error const &self) -> error const & if (self.m_cause) return self.m_cause->root_cause(); - return self; + return self; //NOLINT(bugprone-return-const-ref-from-parameter) } auto error::str(this error const &self) -> std::string diff --git a/nihil.error/error.ccm b/nihil.error/error.ccm index b624184..12d47cc 100644 --- a/nihil.error/error.ccm +++ b/nihil.error/error.ccm @@ -53,7 +53,7 @@ export struct error : std::exception { error(); // Destroy an error. - virtual ~error(); + ~error() override; // Create an error from a freeform string. error(std::string_view what, error cause); diff --git a/nihil.generator/CMakeLists.txt b/nihil.generator/CMakeLists.txt index 7d278a8..56afdac 100644 --- a/nihil.generator/CMakeLists.txt +++ b/nihil.generator/CMakeLists.txt @@ -3,7 +3,13 @@ add_library(nihil.generator STATIC) target_sources(nihil.generator PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.generator.ccm + generator.ccm + elements_of.ccm + manual_lifetime.ccm + promise_base_alloc.ccm + util.ccm ) if(NIHIL_TESTS) diff --git a/nihil.generator/elements_of.ccm b/nihil.generator/elements_of.ccm new file mode 100644 index 0000000..0e34eb9 --- /dev/null +++ b/nihil.generator/elements_of.ccm @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include + +export module nihil.generator:elements_of; + +import :util; + +namespace nihil { + +export template +struct elements_of { + explicit constexpr elements_of(Range &&range) noexcept + requires std::is_default_constructible_v + : m_range(static_cast(range)) + { + } + + constexpr elements_of(Range &&range, Allocator &&alloc) noexcept + : m_range(static_cast(range)) + , m_alloc(static_cast(alloc)) + {} + + constexpr elements_of(elements_of &&) noexcept = default; + + constexpr elements_of(const elements_of &) = delete; + + constexpr auto operator=(this elements_of &, const elements_of &) + -> elements_of & = delete; + constexpr auto operator=(this elements_of &, elements_of &&) noexcept + -> elements_of & = delete; + + [[nodiscard]] constexpr auto + get(this elements_of const &self) noexcept -> Range && + { + return static_cast(self.m_range); + } + + [[nodiscard]] constexpr auto + get_allocator(this elements_of const &self) noexcept -> Allocator + { + return self.m_alloc; + } + +private: + [[no_unique_address]] Allocator m_alloc; + Range &&m_range; +}; + +export template +elements_of(Range &&) -> elements_of; + +export template +elements_of(Range &&, Allocator &&) -> elements_of; + +} // namespace nihil diff --git a/nihil.generator/generator.ccm b/nihil.generator/generator.ccm index f022287..27e8103 100644 --- a/nihil.generator/generator.ccm +++ b/nihil.generator/generator.ccm @@ -21,197 +21,20 @@ module; #include #include -export module nihil.generator; +export module nihil.generator:generator; -namespace nihil { - -template -class __manual_lifetime { - public: - __manual_lifetime() noexcept {} - ~__manual_lifetime() {} - - template - _T& construct(_Args&&... __args) noexcept(std::is_nothrow_constructible_v<_T, _Args...>) { - return *::new (static_cast(std::addressof(__value_))) _T((_Args&&)__args...); - } - - void destruct() noexcept(std::is_nothrow_destructible_v<_T>) { - __value_.~_T(); - } - - _T& get() & noexcept { - return __value_; - } - _T&& get() && noexcept { - return static_cast<_T&&>(__value_); - } - const _T& get() const & noexcept { - return __value_; - } - const _T&& get() const && noexcept { - return static_cast(__value_); - } - - private: - union { - std::remove_const_t<_T> __value_; - }; -}; - -template -class __manual_lifetime<_T&> { - public: - __manual_lifetime() noexcept : __value_(nullptr) {} - ~__manual_lifetime() {} - - _T& construct(_T& __value) noexcept { - __value_ = std::addressof(__value); - return __value; - } - - void destruct() noexcept {} - - _T& get() const noexcept { - return *__value_; - } - - private: - _T* __value_; -}; - -template -class __manual_lifetime<_T&&> { - public: - __manual_lifetime() noexcept : __value_(nullptr) {} - ~__manual_lifetime() {} - - _T&& construct(_T&& __value) noexcept { - __value_ = std::addressof(__value); - return static_cast<_T&&>(__value); - } - - void destruct() noexcept {} - - _T&& get() const noexcept { - return static_cast<_T&&>(*__value_); - } - - private: - _T* __value_; -}; - -struct use_allocator_arg {}; - -namespace ranges { - -export template -struct elements_of { - explicit constexpr elements_of(_Rng&& __rng) noexcept - requires std::is_default_constructible_v<_Allocator> - : __range(static_cast<_Rng&&>(__rng)) - {} - - constexpr elements_of(_Rng&& __rng, _Allocator&& __alloc) noexcept - : __range((_Rng&&)__rng), __alloc((_Allocator&&)__alloc) {} - - constexpr elements_of(elements_of&&) noexcept = default; - - constexpr elements_of(const elements_of &) = delete; - constexpr elements_of &operator=(const elements_of &) = delete; - constexpr elements_of &operator=(elements_of &&) = delete; - - constexpr _Rng&& get() noexcept { - return static_cast<_Rng&&>(__range); - } - - constexpr _Allocator get_allocator() const noexcept { - return __alloc; - } - -private: - [[no_unique_address]] _Allocator __alloc; // \expos - _Rng && __range; // \expos -}; - -export template -elements_of(_Rng &&) -> elements_of<_Rng>; - -export template -elements_of(_Rng &&, Allocator&&) -> elements_of<_Rng, Allocator>; - -} // namespace ranges - -template -static constexpr bool __allocator_needs_to_be_stored = - !std::allocator_traits<_Alloc>::is_always_equal::value || - !std::is_default_constructible_v<_Alloc>; - -// Round s up to next multiple of a. -constexpr size_t __aligned_allocation_size(size_t s, size_t a) { - return (s + a - 1) & ~(a - 1); -} +import :elements_of; +import :manual_lifetime; +import :promise_base_alloc; +import :util; +namespace nihil { export template , typename _Allocator = use_allocator_arg> class generator; -template -class __promise_base_alloc { - static constexpr std::size_t __offset_of_allocator(std::size_t __frameSize) noexcept { - return __aligned_allocation_size(__frameSize, alignof(_Alloc)); - } - - static constexpr std::size_t __padded_frame_size(std::size_t __frameSize) noexcept { - return __offset_of_allocator(__frameSize) + sizeof(_Alloc); - } - - static _Alloc& __get_allocator(void* __frame, std::size_t __frameSize) noexcept { - return *reinterpret_cast<_Alloc*>( - static_cast(__frame) + __offset_of_allocator(__frameSize)); - } - -public: - template - static void* operator new(std::size_t __frameSize, std::allocator_arg_t, _Alloc __alloc, _Args&...) { - void* __frame = __alloc.allocate(__padded_frame_size(__frameSize)); - - // Store allocator at end of the coroutine frame. - // Assuming the allocator's move constructor is non-throwing (a requirement for allocators) - ::new (static_cast(std::addressof(__get_allocator(__frame, __frameSize)))) _Alloc(std::move(__alloc)); - - return __frame; - } - - template - static void* operator new(std::size_t __frameSize, _This&, std::allocator_arg_t, _Alloc __alloc, _Args&...) { - return __promise_base_alloc::operator new(__frameSize, std::allocator_arg, std::move(__alloc)); - } - - static void operator delete(void* __ptr, std::size_t __frameSize) noexcept { - _Alloc& __alloc = __get_allocator(__ptr, __frameSize); - _Alloc __localAlloc(std::move(__alloc)); - __alloc.~Alloc(); - __localAlloc.deallocate(static_cast(__ptr), __padded_frame_size(__frameSize)); - } -}; - -template - requires (!__allocator_needs_to_be_stored<_Alloc>) -class __promise_base_alloc<_Alloc> { -public: - static void* operator new(std::size_t __size) { - _Alloc __alloc; - return __alloc.allocate(__size); - } - - static void operator delete(void* __ptr, std::size_t __size) noexcept { - _Alloc __alloc; - __alloc.deallocate(static_cast(__ptr), __size); - } -}; template struct __generator_promise_base @@ -226,8 +49,8 @@ struct __generator_promise_base // generator coroutine is not used as a nested coroutine). // This member is lazily constructed by the __yield_sequence_awaiter::await_suspend() // method if this generator is used as a nested generator. - __manual_lifetime __exception_; - __manual_lifetime<_Ref> __value_; + manual_lifetime __exception_; + manual_lifetime<_Ref> __value_; explicit __generator_promise_base(std::coroutine_handle<> thisCoro) noexcept : __root_(this) @@ -345,13 +168,13 @@ struct __generator_promise_base template __yield_sequence_awaiter> - yield_value(nihil::ranges::elements_of> __g) noexcept { + yield_value(nihil::elements_of> __g) noexcept { return std::move(__g).get(); } template __yield_sequence_awaiter, _Allocator>> - yield_value(nihil::ranges::elements_of<_Rng, _Allocator> && __x) { + yield_value(nihil::elements_of<_Rng, _Allocator> && __x) { return [](std::allocator_arg_t, _Allocator, auto && __rng) -> generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator> { for(auto && e: __rng) co_yield static_cast(e); @@ -372,7 +195,7 @@ struct __generator_promise; template struct __generator_promise, _ByteAllocator, _ExplicitAllocator> final : public __generator_promise_base<_Ref> - , public __promise_base_alloc<_ByteAllocator> { + , public promise_base_alloc<_ByteAllocator> { __generator_promise() noexcept : __generator_promise_base<_Ref>(std::coroutine_handle<__generator_promise>::from_promise(*this)) {} @@ -387,7 +210,7 @@ struct __generator_promise, _ByteAllocator, _Exp template typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter> - yield_value(nihil::ranges::elements_of<_Rng> && __x) { + yield_value(nihil::elements_of<_Rng> && __x) { static_assert (!_ExplicitAllocator, "This coroutine has an explicit allocator specified with std::allocator_arg so an allocator needs to be passed " "explicitely to std::elements_of"); @@ -682,11 +505,3 @@ private: }; } // namespace nihil - -export namespace std::ranges { - -template -constexpr inline bool enable_view> = true; - -} // namespace std::ranges - diff --git a/nihil.generator/manual_lifetime.ccm b/nihil.generator/manual_lifetime.ccm new file mode 100644 index 0000000..d249e99 --- /dev/null +++ b/nihil.generator/manual_lifetime.ccm @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include +#include + +export module nihil.generator:manual_lifetime; + +namespace nihil { + +template +struct manual_lifetime { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + template + auto construct(this manual_lifetime &self, Args && ...args) + noexcept(std::is_nothrow_constructible_v) + -> T & + { + return *::new (static_cast(std::addressof(self.m_value))) + T(static_cast(args)...); + } + + void destruct(this manual_lifetime &self) + noexcept(std::is_nothrow_destructible_v) + { + self.m_value.~T(); + } + + auto get(this manual_lifetime &self) noexcept -> T & + { + return self.m_value; + } + + auto get(this manual_lifetime &&self) noexcept -> T && + { + return static_cast(self.m_value); + } + + auto get(this manual_lifetime const &self) noexcept -> T const & + { + return self.m_value; + } + + auto get(this manual_lifetime const &&self) noexcept -> T const && + { + return static_cast(self.m_value); + } + +private: + union { + std::remove_const_t m_value; + }; +}; + +template +class manual_lifetime { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + auto construct(this manual_lifetime &self, T &value) noexcept -> T & + { + self.m_value = std::addressof(value); + return self.m_value; + } + + auto destruct(this manual_lifetime &) noexcept -> void + { + } + + auto get(this manual_lifetime const &self) noexcept -> T & + { + return *self.m_value; + } + +private: + T *m_value = nullptr; +}; + +template +class manual_lifetime { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + auto construct(this manual_lifetime &self, T &&value) noexcept -> T && + { + self.m_value = std::addressof(value); + return static_cast(value); + } + + void destruct(this manual_lifetime &) noexcept + { + } + + auto get(this manual_lifetime const &self) noexcept -> T && + { + return static_cast(*self.m_value); + } + +private: + T* m_value = nullptr; +}; + +} diff --git a/nihil.generator/nihil.generator.ccm b/nihil.generator/nihil.generator.ccm new file mode 100644 index 0000000..9eec5b4 --- /dev/null +++ b/nihil.generator/nihil.generator.ccm @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include + +export module nihil.generator; + +export import :elements_of; +export import :generator; +export import :manual_lifetime; +export import :promise_base_alloc; +export import :util; + +export namespace std::ranges { + +template +constexpr inline bool enable_view> = true; + +} // namespace std::ranges + diff --git a/nihil.generator/promise_base_alloc.ccm b/nihil.generator/promise_base_alloc.ccm new file mode 100644 index 0000000..e59fc57 --- /dev/null +++ b/nihil.generator/promise_base_alloc.ccm @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include +#include + +export module nihil.generator:promise_base_alloc; + +import :util; + +namespace nihil { + +template +struct promise_base_alloc +{ + template + static void* operator new(std::size_t frame_size, std::allocator_arg_t, Alloc alloc, Args &...) + { + void* frame = alloc.allocate(padded_frame_size(frame_size)); + + // Store allocator at end of the coroutine frame. Assuming the + // allocator's move constructor is non-throwing (a requirement + // for allocators) + auto *alloc_address = static_cast(std::addressof(get_allocator(frame, frame_size))); + ::new (alloc_address) Alloc(std::move(alloc)); + + return frame; + } + + template + static void* operator new(std::size_t frame_size, This &, std::allocator_arg_t, Alloc alloc, Args &...) + { + return promise_base_alloc::operator new(frame_size, std::allocator_arg, std::move(alloc)); + } + + static void operator delete(void* ptr, std::size_t frame_size) noexcept + { + auto &alloc = get_allocator(ptr, frame_size); + auto local_alloc = Alloc(std::move(alloc)); + + alloc.~Alloc(); + + local_alloc.deallocate(static_cast(ptr), padded_frame_size(frame_size)); + } + +private: + [[nodiscard]] static constexpr auto offset_of_allocator(std::size_t frame_size) noexcept -> std::size_t + { + return aligned_allocation_size(frame_size, alignof(Alloc)); + } + + [[nodiscard]] static constexpr auto padded_frame_size(std::size_t frame_size) noexcept -> std::size_t + { + return offset_of_allocator(frame_size) + sizeof(Alloc); + } + + [[nodiscard]] static auto get_allocator(void* frame, std::size_t frame_size) noexcept -> Alloc & + { + return *reinterpret_cast( + static_cast(frame) + offset_of_allocator(frame_size)); + } + +}; + +template +requires (!allocator_needs_to_be_stored) +struct promise_base_alloc +{ + static void* operator new(std::size_t size) + { + auto alloc = Alloc(); + return alloc.allocate(size); + } + + static void operator delete(void *ptr, std::size_t size) noexcept + { + auto alloc = Alloc(); + alloc.deallocate(static_cast(ptr), size); + } +}; + +} // namespace nihil diff --git a/nihil.generator/test.cc b/nihil.generator/test.cc index d167f30..49272b4 100644 --- a/nihil.generator/test.cc +++ b/nihil.generator/test.cc @@ -46,7 +46,7 @@ TEST_CASE("generator: elements_of", "[generator]") }; auto fn2 = [&fn1] -> nihil::generator { - co_yield nihil::ranges::elements_of(fn1()); + co_yield nihil::elements_of(fn1()); }; auto values = std::vector(); diff --git a/nihil.generator/util.ccm b/nihil.generator/util.ccm new file mode 100644 index 0000000..4d732b9 --- /dev/null +++ b/nihil.generator/util.ccm @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include +#include + +export module nihil.generator:util; + +namespace nihil { + +export struct use_allocator_arg {}; + +template +constexpr bool allocator_needs_to_be_stored = + !std::allocator_traits::is_always_equal::value || + !std::is_default_constructible_v; + +// Round s up to next multiple of a. +[[nodiscard]] constexpr auto +aligned_allocation_size(std::size_t s, std::size_t a) -> std::size_t +{ + return (s + a - 1) & ~(a - 1); +} + +} // namespace nihil diff --git a/nihil.guard/guard.ccm b/nihil.guard/guard.ccm index c586a20..7b6cf66 100644 --- a/nihil.guard/guard.ccm +++ b/nihil.guard/guard.ccm @@ -26,22 +26,24 @@ struct guard final { * We are being destroyed, so call the callable. * If the callable throws, std::terminate() will be called. */ - ~guard() { + ~guard() + { if (m_func) std::invoke(*m_func); } // Release the guard. This turns the destructor into a no-op. - void release() noexcept { - m_func.reset(); + 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; - guard &operator=(guard const &) = delete; - guard &operator=(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. diff --git a/nihil.posix/posix.spawn.ccm b/nihil.posix/posix.spawn.ccm index 5812716..4cce334 100644 --- a/nihil.posix/posix.spawn.ccm +++ b/nihil.posix/posix.spawn.ccm @@ -229,12 +229,9 @@ spawn(executor auto &&executor, auto &&...actions) auto proc = process(pid); if (pid == 0) { - // We are in the child. Release the process so we don't - // try to wait for ourselves, then run child handlers and - // exec the process. - - std::ignore = std::move(proc).release(); + // We are in the child. (actions.run_in_child(proc), ...); + std::ignore = std::move(proc).release(); auto err = executor.exec(); std::print("{}\n", err.error()); diff --git a/nihil.posix/posix.tempfile.cc b/nihil.posix/posix.tempfile.cc index 801aa5e..b1d3dee 100644 --- a/nihil.posix/posix.tempfile.cc +++ b/nihil.posix/posix.tempfile.cc @@ -40,22 +40,7 @@ temporary_file::temporary_file(temporary_file &&other) noexcept { } -auto temporary_file::operator=(this temporary_file &self, - temporary_file &&other) - noexcept -> temporary_file & -{ - if (&self != &other) { - if (self.m_fd) - self.release(); - - self.m_fd = std::move(other.m_fd); - self.m_path = std::move(other.m_path); - } - - return self; -} - -temporary_file::~temporary_file() +temporary_file::~temporary_file() //NOLINT(bugprone-exception-escape) { if (m_fd) release(); diff --git a/nihil.posix/posix.tempfile.ccm b/nihil.posix/posix.tempfile.ccm index 20378b5..82f3be4 100644 --- a/nihil.posix/posix.tempfile.ccm +++ b/nihil.posix/posix.tempfile.ccm @@ -56,13 +56,15 @@ export struct temporary_file final { // Not copyable. temporary_file(temporary_file const &) = delete; - auto operator=(this temporary_file &, temporary_file const &) - -> temporary_file & = delete; // Movable. temporary_file(temporary_file &&other) noexcept; + + // Not assignable. + auto operator=(this temporary_file &, temporary_file const &) + -> temporary_file & = delete; auto operator=(this temporary_file &, temporary_file &&) noexcept - -> temporary_file &; + -> temporary_file & = delete; private: // The file descriptor for the file. diff --git a/nihil.posix/test.fd.cc b/nihil.posix/test.fd.cc index 8dff323..6b6394b 100644 --- a/nihil.posix/test.fd.cc +++ b/nihil.posix/test.fd.cc @@ -64,7 +64,7 @@ TEST_CASE("fd: move construct", "[fd]") { REQUIRE(fd_is_open(fd1.get())); auto fd2(std::move(fd1)); - REQUIRE(!fd1); + REQUIRE(!fd1); //NOLINT REQUIRE(fd2); REQUIRE(fd2.get() == file); REQUIRE(fd_is_open(file)); @@ -82,7 +82,7 @@ TEST_CASE("fd: move assign", "[fd]") { fd2 = std::move(fd1); - REQUIRE(!fd1); + REQUIRE(!fd1); //NOLINT REQUIRE(fd2); REQUIRE(fd2.get() == file); REQUIRE(fd_is_open(file)); @@ -94,7 +94,7 @@ TEST_CASE("fd: release", "[fd]") { auto fd = nihil::fd(file); auto fdesc = std::move(fd).release(); - REQUIRE(!fd); + REQUIRE(!fd); //NOLINT REQUIRE(fdesc == file); } diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm index 0ea490c..e35a471 100644 --- a/nihil.ucl/integer.ccm +++ b/nihil.ucl/integer.ccm @@ -81,7 +81,7 @@ export constexpr auto operator""_ucl (unsigned long long i) -> integer if (std::cmp_greater(i, std::numeric_limits::max())) throw std::out_of_range("literal out of range"); - return integer(i); + return integer(static_cast(i)); } } // namespace nihil::ucl::literals diff --git a/nihil.ucl/map.ccm b/nihil.ucl/map.ccm index 1c5dd19..fa77601 100644 --- a/nihil.ucl/map.ccm +++ b/nihil.ucl/map.ccm @@ -88,7 +88,8 @@ private: struct state { state(::ucl_object_t const *obj) { - if ((iter = ::ucl_object_iterate_new(obj)) == nullptr) + iter = ::ucl_object_iterate_new(obj); + if (iter == nullptr) throw std::system_error(make_error_code( std::errc(errno))); } diff --git a/nihil.ucl/object.cc b/nihil.ucl/object.cc index 9a150fb..53fc4c7 100644 --- a/nihil.ucl/object.cc +++ b/nihil.ucl/object.cc @@ -33,10 +33,12 @@ object::object(object &&other) noexcept : m_object(std::exchange(other.m_object, nullptr)) {} -object::object(object const &other) noexcept +object::object(object const &other) : m_object(nullptr) { - *this = other; + m_object = ::ucl_object_copy(other.get_ucl_object()); + if (m_object == nullptr) + throw std::runtime_error("failed to copy UCL object"); } auto object::operator=(this object &self, object &&other) noexcept @@ -47,20 +49,9 @@ auto object::operator=(this object &self, object &&other) noexcept return self; } -auto object::operator=(this object &self, object const &other) - -> object & +auto object::operator=(this object &self, object const &other) -> object & { - if (&self != &other) { - auto *new_uobj = ::ucl_object_copy(other.get_ucl_object()); - if (new_uobj == nullptr) - throw std::runtime_error("failed to copy UCL object"); - - if (self.m_object != nullptr) - ::ucl_object_unref(self.m_object); - self.m_object = new_uobj; - } - - return self; + return self = object(other); } auto object::ref(this object const &self) -> object diff --git a/nihil.ucl/object.ccm b/nihil.ucl/object.ccm index dffb54e..9a7eaf7 100644 --- a/nihil.ucl/object.ccm +++ b/nihil.ucl/object.ccm @@ -49,7 +49,7 @@ export struct object { // Copyable. // Note that this copies the entire UCL object. - object(object const &other) noexcept; + object(object const &other); auto operator=(this object &self, object const &other) -> object &; // Increase the refcount of this object. diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm index 20e5c66..5fa3495 100644 --- a/nihil.ucl/parser.ccm +++ b/nihil.ucl/parser.ccm @@ -72,7 +72,7 @@ struct parser { requires (std::same_as>) { auto handler = std::make_unique( - std::move(func)); + std::forward(func)); auto cname = std::string(name); ::ucl_parser_register_macro( diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm index c491553..f425a9a 100644 --- a/nihil.ucl/real.ccm +++ b/nihil.ucl/real.ccm @@ -78,7 +78,7 @@ export constexpr auto operator""_ucl (long double d) -> real d < static_cast(std::numeric_limits::min())) throw std::out_of_range("literal out of range"); - return real(d); + return real(static_cast(d)); } } // namespace nihil::ucl::literals diff --git a/nihil.ucl/tests/map.cc b/nihil.ucl/tests/map.cc index 5d2fbe1..7240cb3 100644 --- a/nihil.ucl/tests/map.cc +++ b/nihil.ucl/tests/map.cc @@ -9,6 +9,8 @@ import nihil.ucl; +//NOLINTBEGIN(bugprone-unchecked-optional-access) + TEST_CASE("ucl: map: invariants", "[ucl]") { using namespace nihil::ucl; @@ -110,11 +112,10 @@ TEST_CASE("ucl: map: find", "[ucl]") }; auto obj = map.find("42"); - REQUIRE(obj != std::nullopt); - REQUIRE(*obj == 42); + REQUIRE(obj.value() == 42); obj = map.find("43"); - REQUIRE(obj == std::nullopt); + REQUIRE(!obj.has_value()); } TEST_CASE("ucl: map: iterate", "[ucl]") @@ -179,12 +180,13 @@ TEST_CASE("ucl: map: pop", "[uc]") REQUIRE(map.find("42") != std::nullopt); auto obj = map.pop("42"); - REQUIRE(obj != std::nullopt); - REQUIRE(*obj == 42); + REQUIRE(obj.value() == 42); - REQUIRE(map.find("42") == std::nullopt); + REQUIRE(!map.find("42")); REQUIRE(map["1"] == 1); obj = map.pop("42"); - REQUIRE(obj == std::nullopt); + REQUIRE(!obj); } + +//NOLINTEND(bugprone-unchecked-optional-access) diff --git a/nihil.util/capture_stream.ccm b/nihil.util/capture_stream.ccm index 3333505..7ec39a9 100644 --- a/nihil.util/capture_stream.ccm +++ b/nihil.util/capture_stream.ccm @@ -24,10 +24,11 @@ struct capture_stream { m_stream->rdbuf(m_buffer.rdbuf()); } - ~capture_stream() { + ~capture_stream() + { if (m_old_streambuf == nullptr) return; - release(); + m_stream->rdbuf(m_old_streambuf); } /* diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm index c692578..c95ac50 100644 --- a/nihil.util/parse_size.ccm +++ b/nihil.util/parse_size.ccm @@ -29,11 +29,11 @@ auto get_multiplier(Char c) -> std::expected auto ret = std::uint64_t{1}; switch (c) { - case 'p': case 'P': ret *= 1024; - case 't': case 'T': ret *= 1024; - case 'g': case 'G': ret *= 1024; - case 'm': case 'M': ret *= 1024; - case 'k': case 'K': ret *= 1024; + 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: diff --git a/nihil.uuid/test.cc b/nihil.uuid/test.cc index 04a6ab5..0f21298 100644 --- a/nihil.uuid/test.cc +++ b/nihil.uuid/test.cc @@ -29,6 +29,8 @@ #include +//NOLINTBEGIN(bugprone-unchecked-optional-access) + namespace { @@ -69,7 +71,7 @@ TEST_CASE("uuid: Test multiple default generators", "[uuid]") { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::ranges::generate(seed_data, std::ref(rd)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 generator(seq); @@ -82,7 +84,7 @@ TEST_CASE("uuid: Test multiple default generators", "[uuid]") { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::ranges::generate(seed_data, std::ref(rd)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 generator(seq); @@ -99,7 +101,7 @@ TEST_CASE("uuid: Test default generator", "[uuid]") { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::ranges::generate(seed_data, std::ref(rd)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 generator(seq); @@ -114,7 +116,7 @@ TEST_CASE("uuid: Test random generator (conversion ctor w/ smart ptr)", { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::ranges::generate(seed_data, std::ref(rd)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 generator(seq); @@ -136,7 +138,7 @@ TEST_CASE("uuid: Test random generator (conversion ctor w/ ptr)", "[uuid]") { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + 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(seq); @@ -158,7 +160,7 @@ TEST_CASE("uuid: Test random generator (conversion ctor w/ ref)", "[uuid]") { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::ranges::generate(seed_data, std::ref(rd)); std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); std::mt19937 generator(seq); @@ -181,7 +183,7 @@ TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ptr) " { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + 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); @@ -204,7 +206,7 @@ TEST_CASE("uuid: Test basic random generator (conversion ctor w/ smart ptr) " { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + 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(seq); @@ -227,7 +229,7 @@ TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ref) " { std::random_device rd; auto seed_data = std::array {}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + 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); @@ -347,7 +349,8 @@ TEST_CASE("uuid: Test name generator equality (char const*, std::string, " { using namespace std::literals; - uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); + 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); @@ -791,7 +794,7 @@ TEST_CASE("uuid: Test iterators constructor", "[uuid]") } { - uuid::value_type arr[16] = { + uuid::value_type arr[16] = { // NOLINT 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, @@ -837,7 +840,7 @@ TEST_CASE("uuid: Test array constructors", "[uuid]") } { - uuid::value_type arr[16] { + uuid::value_type arr[16] { //NOLINT 0x47, 0x18, 0x38, 0x23, 0x25, 0x74, 0x4b, 0xfd, @@ -995,3 +998,4 @@ TEST_CASE("uuid: Test as_bytes", "[uuid]") } } +//NOLINTEND(bugprone-unchecked-optional-access) diff --git a/nihil.uuid/uuid.ccm b/nihil.uuid/uuid.ccm index 0c46b60..4aa424e 100644 --- a/nihil.uuid/uuid.ccm +++ b/nihil.uuid/uuid.ccm @@ -48,28 +48,37 @@ export module nihil.uuid; namespace nihil { template -[[nodiscard]] constexpr unsigned char hex2char(TChar const ch) noexcept +[[nodiscard]] constexpr auto hex2char(TChar const ch) noexcept -> unsigned char { if (ch >= static_cast('0') && ch <= static_cast('9')) - return static_cast(ch - static_cast('0')); + return static_cast( + ch - static_cast('0')); + if (ch >= static_cast('a') && ch <= static_cast('f')) - return static_cast(10 + ch - static_cast('a')); + return static_cast( + 10 + ch - static_cast('a')); + if (ch >= static_cast('A') && ch <= static_cast('F')) - return static_cast(10 + ch - static_cast('A')); + return static_cast( + 10 + ch - static_cast('A')); + return 0; } template -[[nodiscard]] constexpr bool is_hex(TChar const ch) noexcept +[[nodiscard]] constexpr auto is_hex(TChar const ch) noexcept -> bool { - return - (ch >= static_cast('0') && ch <= static_cast('9')) || - (ch >= static_cast('a') && ch <= static_cast('f')) || - (ch >= static_cast('A') && ch <= static_cast('F')); + return (ch >= static_cast('0') && + ch <= static_cast('9')) || + (ch >= static_cast('a') && + ch <= static_cast('f')) || + (ch >= static_cast('A') && + ch <= static_cast('F')); } template -[[nodiscard]] constexpr std::basic_string_view to_string_view(TChar const * str) noexcept +[[nodiscard]] constexpr auto to_string_view(TChar const *str) noexcept + -> std::basic_string_view { if (str) return str; @@ -77,101 +86,117 @@ template } template -[[nodiscard]] -constexpr std::basic_string_view< - typename StringType::value_type, - typename StringType::traits_type> -to_string_view(StringType const & str) noexcept +[[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 = uint32_t[5]; - using digest8_t = uint8_t[20]; + using digest32_t = std::array; + using digest8_t = std::array; static constexpr unsigned int block_bytes = 64; - [[nodiscard]] inline static uint32_t left_rotate(uint32_t value, size_t const count) noexcept + sha1() { - return (value << count) ^ (value >> (32 - count)); + reset(); } - sha1() + [[nodiscard]] inline static auto + left_rotate(std::uint32_t value, std::size_t const count) noexcept + -> std::uint32_t { - reset(); + return (value << count) ^ (value >> (32 - count)); } - void reset() noexcept + auto reset(this sha1 &self) noexcept -> void { - m_digest[0] = 0x67452301; - m_digest[1] = 0xEFCDAB89; - m_digest[2] = 0x98BADCFE; - m_digest[3] = 0x10325476; - m_digest[4] = 0xC3D2E1F0; - m_blockByteIndex = 0; - m_byteCount = 0; + 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; } - void process_byte(uint8_t octet) + auto process_byte(this sha1 &self, std::uint8_t octet) -> void { - this->m_block[this->m_blockByteIndex++] = octet; - ++this->m_byteCount; - if (m_blockByteIndex == block_bytes) { - this->m_blockByteIndex = 0; - process_block(); + self.m_block[self.m_blockByteIndex++] = octet; + ++self.m_byteCount; + + if (self.m_blockByteIndex == block_bytes) { + self.m_blockByteIndex = 0; + self.process_block(); } } - void process_block(void const * const start, void const * const end) + auto process_block(this sha1 &self, + void const *const start, + void const *const end) + -> void { - auto *begin = static_cast(start); - auto *finish = static_cast(end); - while (begin != finish) { - process_byte(*begin); - begin++; + auto *first = static_cast(start); + auto *last = static_cast(end); + + while (first != last) { + self.process_byte(*first); + first++; } } - void process_bytes(void const * const data, size_t const len) + auto process_bytes(this sha1 &self, + void const *const data, + size_t const len) + -> void { - auto *block = static_cast(data); - process_block(block, block + len); + auto *block = static_cast(data); + self.process_block(block, block + len); } - uint32_t const * get_digest(digest32_t digest) + auto get_digest(this sha1 &self) -> digest32_t { - size_t const bitCount = this->m_byteCount * 8; - process_byte(0x80); - if (this->m_blockByteIndex > 56) { - while (m_blockByteIndex != 0) - process_byte(0); - while (m_blockByteIndex < 56) - process_byte(0); + 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 (m_blockByteIndex < 56) - process_byte(0); + while (self.m_blockByteIndex < 56) + self.process_byte(0); } - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(static_cast((bitCount >> 24) & 0xFF)); - process_byte(static_cast((bitCount >> 16) & 0xFF)); - process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((bitCount) & 0xFF)); - - std::memcpy(digest, m_digest, 5 * sizeof(uint32_t)); - return digest; + self.process_byte(0); + self.process_byte(0); + self.process_byte(0); + self.process_byte(0); + self.process_byte(static_cast( + (bit_count >> 24) & 0xFF)); + self.process_byte(static_cast( + (bit_count >> 16) & 0xFF)); + self.process_byte(static_cast( + (bit_count >> 8) & 0xFF)); + self.process_byte(static_cast( + (bit_count) & 0xFF)); + + return self.m_digest; } - uint8_t const * get_digest_bytes(digest8_t digest) + auto get_digest_bytes(this sha1 &self) -> digest8_t { - digest32_t d32; - get_digest(d32); - size_t di = 0; + auto d32 = self.get_digest(); + auto digest = digest8_t{}; + + auto di = std::size_t{0}; + digest[di++] = static_cast(d32[0] >> 24); digest[di++] = static_cast(d32[0] >> 16); digest[di++] = static_cast(d32[0] >> 8); @@ -201,28 +226,35 @@ struct sha1 } private: - void process_block() + auto process_block(this sha1 &self) -> void { - uint32_t w[80]; - for (size_t i = 0; i < 16; i++) { - w[i] = static_cast(m_block[i * 4 + 0] << 24); - w[i] |= static_cast(m_block[i * 4 + 1] << 16); - w[i] |= static_cast(m_block[i * 4 + 2] << 8); - w[i] |= static_cast(m_block[i * 4 + 3]); + auto w = std::array(); + + for (std::size_t i = 0; i < 16; i++) { + w[i] = static_cast( + self.m_block[i * 4 + 0]) << 24; + w[i] |= static_cast( + self.m_block[i * 4 + 1]) << 16; + w[i] |= static_cast( + self.m_block[i * 4 + 2]) << 8; + w[i] |= static_cast( + self.m_block[i * 4 + 3]); } - for (size_t i = 16; i < 80; i++) { - w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + + for (std::size_t i = 16; i < 80; i++) { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ + w[i - 14] ^ w[i - 16]), 1); } - uint32_t a = m_digest[0]; - uint32_t b = m_digest[1]; - uint32_t c = m_digest[2]; - uint32_t d = m_digest[3]; - uint32_t e = m_digest[4]; + 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) { - uint32_t f = 0; - uint32_t k = 0; + auto f = std::uint32_t{0}; + auto k = std::uint32_t{0}; if (i < 20) { f = (b & c) | (~b & d); @@ -238,7 +270,8 @@ private: k = 0xCA62C1D6; } - uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; + auto temp = std::uint32_t{left_rotate(a, 5) + + f + e + k + w[i]}; e = d; d = c; c = left_rotate(b, 30); @@ -246,45 +279,54 @@ private: a = temp; } - m_digest[0] += a; - m_digest[1] += b; - m_digest[2] += c; - m_digest[3] += d; - m_digest[4] += e; + 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; - uint8_t m_block[64]; - size_t m_blockByteIndex; - size_t m_byteCount; + digest32_t m_digest; + std::array m_block; + std::size_t m_blockByteIndex; + std::size_t m_byteCount; }; template -inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; +inline constexpr std::string_view empty_guid = + "00000000-0000-0000-0000-000000000000"; template <> -inline constexpr wchar_t empty_guid[37] = L"00000000-0000-0000-0000-000000000000"; +inline constexpr std::wstring_view empty_guid + = L"00000000-0000-0000-0000-000000000000"; template -inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; +inline constexpr std::string_view guid_encoder = "0123456789abcdef"; template <> -inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; +inline constexpr std::wstring_view guid_encoder = L"0123456789abcdef"; -// -------------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------- // UUID format https://tools.ietf.org/html/rfc4122 -// -------------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------- -// -------------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------- // Field NDR Data Type Octet # 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. -// -------------------------------------------------------------------------------------------------------------------------- +// 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -297,18 +339,22 @@ inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; // | node (2-5) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// -------------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------- // enumerations -// -------------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------- -// indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx +// indicated by a bit pattern in octet 8, marked with N in +// xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx export enum struct uuid_variant { - // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) + // 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 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 + // > the final 7 octets are a 56-bit host ID in the form specified by + // the address family ncs, // RFC 4122/DCE 1.1 @@ -327,37 +373,45 @@ export enum struct uuid_variant { reserved }; -// indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx +// indicated by a bit pattern in octet 6, marked with M in +// xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx export enum struct uuid_version { - none = 0, // only possible for nil or invalid uuids - time_based = 1, // The time-based version specified in RFC 4122 - dce_security = 2, // DCE Security version, with embedded POSIX UIDs. - name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing - random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 - name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing + // 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 & to_string so that we can declare to_string as a friend later. +// Forward declare uuid and to_string so that we can declare to_string as a +// friend later. export struct uuid; export template , typename Allocator = std::allocator> -std::basic_string to_string(uuid const &id); +auto to_string(uuid const &id) -> std::basic_string; // -------------------------------------------------------------------------------------------------------------------------- // uuid class // -------------------------------------------------------------------------------------------------------------------------- export struct uuid { - using value_type = uint8_t; + using value_type = std::uint8_t; constexpr uuid() noexcept = default; - uuid(value_type(&arr)[16]) noexcept + uuid(value_type(&arr)[16]) noexcept // NOLINT { std::ranges::copy(arr, std::ranges::begin(data)); } - constexpr uuid(std::array const & arr) noexcept + constexpr uuid(std::array const &arr) noexcept : data{arr} { } @@ -367,14 +421,23 @@ export struct uuid { std::ranges::copy(bytes, std::ranges::begin(data)); } + explicit uuid(std::span bytes) + { + if (bytes.size() != 16) + throw std::logic_error("wrong size for uuid"); + std::ranges::copy(bytes, std::ranges::begin(data)); + } + template explicit uuid(ForwardIterator first, ForwardIterator last) { - if (std::distance(first, last) == 16) - std::copy(first, last, std::begin(data)); + if (std::distance(first, last) != 16) + throw std::logic_error("wrong size for uuid"); + + std::copy(first, last, std::begin(data)); } - [[nodiscard]] constexpr uuid_variant variant() const noexcept + [[nodiscard]] constexpr auto variant() const noexcept -> uuid_variant { if ((data[8] & 0x80) == 0x00) return uuid_variant::ncs; @@ -386,7 +449,7 @@ export struct uuid { return uuid_variant::reserved; } - [[nodiscard]] constexpr uuid_version version() const noexcept + [[nodiscard]] constexpr auto version() const noexcept -> uuid_version { if ((data[6] & 0xF0) == 0x10) return uuid_version::time_based; @@ -402,37 +465,43 @@ export struct uuid { return uuid_version::none; } - [[nodiscard]] constexpr bool is_nil() const noexcept + [[nodiscard]] constexpr auto is_nil() const noexcept -> bool { - for (size_t i = 0; i < data.size(); ++i) - if (data[i] != 0) + for (auto i : data) + if (i != 0) return false; return true; } - void swap(uuid &other) noexcept + auto swap(uuid &other) noexcept -> void { data.swap(other.data); } - [[nodiscard]] inline std::span as_bytes() const + [[nodiscard]] inline auto as_bytes() const + -> std::span { - return std::span(reinterpret_cast(data.data()), 16); + return std::span( + reinterpret_cast(data.data()), + 16); } template - [[nodiscard]] constexpr static bool is_valid_uuid(StringType const & in_str) noexcept + [[nodiscard]] constexpr static auto + is_valid_uuid(StringType const &in_str) noexcept + -> bool { auto str = to_string_view(in_str); - bool firstDigit = true; - size_t hasBraces = 0; - size_t index = 0; + 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; @@ -458,7 +527,9 @@ export struct uuid { } template - [[nodiscard]] constexpr static std::optional from_string(StringType const & in_str) noexcept + [[nodiscard]] constexpr static auto + from_string(StringType const & in_str) noexcept + -> std::optional { auto str = to_string_view(in_str); bool firstDigit = true; @@ -503,14 +574,17 @@ export struct uuid { private: std::array data{ { 0 } }; - friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; - friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; + friend auto operator==(uuid const &, uuid const &) noexcept -> bool; + friend auto operator<(uuid const &, uuid const &) noexcept -> bool; template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + friend auto operator<<(std::basic_ostream &s, + uuid const &id) + -> std::basic_ostream &; template - friend std::basic_string to_string(uuid const& id); + friend auto to_string(uuid const &id) + -> std::basic_string; friend std::hash; }; @@ -520,19 +594,19 @@ private: // -------------------------------------------------------------------------------------------------------------------------- export [[nodiscard]] -auto operator== (uuid const& lhs, uuid const& rhs) noexcept -> bool +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 +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 +auto operator< (uuid const &lhs, uuid const &rhs) noexcept -> bool { return lhs.data < rhs.data; } @@ -541,9 +615,10 @@ export template [[nodiscard]] auto to_string(uuid const &id) -> std::basic_string { - std::basic_string uustr{empty_guid}; + auto uustr = std::basic_string( + std::from_range, empty_guid); - for (size_t i = 0, index = 0; i < 36; ++i) { + for (std::size_t i = 0, index = 0; i < 36; ++i) { if (i == 8 || i == 13 || i == 18 || i == 23) continue; @@ -556,14 +631,14 @@ export template } export template -auto operator<<(std::basic_ostream& s, uuid const &id) - -> std::basic_ostream& +auto operator<<(std::basic_ostream &s, uuid const &id) + -> std::basic_ostream & { s << to_string(id); return s; } -export void swap(uuid & lhs, uuid & rhs) noexcept +export auto swap(uuid &lhs, uuid &rhs) noexcept -> void { lhs.swap(rhs); } @@ -608,21 +683,24 @@ struct basic_uuid_random_generator { using engine_type = UniformRandomNumberGenerator; - explicit basic_uuid_random_generator(engine_type& gen) + explicit basic_uuid_random_generator(engine_type &gen) : generator(&gen, [](auto) {}) { } - explicit basic_uuid_random_generator(engine_type* gen) + explicit basic_uuid_random_generator(engine_type *gen) : generator(gen, [](auto) {}) { } - [[nodiscard]] uuid operator()() + [[nodiscard]] auto operator()() -> uuid { - alignas(uint32_t) uint8_t bytes[16]; + alignas(std::uint32_t) + auto bytes = std::array{}; + for (int i = 0; i < 16; i += 4) - *reinterpret_cast(bytes + i) = distribution(*generator); + *reinterpret_cast(bytes.data() + i) = + distribution(*generator); // variant must be 10xxxxxx bytes[8] &= 0xBF; @@ -636,7 +714,7 @@ struct basic_uuid_random_generator } private: - std::uniform_int_distribution distribution; + std::uniform_int_distribution distribution; std::shared_ptr generator; }; @@ -644,13 +722,13 @@ export using uuid_random_generator = basic_uuid_random_generator; export struct uuid_name_generator { - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + explicit uuid_name_generator(uuid const &namespace_uuid) noexcept : nsuuid(namespace_uuid) { } template - [[nodiscard]] uuid operator()(StringType const & name) + [[nodiscard]] auto operator()(StringType const &name) -> uuid { reset(); process_characters(to_string_view(name)); @@ -658,32 +736,38 @@ export struct uuid_name_generator } private: - void reset() + auto reset() -> void { hasher.reset(); - std::byte bytes[16]; + auto nsbytes = nsuuid.as_bytes(); - std::ranges::copy(nsbytes, bytes); - hasher.process_bytes(bytes, 16); + + auto bytes = std::array(); + std::ranges::copy(nsbytes, std::ranges::begin(bytes)); + + hasher.process_bytes(bytes.data(), bytes.size()); } template - void process_characters(std::basic_string_view const str) + auto process_characters( + std::basic_string_view const str) -> void { - for (uint32_t c : str) { - hasher.process_byte(static_cast(c & 0xFF)); + for (std::uint32_t c : str) { + hasher.process_byte(static_cast(c & 0xFF)); if constexpr (!std::is_same_v) { - hasher.process_byte(static_cast((c >> 8) & 0xFF)); - hasher.process_byte(static_cast((c >> 16) & 0xFF)); - hasher.process_byte(static_cast((c >> 24) & 0xFF)); + hasher.process_byte( + static_cast((c >> 8) & 0xFF)); + hasher.process_byte( + static_cast((c >> 16) & 0xFF)); + hasher.process_byte( + static_cast((c >> 24) & 0xFF)); } } } - [[nodiscard]] uuid make_uuid() + [[nodiscard]] auto make_uuid() -> uuid { - sha1::digest8_t digest; - hasher.get_digest_bytes(digest); + auto digest = hasher.get_digest_bytes(); // variant must be 0b10xxxxxx digest[8] &= 0xBF; @@ -693,7 +777,7 @@ private: digest[6] &= 0x5F; digest[6] |= 0x50; - return uuid{ digest, digest + 16 }; + return uuid(std::span(digest).subspan(0, 16)); } private: @@ -708,9 +792,10 @@ export auto random_uuid() -> uuid { auto rd = std::random_device(); auto seed_data = std::array {}; - std::ranges::generate(seed_data, std::ref(rd)); - auto seq = std::seed_seq(std::begin(seed_data), std::end(seed_data)); + + 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}; @@ -727,9 +812,10 @@ struct hash using argument_type = nihil::uuid; using result_type = std::size_t; - [[nodiscard]] result_type operator()(argument_type const &uuid) const + [[nodiscard]] auto operator()(argument_type const &uuid) const + -> result_type { - uint64_t l = + std::uint64_t l = static_cast(uuid.data[0]) << 56 | static_cast(uuid.data[1]) << 48 | static_cast(uuid.data[2]) << 40 | @@ -738,7 +824,8 @@ struct hash static_cast(uuid.data[5]) << 16 | static_cast(uuid.data[6]) << 8 | static_cast(uuid.data[7]); - uint64_t h = + + std::uint64_t h = static_cast(uuid.data[8]) << 56 | static_cast(uuid.data[9]) << 48 | static_cast(uuid.data[10]) << 40 | @@ -748,12 +835,7 @@ struct hash static_cast(uuid.data[14]) << 8 | static_cast(uuid.data[15]); - if constexpr (sizeof(result_type) > 4) { - return result_type(l ^ h); - } else { - uint64_t hash64 = l ^ h; - return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64)); - } + return std::hash{}(l ^ h); } }; -- cgit v1.2.3