From cf7db48c261ee9c896c813a38ff8c59da5b8fe07 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Sun, 26 Jan 2025 00:40:51 +0100 Subject: [PATCH] Fix line endings --- .bazelrc | 88 +-- .clang-tidy | 68 +- .gitignore | 6 +- BUILD.bazel | 36 +- MODULE.bazel | 30 +- asl/BUILD.bazel | 156 ++-- asl/allocator.cpp | 112 +-- asl/allocator.hpp | 116 +-- asl/annotations.hpp | 18 +- asl/assert.hpp | 66 +- asl/box.hpp | 230 +++--- asl/buffer.hpp | 918 +++++++++++------------ asl/config.hpp | 34 +- asl/float.hpp | 34 +- asl/format.cpp | 402 +++++----- asl/format.hpp | 216 +++--- asl/format_float.cpp | 196 ++--- asl/functional.hpp | 130 ++-- asl/hash.hpp | 276 +++---- asl/hash_cityhash.cpp | 1034 ++++++++++++------------- asl/hash_map.hpp | 356 ++++----- asl/hash_set.hpp | 836 ++++++++++----------- asl/integers.hpp | 80 +- asl/io.hpp | 38 +- asl/layout.hpp | 74 +- asl/log/BUILD.bazel | 52 +- asl/maybe_uninit.hpp | 144 ++-- asl/memory.hpp | 274 +++---- asl/meta.hpp | 442 +++++------ asl/option.hpp | 1016 ++++++++++++------------- asl/print.cpp | 62 +- asl/print.hpp | 58 +- asl/span.hpp | 6 +- asl/status.cpp | 2 +- asl/status.hpp | 4 +- asl/status_or.hpp | 358 ++++----- asl/string.hpp | 2 +- asl/string_builder.hpp | 2 +- asl/string_view.hpp | 2 +- asl/testing/BUILD.bazel | 26 +- asl/testing/testing.cpp | 162 ++-- asl/testing/testing.hpp | 92 +-- asl/tests/box_tests.cpp | 156 ++-- asl/tests/buffer_tests.cpp | 1206 +++++++++++++++--------------- asl/tests/float_tests.cpp | 46 +- asl/tests/format_tests.cpp | 220 +++--- asl/tests/functional_tests.cpp | 146 ++-- asl/tests/hash_map_tests.cpp | 96 +-- asl/tests/hash_set_tests.cpp | 370 ++++----- asl/tests/hash_tests.cpp | 520 ++++++------- asl/tests/integers_tests.cpp | 30 +- asl/tests/maybe_uninit_tests.cpp | 44 +- asl/tests/meta_tests.cpp | 498 ++++++------ asl/tests/option_tests.cpp | 658 ++++++++-------- asl/tests/status_or_tests.cpp | 168 ++--- asl/tests/status_tests.cpp | 4 +- asl/tests/string_view_tests.cpp | 8 +- asl/tests/test_types.hpp | 182 ++--- asl/tests/utility_tests.cpp | 2 +- asl/utility.hpp | 168 ++--- tools/BUILD.bazel | 8 + tools/fix_line_endings.py | 68 ++ vendor/cityhash/LICENSE.txt | 38 +- vendor/dragonbox/BUILD.bazel | 12 +- 64 files changed, 6490 insertions(+), 6412 deletions(-) create mode 100644 tools/BUILD.bazel create mode 100644 tools/fix_line_endings.py diff --git a/.bazelrc b/.bazelrc index 97a7861..19154d5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,44 +1,44 @@ -startup --windows_enable_symlinks -build:windows --enable_runfiles=true - -build --build_python_zip=false - -build:windows --extra_execution_platforms=//:x64_windows-clang-cl -# @Todo(bazel) We should be able to use @local_config_cc... -build:windows --extra_toolchains=@@rules_cc++cc_configure_extension+local_config_cc//:cc-toolchain-x64_windows-clang-cl - -build:linux --repo_env=CC=clang - -build:windows --cxxopt=-Xclang=-std=c++20 -build:linux --cxxopt=-std=c++20 - -build --cxxopt=-Wall -build --cxxopt=-Wno-c++98-compat -build --cxxopt=-Wno-c++98-compat-pedantic -build --cxxopt=-Wno-pre-c++17-compat -build --cxxopt=-Wno-c++20-compat -build --cxxopt=-Wno-unused-macros -build --cxxopt=-Wno-documentation-unknown-command -build --cxxopt=-Wno-extra-semi-stmt -build --cxxopt=-Wno-extra-semi -build --cxxopt=-Wno-global-constructors -build --cxxopt=-Wno-unsafe-buffer-usage -build --cxxopt=-Wno-covered-switch-default - -build:windows_san --config=windows -build:windows_san --copt=-fno-sanitize-ignorelist -build:windows_san --copt=-fsanitize=address -build:windows_san --copt=-fsanitize=undefined -build:windows_san --copt=-fno-sanitize-recover=all -build:windows_san --linkopt=clang_rt.asan-x86_64.lib -build:windows_san --copt=/MT - -build:linux_san --config=linux -build:linux_san --copt=-fsanitize=address -build:linux_san --linkopt=-fsanitize=address -build:linux_san --copt=-fsanitize=undefined -build:linux_san --copt=-fno-sanitize-recover=all -build:linux_san --linkopt=-fsanitize=undefined -build:linux_san --linkopt=-fsanitize-link-c++-runtime - -test --test_output=errors +startup --windows_enable_symlinks +build:windows --enable_runfiles=true + +build --build_python_zip=false + +build:windows --extra_execution_platforms=//:x64_windows-clang-cl +# @Todo(bazel) We should be able to use @local_config_cc... +build:windows --extra_toolchains=@@rules_cc++cc_configure_extension+local_config_cc//:cc-toolchain-x64_windows-clang-cl + +build:linux --repo_env=CC=clang + +build:windows --cxxopt=-Xclang=-std=c++20 +build:linux --cxxopt=-std=c++20 + +build --cxxopt=-Wall +build --cxxopt=-Wno-c++98-compat +build --cxxopt=-Wno-c++98-compat-pedantic +build --cxxopt=-Wno-pre-c++17-compat +build --cxxopt=-Wno-c++20-compat +build --cxxopt=-Wno-unused-macros +build --cxxopt=-Wno-documentation-unknown-command +build --cxxopt=-Wno-extra-semi-stmt +build --cxxopt=-Wno-extra-semi +build --cxxopt=-Wno-global-constructors +build --cxxopt=-Wno-unsafe-buffer-usage +build --cxxopt=-Wno-covered-switch-default + +build:windows_san --config=windows +build:windows_san --copt=-fno-sanitize-ignorelist +build:windows_san --copt=-fsanitize=address +build:windows_san --copt=-fsanitize=undefined +build:windows_san --copt=-fno-sanitize-recover=all +build:windows_san --linkopt=clang_rt.asan-x86_64.lib +build:windows_san --copt=/MT + +build:linux_san --config=linux +build:linux_san --copt=-fsanitize=address +build:linux_san --linkopt=-fsanitize=address +build:linux_san --copt=-fsanitize=undefined +build:linux_san --copt=-fno-sanitize-recover=all +build:linux_san --linkopt=-fsanitize=undefined +build:linux_san --linkopt=-fsanitize-link-c++-runtime + +test --test_output=errors diff --git a/.clang-tidy b/.clang-tidy index d7337bd..d988d1b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,34 +1,34 @@ -Checks: - - "hicpp-*" - - "cppcoreguidelines-*" - - "misc-*" - - "clang-analyzer-*" - - "-misc-include-cleaner" - - "performance-*" - - "readability-*" - - "-*-named-parameter" - - "-*-avoid-do-while" - - "-*-magic-numbers" - - "-*-identifier-length" - - "-*-union-access" - - "-*-vararg" - - "-*-macro-usage" - - "-*-non-private-member-variables-in-classes" - - "-*-avoid-non-const-global-variables" - - "-*-missing-std-forward" - - "-*-owning-memory" - - "-*-no-malloc" - - "-*-avoid-c-arrays" - - "-*-use-anonymous-namespace" - - "-*-reinterpret-cast" - - "-*-noexcept-swap" - - "-*-noexcept-move" - - "-*-noexcept-move-constructor" - - "-*-noexcept-move-operations" - - "-*-bounds-array-to-pointer-decay" - - "-*-no-array-decay" - - "-*-signed-bitwise" - - "-readability-use-anyofallof" - - "-readability-function-cognitive-complexity" - - "-readability-math-missing-parentheses" - - "-*-rvalue-reference-param-not-moved" +Checks: + - "hicpp-*" + - "cppcoreguidelines-*" + - "misc-*" + - "clang-analyzer-*" + - "-misc-include-cleaner" + - "performance-*" + - "readability-*" + - "-*-named-parameter" + - "-*-avoid-do-while" + - "-*-magic-numbers" + - "-*-identifier-length" + - "-*-union-access" + - "-*-vararg" + - "-*-macro-usage" + - "-*-non-private-member-variables-in-classes" + - "-*-avoid-non-const-global-variables" + - "-*-missing-std-forward" + - "-*-owning-memory" + - "-*-no-malloc" + - "-*-avoid-c-arrays" + - "-*-use-anonymous-namespace" + - "-*-reinterpret-cast" + - "-*-noexcept-swap" + - "-*-noexcept-move" + - "-*-noexcept-move-constructor" + - "-*-noexcept-move-operations" + - "-*-bounds-array-to-pointer-decay" + - "-*-no-array-decay" + - "-*-signed-bitwise" + - "-readability-use-anyofallof" + - "-readability-function-cognitive-complexity" + - "-readability-math-missing-parentheses" + - "-*-rvalue-reference-param-not-moved" diff --git a/.gitignore b/.gitignore index deb8fd6..fd35d2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -bazel-*/ -compile_commands.json +bazel-*/ +compile_commands.json +.cache/ +external/ diff --git a/BUILD.bazel b/BUILD.bazel index d861b66..c5a2f79 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,18 +1,18 @@ -load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") - -refresh_compile_commands( - name = "refresh_clangd", - targets = "//...", -) - -platform( - name = "x64_windows-clang-cl", - constraint_values = [ - "@platforms//cpu:x86_64", - "@platforms//os:windows", - # @Todo(bazel) Bit weird to use a private thing. - # We used to use @bazel_tools//tools/cpp:clang-cl but it's deprecated - # in favor of... this?... - "@rules_cc//cc/private/toolchain:clang-cl", - ], -) +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_clangd", + targets = "//...", +) + +platform( + name = "x64_windows-clang-cl", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + # @Todo(bazel) Bit weird to use a private thing. + # We used to use @bazel_tools//tools/cpp:clang-cl but it's deprecated + # in favor of... this?... + "@rules_cc//cc/private/toolchain:clang-cl", + ], +) diff --git a/MODULE.bazel b/MODULE.bazel index a10bf6f..197d3cc 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,15 +1,15 @@ -module(name = "asl") - -bazel_dep(name = "platforms", version = "0.0.10") -bazel_dep(name = "rules_cc", version = "0.0.17") - -bazel_dep(name = "hedron_compile_commands", dev_dependency = True) -git_override( - module_name = "hedron_compile_commands", - remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", - commit = "4f28899228fb3ad0126897876f147ca15026151e", -) - -bazel_dep(name = "rules_python", version = "1.1.0", dev_dependency = True) -python = use_extension("@rules_python//python/extensions:python.bzl", "python") -python.toolchain(python_version = "3.13", is_default = True) +module(name = "asl") + +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep(name = "rules_cc", version = "0.0.17") + +bazel_dep(name = "hedron_compile_commands", dev_dependency = True) +git_override( + module_name = "hedron_compile_commands", + remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", + commit = "4f28899228fb3ad0126897876f147ca15026151e", +) + +bazel_dep(name = "rules_python", version = "1.1.0", dev_dependency = True) +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain(python_version = "3.13", is_default = True) diff --git a/asl/BUILD.bazel b/asl/BUILD.bazel index 2d82f4b..2cc4af5 100644 --- a/asl/BUILD.bazel +++ b/asl/BUILD.bazel @@ -1,78 +1,78 @@ -cc_library( - name = "asl", - hdrs = [ - "allocator.hpp", - "annotations.hpp", - "assert.hpp", - "atomic.hpp", - "box.hpp", - "buffer.hpp", - "config.hpp", - "float.hpp", - "format.hpp", - "functional.hpp", - "hash.hpp", - "hash_map.hpp", - "hash_set.hpp", - "integers.hpp", - "io.hpp", - "layout.hpp", - "maybe_uninit.hpp", - "memory.hpp", - "meta.hpp", - "option.hpp", - "print.hpp", - "span.hpp", - "status.hpp", - "status_or.hpp", - "string.hpp", - "string_builder.hpp", - "string_view.hpp", - "utility.hpp", - ], - srcs = [ - "allocator.cpp", - "assert.cpp", - "format.cpp", - "format_float.cpp", - "hash_cityhash.cpp", - "print.cpp", - "status.cpp", - ], - deps = [ - "//vendor/dragonbox", - ], - visibility = ["//visibility:public"], -) - -[cc_test( - name = "%s_tests" % name, - srcs = [ - "tests/%s_tests.cpp" % name, - "tests/test_types.hpp", - ], - deps = [ - ":asl", - "//asl/testing", - ], -) for name in [ - "box", - "buffer", - "float", - "format", - "functional", - "hash", - "hash_map", - "hash_set", - "integers", - "maybe_uninit", - "meta", - "option", - "span", - "status", - "status_or", - "string", - "string_builder", - "string_view", - "utility", -]] +cc_library( + name = "asl", + hdrs = [ + "allocator.hpp", + "annotations.hpp", + "assert.hpp", + "atomic.hpp", + "box.hpp", + "buffer.hpp", + "config.hpp", + "float.hpp", + "format.hpp", + "functional.hpp", + "hash.hpp", + "hash_map.hpp", + "hash_set.hpp", + "integers.hpp", + "io.hpp", + "layout.hpp", + "maybe_uninit.hpp", + "memory.hpp", + "meta.hpp", + "option.hpp", + "print.hpp", + "span.hpp", + "status.hpp", + "status_or.hpp", + "string.hpp", + "string_builder.hpp", + "string_view.hpp", + "utility.hpp", + ], + srcs = [ + "allocator.cpp", + "assert.cpp", + "format.cpp", + "format_float.cpp", + "hash_cityhash.cpp", + "print.cpp", + "status.cpp", + ], + deps = [ + "//vendor/dragonbox", + ], + visibility = ["//visibility:public"], +) + +[cc_test( + name = "%s_tests" % name, + srcs = [ + "tests/%s_tests.cpp" % name, + "tests/test_types.hpp", + ], + deps = [ + ":asl", + "//asl/testing", + ], +) for name in [ + "box", + "buffer", + "float", + "format", + "functional", + "hash", + "hash_map", + "hash_set", + "integers", + "maybe_uninit", + "meta", + "option", + "span", + "status", + "status_or", + "string", + "string_builder", + "string_view", + "utility", +]] diff --git a/asl/allocator.cpp b/asl/allocator.cpp index 5dbdce0..59bff76 100644 --- a/asl/allocator.cpp +++ b/asl/allocator.cpp @@ -1,56 +1,56 @@ -#include "asl/allocator.hpp" -#include "asl/assert.hpp" -#include "asl/utility.hpp" -#include "asl/memory.hpp" -#include "asl/print.hpp" - -#include - -// @Todo zalloc -// @Todo Cookies -// @Todo Debug values - -void* asl::GlobalHeap::alloc(const layout& layout) -{ -#if ASL_OS_WINDOWS - void* ptr = ::_aligned_malloc( - static_cast(layout.size), - static_cast(layout.align)); -#elif ASL_OS_LINUX - void* ptr = ::aligned_alloc( - static_cast(layout.align), - static_cast(layout.size)); -#endif - ASL_ASSERT(ptr != nullptr); // @Todo panic - return ptr; -} - -void* asl::GlobalHeap::realloc(void* old_ptr, [[maybe_unused]] const layout& old_layout, const layout& new_layout) -{ -#if ASL_OS_WINDOWS - return ::_aligned_realloc(old_ptr, - static_cast(new_layout.size), - static_cast(new_layout.align)); -#elif ASL_OS_LINUX - if (new_layout.align <= old_layout.align) - { - void* new_ptr = ::realloc(old_ptr, static_cast(new_layout.size)); - ASL_ASSERT(new_ptr != nullptr); // @Todo panic - return new_ptr; - } - - void* new_ptr = alloc(new_layout); - asl::memcpy(new_ptr, old_ptr, asl::min(old_layout.size, new_layout.size)); - dealloc(old_ptr, old_layout); - return new_ptr; -#endif -} - -void asl::GlobalHeap::dealloc(void* ptr, const layout&) -{ -#if ASL_OS_WINDOWS - ::_aligned_free(ptr); -#elif ASL_OS_LINUX - ::free(ptr); -#endif -} +#include "asl/allocator.hpp" +#include "asl/assert.hpp" +#include "asl/utility.hpp" +#include "asl/memory.hpp" +#include "asl/print.hpp" + +#include + +// @Todo zalloc +// @Todo Cookies +// @Todo Debug values + +void* asl::GlobalHeap::alloc(const layout& layout) +{ +#if ASL_OS_WINDOWS + void* ptr = ::_aligned_malloc( + static_cast(layout.size), + static_cast(layout.align)); +#elif ASL_OS_LINUX + void* ptr = ::aligned_alloc( + static_cast(layout.align), + static_cast(layout.size)); +#endif + ASL_ASSERT(ptr != nullptr); // @Todo panic + return ptr; +} + +void* asl::GlobalHeap::realloc(void* old_ptr, [[maybe_unused]] const layout& old_layout, const layout& new_layout) +{ +#if ASL_OS_WINDOWS + return ::_aligned_realloc(old_ptr, + static_cast(new_layout.size), + static_cast(new_layout.align)); +#elif ASL_OS_LINUX + if (new_layout.align <= old_layout.align) + { + void* new_ptr = ::realloc(old_ptr, static_cast(new_layout.size)); + ASL_ASSERT(new_ptr != nullptr); // @Todo panic + return new_ptr; + } + + void* new_ptr = alloc(new_layout); + asl::memcpy(new_ptr, old_ptr, asl::min(old_layout.size, new_layout.size)); + dealloc(old_ptr, old_layout); + return new_ptr; +#endif +} + +void asl::GlobalHeap::dealloc(void* ptr, const layout&) +{ +#if ASL_OS_WINDOWS + ::_aligned_free(ptr); +#elif ASL_OS_LINUX + ::free(ptr); +#endif +} diff --git a/asl/allocator.hpp b/asl/allocator.hpp index 265378b..90793dd 100644 --- a/asl/allocator.hpp +++ b/asl/allocator.hpp @@ -1,58 +1,58 @@ -#pragma once - -#include "asl/layout.hpp" -#include "asl/meta.hpp" -#include "asl/memory.hpp" - -namespace asl -{ - -template -concept allocator = moveable && equality_comparable && - requires(T& alloc, layout layout, void* ptr) - { - { alloc.alloc(layout) } -> same_as; - { alloc.realloc(ptr, layout, layout) } -> same_as; - alloc.dealloc(ptr, layout); - }; - -class GlobalHeap -{ -public: - static void* alloc(const layout&); - static void* realloc(void* ptr, const layout& old, const layout& new_layout); - static void dealloc(void* ptr, const layout&); - - constexpr bool operator==(const GlobalHeap&) const { return true; } -}; -static_assert(allocator); - -using DefaultAllocator = GlobalHeap; - -template -T* alloc_new(allocator auto& a, auto&&... args) -{ - void* ptr = a.alloc(layout::of()); - return construct_at(ptr, ASL_FWD(args)...); -} - -template -void alloc_delete(allocator auto& a, T* ptr) -{ - destroy(ptr); - a.dealloc(ptr, layout::of()); -} - -template -constexpr T* alloc_new_default(auto&&... args) -{ - return alloc_new(DefaultAllocator{}, ASL_FWD(args)...); -} - -template -void alloc_delete_default(T* ptr) -{ - alloc_delete(DefaultAllocator{}, ptr); -} - -} // namespace asl +#pragma once + +#include "asl/layout.hpp" +#include "asl/meta.hpp" +#include "asl/memory.hpp" + +namespace asl +{ + +template +concept allocator = moveable && equality_comparable && + requires(T& alloc, layout layout, void* ptr) + { + { alloc.alloc(layout) } -> same_as; + { alloc.realloc(ptr, layout, layout) } -> same_as; + alloc.dealloc(ptr, layout); + }; + +class GlobalHeap +{ +public: + static void* alloc(const layout&); + static void* realloc(void* ptr, const layout& old, const layout& new_layout); + static void dealloc(void* ptr, const layout&); + + constexpr bool operator==(const GlobalHeap&) const { return true; } +}; +static_assert(allocator); + +using DefaultAllocator = GlobalHeap; + +template +T* alloc_new(allocator auto& a, auto&&... args) +{ + void* ptr = a.alloc(layout::of()); + return construct_at(ptr, ASL_FWD(args)...); +} + +template +void alloc_delete(allocator auto& a, T* ptr) +{ + destroy(ptr); + a.dealloc(ptr, layout::of()); +} + +template +constexpr T* alloc_new_default(auto&&... args) +{ + return alloc_new(DefaultAllocator{}, ASL_FWD(args)...); +} + +template +void alloc_delete_default(T* ptr) +{ + alloc_delete(DefaultAllocator{}, ptr); +} + +} // namespace asl diff --git a/asl/annotations.hpp b/asl/annotations.hpp index fc65378..6ea3a84 100644 --- a/asl/annotations.hpp +++ b/asl/annotations.hpp @@ -1,9 +1,9 @@ -#pragma once - -#include "asl/config.hpp" - -#if ASL_COMPILER_CLANG_CL - #define ASL_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -#elif ASL_COMPILER_CLANG - #define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]] -#endif +#pragma once + +#include "asl/config.hpp" + +#if ASL_COMPILER_CLANG_CL + #define ASL_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif ASL_COMPILER_CLANG + #define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]] +#endif diff --git a/asl/assert.hpp b/asl/assert.hpp index bf6a795..7a3ae00 100644 --- a/asl/assert.hpp +++ b/asl/assert.hpp @@ -1,33 +1,33 @@ -#pragma once - -#include "asl/config.hpp" -#include "asl/meta.hpp" - -namespace asl -{ - -void report_assert_failure( const char* msg, const source_location& sl = source_location{}); - -} // namespace asl - -#if ASL_COMPILER_CLANG_CL - #define ASL_DEBUG_BREAK() __debugbreak() -#elif ASL_COMPILER_CLANG - #define ASL_DEBUG_BREAK() __builtin_debugtrap() -#endif - -#define ASL_ASSERT(...) \ - if (__VA_ARGS__) {} \ - else \ - { \ - ::asl::report_assert_failure(#__VA_ARGS__); \ - ASL_DEBUG_BREAK(); \ - } - -#define ASL_ASSERT_RELEASE(...) \ - if (__VA_ARGS__) {} \ - else \ - { \ - ::asl::report_assert_failure(#__VA_ARGS__); \ - ASL_DEBUG_BREAK(); \ - } +#pragma once + +#include "asl/config.hpp" +#include "asl/meta.hpp" + +namespace asl +{ + +void report_assert_failure( const char* msg, const source_location& sl = source_location{}); + +} // namespace asl + +#if ASL_COMPILER_CLANG_CL + #define ASL_DEBUG_BREAK() __debugbreak() +#elif ASL_COMPILER_CLANG + #define ASL_DEBUG_BREAK() __builtin_debugtrap() +#endif + +#define ASL_ASSERT(...) \ + if (__VA_ARGS__) {} \ + else \ + { \ + ::asl::report_assert_failure(#__VA_ARGS__); \ + ASL_DEBUG_BREAK(); \ + } + +#define ASL_ASSERT_RELEASE(...) \ + if (__VA_ARGS__) {} \ + else \ + { \ + ::asl::report_assert_failure(#__VA_ARGS__); \ + ASL_DEBUG_BREAK(); \ + } diff --git a/asl/box.hpp b/asl/box.hpp index 340a37c..f193853 100644 --- a/asl/box.hpp +++ b/asl/box.hpp @@ -1,115 +1,115 @@ -#pragma once - -#include "asl/allocator.hpp" -#include "asl/assert.hpp" -#include "asl/annotations.hpp" -#include "asl/memory.hpp" -#include "asl/utility.hpp" -#include "asl/hash.hpp" - -namespace asl -{ - -template -class box -{ - T* m_ptr; - ASL_NO_UNIQUE_ADDRESS Allocator m_alloc; - -public: - explicit constexpr box(niche_t) - requires default_constructible - : m_ptr{nullptr} - , m_alloc{} - {} - - constexpr box(T* ptr, Allocator alloc) - : m_ptr{ptr} - , m_alloc{ASL_MOVE(alloc)} - { - ASL_ASSERT(m_ptr != nullptr); - } - - constexpr box(box&& other) - : m_ptr{exchange(other.m_ptr, nullptr)} - , m_alloc{ASL_MOVE(other.m_alloc)} - {} - - constexpr box& operator=(box&& other) - { - if (this == &other) { return *this; } - - if (m_ptr != nullptr) { reset(); } - - m_ptr = exchange(other.m_ptr, nullptr); - m_alloc = ASL_MOVE(other.m_alloc); - - return *this; - } - - box(const box&) = delete; - box& operator=(const box&) = delete; - - constexpr ~box() - { - reset(); - } - - constexpr void reset() - { - if (m_ptr != nullptr) - { - destroy(m_ptr); - m_alloc.dealloc(m_ptr, layout::of()); - m_ptr = nullptr; - } - } - - constexpr T* get() const { return m_ptr; } - - constexpr T& operator*() const - { - ASL_ASSERT(m_ptr != nullptr); - return *m_ptr; - } - - constexpr T* operator->() const - { - ASL_ASSERT(m_ptr != nullptr); - return m_ptr; - } - - constexpr bool operator==(niche_t) const - { - return m_ptr == nullptr; - } - - template - requires hashable - friend H AslHashValue(H h, const box& b) - { - return H::combine(ASL_MOVE(h), *b); - } -}; - -template -constexpr box make_box_in(Allocator allocator, Args&&... args) - requires constructible_from -{ - void* raw_ptr = allocator.alloc(layout::of()); - auto* ptr = construct_at(raw_ptr, ASL_FWD(args)...); - return box(ptr, ASL_MOVE(allocator)); -} - -template -constexpr box make_box(Args&&... args) - requires default_constructible && constructible_from -{ - Allocator allocator{}; - void* raw_ptr = allocator.alloc(layout::of()); - auto* ptr = construct_at(raw_ptr, ASL_FWD(args)...); - return box(ptr, ASL_MOVE(allocator)); -} - -} // namespace asl - +#pragma once + +#include "asl/allocator.hpp" +#include "asl/assert.hpp" +#include "asl/annotations.hpp" +#include "asl/memory.hpp" +#include "asl/utility.hpp" +#include "asl/hash.hpp" + +namespace asl +{ + +template +class box +{ + T* m_ptr; + ASL_NO_UNIQUE_ADDRESS Allocator m_alloc; + +public: + explicit constexpr box(niche_t) + requires default_constructible + : m_ptr{nullptr} + , m_alloc{} + {} + + constexpr box(T* ptr, Allocator alloc) + : m_ptr{ptr} + , m_alloc{ASL_MOVE(alloc)} + { + ASL_ASSERT(m_ptr != nullptr); + } + + constexpr box(box&& other) + : m_ptr{exchange(other.m_ptr, nullptr)} + , m_alloc{ASL_MOVE(other.m_alloc)} + {} + + constexpr box& operator=(box&& other) + { + if (this == &other) { return *this; } + + if (m_ptr != nullptr) { reset(); } + + m_ptr = exchange(other.m_ptr, nullptr); + m_alloc = ASL_MOVE(other.m_alloc); + + return *this; + } + + box(const box&) = delete; + box& operator=(const box&) = delete; + + constexpr ~box() + { + reset(); + } + + constexpr void reset() + { + if (m_ptr != nullptr) + { + destroy(m_ptr); + m_alloc.dealloc(m_ptr, layout::of()); + m_ptr = nullptr; + } + } + + constexpr T* get() const { return m_ptr; } + + constexpr T& operator*() const + { + ASL_ASSERT(m_ptr != nullptr); + return *m_ptr; + } + + constexpr T* operator->() const + { + ASL_ASSERT(m_ptr != nullptr); + return m_ptr; + } + + constexpr bool operator==(niche_t) const + { + return m_ptr == nullptr; + } + + template + requires hashable + friend H AslHashValue(H h, const box& b) + { + return H::combine(ASL_MOVE(h), *b); + } +}; + +template +constexpr box make_box_in(Allocator allocator, Args&&... args) + requires constructible_from +{ + void* raw_ptr = allocator.alloc(layout::of()); + auto* ptr = construct_at(raw_ptr, ASL_FWD(args)...); + return box(ptr, ASL_MOVE(allocator)); +} + +template +constexpr box make_box(Args&&... args) + requires default_constructible && constructible_from +{ + Allocator allocator{}; + void* raw_ptr = allocator.alloc(layout::of()); + auto* ptr = construct_at(raw_ptr, ASL_FWD(args)...); + return box(ptr, ASL_MOVE(allocator)); +} + +} // namespace asl + diff --git a/asl/buffer.hpp b/asl/buffer.hpp index b714e5a..5cf9964 100644 --- a/asl/buffer.hpp +++ b/asl/buffer.hpp @@ -1,459 +1,459 @@ -#pragma once - -#include "asl/meta.hpp" -#include "asl/allocator.hpp" -#include "asl/annotations.hpp" -#include "asl/memory.hpp" -#include "asl/assert.hpp" -#include "asl/span.hpp" -#include "asl/hash.hpp" - -namespace asl -{ - -template -class buffer -{ - T* m_data{}; - isize_t m_capacity{}; - - static constexpr size_t kOnHeapMask = 0x8000'0000'0000'0000ULL; - - // bit 63 : 1 = on heap, 0 = inline - // bits [62:56] : size when inline - // bits [62:0] : size when on heap - size_t m_size_encoded_{}; - - ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; - - static constexpr isize_t kInlineRegionSize = size_of + size_of + size_of; - -public: - static constexpr isize_t kInlineCapacity = []() { - // 1 byte is used for size inline in m_size_encoded. - // This is enough because we have at most 24 bytes available, - // so 23 chars of capacity. - const isize_t available_size = kInlineRegionSize - 1; - return available_size / size_of; - }(); - -private: - static_assert(align_of <= align_of); - static_assert(align_of == align_of); - static_assert(align_of == align_of); - - constexpr size_t load_size_encoded() const - { - size_t s{}; - asl::memcpy(&s, &m_size_encoded_, sizeof(size_t)); - return s; - } - - constexpr void store_size_encoded(size_t encoded) - { - asl::memcpy(&m_size_encoded_, &encoded, sizeof(size_t)); - } - - static constexpr bool is_on_heap(size_t size_encoded) - { - return (size_encoded & kOnHeapMask) != 0; - } - - static constexpr size_t encode_size_heap(isize_t size) - { - return static_cast(size) | kOnHeapMask; - } - - static constexpr isize_t decode_size(size_t size_encoded) - { - if constexpr (kInlineCapacity == 0) - { - return is_on_heap(size_encoded) - ? static_cast(size_encoded & (~kOnHeapMask)) - : 0; - } - else - { - return is_on_heap(size_encoded) - ? static_cast(size_encoded & (~kOnHeapMask)) - : static_cast(size_encoded >> 56); - } - } - - constexpr bool is_on_heap() const - { - return is_on_heap(load_size_encoded()); - } - - constexpr T* push_uninit() - { - isize_t sz = size(); - resize_uninit_inner(sz + 1); - return data() + sz; - } - - constexpr void resize_uninit_inner(isize_t new_size) - { - isize_t old_size = size(); - if (!trivially_destructible && new_size < old_size) - { - destroy_n(data() + new_size, old_size - new_size); - } - reserve_capacity(new_size); - set_size(new_size); - } - - constexpr void set_size_inline(isize_t new_size) - { - ASL_ASSERT(new_size >= 0 && new_size <= kInlineCapacity); - size_t size_encoded = (load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff}) | (bit_cast(new_size) << 56); - store_size_encoded(size_encoded); - } - - constexpr void set_size(isize_t new_size) - { - ASL_ASSERT(new_size >= 0 && new_size <= capacity()); - if (is_on_heap()) - { - store_size_encoded(encode_size_heap(new_size)); - } - else - { - set_size_inline(new_size); - } - } - - // NOLINTNEXTLINE(*-rvalue-reference-param-not-moved) - void move_from_other(buffer&& other, bool assign) - { - if (other.is_on_heap()) - { - destroy(); - m_data = other.m_data; - m_capacity = other.m_capacity; - store_size_encoded(other.load_size_encoded()); - } - else if (trivially_move_constructible) - { - destroy(); - asl::memcpy(this, &other, kInlineRegionSize); - } - else if (!assign || m_allocator == other.m_allocator) - { - isize_t other_n = other.size(); - isize_t this_n = size(); - resize_uninit_inner(other_n); - if (other_n <= this_n) - { - relocate_assign_n(data(), other.data(), other_n); - } - else - { - relocate_assign_n(data(), other.data(), this_n); - relocate_uninit_n(data() + this_n, other.data() + this_n, other_n - this_n); - } - } - else - { - destroy(); - isize_t n = other.size(); - ASL_ASSERT(n <= kInlineCapacity); - relocate_uninit_n(data(), other.data(), n); - set_size_inline(n); - } - - other.set_size_inline(0); - - if (assign) - { - m_allocator = ASL_MOVE(other.m_allocator); - } - } - - void copy_range(span to_copy) - { - isize_t this_size = size(); - isize_t new_size = to_copy.size(); - - resize_uninit_inner(to_copy.size()); - ASL_ASSERT(capacity() >= new_size); - ASL_ASSERT(size() == to_copy.size()); - - if (new_size <= this_size) - { - copy_assign_n(data(), to_copy.data(), new_size); - } - else - { - copy_assign_n(data(), to_copy.data(), this_size); - copy_uninit_n(data() + this_size, to_copy.data() + this_size, new_size - this_size); - } - } - - template - void resize_inner(isize_t new_size, Args&&... args) - requires constructible_from - { - ASL_ASSERT(new_size >= 0); - - isize_t old_size = size(); - resize_uninit_inner(new_size); - - T* data_ptr = data(); - T* end = data_ptr + new_size; - - // NOLINTNEXTLINE(*-pointer-arithmetic) - for (T* it = data_ptr + old_size; it < end; ++it) - { - construct_at(it, ASL_FWD(args)...); - } - } - -public: - constexpr buffer() requires default_constructible = default; - - explicit constexpr buffer(span s) - requires default_constructible - : buffer{} - { - copy_range(s); - } - - explicit constexpr buffer(Allocator allocator) - : m_allocator{ASL_MOVE(allocator)} - {} - - explicit constexpr buffer(span s, Allocator allocator) - : m_allocator{ASL_MOVE(allocator)} - { - copy_range(s); - } - - constexpr buffer(const buffer& other) - requires copy_constructible && copyable - : m_allocator{other.m_allocator} - { - copy_range(other); - } - - constexpr buffer(buffer&& other) - requires moveable - : buffer(ASL_MOVE(other.m_allocator)) - { - move_from_other(ASL_MOVE(other), false); - } - - constexpr buffer& operator=(const buffer& other) - requires copyable - { - if (&other == this) { return *this; } - copy_range(other); - return *this; - } - - constexpr buffer& operator=(buffer&& other) - requires moveable - { - if (&other == this) { return *this; } - move_from_other(ASL_MOVE(other), true); - return *this; - } - - ~buffer() - { - destroy(); - } - - constexpr isize_t size() const - { - return decode_size(load_size_encoded()); - } - - constexpr isize_t capacity() const - { - if constexpr (kInlineCapacity == 0) - { - return m_capacity; - } - else - { - return is_on_heap() ? m_capacity : kInlineCapacity; - } - } - - void clear() - { - isize_t current_size = size(); - if (current_size == 0) { return; } - - destroy_n(data(), current_size); - set_size(0); - } - - void destroy() - { - clear(); - if (is_on_heap()) - { - if (m_data != nullptr) - { - auto current_layout = layout::array(m_capacity); - m_allocator.dealloc(m_data, current_layout); - } - set_size_inline(0); - } - } - - void reserve_capacity(isize_t new_capacity) - { - ASL_ASSERT(new_capacity >= 0); - ASL_ASSERT_RELEASE(new_capacity <= 0x4000'0000'0000'0000); - - if (new_capacity <= capacity()) { return; } - ASL_ASSERT(new_capacity > kInlineCapacity); - - new_capacity = static_cast(round_up_pow2(static_cast(new_capacity))); - - T* old_data = data(); - const isize_t old_capacity = capacity(); - const isize_t current_size = size(); - const bool currently_on_heap = is_on_heap(); - - auto old_layout = layout::array(old_capacity); - auto new_layout = layout::array(new_capacity); - - if (currently_on_heap && trivially_move_constructible) - { - m_data = reinterpret_cast(m_allocator.realloc(m_data, old_layout, new_layout)); - m_capacity = new_capacity; - return; - } - - T* new_data = reinterpret_cast(m_allocator.alloc(new_layout)); - - relocate_uninit_n(new_data, old_data, current_size); - - if (currently_on_heap) - { - m_allocator.dealloc(old_data, old_layout); - } - - m_data = new_data; - m_capacity = new_capacity; - store_size_encoded(encode_size_heap(current_size)); - } - - constexpr void resize_uninit(isize_t new_size) - requires trivially_default_constructible && trivially_destructible - { - reserve_capacity(new_size); - set_size(new_size); - } - - constexpr void resize_zero(isize_t new_size) - requires trivially_default_constructible && trivially_destructible - { - isize_t old_size = size(); - resize_uninit(new_size); - - if (new_size > old_size) - { - memzero(data() + old_size, (new_size - old_size) * size_of); - } - } - - void resize(isize_t new_size) - requires default_constructible - { - resize_inner(new_size); - } - - void resize(isize_t new_size, const T& value) - { - resize_inner(new_size, value); - } - - constexpr T& push(auto&&... args) - requires constructible_from - { - T* uninit = push_uninit(); - T* init = construct_at(uninit, ASL_FWD(args)...); - return *init; - } - - // @Todo(C++23) Use deducing this - const T* data() const - { - if constexpr (kInlineCapacity == 0) - { - return m_data; - } - else - { - return is_on_heap() ? m_data : reinterpret_cast(this); - } - } - - T* data() - { - if constexpr (kInlineCapacity == 0) - { - return m_data; - } - else - { - return is_on_heap() ? m_data : reinterpret_cast(this); - } - } - - // @Todo(C++23) Use deducing this - constexpr contiguous_iterator begin() const { return contiguous_iterator{data()}; } - constexpr contiguous_iterator end() const { return contiguous_iterator{data() + size()}; } - - constexpr contiguous_iterator begin() { return contiguous_iterator{data()}; } - constexpr contiguous_iterator end() { return contiguous_iterator{data() + size()}; } - - // @Todo(C++23) Deducing this - constexpr operator span() const // NOLINT(*-explicit-conversions) - { - return as_span(); - } - - constexpr operator span() // NOLINT(*-explicit-conversions) - { - return as_span(); - } - - constexpr span as_span() const - { - return span{data(), size()}; - } - - constexpr span as_span() - { - return span{data(), size()}; - } - - // @Todo(C++23) Use deducing this - constexpr T& operator[](isize_t i) - { - ASL_ASSERT(i >= 0 && i <= size()); - return data()[i]; - } - - constexpr const T& operator[](isize_t i) const - { - ASL_ASSERT(i >= 0 && i <= size()); - return data()[i]; - } - - template - requires hashable - friend H AslHashValue(H h, const buffer& b) - { - return H::combine_contiguous(ASL_MOVE(h), b.as_span()); - } -}; - -} // namespace asl - +#pragma once + +#include "asl/meta.hpp" +#include "asl/allocator.hpp" +#include "asl/annotations.hpp" +#include "asl/memory.hpp" +#include "asl/assert.hpp" +#include "asl/span.hpp" +#include "asl/hash.hpp" + +namespace asl +{ + +template +class buffer +{ + T* m_data{}; + isize_t m_capacity{}; + + static constexpr size_t kOnHeapMask = 0x8000'0000'0000'0000ULL; + + // bit 63 : 1 = on heap, 0 = inline + // bits [62:56] : size when inline + // bits [62:0] : size when on heap + size_t m_size_encoded_{}; + + ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; + + static constexpr isize_t kInlineRegionSize = size_of + size_of + size_of; + +public: + static constexpr isize_t kInlineCapacity = []() { + // 1 byte is used for size inline in m_size_encoded. + // This is enough because we have at most 24 bytes available, + // so 23 chars of capacity. + const isize_t available_size = kInlineRegionSize - 1; + return available_size / size_of; + }(); + +private: + static_assert(align_of <= align_of); + static_assert(align_of == align_of); + static_assert(align_of == align_of); + + constexpr size_t load_size_encoded() const + { + size_t s{}; + asl::memcpy(&s, &m_size_encoded_, sizeof(size_t)); + return s; + } + + constexpr void store_size_encoded(size_t encoded) + { + asl::memcpy(&m_size_encoded_, &encoded, sizeof(size_t)); + } + + static constexpr bool is_on_heap(size_t size_encoded) + { + return (size_encoded & kOnHeapMask) != 0; + } + + static constexpr size_t encode_size_heap(isize_t size) + { + return static_cast(size) | kOnHeapMask; + } + + static constexpr isize_t decode_size(size_t size_encoded) + { + if constexpr (kInlineCapacity == 0) + { + return is_on_heap(size_encoded) + ? static_cast(size_encoded & (~kOnHeapMask)) + : 0; + } + else + { + return is_on_heap(size_encoded) + ? static_cast(size_encoded & (~kOnHeapMask)) + : static_cast(size_encoded >> 56); + } + } + + constexpr bool is_on_heap() const + { + return is_on_heap(load_size_encoded()); + } + + constexpr T* push_uninit() + { + isize_t sz = size(); + resize_uninit_inner(sz + 1); + return data() + sz; + } + + constexpr void resize_uninit_inner(isize_t new_size) + { + isize_t old_size = size(); + if (!trivially_destructible && new_size < old_size) + { + destroy_n(data() + new_size, old_size - new_size); + } + reserve_capacity(new_size); + set_size(new_size); + } + + constexpr void set_size_inline(isize_t new_size) + { + ASL_ASSERT(new_size >= 0 && new_size <= kInlineCapacity); + size_t size_encoded = (load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff}) | (bit_cast(new_size) << 56); + store_size_encoded(size_encoded); + } + + constexpr void set_size(isize_t new_size) + { + ASL_ASSERT(new_size >= 0 && new_size <= capacity()); + if (is_on_heap()) + { + store_size_encoded(encode_size_heap(new_size)); + } + else + { + set_size_inline(new_size); + } + } + + // NOLINTNEXTLINE(*-rvalue-reference-param-not-moved) + void move_from_other(buffer&& other, bool assign) + { + if (other.is_on_heap()) + { + destroy(); + m_data = other.m_data; + m_capacity = other.m_capacity; + store_size_encoded(other.load_size_encoded()); + } + else if (trivially_move_constructible) + { + destroy(); + asl::memcpy(this, &other, kInlineRegionSize); + } + else if (!assign || m_allocator == other.m_allocator) + { + isize_t other_n = other.size(); + isize_t this_n = size(); + resize_uninit_inner(other_n); + if (other_n <= this_n) + { + relocate_assign_n(data(), other.data(), other_n); + } + else + { + relocate_assign_n(data(), other.data(), this_n); + relocate_uninit_n(data() + this_n, other.data() + this_n, other_n - this_n); + } + } + else + { + destroy(); + isize_t n = other.size(); + ASL_ASSERT(n <= kInlineCapacity); + relocate_uninit_n(data(), other.data(), n); + set_size_inline(n); + } + + other.set_size_inline(0); + + if (assign) + { + m_allocator = ASL_MOVE(other.m_allocator); + } + } + + void copy_range(span to_copy) + { + isize_t this_size = size(); + isize_t new_size = to_copy.size(); + + resize_uninit_inner(to_copy.size()); + ASL_ASSERT(capacity() >= new_size); + ASL_ASSERT(size() == to_copy.size()); + + if (new_size <= this_size) + { + copy_assign_n(data(), to_copy.data(), new_size); + } + else + { + copy_assign_n(data(), to_copy.data(), this_size); + copy_uninit_n(data() + this_size, to_copy.data() + this_size, new_size - this_size); + } + } + + template + void resize_inner(isize_t new_size, Args&&... args) + requires constructible_from + { + ASL_ASSERT(new_size >= 0); + + isize_t old_size = size(); + resize_uninit_inner(new_size); + + T* data_ptr = data(); + T* end = data_ptr + new_size; + + // NOLINTNEXTLINE(*-pointer-arithmetic) + for (T* it = data_ptr + old_size; it < end; ++it) + { + construct_at(it, ASL_FWD(args)...); + } + } + +public: + constexpr buffer() requires default_constructible = default; + + explicit constexpr buffer(span s) + requires default_constructible + : buffer{} + { + copy_range(s); + } + + explicit constexpr buffer(Allocator allocator) + : m_allocator{ASL_MOVE(allocator)} + {} + + explicit constexpr buffer(span s, Allocator allocator) + : m_allocator{ASL_MOVE(allocator)} + { + copy_range(s); + } + + constexpr buffer(const buffer& other) + requires copy_constructible && copyable + : m_allocator{other.m_allocator} + { + copy_range(other); + } + + constexpr buffer(buffer&& other) + requires moveable + : buffer(ASL_MOVE(other.m_allocator)) + { + move_from_other(ASL_MOVE(other), false); + } + + constexpr buffer& operator=(const buffer& other) + requires copyable + { + if (&other == this) { return *this; } + copy_range(other); + return *this; + } + + constexpr buffer& operator=(buffer&& other) + requires moveable + { + if (&other == this) { return *this; } + move_from_other(ASL_MOVE(other), true); + return *this; + } + + ~buffer() + { + destroy(); + } + + constexpr isize_t size() const + { + return decode_size(load_size_encoded()); + } + + constexpr isize_t capacity() const + { + if constexpr (kInlineCapacity == 0) + { + return m_capacity; + } + else + { + return is_on_heap() ? m_capacity : kInlineCapacity; + } + } + + void clear() + { + isize_t current_size = size(); + if (current_size == 0) { return; } + + destroy_n(data(), current_size); + set_size(0); + } + + void destroy() + { + clear(); + if (is_on_heap()) + { + if (m_data != nullptr) + { + auto current_layout = layout::array(m_capacity); + m_allocator.dealloc(m_data, current_layout); + } + set_size_inline(0); + } + } + + void reserve_capacity(isize_t new_capacity) + { + ASL_ASSERT(new_capacity >= 0); + ASL_ASSERT_RELEASE(new_capacity <= 0x4000'0000'0000'0000); + + if (new_capacity <= capacity()) { return; } + ASL_ASSERT(new_capacity > kInlineCapacity); + + new_capacity = static_cast(round_up_pow2(static_cast(new_capacity))); + + T* old_data = data(); + const isize_t old_capacity = capacity(); + const isize_t current_size = size(); + const bool currently_on_heap = is_on_heap(); + + auto old_layout = layout::array(old_capacity); + auto new_layout = layout::array(new_capacity); + + if (currently_on_heap && trivially_move_constructible) + { + m_data = reinterpret_cast(m_allocator.realloc(m_data, old_layout, new_layout)); + m_capacity = new_capacity; + return; + } + + T* new_data = reinterpret_cast(m_allocator.alloc(new_layout)); + + relocate_uninit_n(new_data, old_data, current_size); + + if (currently_on_heap) + { + m_allocator.dealloc(old_data, old_layout); + } + + m_data = new_data; + m_capacity = new_capacity; + store_size_encoded(encode_size_heap(current_size)); + } + + constexpr void resize_uninit(isize_t new_size) + requires trivially_default_constructible && trivially_destructible + { + reserve_capacity(new_size); + set_size(new_size); + } + + constexpr void resize_zero(isize_t new_size) + requires trivially_default_constructible && trivially_destructible + { + isize_t old_size = size(); + resize_uninit(new_size); + + if (new_size > old_size) + { + memzero(data() + old_size, (new_size - old_size) * size_of); + } + } + + void resize(isize_t new_size) + requires default_constructible + { + resize_inner(new_size); + } + + void resize(isize_t new_size, const T& value) + { + resize_inner(new_size, value); + } + + constexpr T& push(auto&&... args) + requires constructible_from + { + T* uninit = push_uninit(); + T* init = construct_at(uninit, ASL_FWD(args)...); + return *init; + } + + // @Todo(C++23) Use deducing this + const T* data() const + { + if constexpr (kInlineCapacity == 0) + { + return m_data; + } + else + { + return is_on_heap() ? m_data : reinterpret_cast(this); + } + } + + T* data() + { + if constexpr (kInlineCapacity == 0) + { + return m_data; + } + else + { + return is_on_heap() ? m_data : reinterpret_cast(this); + } + } + + // @Todo(C++23) Use deducing this + constexpr contiguous_iterator begin() const { return contiguous_iterator{data()}; } + constexpr contiguous_iterator end() const { return contiguous_iterator{data() + size()}; } + + constexpr contiguous_iterator begin() { return contiguous_iterator{data()}; } + constexpr contiguous_iterator end() { return contiguous_iterator{data() + size()}; } + + // @Todo(C++23) Deducing this + constexpr operator span() const // NOLINT(*-explicit-conversions) + { + return as_span(); + } + + constexpr operator span() // NOLINT(*-explicit-conversions) + { + return as_span(); + } + + constexpr span as_span() const + { + return span{data(), size()}; + } + + constexpr span as_span() + { + return span{data(), size()}; + } + + // @Todo(C++23) Use deducing this + constexpr T& operator[](isize_t i) + { + ASL_ASSERT(i >= 0 && i <= size()); + return data()[i]; + } + + constexpr const T& operator[](isize_t i) const + { + ASL_ASSERT(i >= 0 && i <= size()); + return data()[i]; + } + + template + requires hashable + friend H AslHashValue(H h, const buffer& b) + { + return H::combine_contiguous(ASL_MOVE(h), b.as_span()); + } +}; + +} // namespace asl + diff --git a/asl/config.hpp b/asl/config.hpp index 1e28252..e182569 100644 --- a/asl/config.hpp +++ b/asl/config.hpp @@ -1,17 +1,17 @@ -#pragma once - -#if defined(_WIN32) - #define ASL_OS_WINDOWS 1 -#elif defined(__linux__) - #define ASL_OS_LINUX 1 -#else - #error Unknown OS -#endif - -#if defined(__clang__) && defined(_MSC_VER) - #define ASL_COMPILER_CLANG_CL 1 -#elif defined(__clang__) - #define ASL_COMPILER_CLANG 1 -#else - #error Unknown compiler -#endif +#pragma once + +#if defined(_WIN32) + #define ASL_OS_WINDOWS 1 +#elif defined(__linux__) + #define ASL_OS_LINUX 1 +#else + #error Unknown OS +#endif + +#if defined(__clang__) && defined(_MSC_VER) + #define ASL_COMPILER_CLANG_CL 1 +#elif defined(__clang__) + #define ASL_COMPILER_CLANG 1 +#else + #error Unknown compiler +#endif diff --git a/asl/float.hpp b/asl/float.hpp index 8ecf5fa..99c3f32 100644 --- a/asl/float.hpp +++ b/asl/float.hpp @@ -1,17 +1,17 @@ -#pragma once - -#include "asl/meta.hpp" - -namespace asl -{ - -template constexpr T infinity() { return __builtin_inf(); } - -template constexpr T nan() { return static_cast(__builtin_nanf("")); } - -template constexpr bool is_infinity(T f) { return __builtin_isinf(f); } - -template constexpr bool is_nan(T f) { return __builtin_isnan(f); } - -} // namespace asl - +#pragma once + +#include "asl/meta.hpp" + +namespace asl +{ + +template constexpr T infinity() { return __builtin_inf(); } + +template constexpr T nan() { return static_cast(__builtin_nanf("")); } + +template constexpr bool is_infinity(T f) { return __builtin_isinf(f); } + +template constexpr bool is_nan(T f) { return __builtin_isnan(f); } + +} // namespace asl + diff --git a/asl/format.cpp b/asl/format.cpp index 9e31888..91a7be9 100644 --- a/asl/format.cpp +++ b/asl/format.cpp @@ -1,201 +1,201 @@ -#include "asl/format.hpp" -#include "asl/utility.hpp" -#include "asl/assert.hpp" -#include "asl/memory.hpp" - -void asl::format_internals::format( - Writer* writer, - string_view fmt, - span args) -{ - Formatter f(writer); - - auto arg_it = args.begin(); - auto arg_end = args.end(); - - isize_t i = 0; - while (i < fmt.size()) - { - if (fmt[i] == '{') - { - if (i + 1 < fmt.size()) - { - if (fmt[i + 1] == '}') - { - f.write(fmt.substr(0, i)); - fmt = fmt.substr(i + 2); - i = 0; - - if (arg_it == arg_end) - { - f.write(""); - } - else - { - arg_it->fn(f, arg_it->data); - arg_it++; - } - - continue; - } - - if (fmt[i + 1] == '{') - { - f.write(fmt.substr(0, i + 1)); - fmt = fmt.substr(i + 2); - i = 0; - - continue; - } - } - - f.write(fmt.substr(0, i)); - fmt = fmt.substr(i + 1); - i = 0; - - f.write(""); - } - else if (i + 1 < fmt.size() && fmt[i] == '}' && fmt[i + 1] == '}') - { - f.write(fmt.substr(0, i + 1)); - fmt = fmt.substr(i + 2); - i = 0; - } - else - { - i += 1; - } - } - - f.write(fmt); -} - -void asl::AslFormat(Formatter& f, const char* str) -{ - f.write({str, asl::strlen(str)}); -} - -void asl::AslFormat(Formatter& f, bool v) -{ - if (v) - { - f.write("true"); - } - else - { - f.write("false"); - } -} - -void asl::AslFormat(Formatter& f, uint8_t v) -{ - AslFormat(f, static_cast(v)); -} - -void asl::AslFormat(Formatter& f, uint16_t v) -{ - AslFormat(f, static_cast(v)); -} - -void asl::AslFormat(Formatter& f, uint32_t v) -{ - AslFormat(f, static_cast(v)); -} - -static constexpr int32_t kMaxUint64Digits = 20; - -asl::string_view asl::format_uint64(uint64_t v, asl::span buffer) -{ - static constexpr char s_pairs_storage[] = { - '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', - '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', - '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', - '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', - '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', - '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', - '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', - '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', - '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', - '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', - '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', - '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', - '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', - '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', - '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', - '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', - '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', - '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', - '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', - '9', '5', '9', '6', '9', '7', '9', '8', '9', '9', - }; - - static constexpr span s_pairs = s_pairs_storage; - int32_t cursor = kMaxUint64Digits; - - auto write_two = [&buffer, &cursor](span str) - { - ASL_ASSERT(cursor >= 2); - buffer[--cursor] = str[1]; - buffer[--cursor] = str[0]; - }; - - auto write_one = [&buffer, &cursor](char c) - { - ASL_ASSERT(cursor >= 1); - buffer[--cursor] = c; - }; - - while (v >= 100) - { - uint64_t x = v % 100; - v /= 100; - write_two(s_pairs.subspan(static_cast(x * 2)).first<2>()); - } - - if (v >= 10) - { - write_two(s_pairs.subspan(static_cast(v * 2)).first<2>()); - } - else if (v > 0 || cursor == kMaxUint64Digits) - { - ASL_ASSERT(v < 10); - write_one(static_cast('0' + v)); - } - - return string_view(buffer.data(), kMaxUint64Digits).substr(cursor); -} - -void asl::AslFormat(Formatter& f, uint64_t v) -{ - char buffer[kMaxUint64Digits]; - f.write(format_uint64(v, buffer)); -} - -void asl::AslFormat(Formatter& f, int8_t v) -{ - AslFormat(f, static_cast(v)); -} - -void asl::AslFormat(Formatter& f, int16_t v) -{ - AslFormat(f, static_cast(v)); -} - -void asl::AslFormat(Formatter& f, int32_t v) -{ - AslFormat(f, static_cast(v)); -} - -void asl::AslFormat(Formatter& f, int64_t v) -{ - if (v < 0) - { - f.write("-"); - uint64_t absolute_value = ~(bit_cast(v) - 1); - AslFormat(f, absolute_value); - } - else - { - AslFormat(f, static_cast(v)); - } -} +#include "asl/format.hpp" +#include "asl/utility.hpp" +#include "asl/assert.hpp" +#include "asl/memory.hpp" + +void asl::format_internals::format( + Writer* writer, + string_view fmt, + span args) +{ + Formatter f(writer); + + auto arg_it = args.begin(); + auto arg_end = args.end(); + + isize_t i = 0; + while (i < fmt.size()) + { + if (fmt[i] == '{') + { + if (i + 1 < fmt.size()) + { + if (fmt[i + 1] == '}') + { + f.write(fmt.substr(0, i)); + fmt = fmt.substr(i + 2); + i = 0; + + if (arg_it == arg_end) + { + f.write(""); + } + else + { + arg_it->fn(f, arg_it->data); + arg_it++; + } + + continue; + } + + if (fmt[i + 1] == '{') + { + f.write(fmt.substr(0, i + 1)); + fmt = fmt.substr(i + 2); + i = 0; + + continue; + } + } + + f.write(fmt.substr(0, i)); + fmt = fmt.substr(i + 1); + i = 0; + + f.write(""); + } + else if (i + 1 < fmt.size() && fmt[i] == '}' && fmt[i + 1] == '}') + { + f.write(fmt.substr(0, i + 1)); + fmt = fmt.substr(i + 2); + i = 0; + } + else + { + i += 1; + } + } + + f.write(fmt); +} + +void asl::AslFormat(Formatter& f, const char* str) +{ + f.write({str, asl::strlen(str)}); +} + +void asl::AslFormat(Formatter& f, bool v) +{ + if (v) + { + f.write("true"); + } + else + { + f.write("false"); + } +} + +void asl::AslFormat(Formatter& f, uint8_t v) +{ + AslFormat(f, static_cast(v)); +} + +void asl::AslFormat(Formatter& f, uint16_t v) +{ + AslFormat(f, static_cast(v)); +} + +void asl::AslFormat(Formatter& f, uint32_t v) +{ + AslFormat(f, static_cast(v)); +} + +static constexpr int32_t kMaxUint64Digits = 20; + +asl::string_view asl::format_uint64(uint64_t v, asl::span buffer) +{ + static constexpr char s_pairs_storage[] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', + '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', + '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', + '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', + '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', + '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', + '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', + '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', + '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', + '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', + '9', '5', '9', '6', '9', '7', '9', '8', '9', '9', + }; + + static constexpr span s_pairs = s_pairs_storage; + int32_t cursor = kMaxUint64Digits; + + auto write_two = [&buffer, &cursor](span str) + { + ASL_ASSERT(cursor >= 2); + buffer[--cursor] = str[1]; + buffer[--cursor] = str[0]; + }; + + auto write_one = [&buffer, &cursor](char c) + { + ASL_ASSERT(cursor >= 1); + buffer[--cursor] = c; + }; + + while (v >= 100) + { + uint64_t x = v % 100; + v /= 100; + write_two(s_pairs.subspan(static_cast(x * 2)).first<2>()); + } + + if (v >= 10) + { + write_two(s_pairs.subspan(static_cast(v * 2)).first<2>()); + } + else if (v > 0 || cursor == kMaxUint64Digits) + { + ASL_ASSERT(v < 10); + write_one(static_cast('0' + v)); + } + + return string_view(buffer.data(), kMaxUint64Digits).substr(cursor); +} + +void asl::AslFormat(Formatter& f, uint64_t v) +{ + char buffer[kMaxUint64Digits]; + f.write(format_uint64(v, buffer)); +} + +void asl::AslFormat(Formatter& f, int8_t v) +{ + AslFormat(f, static_cast(v)); +} + +void asl::AslFormat(Formatter& f, int16_t v) +{ + AslFormat(f, static_cast(v)); +} + +void asl::AslFormat(Formatter& f, int32_t v) +{ + AslFormat(f, static_cast(v)); +} + +void asl::AslFormat(Formatter& f, int64_t v) +{ + if (v < 0) + { + f.write("-"); + uint64_t absolute_value = ~(bit_cast(v) - 1); + AslFormat(f, absolute_value); + } + else + { + AslFormat(f, static_cast(v)); + } +} diff --git a/asl/format.hpp b/asl/format.hpp index 70ccd09..70de9b9 100644 --- a/asl/format.hpp +++ b/asl/format.hpp @@ -1,109 +1,109 @@ -#pragma once - +#pragma once + #include "asl/integers.hpp" -#include "asl/meta.hpp" -#include "asl/io.hpp" -#include "asl/span.hpp" -#include "asl/string_view.hpp" - -namespace asl -{ - -class Formatter; - -template -concept formattable = requires (Formatter& f, const T& value) -{ - AslFormat(f, value); -}; - -namespace format_internals -{ - -struct type_erased_arg -{ - const void* data; - void (*fn)(Formatter&, const void*); - - template - static constexpr void erased_fn(Formatter& f, const void* data) - { - AslFormat(f, *reinterpret_cast(data)); - } - - template - explicit constexpr type_erased_arg(const T& arg) - : data{&arg} - , fn{erased_fn} - {} -}; - -void format(Writer*, string_view fmt, span args); - -} // namespace internals - -class Formatter -{ - Writer* m_writer; - -public: - explicit constexpr Formatter(Writer* writer) - : m_writer{writer} - {} - - constexpr void write(string_view s) - { - m_writer->write(as_bytes(s.as_span())); - } - - constexpr Writer* writer() const { return m_writer; } -}; - -template -void format(Writer* w, string_view fmt, const Args&... args) -{ - if constexpr (types_count > 0) - { - format_internals::type_erased_arg type_erased_args[] = { - format_internals::type_erased_arg(args)... - }; - - format_internals::format(w, fmt, type_erased_args); - } - else - { - format_internals::format(w, fmt, {}); - } -} - -template -void AslFormat(Formatter& f, const char (&str)[N]) -{ - f.write(string_view(str, N - 1)); -} - -void AslFormat(Formatter& f, const char* str); - -inline void AslFormat(Formatter& f, string_view sv) -{ - f.write(sv); -} - -void AslFormat(Formatter& f, float); -void AslFormat(Formatter& f, double); - -void AslFormat(Formatter& f, bool); - -void AslFormat(Formatter& f, uint8_t); -void AslFormat(Formatter& f, uint16_t); -void AslFormat(Formatter& f, uint32_t); -void AslFormat(Formatter& f, uint64_t); - -void AslFormat(Formatter& f, int8_t); -void AslFormat(Formatter& f, int16_t); -void AslFormat(Formatter& f, int32_t); -void AslFormat(Formatter& f, int64_t); - -string_view format_uint64(uint64_t value, span buffer); - -} // namespace asl +#include "asl/meta.hpp" +#include "asl/io.hpp" +#include "asl/span.hpp" +#include "asl/string_view.hpp" + +namespace asl +{ + +class Formatter; + +template +concept formattable = requires (Formatter& f, const T& value) +{ + AslFormat(f, value); +}; + +namespace format_internals +{ + +struct type_erased_arg +{ + const void* data; + void (*fn)(Formatter&, const void*); + + template + static constexpr void erased_fn(Formatter& f, const void* data) + { + AslFormat(f, *reinterpret_cast(data)); + } + + template + explicit constexpr type_erased_arg(const T& arg) + : data{&arg} + , fn{erased_fn} + {} +}; + +void format(Writer*, string_view fmt, span args); + +} // namespace internals + +class Formatter +{ + Writer* m_writer; + +public: + explicit constexpr Formatter(Writer* writer) + : m_writer{writer} + {} + + constexpr void write(string_view s) + { + m_writer->write(as_bytes(s.as_span())); + } + + constexpr Writer* writer() const { return m_writer; } +}; + +template +void format(Writer* w, string_view fmt, const Args&... args) +{ + if constexpr (types_count > 0) + { + format_internals::type_erased_arg type_erased_args[] = { + format_internals::type_erased_arg(args)... + }; + + format_internals::format(w, fmt, type_erased_args); + } + else + { + format_internals::format(w, fmt, {}); + } +} + +template +void AslFormat(Formatter& f, const char (&str)[N]) +{ + f.write(string_view(str, N - 1)); +} + +void AslFormat(Formatter& f, const char* str); + +inline void AslFormat(Formatter& f, string_view sv) +{ + f.write(sv); +} + +void AslFormat(Formatter& f, float); +void AslFormat(Formatter& f, double); + +void AslFormat(Formatter& f, bool); + +void AslFormat(Formatter& f, uint8_t); +void AslFormat(Formatter& f, uint16_t); +void AslFormat(Formatter& f, uint32_t); +void AslFormat(Formatter& f, uint64_t); + +void AslFormat(Formatter& f, int8_t); +void AslFormat(Formatter& f, int16_t); +void AslFormat(Formatter& f, int32_t); +void AslFormat(Formatter& f, int64_t); + +string_view format_uint64(uint64_t value, span buffer); + +} // namespace asl diff --git a/asl/format_float.cpp b/asl/format_float.cpp index 0a081d5..1b3186e 100644 --- a/asl/format_float.cpp +++ b/asl/format_float.cpp @@ -1,98 +1,98 @@ -#include "asl/format.hpp" -#include "asl/float.hpp" - -#define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0 -#define JKJ_STATIC_DATA_SECTION_DEFINED 0 -#include - -static constexpr isize_t kZeroCount = 100; -static constexpr char kZeros[kZeroCount] = { - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', -}; - -template -static void format_float(asl::Formatter& f, T value) -{ - if (asl::is_infinity(value)) - { - if (value > 0) - { - f.write("Infinity"_sv); - } - else - { - f.write("-Infinity"_sv); - } - return; - } - - if (value == static_cast(0)) - { - f.write("0"_sv); - return; - } - - if (asl::is_nan(value)) - { - f.write("NaN"_sv); - return; - } - - auto decimal = jkj::dragonbox::to_decimal(value); - - if (decimal.is_negative) { f.write("-"); } - - char buffer[20]; - asl::string_view digits = asl::format_uint64(decimal.significand, buffer); - - if (decimal.exponent >= 0) - { - f.write(digits); - while (decimal.exponent > 0) - { - isize_t to_write = asl::min(static_cast(decimal.exponent), kZeroCount); - f.write(asl::string_view(kZeros, to_write)); - decimal.exponent -= to_write; - } - } - else - { - if (digits.size() <= -decimal.exponent) - { - f.write("0."); - decimal.exponent = -decimal.exponent - static_cast(digits.size()); - while (decimal.exponent > 0) - { - isize_t to_write = asl::min(static_cast(decimal.exponent), kZeroCount); - f.write(asl::string_view(kZeros, to_write)); - decimal.exponent -= to_write; - } - f.write(digits); - } - else - { - f.write(digits.first(digits.size() + decimal.exponent)); - f.write("."); - f.write(digits.last(-decimal.exponent)); - } - } -} - -void asl::AslFormat(Formatter& f, float value) -{ - format_float(f, value); -} - -void asl::AslFormat(Formatter& f, double value) -{ - format_float(f, value); -} +#include "asl/format.hpp" +#include "asl/float.hpp" + +#define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0 +#define JKJ_STATIC_DATA_SECTION_DEFINED 0 +#include + +static constexpr isize_t kZeroCount = 100; +static constexpr char kZeros[kZeroCount] = { + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', +}; + +template +static void format_float(asl::Formatter& f, T value) +{ + if (asl::is_infinity(value)) + { + if (value > 0) + { + f.write("Infinity"_sv); + } + else + { + f.write("-Infinity"_sv); + } + return; + } + + if (value == static_cast(0)) + { + f.write("0"_sv); + return; + } + + if (asl::is_nan(value)) + { + f.write("NaN"_sv); + return; + } + + auto decimal = jkj::dragonbox::to_decimal(value); + + if (decimal.is_negative) { f.write("-"); } + + char buffer[20]; + asl::string_view digits = asl::format_uint64(decimal.significand, buffer); + + if (decimal.exponent >= 0) + { + f.write(digits); + while (decimal.exponent > 0) + { + isize_t to_write = asl::min(static_cast(decimal.exponent), kZeroCount); + f.write(asl::string_view(kZeros, to_write)); + decimal.exponent -= to_write; + } + } + else + { + if (digits.size() <= -decimal.exponent) + { + f.write("0."); + decimal.exponent = -decimal.exponent - static_cast(digits.size()); + while (decimal.exponent > 0) + { + isize_t to_write = asl::min(static_cast(decimal.exponent), kZeroCount); + f.write(asl::string_view(kZeros, to_write)); + decimal.exponent -= to_write; + } + f.write(digits); + } + else + { + f.write(digits.first(digits.size() + decimal.exponent)); + f.write("."); + f.write(digits.last(-decimal.exponent)); + } + } +} + +void asl::AslFormat(Formatter& f, float value) +{ + format_float(f, value); +} + +void asl::AslFormat(Formatter& f, double value) +{ + format_float(f, value); +} diff --git a/asl/functional.hpp b/asl/functional.hpp index 967a2a2..f5ff0d9 100644 --- a/asl/functional.hpp +++ b/asl/functional.hpp @@ -1,65 +1,65 @@ -#pragma once - -#include "asl/meta.hpp" -#include "asl/utility.hpp" - -namespace asl { - -template -constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args) - requires requires { - (self.*f)(ASL_FWD(args)...); - } -{ - return (ASL_FWD(self).*f)(ASL_FWD(args)...); -} - -template -constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args) - requires requires { - (self->*f)(ASL_FWD(args)...); - } -{ - return (self->*f)(ASL_FWD(args)...); -} - -template -constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...) - requires ( - sizeof...(Args) == 0 && - requires { self.*m; } - ) -{ - return ASL_FWD(self).*m; -} - -template -constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...) - requires ( - sizeof...(Args) == 0 && - requires { self->*m; } - ) -{ - return self->*m; -} - -template -constexpr auto invoke(auto&& f, Args&&... args) - requires requires { - f(ASL_FWD(args)...); - } -{ - return ASL_FWD(f)(ASL_FWD(args)...); -} - -template struct _result_of_helper; - -template -struct _result_of_helper -{ - using type = decltype(invoke(declval(), declval()...)); -}; - -template using result_of_t = _result_of_helper::type; - -} // namespace asl +#pragma once + +#include "asl/meta.hpp" +#include "asl/utility.hpp" + +namespace asl { + +template +constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args) + requires requires { + (self.*f)(ASL_FWD(args)...); + } +{ + return (ASL_FWD(self).*f)(ASL_FWD(args)...); +} + +template +constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args) + requires requires { + (self->*f)(ASL_FWD(args)...); + } +{ + return (self->*f)(ASL_FWD(args)...); +} + +template +constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...) + requires ( + sizeof...(Args) == 0 && + requires { self.*m; } + ) +{ + return ASL_FWD(self).*m; +} + +template +constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...) + requires ( + sizeof...(Args) == 0 && + requires { self->*m; } + ) +{ + return self->*m; +} + +template +constexpr auto invoke(auto&& f, Args&&... args) + requires requires { + f(ASL_FWD(args)...); + } +{ + return ASL_FWD(f)(ASL_FWD(args)...); +} + +template struct _result_of_helper; + +template +struct _result_of_helper +{ + using type = decltype(invoke(declval(), declval()...)); +}; + +template using result_of_t = _result_of_helper::type; + +} // namespace asl diff --git a/asl/hash.hpp b/asl/hash.hpp index 2e48a02..9539755 100644 --- a/asl/hash.hpp +++ b/asl/hash.hpp @@ -1,138 +1,138 @@ -#pragma once - -#include "asl/integers.hpp" -#include "asl/meta.hpp" -#include "asl/span.hpp" -#include "asl/utility.hpp" - -namespace asl::city_hash -{ - -// Hash function for a byte array. -uint64_t CityHash64(const char *s, size_t len); - -// Hash function for a byte array. For convenience, a 64-bit seed is also -// hashed into the result. -uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed); - -// Hash function for a byte array. For convenience, two seeds are also -// hashed into the result. -uint64_t CityHash64WithSeeds(const char *s, size_t len, - uint64_t seed0, uint64_t seed1); - -// Hash function for a byte array. -uint128_t CityHash128(const char *s, size_t len); - -// Hash function for a byte array. For convenience, a 128-bit seed is also -// hashed into the result. -uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed); - -// Hash function for a byte array. Most useful in 32-bit binaries. -uint32_t CityHash32(const char *s, size_t len); - -// Hash 128 input bits down to 64 bits of output. -// This is intended to be a reasonably good hash function. -constexpr uint64_t Hash128to64(uint64_t high, uint64_t low) -{ - // Murmur-inspired hashing. - const uint64_t kMul = 0x9ddfea08eb382d69ULL; - uint64_t a = (low ^ high) * kMul; - a ^= (a >> 47); - uint64_t b = (high ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - -// Hash 128 input bits down to 64 bits of output. -// This is intended to be a reasonably good hash function. -constexpr uint64_t Hash128to64(const uint128_t& x) -{ - return Hash128to64(x.high, x.low); -} - -} // namespace asl::city_hash - -namespace asl -{ - -template -concept hashable_generic = requires(const T& value, H h) -{ - { AslHashValue(h, value) } -> same_as; -}; - -struct HashState -{ - uint128_t state{}; - - constexpr HashState() = default; - explicit constexpr HashState(uint128_t s) : state{s} {} - - template - static HashState combine_contiguous(HashState h, span s) - { - if constexpr (uniquely_represented) - { - auto bytes = as_bytes(s); - auto hashed = city_hash::CityHash128WithSeed( - reinterpret_cast(bytes.data()), - static_cast(bytes.size()), - h.state); - return HashState{hashed}; - } - else - { - for (const auto& value: s) - { - h = AslHashValue(ASL_MOVE(h), value); - } - return h; - } - } - - static constexpr HashState combine(HashState h) - { - return h; - } - - template Arg, hashable_generic... Remaining> - static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining) - { - return combine(AslHashValue(ASL_MOVE(h), arg), remaining...); - } -}; - -template -concept hashable = hashable_generic; - -template -constexpr H AslHashValue(H h, const T& value) -{ - return H::combine_contiguous(ASL_MOVE(h), span{&value, 1}); -} - -template -constexpr H AslHashValue(H h, bool value) -{ - return AslHashValue(ASL_MOVE(h), value ? 1 : 0); -} - -template -constexpr void AslHashValue(H h, T*); // Don't hash pointers - -template -constexpr H AslHashValue(H h, const span& s) -{ - return H::combine_contiguous(ASL_MOVE(h), span{s.data(), s.size()}); -} - -template -constexpr uint64_t hash_value(const T& value) -{ - auto result = AslHashValue(HashState{}, value).state; - return city_hash::Hash128to64(result); -} - -} // namespace asl - +#pragma once + +#include "asl/integers.hpp" +#include "asl/meta.hpp" +#include "asl/span.hpp" +#include "asl/utility.hpp" + +namespace asl::city_hash +{ + +// Hash function for a byte array. +uint64_t CityHash64(const char *s, size_t len); + +// Hash function for a byte array. For convenience, a 64-bit seed is also +// hashed into the result. +uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed); + +// Hash function for a byte array. For convenience, two seeds are also +// hashed into the result. +uint64_t CityHash64WithSeeds(const char *s, size_t len, + uint64_t seed0, uint64_t seed1); + +// Hash function for a byte array. +uint128_t CityHash128(const char *s, size_t len); + +// Hash function for a byte array. For convenience, a 128-bit seed is also +// hashed into the result. +uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed); + +// Hash function for a byte array. Most useful in 32-bit binaries. +uint32_t CityHash32(const char *s, size_t len); + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +constexpr uint64_t Hash128to64(uint64_t high, uint64_t low) +{ + // Murmur-inspired hashing. + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (low ^ high) * kMul; + a ^= (a >> 47); + uint64_t b = (high ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +constexpr uint64_t Hash128to64(const uint128_t& x) +{ + return Hash128to64(x.high, x.low); +} + +} // namespace asl::city_hash + +namespace asl +{ + +template +concept hashable_generic = requires(const T& value, H h) +{ + { AslHashValue(h, value) } -> same_as; +}; + +struct HashState +{ + uint128_t state{}; + + constexpr HashState() = default; + explicit constexpr HashState(uint128_t s) : state{s} {} + + template + static HashState combine_contiguous(HashState h, span s) + { + if constexpr (uniquely_represented) + { + auto bytes = as_bytes(s); + auto hashed = city_hash::CityHash128WithSeed( + reinterpret_cast(bytes.data()), + static_cast(bytes.size()), + h.state); + return HashState{hashed}; + } + else + { + for (const auto& value: s) + { + h = AslHashValue(ASL_MOVE(h), value); + } + return h; + } + } + + static constexpr HashState combine(HashState h) + { + return h; + } + + template Arg, hashable_generic... Remaining> + static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining) + { + return combine(AslHashValue(ASL_MOVE(h), arg), remaining...); + } +}; + +template +concept hashable = hashable_generic; + +template +constexpr H AslHashValue(H h, const T& value) +{ + return H::combine_contiguous(ASL_MOVE(h), span{&value, 1}); +} + +template +constexpr H AslHashValue(H h, bool value) +{ + return AslHashValue(ASL_MOVE(h), value ? 1 : 0); +} + +template +constexpr void AslHashValue(H h, T*); // Don't hash pointers + +template +constexpr H AslHashValue(H h, const span& s) +{ + return H::combine_contiguous(ASL_MOVE(h), span{s.data(), s.size()}); +} + +template +constexpr uint64_t hash_value(const T& value) +{ + auto result = AslHashValue(HashState{}, value).state; + return city_hash::Hash128to64(result); +} + +} // namespace asl + diff --git a/asl/hash_cityhash.cpp b/asl/hash_cityhash.cpp index eef204a..63dd6bb 100644 --- a/asl/hash_cityhash.cpp +++ b/asl/hash_cityhash.cpp @@ -1,517 +1,517 @@ -// Copyright (c) 2011 Google, Inc. -// -// 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. -// -// CityHash, by Geoff Pike and Jyrki Alakuijala -// -// This file provides CityHash64() and related functions. -// -// It's probably possible to create even faster hash functions by -// writing a program that systematically explores some of the space of -// possible hash functions, by using SIMD instructions, or by -// compromising on hash quality. - -#include "asl/hash.hpp" -#include "asl/memory.hpp" - -using uint8 = uint8_t; -using uint32 = uint32_t; -using uint64 = uint64_t; -using uint128 = uint128_t; - -// NOLINTBEGIN - -constexpr uint64 UNALIGNED_LOAD64(const char *p) { - uint64 result; - asl::memcpy(&result, p, sizeof(result)); - return result; -} - -constexpr uint32 UNALIGNED_LOAD32(const char *p) { - uint32 result; - asl::memcpy(&result, p, sizeof(result)); - return result; -} - -#ifdef _MSC_VER - -#include -#define bswap_32(x) _byteswap_ulong(x) -#define bswap_64(x) _byteswap_uint64(x) - -#elif defined(__APPLE__) - -// Mac OS X / Darwin features -#include -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) - -#elif defined(__sun) || defined(sun) - -#include -#define bswap_32(x) BSWAP_32(x) -#define bswap_64(x) BSWAP_64(x) - -#elif defined(__FreeBSD__) - -#include -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) - -#elif defined(__OpenBSD__) - -#include -#define bswap_32(x) swap32(x) -#define bswap_64(x) swap64(x) - -#elif defined(__NetBSD__) - -#include -#include -#if defined(__BSWAP_RENAME) && !defined(__bswap_32) -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) -#endif - -#else - -#include - -#endif - -#ifdef WORDS_BIGENDIAN -#define uint32_in_expected_order(x) (bswap_32(x)) -#define uint64_in_expected_order(x) (bswap_64(x)) -#else -#define uint32_in_expected_order(x) (x) -#define uint64_in_expected_order(x) (x) -#endif - -#if !defined(LIKELY) -#if __has_builtin(__builtin_expect) -#define LIKELY(x) (__builtin_expect(!!(x), 1)) -#else -#define LIKELY(x) (x) -#endif -#endif - -static uint64 Fetch64(const char *p) { - return uint64_in_expected_order(UNALIGNED_LOAD64(p)); -} - -static uint32 Fetch32(const char *p) { - return uint32_in_expected_order(UNALIGNED_LOAD32(p)); -} - -// Some primes between 2^63 and 2^64 for various uses. -static const uint64 k0 = 0xc3a5c85c97cb3127ULL; -static const uint64 k1 = 0xb492b66fbe98f273ULL; -static const uint64 k2 = 0x9ae16a3b2f90404fULL; - -// Magic numbers for 32-bit hashing. Copied from Murmur3. -static const uint32 c1 = 0xcc9e2d51; -static const uint32 c2 = 0x1b873593; - -// A 32-bit to 32-bit integer hash copied from Murmur3. -static uint32 fmix(uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - return h; -} - -static uint32 Rotate32(uint32 val, int shift) { - // Avoid shifting by 32: doing so yields an undefined result. - return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); -} - -#undef PERMUTE3 -#define PERMUTE3(a, b, c) do { asl::swap(a, b); asl::swap(a, c); } while (0) - -static uint32 Mur(uint32 a, uint32 h) { - // Helper from Murmur3 for combining two 32-bit values. - a *= c1; - a = Rotate32(a, 17); - a *= c2; - h ^= a; - h = Rotate32(h, 19); - return h * 5 + 0xe6546b64; -} - -static uint32 Hash32Len13to24(const char *s, size_t len) { - uint32 a = Fetch32(s - 4 + (len >> 1)); - uint32 b = Fetch32(s + 4); - uint32 c = Fetch32(s + len - 8); - uint32 d = Fetch32(s + (len >> 1)); - uint32 e = Fetch32(s); - uint32 f = Fetch32(s + len - 4); - uint32 h = static_cast(len); - - return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); -} - -static uint32 Hash32Len0to4(const char *s, size_t len) { - uint32 b = 0; - uint32 c = 9; - for (size_t i = 0; i < len; i++) { - signed char v = static_cast(s[i]); - b = b * c1 + static_cast(v); - c ^= b; - } - return fmix(Mur(b, Mur(static_cast(len), c))); -} - -static uint32 Hash32Len5to12(const char *s, size_t len) { - uint32 a = static_cast(len), b = a * 5, c = 9, d = b; - a += Fetch32(s); - b += Fetch32(s + len - 4); - c += Fetch32(s + ((len >> 1) & 4)); - return fmix(Mur(c, Mur(b, Mur(a, d)))); -} - -uint32 asl::city_hash::CityHash32(const char *s, size_t len) { - if (len <= 24) { - return len <= 12 ? - (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : - Hash32Len13to24(s, len); - } - - // len > 24 - uint32 h = static_cast(len), g = c1 * h, f = g; - uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; - uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; - uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; - uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; - uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; - h ^= a0; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - h ^= a2; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a1; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - g ^= a3; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - f += a4; - f = Rotate32(f, 19); - f = f * 5 + 0xe6546b64; - size_t iters = (len - 1) / 20; - do { - uint32 a0_ = Rotate32(Fetch32(s) * c1, 17) * c2; - uint32 a1_ = Fetch32(s + 4); - uint32 a2_ = Rotate32(Fetch32(s + 8) * c1, 17) * c2; - uint32 a3_ = Rotate32(Fetch32(s + 12) * c1, 17) * c2; - uint32 a4_ = Fetch32(s + 16); - h ^= a0_; - h = Rotate32(h, 18); - h = h * 5 + 0xe6546b64; - f += a1_; - f = Rotate32(f, 19); - f = f * c1; - g += a2_; - g = Rotate32(g, 18); - g = g * 5 + 0xe6546b64; - h ^= a3_ + a1_; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a4_; - g = bswap_32(g) * 5; - h += a4_ * 5; - h = bswap_32(h); - f += a0_; - PERMUTE3(f, h, g); - s += 20; - } while (--iters != 0); - g = Rotate32(g, 11) * c1; - g = Rotate32(g, 17) * c1; - f = Rotate32(f, 11) * c1; - f = Rotate32(f, 17) * c1; - h = Rotate32(h + g, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - h = Rotate32(h + f, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - return h; -} - -// Bitwise right rotate. Normally this will compile to a single -// instruction, especially if the shift is a manifest constant. -static uint64 Rotate(uint64 val, int shift) { - // Avoid shifting by 64: doing so yields an undefined result. - return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); -} - -static uint64 ShiftMix(uint64 val) { - return val ^ (val >> 47); -} - -static uint64 HashLen16(uint64 u, uint64 v) { - return asl::city_hash::Hash128to64(uint128{u, v}); -} - -static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { - // Murmur-inspired hashing. - uint64 a = (u ^ v) * mul; - a ^= (a >> 47); - uint64 b = (v ^ a) * mul; - b ^= (b >> 47); - b *= mul; - return b; -} - -static uint64 HashLen0to16(const char *s, size_t len) { - if (len >= 8) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) + k2; - uint64 b = Fetch64(s + len - 8); - uint64 c = Rotate(b, 37) * mul + a; - uint64 d = (Rotate(a, 25) + b) * mul; - return HashLen16(c, d, mul); - } - if (len >= 4) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch32(s); - return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); - } - if (len > 0) { - uint8 a = static_cast(s[0]); - uint8 b = static_cast(s[len >> 1]); - uint8 c = static_cast(s[len - 1]); - uint32 y = static_cast(a) + (static_cast(b) << 8); - uint32 z = static_cast(len) + (static_cast(c) << 2); - return ShiftMix(y * k2 ^ z * k0) * k2; - } - return k2; -} - -// This probably works well for 16-byte strings as well, but it may be overkill -// in that case. -static uint64 HashLen17to32(const char *s, size_t len) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) * k1; - uint64 b = Fetch64(s + 8); - uint64 c = Fetch64(s + len - 8) * mul; - uint64 d = Fetch64(s + len - 16) * k2; - return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, - a + Rotate(b + k2, 18) + c, mul); -} - -// Return a 16-byte hash for 48 bytes. Quick and dirty. -// Callers do best to use "random-looking" values for a and b. -static uint128 WeakHashLen32WithSeeds( - uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { - a += w; - b = Rotate(b + a + z, 21); - uint64 c = a; - a += x; - a += y; - b += Rotate(a, 44); - return {a + z, b + c}; -} - -// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. -static uint128 WeakHashLen32WithSeeds( - const char* s, uint64 a, uint64 b) { - return WeakHashLen32WithSeeds(Fetch64(s), - Fetch64(s + 8), - Fetch64(s + 16), - Fetch64(s + 24), - a, - b); -} - -// Return an 8-byte hash for 33 to 64 bytes. -static uint64 HashLen33to64(const char *s, size_t len) { - uint64 mul = k2 + len * 2; - uint64 a = Fetch64(s) * k2; - uint64 b = Fetch64(s + 8); - uint64 c = Fetch64(s + len - 24); - uint64 d = Fetch64(s + len - 32); - uint64 e = Fetch64(s + 16) * k2; - uint64 f = Fetch64(s + 24) * 9; - uint64 g = Fetch64(s + len - 8); - uint64 h = Fetch64(s + len - 16) * mul; - uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; - uint64 v = ((a + g) ^ d) + f + 1; - uint64 w = bswap_64((u + v) * mul) + h; - uint64 x = Rotate(e + f, 42) + c; - uint64 y = (bswap_64((v + w) * mul) + g) * mul; - uint64 z = e + f + c; - a = bswap_64((x + z) * mul + y) + b; - b = ShiftMix((z + a) * mul + d + h) * mul; - return b + x; -} - -uint64 asl::city_hash::CityHash64(const char *s, size_t len) { - if (len <= 32) { - if (len <= 16) { - return HashLen0to16(s, len); - } else { - return HashLen17to32(s, len); - } - } else if (len <= 64) { - return HashLen33to64(s, len); - } - - // For strings over 64 bytes we hash the end first, and then as we - // loop we keep 56 bytes of state: v, w, x, y, and z. - uint64 x = Fetch64(s + len - 40); - uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); - uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); - uint128 v = WeakHashLen32WithSeeds(s + len - 64, len, z); - uint128 w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); - x = x * k1 + Fetch64(s); - - // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. - len = (len - 1) & ~static_cast(63); - do { - x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; - x ^= w.low; - y += v.high + Fetch64(s + 40); - z = Rotate(z + w.high, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); - w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); - asl::swap(z, x); - s += 64; - len -= 64; - } while (len != 0); - return HashLen16(HashLen16(v.high, w.high) + ShiftMix(y) * k1 + z, - HashLen16(v.low, w.low) + x); -} - -uint64 asl::city_hash::CityHash64WithSeed(const char *s, size_t len, uint64 seed) { - return CityHash64WithSeeds(s, len, k2, seed); -} - -uint64 asl::city_hash::CityHash64WithSeeds(const char *s, size_t len, - uint64 seed0, uint64 seed1) { - return HashLen16(CityHash64(s, len) - seed0, seed1); -} - -// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings -// of any length representable in signed long. Based on City and Murmur. -static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { - uint64 a = seed.low; - uint64 b = seed.high; - uint64 c = 0; - uint64 d = 0; - if (len <= 16) { - a = ShiftMix(a * k1) * k1; - c = b * k1 + HashLen0to16(s, len); - d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); - } else { - c = HashLen16(Fetch64(s + len - 8) + k1, a); - d = HashLen16(b + len, c + Fetch64(s + len - 16)); - a += d; - // len > 16 here, so do...while is safe - do { - a ^= ShiftMix(Fetch64(s) * k1) * k1; - a *= k1; - b ^= a; - c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; - c *= k1; - d ^= c; - s += 16; - len -= 16; - } while (len > 16); - } - a = HashLen16(a, c); - b = HashLen16(d, b); - return uint128{a ^ b, HashLen16(b, a)}; -} - -uint128 asl::city_hash::CityHash128WithSeed(const char *s, size_t len, uint128 seed) { - if (len < 128) { - return CityMurmur(s, len, seed); - } - - // We expect len >= 128 to be the common case. Keep 56 bytes of state: - // v, w, x, y, and z. - uint128 v, w; - uint64 x = seed.low; - uint64 y = seed.high; - uint64 z = len * k1; - v.high = Rotate(y ^ k1, 49) * k1 + Fetch64(s); - v.low = Rotate(v.high, 42) * k1 + Fetch64(s + 8); - w.high = Rotate(y + z, 35) * k1 + x; - w.low = Rotate(x + Fetch64(s + 88), 53) * k1; - - // This is the same inner loop as CityHash64(), manually unrolled. - do { - x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; - x ^= w.low; - y += v.high + Fetch64(s + 40); - z = Rotate(z + w.high, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); - w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); - asl::swap(z, x); - s += 64; - x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; - y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; - x ^= w.low; - y += v.high + Fetch64(s + 40); - z = Rotate(z + w.high, 33) * k1; - v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); - w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); - asl::swap(z, x); - s += 64; - len -= 128; - } while (LIKELY(len >= 128)); - x += Rotate(v.high + z, 49) * k0; - y = y * k0 + Rotate(w.low, 37); - z = z * k0 + Rotate(w.high, 27); - w.high *= 9; - v.high *= k0; - // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. - for (size_t tail_done = 0; tail_done < len; ) { - tail_done += 32; - y = Rotate(x + y, 42) * k0 + v.low; - w.high += Fetch64(s + len - tail_done + 16); - x = x * k0 + w.high; - z += w.low + Fetch64(s + len - tail_done); - w.low += v.high; - v = WeakHashLen32WithSeeds(s + len - tail_done, v.high + z, v.low); - v.high *= k0; - } - // At this point our 56 bytes of state should contain more than - // enough information for a strong 128-bit hash. We use two - // different 56-byte-to-8-byte hashes to get a 16-byte final result. - x = HashLen16(x, v.high); - y = HashLen16(y + z, w.high); - return uint128{HashLen16(x + v.low, w.low) + y, - HashLen16(x + w.low, y + v.low)}; -} - -uint128 asl::city_hash::CityHash128(const char *s, size_t len) { - return len >= 16 ? - asl::city_hash::CityHash128WithSeed(s + 16, len - 16, - uint128{Fetch64(s), Fetch64(s + 8) + k0}) : - asl::city_hash::CityHash128WithSeed(s, len, uint128{k0, k1}); -} - -// NOLINTEND +// Copyright (c) 2011 Google, Inc. +// +// 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. +// +// CityHash, by Geoff Pike and Jyrki Alakuijala +// +// This file provides CityHash64() and related functions. +// +// It's probably possible to create even faster hash functions by +// writing a program that systematically explores some of the space of +// possible hash functions, by using SIMD instructions, or by +// compromising on hash quality. + +#include "asl/hash.hpp" +#include "asl/memory.hpp" + +using uint8 = uint8_t; +using uint32 = uint32_t; +using uint64 = uint64_t; +using uint128 = uint128_t; + +// NOLINTBEGIN + +constexpr uint64 UNALIGNED_LOAD64(const char *p) { + uint64 result; + asl::memcpy(&result, p, sizeof(result)); + return result; +} + +constexpr uint32 UNALIGNED_LOAD32(const char *p) { + uint32 result; + asl::memcpy(&result, p, sizeof(result)); + return result; +} + +#ifdef _MSC_VER + +#include +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) + +// Mac OS X / Darwin features +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(__sun) || defined(sun) + +#include +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#elif defined(__FreeBSD__) + +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(__OpenBSD__) + +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) + +#elif defined(__NetBSD__) + +#include +#include +#if defined(__BSWAP_RENAME) && !defined(__bswap_32) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#endif + +#else + +#include + +#endif + +#ifdef WORDS_BIGENDIAN +#define uint32_in_expected_order(x) (bswap_32(x)) +#define uint64_in_expected_order(x) (bswap_64(x)) +#else +#define uint32_in_expected_order(x) (x) +#define uint64_in_expected_order(x) (x) +#endif + +#if !defined(LIKELY) +#if __has_builtin(__builtin_expect) +#define LIKELY(x) (__builtin_expect(!!(x), 1)) +#else +#define LIKELY(x) (x) +#endif +#endif + +static uint64 Fetch64(const char *p) { + return uint64_in_expected_order(UNALIGNED_LOAD64(p)); +} + +static uint32 Fetch32(const char *p) { + return uint32_in_expected_order(UNALIGNED_LOAD32(p)); +} + +// Some primes between 2^63 and 2^64 for various uses. +static const uint64 k0 = 0xc3a5c85c97cb3127ULL; +static const uint64 k1 = 0xb492b66fbe98f273ULL; +static const uint64 k2 = 0x9ae16a3b2f90404fULL; + +// Magic numbers for 32-bit hashing. Copied from Murmur3. +static const uint32 c1 = 0xcc9e2d51; +static const uint32 c2 = 0x1b873593; + +// A 32-bit to 32-bit integer hash copied from Murmur3. +static uint32 fmix(uint32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +static uint32 Rotate32(uint32 val, int shift) { + // Avoid shifting by 32: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); +} + +#undef PERMUTE3 +#define PERMUTE3(a, b, c) do { asl::swap(a, b); asl::swap(a, c); } while (0) + +static uint32 Mur(uint32 a, uint32 h) { + // Helper from Murmur3 for combining two 32-bit values. + a *= c1; + a = Rotate32(a, 17); + a *= c2; + h ^= a; + h = Rotate32(h, 19); + return h * 5 + 0xe6546b64; +} + +static uint32 Hash32Len13to24(const char *s, size_t len) { + uint32 a = Fetch32(s - 4 + (len >> 1)); + uint32 b = Fetch32(s + 4); + uint32 c = Fetch32(s + len - 8); + uint32 d = Fetch32(s + (len >> 1)); + uint32 e = Fetch32(s); + uint32 f = Fetch32(s + len - 4); + uint32 h = static_cast(len); + + return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); +} + +static uint32 Hash32Len0to4(const char *s, size_t len) { + uint32 b = 0; + uint32 c = 9; + for (size_t i = 0; i < len; i++) { + signed char v = static_cast(s[i]); + b = b * c1 + static_cast(v); + c ^= b; + } + return fmix(Mur(b, Mur(static_cast(len), c))); +} + +static uint32 Hash32Len5to12(const char *s, size_t len) { + uint32 a = static_cast(len), b = a * 5, c = 9, d = b; + a += Fetch32(s); + b += Fetch32(s + len - 4); + c += Fetch32(s + ((len >> 1) & 4)); + return fmix(Mur(c, Mur(b, Mur(a, d)))); +} + +uint32 asl::city_hash::CityHash32(const char *s, size_t len) { + if (len <= 24) { + return len <= 12 ? + (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : + Hash32Len13to24(s, len); + } + + // len > 24 + uint32 h = static_cast(len), g = c1 * h, f = g; + uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; + uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; + uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; + uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; + uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; + h ^= a0; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + h ^= a2; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a1; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + g ^= a3; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + f += a4; + f = Rotate32(f, 19); + f = f * 5 + 0xe6546b64; + size_t iters = (len - 1) / 20; + do { + uint32 a0_ = Rotate32(Fetch32(s) * c1, 17) * c2; + uint32 a1_ = Fetch32(s + 4); + uint32 a2_ = Rotate32(Fetch32(s + 8) * c1, 17) * c2; + uint32 a3_ = Rotate32(Fetch32(s + 12) * c1, 17) * c2; + uint32 a4_ = Fetch32(s + 16); + h ^= a0_; + h = Rotate32(h, 18); + h = h * 5 + 0xe6546b64; + f += a1_; + f = Rotate32(f, 19); + f = f * c1; + g += a2_; + g = Rotate32(g, 18); + g = g * 5 + 0xe6546b64; + h ^= a3_ + a1_; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a4_; + g = bswap_32(g) * 5; + h += a4_ * 5; + h = bswap_32(h); + f += a0_; + PERMUTE3(f, h, g); + s += 20; + } while (--iters != 0); + g = Rotate32(g, 11) * c1; + g = Rotate32(g, 17) * c1; + f = Rotate32(f, 11) * c1; + f = Rotate32(f, 17) * c1; + h = Rotate32(h + g, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + h = Rotate32(h + f, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + return h; +} + +// Bitwise right rotate. Normally this will compile to a single +// instruction, especially if the shift is a manifest constant. +static uint64 Rotate(uint64 val, int shift) { + // Avoid shifting by 64: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); +} + +static uint64 ShiftMix(uint64 val) { + return val ^ (val >> 47); +} + +static uint64 HashLen16(uint64 u, uint64 v) { + return asl::city_hash::Hash128to64(uint128{u, v}); +} + +static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { + // Murmur-inspired hashing. + uint64 a = (u ^ v) * mul; + a ^= (a >> 47); + uint64 b = (v ^ a) * mul; + b ^= (b >> 47); + b *= mul; + return b; +} + +static uint64 HashLen0to16(const char *s, size_t len) { + if (len >= 8) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) + k2; + uint64 b = Fetch64(s + len - 8); + uint64 c = Rotate(b, 37) * mul + a; + uint64 d = (Rotate(a, 25) + b) * mul; + return HashLen16(c, d, mul); + } + if (len >= 4) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch32(s); + return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); + } + if (len > 0) { + uint8 a = static_cast(s[0]); + uint8 b = static_cast(s[len >> 1]); + uint8 c = static_cast(s[len - 1]); + uint32 y = static_cast(a) + (static_cast(b) << 8); + uint32 z = static_cast(len) + (static_cast(c) << 2); + return ShiftMix(y * k2 ^ z * k0) * k2; + } + return k2; +} + +// This probably works well for 16-byte strings as well, but it may be overkill +// in that case. +static uint64 HashLen17to32(const char *s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k1; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 8) * mul; + uint64 d = Fetch64(s + len - 16) * k2; + return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, + a + Rotate(b + k2, 18) + c, mul); +} + +// Return a 16-byte hash for 48 bytes. Quick and dirty. +// Callers do best to use "random-looking" values for a and b. +static uint128 WeakHashLen32WithSeeds( + uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { + a += w; + b = Rotate(b + a + z, 21); + uint64 c = a; + a += x; + a += y; + b += Rotate(a, 44); + return {a + z, b + c}; +} + +// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. +static uint128 WeakHashLen32WithSeeds( + const char* s, uint64 a, uint64 b) { + return WeakHashLen32WithSeeds(Fetch64(s), + Fetch64(s + 8), + Fetch64(s + 16), + Fetch64(s + 24), + a, + b); +} + +// Return an 8-byte hash for 33 to 64 bytes. +static uint64 HashLen33to64(const char *s, size_t len) { + uint64 mul = k2 + len * 2; + uint64 a = Fetch64(s) * k2; + uint64 b = Fetch64(s + 8); + uint64 c = Fetch64(s + len - 24); + uint64 d = Fetch64(s + len - 32); + uint64 e = Fetch64(s + 16) * k2; + uint64 f = Fetch64(s + 24) * 9; + uint64 g = Fetch64(s + len - 8); + uint64 h = Fetch64(s + len - 16) * mul; + uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; + uint64 v = ((a + g) ^ d) + f + 1; + uint64 w = bswap_64((u + v) * mul) + h; + uint64 x = Rotate(e + f, 42) + c; + uint64 y = (bswap_64((v + w) * mul) + g) * mul; + uint64 z = e + f + c; + a = bswap_64((x + z) * mul + y) + b; + b = ShiftMix((z + a) * mul + d + h) * mul; + return b + x; +} + +uint64 asl::city_hash::CityHash64(const char *s, size_t len) { + if (len <= 32) { + if (len <= 16) { + return HashLen0to16(s, len); + } else { + return HashLen17to32(s, len); + } + } else if (len <= 64) { + return HashLen33to64(s, len); + } + + // For strings over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + uint64 x = Fetch64(s + len - 40); + uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); + uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); + uint128 v = WeakHashLen32WithSeeds(s + len - 64, len, z); + uint128 w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); + x = x * k1 + Fetch64(s); + + // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. + len = (len - 1) & ~static_cast(63); + do { + x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; + x ^= w.low; + y += v.high + Fetch64(s + 40); + z = Rotate(z + w.high, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); + w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); + asl::swap(z, x); + s += 64; + len -= 64; + } while (len != 0); + return HashLen16(HashLen16(v.high, w.high) + ShiftMix(y) * k1 + z, + HashLen16(v.low, w.low) + x); +} + +uint64 asl::city_hash::CityHash64WithSeed(const char *s, size_t len, uint64 seed) { + return CityHash64WithSeeds(s, len, k2, seed); +} + +uint64 asl::city_hash::CityHash64WithSeeds(const char *s, size_t len, + uint64 seed0, uint64 seed1) { + return HashLen16(CityHash64(s, len) - seed0, seed1); +} + +// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings +// of any length representable in signed long. Based on City and Murmur. +static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { + uint64 a = seed.low; + uint64 b = seed.high; + uint64 c = 0; + uint64 d = 0; + if (len <= 16) { + a = ShiftMix(a * k1) * k1; + c = b * k1 + HashLen0to16(s, len); + d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); + } else { + c = HashLen16(Fetch64(s + len - 8) + k1, a); + d = HashLen16(b + len, c + Fetch64(s + len - 16)); + a += d; + // len > 16 here, so do...while is safe + do { + a ^= ShiftMix(Fetch64(s) * k1) * k1; + a *= k1; + b ^= a; + c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; + c *= k1; + d ^= c; + s += 16; + len -= 16; + } while (len > 16); + } + a = HashLen16(a, c); + b = HashLen16(d, b); + return uint128{a ^ b, HashLen16(b, a)}; +} + +uint128 asl::city_hash::CityHash128WithSeed(const char *s, size_t len, uint128 seed) { + if (len < 128) { + return CityMurmur(s, len, seed); + } + + // We expect len >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + uint128 v, w; + uint64 x = seed.low; + uint64 y = seed.high; + uint64 z = len * k1; + v.high = Rotate(y ^ k1, 49) * k1 + Fetch64(s); + v.low = Rotate(v.high, 42) * k1 + Fetch64(s + 8); + w.high = Rotate(y + z, 35) * k1 + x; + w.low = Rotate(x + Fetch64(s + 88), 53) * k1; + + // This is the same inner loop as CityHash64(), manually unrolled. + do { + x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; + x ^= w.low; + y += v.high + Fetch64(s + 40); + z = Rotate(z + w.high, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); + w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); + asl::swap(z, x); + s += 64; + x = Rotate(x + y + v.high + Fetch64(s + 8), 37) * k1; + y = Rotate(y + v.low + Fetch64(s + 48), 42) * k1; + x ^= w.low; + y += v.high + Fetch64(s + 40); + z = Rotate(z + w.high, 33) * k1; + v = WeakHashLen32WithSeeds(s, v.low * k1, x + w.high); + w = WeakHashLen32WithSeeds(s + 32, z + w.low, y + Fetch64(s + 16)); + asl::swap(z, x); + s += 64; + len -= 128; + } while (LIKELY(len >= 128)); + x += Rotate(v.high + z, 49) * k0; + y = y * k0 + Rotate(w.low, 37); + z = z * k0 + Rotate(w.high, 27); + w.high *= 9; + v.high *= k0; + // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. + for (size_t tail_done = 0; tail_done < len; ) { + tail_done += 32; + y = Rotate(x + y, 42) * k0 + v.low; + w.high += Fetch64(s + len - tail_done + 16); + x = x * k0 + w.high; + z += w.low + Fetch64(s + len - tail_done); + w.low += v.high; + v = WeakHashLen32WithSeeds(s + len - tail_done, v.high + z, v.low); + v.high *= k0; + } + // At this point our 56 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 56-byte-to-8-byte hashes to get a 16-byte final result. + x = HashLen16(x, v.high); + y = HashLen16(y + z, w.high); + return uint128{HashLen16(x + v.low, w.low) + y, + HashLen16(x + w.low, y + v.low)}; +} + +uint128 asl::city_hash::CityHash128(const char *s, size_t len) { + return len >= 16 ? + asl::city_hash::CityHash128WithSeed(s + 16, len - 16, + uint128{Fetch64(s), Fetch64(s + 8) + k0}) : + asl::city_hash::CityHash128WithSeed(s, len, uint128{k0, k1}); +} + +// NOLINTEND diff --git a/asl/hash_map.hpp b/asl/hash_map.hpp index 93f93e7..edb3b4c 100644 --- a/asl/hash_map.hpp +++ b/asl/hash_map.hpp @@ -1,178 +1,178 @@ -#pragma once - -#include "asl/meta.hpp" -#include "asl/utility.hpp" -#include "asl/hash.hpp" -#include "asl/allocator.hpp" -#include "asl/hash_set.hpp" - -namespace asl -{ - -namespace hash_map_internal -{ - -template -struct Slot -{ - K key; - V value; -}; - -template KeyHasher> -struct SlotHasher : public KeyHasher -{ - using KeyHasher::hash; - - constexpr static uint64_t hash(const Slot& slot) - { - return KeyHasher::hash(slot.key); - } -}; - -template KeyComparator> -struct SlotComparator : public KeyComparator -{ - using KeyComparator::eq; - - constexpr static bool eq(const Slot& a, const Slot& b) - { - return KeyComparator::eq(a.key, b.key); - } - - constexpr static bool eq(const Slot& a, const K& b) - { - return KeyComparator::eq(a.key, b); - } -}; - -} // namespace hash_map_internal - -template< - is_object K, - is_object V, - allocator Allocator = DefaultAllocator, - key_hasher KeyHasher = default_key_hasher, - key_comparator KeyComparator = default_key_comparator -> -requires moveable && moveable -class hash_map : protected hash_set< - hash_map_internal::Slot, - Allocator, - hash_map_internal::SlotHasher, - hash_map_internal::SlotComparator> -{ - using Base = - hash_set< - hash_map_internal::Slot, - Allocator, - hash_map_internal::SlotHasher, - hash_map_internal::SlotComparator>; - -public: - constexpr hash_map() requires default_constructible = default; - - explicit constexpr hash_map(Allocator allocator) - : Base{ASL_MOVE(allocator)} - {} - - hash_map(const hash_map&) requires copyable && copyable = default; - - hash_map& operator=(const hash_map&) requires copyable && copyable = default; - - hash_map(hash_map&&) = default; - - hash_map& operator=(hash_map&&) = default; - - ~hash_map() = default; - - using Base::destroy; - - using Base::clear; - - using Base::size; - - using Base::remove; - - using Base::contains; - - template - requires - key_hasher && - key_comparator && - constructible_from && - constructible_from - void insert(U&& key, Arg0&& arg0, Args1&&... args1) - { - Base::maybe_grow_to_fit_one_more(); - - auto result = Base::find_slot_insert(key); - - // NOLINTBEGIN(*-pointer-arithmetic) - - ASL_ASSERT(result.first_available_index >= 0); - - if (result.already_present_index >= 0) - { - if (result.already_present_index != result.first_available_index) - { - ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); - - Base::m_values[result.first_available_index].construct_unsafe(ASL_MOVE(Base::m_values[result.already_present_index].as_init_unsafe())); - Base::m_values[result.already_present_index].destroy_unsafe(); - - Base::m_tags[result.first_available_index] = result.tag; - Base::m_tags[result.already_present_index] = Base::kTombstone; - } - - ASL_ASSERT(Base::m_tags[result.first_available_index] == result.tag); - - if constexpr (sizeof...(Args1) == 0 && assignable_from) - { - Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_FWD(arg0); - } - else - { - Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_MOVE(V{ASL_FWD(arg0), ASL_FWD(args1)...}); - } - } - else - { - ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); - Base::m_values[result.first_available_index].construct_unsafe(ASL_FWD(key), V{ASL_FWD(arg0), ASL_FWD(args1)...}); - Base::m_tags[result.first_available_index] = result.tag; - Base::m_size += 1; - } - - // NOLINTEND(*-pointer-arithmetic) - } - - // @Todo(C++23) Deducing this - template - requires key_hasher && key_comparator - const V* get(const U& value) const - { - isize_t index = Base::find_slot_lookup(value); - if (index >= 0) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - return &Base::m_values[index].as_init_unsafe().value; - } - return nullptr; - } - - template - requires key_hasher && key_comparator - V* get(const U& value) - { - isize_t index = Base::find_slot_lookup(value); - if (index >= 0) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - return &Base::m_values[index].as_init_unsafe().value; - } - return nullptr; - } -}; - -} // namespace asl +#pragma once + +#include "asl/meta.hpp" +#include "asl/utility.hpp" +#include "asl/hash.hpp" +#include "asl/allocator.hpp" +#include "asl/hash_set.hpp" + +namespace asl +{ + +namespace hash_map_internal +{ + +template +struct Slot +{ + K key; + V value; +}; + +template KeyHasher> +struct SlotHasher : public KeyHasher +{ + using KeyHasher::hash; + + constexpr static uint64_t hash(const Slot& slot) + { + return KeyHasher::hash(slot.key); + } +}; + +template KeyComparator> +struct SlotComparator : public KeyComparator +{ + using KeyComparator::eq; + + constexpr static bool eq(const Slot& a, const Slot& b) + { + return KeyComparator::eq(a.key, b.key); + } + + constexpr static bool eq(const Slot& a, const K& b) + { + return KeyComparator::eq(a.key, b); + } +}; + +} // namespace hash_map_internal + +template< + is_object K, + is_object V, + allocator Allocator = DefaultAllocator, + key_hasher KeyHasher = default_key_hasher, + key_comparator KeyComparator = default_key_comparator +> +requires moveable && moveable +class hash_map : protected hash_set< + hash_map_internal::Slot, + Allocator, + hash_map_internal::SlotHasher, + hash_map_internal::SlotComparator> +{ + using Base = + hash_set< + hash_map_internal::Slot, + Allocator, + hash_map_internal::SlotHasher, + hash_map_internal::SlotComparator>; + +public: + constexpr hash_map() requires default_constructible = default; + + explicit constexpr hash_map(Allocator allocator) + : Base{ASL_MOVE(allocator)} + {} + + hash_map(const hash_map&) requires copyable && copyable = default; + + hash_map& operator=(const hash_map&) requires copyable && copyable = default; + + hash_map(hash_map&&) = default; + + hash_map& operator=(hash_map&&) = default; + + ~hash_map() = default; + + using Base::destroy; + + using Base::clear; + + using Base::size; + + using Base::remove; + + using Base::contains; + + template + requires + key_hasher && + key_comparator && + constructible_from && + constructible_from + void insert(U&& key, Arg0&& arg0, Args1&&... args1) + { + Base::maybe_grow_to_fit_one_more(); + + auto result = Base::find_slot_insert(key); + + // NOLINTBEGIN(*-pointer-arithmetic) + + ASL_ASSERT(result.first_available_index >= 0); + + if (result.already_present_index >= 0) + { + if (result.already_present_index != result.first_available_index) + { + ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); + + Base::m_values[result.first_available_index].construct_unsafe(ASL_MOVE(Base::m_values[result.already_present_index].as_init_unsafe())); + Base::m_values[result.already_present_index].destroy_unsafe(); + + Base::m_tags[result.first_available_index] = result.tag; + Base::m_tags[result.already_present_index] = Base::kTombstone; + } + + ASL_ASSERT(Base::m_tags[result.first_available_index] == result.tag); + + if constexpr (sizeof...(Args1) == 0 && assignable_from) + { + Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_FWD(arg0); + } + else + { + Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_MOVE(V{ASL_FWD(arg0), ASL_FWD(args1)...}); + } + } + else + { + ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); + Base::m_values[result.first_available_index].construct_unsafe(ASL_FWD(key), V{ASL_FWD(arg0), ASL_FWD(args1)...}); + Base::m_tags[result.first_available_index] = result.tag; + Base::m_size += 1; + } + + // NOLINTEND(*-pointer-arithmetic) + } + + // @Todo(C++23) Deducing this + template + requires key_hasher && key_comparator + const V* get(const U& value) const + { + isize_t index = Base::find_slot_lookup(value); + if (index >= 0) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + return &Base::m_values[index].as_init_unsafe().value; + } + return nullptr; + } + + template + requires key_hasher && key_comparator + V* get(const U& value) + { + isize_t index = Base::find_slot_lookup(value); + if (index >= 0) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + return &Base::m_values[index].as_init_unsafe().value; + } + return nullptr; + } +}; + +} // namespace asl diff --git a/asl/hash_set.hpp b/asl/hash_set.hpp index 979235d..d252b37 100644 --- a/asl/hash_set.hpp +++ b/asl/hash_set.hpp @@ -1,418 +1,418 @@ -#pragma once - -#include "asl/annotations.hpp" -#include "asl/meta.hpp" -#include "asl/utility.hpp" -#include "asl/maybe_uninit.hpp" -#include "asl/hash.hpp" -#include "asl/allocator.hpp" -#include "asl/memory.hpp" - -namespace asl -{ - -template -concept key_hasher = requires (const T& value) -{ - { H::hash(value) } -> same_as; -}; - -template -struct default_key_hasher -{ - constexpr static uint64_t hash(const T& value) - { - return hash_value(value); - } -}; - -template -concept key_comparator = requires(const U& a, const V& b) -{ - { C::eq(a, b) } -> same_as; -}; - -template -struct default_key_comparator -{ - constexpr static bool eq(const T& a, const T& b) - { - return a == b; - } -}; - -template< - is_object T, - allocator Allocator = DefaultAllocator, - key_hasher KeyHasher = default_key_hasher, - key_comparator KeyComparator = default_key_comparator -> -requires moveable -class hash_set -{ -protected: - static constexpr uint8_t kHasValue = 0x80; - static constexpr uint8_t kHashMask = 0x7f; - static constexpr uint8_t kEmpty = 0x00; - static constexpr uint8_t kTombstone = 0x01; - - static constexpr isize_t kMinCapacity = 8; - - // Important so we can memzero the tags - static_assert(kEmpty == 0); - - uint8_t* m_tags{}; - maybe_uninit* m_values{}; - isize_t m_capacity{}; - isize_t m_size{}; - - ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; - - constexpr isize_t max_size() const - { - // Max load factor is 75% - return (m_capacity >> 1) + (m_capacity >> 2); - } - - static isize_t size_to_capacity(isize_t size) - { - ASL_ASSERT(size > 0); - return max( - kMinCapacity, - static_cast(round_up_pow2((static_cast(size) * 4 + 2) / 3))); - } - - static void insert_inner( - T&& value, - uint8_t* tags, - maybe_uninit* values, - isize_t capacity, - isize_t* size) - { - ASL_ASSERT(*size < capacity); - - const auto result = find_slot_insert(value, tags, values, capacity); - - // NOLINTBEGIN(*-pointer-arithmetic) - - ASL_ASSERT(result.first_available_index >= 0); - - if (result.already_present_index != result.first_available_index) - { - ASL_ASSERT((tags[result.first_available_index] & kHasValue) == 0); - if (result.already_present_index >= 0) - { - ASL_ASSERT((tags[result.already_present_index] & kHasValue) != 0); - values[result.already_present_index].destroy_unsafe(); - tags[result.already_present_index] = kTombstone; - } - else - { - *size += 1; - } - - values[result.first_available_index].construct_unsafe(ASL_MOVE(value)); - tags[result.first_available_index] = result.tag; - } - - // NOLINTEND(*-pointer-arithmetic) - } - - void grow_and_rehash() - { - grow_and_rehash(max(kMinCapacity, m_capacity * 2)); - } - - void grow_and_rehash(isize_t new_capacity) - { - ASL_ASSERT(new_capacity >= kMinCapacity && is_pow2(new_capacity) && new_capacity > m_capacity); - - auto* new_tags = reinterpret_cast(m_allocator.alloc(layout::array(new_capacity))); - auto* new_values = reinterpret_cast*>(m_allocator.alloc(layout::array>(new_capacity))); - asl::memzero(new_tags, new_capacity); - - isize_t new_size = 0; - - if (m_size > 0) - { - // NOLINTBEGIN(*-pointer-arithmetic) - for (isize_t i = 0; i < m_capacity; ++i) - { - if ((m_tags[i] & kHasValue) == 0) { continue; } - - insert_inner(ASL_MOVE(m_values[i].as_init_unsafe()), new_tags, new_values, new_capacity, &new_size); - - // Destroy now so that destroy() has less things to do - m_values[i].destroy_unsafe(); - m_tags[i] = kTombstone; - } - // NOLINTEND(*-pointer-arithmetic) - } - - ASL_ASSERT(new_size == m_size); - - m_size = 0; - destroy(); - - m_tags = new_tags; - m_values = new_values; - m_capacity = new_capacity; - m_size = new_size; - } - - void clear_values() - { - if constexpr (!trivially_destructible) - { - if (m_size > 0) - { - for (isize_t i = 0; i < m_capacity; ++i) - { - if ((m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) - { - m_values[i].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) - } - } - } - } - } - - void copy_from(const hash_set& other) - { - if (other.size() > 0) - { - isize_t min_capacity = size_to_capacity(other.size()); - if (m_capacity < min_capacity) - { - grow_and_rehash(min_capacity); - } - ASL_ASSERT(m_capacity >= min_capacity); - - for (isize_t i = 0; i < other.m_capacity; ++i) - { - if ((other.m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) - { - insert(other.m_values[i].as_init_unsafe()); // NOLINT(*-pointer-arithmetic) - } - } - } - } - - struct FindSlotResult - { - uint8_t tag{}; - isize_t first_available_index = -1; - isize_t already_present_index = -1; - }; - - template - requires key_hasher && key_comparator - static FindSlotResult find_slot_insert( - const U& value, - const uint8_t* tags, - const maybe_uninit* values, - isize_t capacity) - { - ASL_ASSERT(is_pow2(capacity)); - - FindSlotResult result{}; - - const isize_t capacity_mask = capacity - 1; - const uint64_t hash = KeyHasher::hash(value); - const auto starting_index = static_cast(hash >> 7) & capacity_mask; - - result.tag = static_cast(hash & kHashMask) | kHasValue; - - // NOLINTBEGIN(*-pointer-arithmetic) - - for ( - isize_t i = starting_index; - i != starting_index || result.first_available_index < 0; - i = (i + 1) & capacity_mask) - { - uint8_t t = tags[i]; - - if ((t & kHasValue) == 0 && result.first_available_index < 0) - { - result.first_available_index = i; - } - - if (t == result.tag && KeyComparator::eq(values[i].as_init_unsafe(), value)) - { - ASL_ASSERT(result.already_present_index < 0); - result.already_present_index = i; - if (result.first_available_index < 0) - { - result.first_available_index = i; - } - break; - } - - if (t == kEmpty) { break; } - } - - // NOLINTEND(*-pointer-arithmetic) - - return result; - } - - template - requires key_hasher && key_comparator - isize_t find_slot_lookup(const U& value) const - { - if (m_size <= 0) { return -1; }; - - ASL_ASSERT(is_pow2(m_capacity)); - - const isize_t capacity_mask = m_capacity - 1; - const uint64_t hash = KeyHasher::hash(value); - const uint8_t tag = static_cast(hash & kHashMask) | kHasValue; - const auto starting_index = static_cast(hash >> 7) & capacity_mask; - - // NOLINTBEGIN(*-pointer-arithmetic) - - isize_t i = starting_index; - do - { - const uint8_t t = m_tags[i]; - - if (t == tag && KeyComparator::eq(m_values[i].as_init_unsafe(), value)) { return i; } - if (t == kEmpty) { break; } - - i = (i + 1) & capacity_mask; - } while (i != starting_index); - - // NOLINTEND(*-pointer-arithmetic) - - return -1; - } - - template - requires key_hasher && key_comparator - FindSlotResult find_slot_insert(const U& value) - { - return find_slot_insert(value, m_tags, m_values, m_capacity); - } - - void maybe_grow_to_fit_one_more() - { - if (m_size >= max_size()) - { - grow_and_rehash(); - } - } - -public: - constexpr hash_set() requires default_constructible - : m_allocator{} - {} - - explicit constexpr hash_set(Allocator allocator) - : m_allocator{ASL_MOVE(allocator)} - {} - - hash_set(const hash_set& other) - requires copy_constructible && copyable - : hash_set{other.m_allocator} - { - copy_from(other); - } - - hash_set& operator=(const hash_set& other) - requires copyable - { - if (&other != this) - { - clear(); - copy_from(other); - } - return *this; - } - - hash_set(hash_set&& other) - requires move_constructible - : m_tags{exchange(other.m_tags, nullptr)} - , m_values{exchange(other.m_values, nullptr)} - , m_capacity{exchange(other.m_capacity, 0)} - , m_size{exchange(other.m_size, 0)} - , m_allocator{ASL_MOVE(other.m_allocator)} - {} - - hash_set& operator=(hash_set&& other) - { - if (&other != this) - { - destroy(); - m_tags = exchange(other.m_tags, nullptr); - m_values = exchange(other.m_values, nullptr); - m_capacity = exchange(other.m_capacity, 0); - m_size = exchange(other.m_size, 0); - m_allocator = ASL_MOVE(other.m_allocator); - } - return *this; - } - - ~hash_set() - { - destroy(); - } - - void destroy() - { - clear_values(); - m_size = 0; - - if (m_capacity > 0) - { - m_allocator.dealloc(m_tags, layout::array(m_capacity)); - m_allocator.dealloc(m_values, layout::array>(m_capacity)); - m_capacity = 0; - } - } - - void clear() - { - clear_values(); - m_size = 0; - - if (m_capacity > 0) - { - asl::memzero(m_tags, m_capacity); - } - } - - constexpr isize_t size() const { return m_size; } - - template - void insert(Args&&... args) - requires constructible_from - { - maybe_grow_to_fit_one_more(); - ASL_ASSERT(m_size < max_size()); - insert_inner(ASL_MOVE(T{ASL_FWD(args)...}), m_tags, m_values, m_capacity, &m_size); - } - - template - requires key_hasher && key_comparator - bool contains(const U& value) const - { - return find_slot_lookup(value) >= 0; - } - - template - requires key_hasher && key_comparator - bool remove(const U& value) - { - isize_t slot = find_slot_lookup(value); - if (slot < 0) { return false; } - - m_values[slot].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) - m_tags[slot] = kTombstone; // NOLINT(*-pointer-arithmetic) - m_size -= 1; - - return true; - } -}; - -} // namespace asl - +#pragma once + +#include "asl/annotations.hpp" +#include "asl/meta.hpp" +#include "asl/utility.hpp" +#include "asl/maybe_uninit.hpp" +#include "asl/hash.hpp" +#include "asl/allocator.hpp" +#include "asl/memory.hpp" + +namespace asl +{ + +template +concept key_hasher = requires (const T& value) +{ + { H::hash(value) } -> same_as; +}; + +template +struct default_key_hasher +{ + constexpr static uint64_t hash(const T& value) + { + return hash_value(value); + } +}; + +template +concept key_comparator = requires(const U& a, const V& b) +{ + { C::eq(a, b) } -> same_as; +}; + +template +struct default_key_comparator +{ + constexpr static bool eq(const T& a, const T& b) + { + return a == b; + } +}; + +template< + is_object T, + allocator Allocator = DefaultAllocator, + key_hasher KeyHasher = default_key_hasher, + key_comparator KeyComparator = default_key_comparator +> +requires moveable +class hash_set +{ +protected: + static constexpr uint8_t kHasValue = 0x80; + static constexpr uint8_t kHashMask = 0x7f; + static constexpr uint8_t kEmpty = 0x00; + static constexpr uint8_t kTombstone = 0x01; + + static constexpr isize_t kMinCapacity = 8; + + // Important so we can memzero the tags + static_assert(kEmpty == 0); + + uint8_t* m_tags{}; + maybe_uninit* m_values{}; + isize_t m_capacity{}; + isize_t m_size{}; + + ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; + + constexpr isize_t max_size() const + { + // Max load factor is 75% + return (m_capacity >> 1) + (m_capacity >> 2); + } + + static isize_t size_to_capacity(isize_t size) + { + ASL_ASSERT(size > 0); + return max( + kMinCapacity, + static_cast(round_up_pow2((static_cast(size) * 4 + 2) / 3))); + } + + static void insert_inner( + T&& value, + uint8_t* tags, + maybe_uninit* values, + isize_t capacity, + isize_t* size) + { + ASL_ASSERT(*size < capacity); + + const auto result = find_slot_insert(value, tags, values, capacity); + + // NOLINTBEGIN(*-pointer-arithmetic) + + ASL_ASSERT(result.first_available_index >= 0); + + if (result.already_present_index != result.first_available_index) + { + ASL_ASSERT((tags[result.first_available_index] & kHasValue) == 0); + if (result.already_present_index >= 0) + { + ASL_ASSERT((tags[result.already_present_index] & kHasValue) != 0); + values[result.already_present_index].destroy_unsafe(); + tags[result.already_present_index] = kTombstone; + } + else + { + *size += 1; + } + + values[result.first_available_index].construct_unsafe(ASL_MOVE(value)); + tags[result.first_available_index] = result.tag; + } + + // NOLINTEND(*-pointer-arithmetic) + } + + void grow_and_rehash() + { + grow_and_rehash(max(kMinCapacity, m_capacity * 2)); + } + + void grow_and_rehash(isize_t new_capacity) + { + ASL_ASSERT(new_capacity >= kMinCapacity && is_pow2(new_capacity) && new_capacity > m_capacity); + + auto* new_tags = reinterpret_cast(m_allocator.alloc(layout::array(new_capacity))); + auto* new_values = reinterpret_cast*>(m_allocator.alloc(layout::array>(new_capacity))); + asl::memzero(new_tags, new_capacity); + + isize_t new_size = 0; + + if (m_size > 0) + { + // NOLINTBEGIN(*-pointer-arithmetic) + for (isize_t i = 0; i < m_capacity; ++i) + { + if ((m_tags[i] & kHasValue) == 0) { continue; } + + insert_inner(ASL_MOVE(m_values[i].as_init_unsafe()), new_tags, new_values, new_capacity, &new_size); + + // Destroy now so that destroy() has less things to do + m_values[i].destroy_unsafe(); + m_tags[i] = kTombstone; + } + // NOLINTEND(*-pointer-arithmetic) + } + + ASL_ASSERT(new_size == m_size); + + m_size = 0; + destroy(); + + m_tags = new_tags; + m_values = new_values; + m_capacity = new_capacity; + m_size = new_size; + } + + void clear_values() + { + if constexpr (!trivially_destructible) + { + if (m_size > 0) + { + for (isize_t i = 0; i < m_capacity; ++i) + { + if ((m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) + { + m_values[i].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) + } + } + } + } + } + + void copy_from(const hash_set& other) + { + if (other.size() > 0) + { + isize_t min_capacity = size_to_capacity(other.size()); + if (m_capacity < min_capacity) + { + grow_and_rehash(min_capacity); + } + ASL_ASSERT(m_capacity >= min_capacity); + + for (isize_t i = 0; i < other.m_capacity; ++i) + { + if ((other.m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) + { + insert(other.m_values[i].as_init_unsafe()); // NOLINT(*-pointer-arithmetic) + } + } + } + } + + struct FindSlotResult + { + uint8_t tag{}; + isize_t first_available_index = -1; + isize_t already_present_index = -1; + }; + + template + requires key_hasher && key_comparator + static FindSlotResult find_slot_insert( + const U& value, + const uint8_t* tags, + const maybe_uninit* values, + isize_t capacity) + { + ASL_ASSERT(is_pow2(capacity)); + + FindSlotResult result{}; + + const isize_t capacity_mask = capacity - 1; + const uint64_t hash = KeyHasher::hash(value); + const auto starting_index = static_cast(hash >> 7) & capacity_mask; + + result.tag = static_cast(hash & kHashMask) | kHasValue; + + // NOLINTBEGIN(*-pointer-arithmetic) + + for ( + isize_t i = starting_index; + i != starting_index || result.first_available_index < 0; + i = (i + 1) & capacity_mask) + { + uint8_t t = tags[i]; + + if ((t & kHasValue) == 0 && result.first_available_index < 0) + { + result.first_available_index = i; + } + + if (t == result.tag && KeyComparator::eq(values[i].as_init_unsafe(), value)) + { + ASL_ASSERT(result.already_present_index < 0); + result.already_present_index = i; + if (result.first_available_index < 0) + { + result.first_available_index = i; + } + break; + } + + if (t == kEmpty) { break; } + } + + // NOLINTEND(*-pointer-arithmetic) + + return result; + } + + template + requires key_hasher && key_comparator + isize_t find_slot_lookup(const U& value) const + { + if (m_size <= 0) { return -1; }; + + ASL_ASSERT(is_pow2(m_capacity)); + + const isize_t capacity_mask = m_capacity - 1; + const uint64_t hash = KeyHasher::hash(value); + const uint8_t tag = static_cast(hash & kHashMask) | kHasValue; + const auto starting_index = static_cast(hash >> 7) & capacity_mask; + + // NOLINTBEGIN(*-pointer-arithmetic) + + isize_t i = starting_index; + do + { + const uint8_t t = m_tags[i]; + + if (t == tag && KeyComparator::eq(m_values[i].as_init_unsafe(), value)) { return i; } + if (t == kEmpty) { break; } + + i = (i + 1) & capacity_mask; + } while (i != starting_index); + + // NOLINTEND(*-pointer-arithmetic) + + return -1; + } + + template + requires key_hasher && key_comparator + FindSlotResult find_slot_insert(const U& value) + { + return find_slot_insert(value, m_tags, m_values, m_capacity); + } + + void maybe_grow_to_fit_one_more() + { + if (m_size >= max_size()) + { + grow_and_rehash(); + } + } + +public: + constexpr hash_set() requires default_constructible + : m_allocator{} + {} + + explicit constexpr hash_set(Allocator allocator) + : m_allocator{ASL_MOVE(allocator)} + {} + + hash_set(const hash_set& other) + requires copy_constructible && copyable + : hash_set{other.m_allocator} + { + copy_from(other); + } + + hash_set& operator=(const hash_set& other) + requires copyable + { + if (&other != this) + { + clear(); + copy_from(other); + } + return *this; + } + + hash_set(hash_set&& other) + requires move_constructible + : m_tags{exchange(other.m_tags, nullptr)} + , m_values{exchange(other.m_values, nullptr)} + , m_capacity{exchange(other.m_capacity, 0)} + , m_size{exchange(other.m_size, 0)} + , m_allocator{ASL_MOVE(other.m_allocator)} + {} + + hash_set& operator=(hash_set&& other) + { + if (&other != this) + { + destroy(); + m_tags = exchange(other.m_tags, nullptr); + m_values = exchange(other.m_values, nullptr); + m_capacity = exchange(other.m_capacity, 0); + m_size = exchange(other.m_size, 0); + m_allocator = ASL_MOVE(other.m_allocator); + } + return *this; + } + + ~hash_set() + { + destroy(); + } + + void destroy() + { + clear_values(); + m_size = 0; + + if (m_capacity > 0) + { + m_allocator.dealloc(m_tags, layout::array(m_capacity)); + m_allocator.dealloc(m_values, layout::array>(m_capacity)); + m_capacity = 0; + } + } + + void clear() + { + clear_values(); + m_size = 0; + + if (m_capacity > 0) + { + asl::memzero(m_tags, m_capacity); + } + } + + constexpr isize_t size() const { return m_size; } + + template + void insert(Args&&... args) + requires constructible_from + { + maybe_grow_to_fit_one_more(); + ASL_ASSERT(m_size < max_size()); + insert_inner(ASL_MOVE(T{ASL_FWD(args)...}), m_tags, m_values, m_capacity, &m_size); + } + + template + requires key_hasher && key_comparator + bool contains(const U& value) const + { + return find_slot_lookup(value) >= 0; + } + + template + requires key_hasher && key_comparator + bool remove(const U& value) + { + isize_t slot = find_slot_lookup(value); + if (slot < 0) { return false; } + + m_values[slot].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) + m_tags[slot] = kTombstone; // NOLINT(*-pointer-arithmetic) + m_size -= 1; + + return true; + } +}; + +} // namespace asl + diff --git a/asl/integers.hpp b/asl/integers.hpp index b9ca9f8..04722a3 100644 --- a/asl/integers.hpp +++ b/asl/integers.hpp @@ -1,40 +1,40 @@ -#pragma once - -#include "asl/config.hpp" - -using int8_t = signed char; -using int16_t = signed short; -using int32_t = signed int; -#if ASL_OS_WINDOWS - using int64_t = signed long long; -#elif ASL_OS_LINUX - using int64_t = signed long; -#endif - -using uint8_t = unsigned char; -using uint16_t = unsigned short; -using uint32_t = unsigned int; -#if ASL_OS_WINDOWS - using uint64_t = unsigned long long; -#elif ASL_OS_LINUX - using uint64_t = unsigned long; -#endif - -struct uint128_t -{ - uint64_t high; - uint64_t low; -}; - -using size_t = uint64_t; -using isize_t = int64_t; - -using uintptr_t = size_t; - -namespace asl -{ - -enum class byte : uint8_t {}; - -} // namespace asl - +#pragma once + +#include "asl/config.hpp" + +using int8_t = signed char; +using int16_t = signed short; +using int32_t = signed int; +#if ASL_OS_WINDOWS + using int64_t = signed long long; +#elif ASL_OS_LINUX + using int64_t = signed long; +#endif + +using uint8_t = unsigned char; +using uint16_t = unsigned short; +using uint32_t = unsigned int; +#if ASL_OS_WINDOWS + using uint64_t = unsigned long long; +#elif ASL_OS_LINUX + using uint64_t = unsigned long; +#endif + +struct uint128_t +{ + uint64_t high; + uint64_t low; +}; + +using size_t = uint64_t; +using isize_t = int64_t; + +using uintptr_t = size_t; + +namespace asl +{ + +enum class byte : uint8_t {}; + +} // namespace asl + diff --git a/asl/io.hpp b/asl/io.hpp index e54f00c..66de6d7 100644 --- a/asl/io.hpp +++ b/asl/io.hpp @@ -1,20 +1,20 @@ -#pragma once - +#pragma once + #include "asl/integers.hpp" -#include "asl/utility.hpp" -#include "asl/span.hpp" - -namespace asl -{ - -class Writer -{ -public: - Writer() = default; - ASL_DELETE_COPY_MOVE(Writer); - virtual ~Writer() = default; - - virtual void write(span) = 0; -}; - -} // namespace asl +#include "asl/utility.hpp" +#include "asl/span.hpp" + +namespace asl +{ + +class Writer +{ +public: + Writer() = default; + ASL_DELETE_COPY_MOVE(Writer); + virtual ~Writer() = default; + + virtual void write(span) = 0; +}; + +} // namespace asl diff --git a/asl/layout.hpp b/asl/layout.hpp index 990af46..0201e94 100644 --- a/asl/layout.hpp +++ b/asl/layout.hpp @@ -1,37 +1,37 @@ -#pragma once - -#include "asl/integers.hpp" -#include "asl/meta.hpp" - -namespace asl -{ - -template -inline constexpr isize_t size_of = static_cast(sizeof(T)); - -template -inline constexpr isize_t align_of = static_cast(alignof(T)); - -struct layout -{ - isize_t size; - isize_t align; - - constexpr bool operator==(const layout&) const = default; - - template - static constexpr layout of() - { - return layout{ size_of, align_of }; - } - - template - static constexpr layout array(isize_t size) - { - return layout{ size_of * size, align_of }; - } -}; - -} // namespace asl - -#define AslOffsetOf(S, M) (static_cast(__builtin_offsetof(S, M))) +#pragma once + +#include "asl/integers.hpp" +#include "asl/meta.hpp" + +namespace asl +{ + +template +inline constexpr isize_t size_of = static_cast(sizeof(T)); + +template +inline constexpr isize_t align_of = static_cast(alignof(T)); + +struct layout +{ + isize_t size; + isize_t align; + + constexpr bool operator==(const layout&) const = default; + + template + static constexpr layout of() + { + return layout{ size_of, align_of }; + } + + template + static constexpr layout array(isize_t size) + { + return layout{ size_of * size, align_of }; + } +}; + +} // namespace asl + +#define AslOffsetOf(S, M) (static_cast(__builtin_offsetof(S, M))) diff --git a/asl/log/BUILD.bazel b/asl/log/BUILD.bazel index 9aabf00..9291a8a 100644 --- a/asl/log/BUILD.bazel +++ b/asl/log/BUILD.bazel @@ -1,26 +1,26 @@ -cc_library( - name = "log", - srcs = [ - "log.hpp", - ], - hdrs = [ - "log.cpp", - ], - deps = [ - "//asl", - ], - visibility = ["//visibility:public"], -) - -cc_test( - name = "tests", - srcs = [ - "log_tests.cpp" - ], - deps = [ - ":log", - "//asl/testing", - ], - visibility = ["//visibility:public"], -) - +cc_library( + name = "log", + srcs = [ + "log.hpp", + ], + hdrs = [ + "log.cpp", + ], + deps = [ + "//asl", + ], + visibility = ["//visibility:public"], +) + +cc_test( + name = "tests", + srcs = [ + "log_tests.cpp" + ], + deps = [ + ":log", + "//asl/testing", + ], + visibility = ["//visibility:public"], +) + diff --git a/asl/maybe_uninit.hpp b/asl/maybe_uninit.hpp index 4f60e4d..a69f39b 100644 --- a/asl/maybe_uninit.hpp +++ b/asl/maybe_uninit.hpp @@ -1,72 +1,72 @@ -#pragma once - -#include "asl/meta.hpp" -#include "asl/utility.hpp" -#include "asl/memory.hpp" - -namespace asl -{ - -template -union maybe_uninit -{ -private: - T m_value; - -public: - constexpr maybe_uninit() requires trivially_default_constructible = default; - constexpr maybe_uninit() requires (!trivially_default_constructible) {} // NOLINT - - template - explicit constexpr maybe_uninit(in_place_t, Args&&... args) - requires constructible_from - : m_value{ASL_FWD(args)...} - {} - - constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible = default; - constexpr maybe_uninit(const maybe_uninit&) requires (!trivially_copy_constructible) {} // NOLINT - - constexpr maybe_uninit(maybe_uninit&&) requires trivially_move_constructible = default; - constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible) {} // NOLINT - - constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable = default; - constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable) {} - - constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable = default; - constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable) {} - - constexpr ~maybe_uninit() requires trivially_destructible = default; - constexpr ~maybe_uninit() requires (!trivially_destructible) {} // NOLINT - - // @Safety Value must not have been initialized yet - template - constexpr void construct_unsafe(Args&&... args) - requires constructible_from - { - construct_at(&m_value, ASL_FWD(args)...); - } - - // @Safety Value must have been initialized - template - constexpr void assign_unsafe(U&& value) - requires assignable_from - { - m_value = ASL_FWD(value); - } - - // @Safety Value must have been initialized - constexpr void destroy_unsafe() - { - if constexpr (!trivially_destructible) - { - destroy(&m_value); - } - } - - // @Safety Value must have been initialized - constexpr const T& as_init_unsafe() const& { return m_value; } - constexpr T& as_init_unsafe() & { return m_value; } - constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); } -}; - -} // namespace asl +#pragma once + +#include "asl/meta.hpp" +#include "asl/utility.hpp" +#include "asl/memory.hpp" + +namespace asl +{ + +template +union maybe_uninit +{ +private: + T m_value; + +public: + constexpr maybe_uninit() requires trivially_default_constructible = default; + constexpr maybe_uninit() requires (!trivially_default_constructible) {} // NOLINT + + template + explicit constexpr maybe_uninit(in_place_t, Args&&... args) + requires constructible_from + : m_value{ASL_FWD(args)...} + {} + + constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible = default; + constexpr maybe_uninit(const maybe_uninit&) requires (!trivially_copy_constructible) {} // NOLINT + + constexpr maybe_uninit(maybe_uninit&&) requires trivially_move_constructible = default; + constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible) {} // NOLINT + + constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable = default; + constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable) {} + + constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable = default; + constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable) {} + + constexpr ~maybe_uninit() requires trivially_destructible = default; + constexpr ~maybe_uninit() requires (!trivially_destructible) {} // NOLINT + + // @Safety Value must not have been initialized yet + template + constexpr void construct_unsafe(Args&&... args) + requires constructible_from + { + construct_at(&m_value, ASL_FWD(args)...); + } + + // @Safety Value must have been initialized + template + constexpr void assign_unsafe(U&& value) + requires assignable_from + { + m_value = ASL_FWD(value); + } + + // @Safety Value must have been initialized + constexpr void destroy_unsafe() + { + if constexpr (!trivially_destructible) + { + destroy(&m_value); + } + } + + // @Safety Value must have been initialized + constexpr const T& as_init_unsafe() const& { return m_value; } + constexpr T& as_init_unsafe() & { return m_value; } + constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); } +}; + +} // namespace asl diff --git a/asl/memory.hpp b/asl/memory.hpp index 8a8a6bf..5a73d26 100644 --- a/asl/memory.hpp +++ b/asl/memory.hpp @@ -1,137 +1,137 @@ -#pragma once - -#include "asl/integers.hpp" -#include "asl/meta.hpp" -#include "asl/layout.hpp" -#include "asl/utility.hpp" - -constexpr void* operator new(size_t, void* ptr) -{ - return ptr; -} - -namespace asl -{ - -constexpr isize_t memcmp(const void* a, const void* b, isize_t size) -{ - return __builtin_memcmp(a, b, static_cast(size)); -} - -constexpr void memcpy(void* dst, const void* src, isize_t size) -{ - __builtin_memcpy(dst, src, static_cast(size)); -} - -inline void memzero(void* dst, isize_t size) -{ - __builtin_memset(dst, 0, static_cast(size)); -} - -constexpr isize_t strlen(const char* s) -{ - return static_cast(__builtin_strlen(s)); -} - -template -constexpr T* construct_at(void* ptr, Args&&... args) - requires constructible_from -{ - return new (ptr) T{ ASL_FWD(args)... }; -} - -template -constexpr void destroy(T* data) -{ - if constexpr (!trivially_destructible) - { - data->~T(); - } -} - -template -constexpr void destroy_n(T* data, isize_t n) -{ - if constexpr (!trivially_destructible) - { - for (isize_t i = 0; i < n; ++i) - { - destroy(data + i); - } - } -} - -template -constexpr void copy_uninit_n(T* to, const T* from, isize_t n) -{ - if constexpr (trivially_copy_constructible) - { - memcpy(to, from, size_of * n); - } - else - { - for (isize_t i = 0; i < n; ++i) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - construct_at(to + i, from[i]); - } - } -} - -template -constexpr void copy_assign_n(T* to, const T* from, isize_t n) -{ - if constexpr (trivially_copy_constructible) - { - memcpy(to, from, size_of * n); - } - else - { - for (isize_t i = 0; i < n; ++i) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - to[i] = from[i]; - } - } -} - -template -constexpr void relocate_uninit_n(T* to, T* from, isize_t n) -{ - if constexpr (trivially_move_constructible) - { - static_assert(trivially_destructible); - memcpy(to, from, size_of * n); - } - else - { - for (isize_t i = 0; i < n; ++i) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - construct_at(to + i, ASL_MOVE(from[i])); - } - destroy_n(from, n); - } -} - -template -constexpr void relocate_assign_n(T* to, T* from, isize_t n) -{ - if constexpr (trivially_move_assignable) - { - static_assert(trivially_destructible); - memcpy(to, from, size_of * n); - } - else - { - for (isize_t i = 0; i < n; ++i) - { - // NOLINTNEXTLINE(*-pointer-arithmetic) - to[i] = ASL_MOVE(from[i]); - } - destroy_n(from, n); - } -} - -} // namespace asl - +#pragma once + +#include "asl/integers.hpp" +#include "asl/meta.hpp" +#include "asl/layout.hpp" +#include "asl/utility.hpp" + +constexpr void* operator new(size_t, void* ptr) +{ + return ptr; +} + +namespace asl +{ + +constexpr isize_t memcmp(const void* a, const void* b, isize_t size) +{ + return __builtin_memcmp(a, b, static_cast(size)); +} + +constexpr void memcpy(void* dst, const void* src, isize_t size) +{ + __builtin_memcpy(dst, src, static_cast(size)); +} + +inline void memzero(void* dst, isize_t size) +{ + __builtin_memset(dst, 0, static_cast(size)); +} + +constexpr isize_t strlen(const char* s) +{ + return static_cast(__builtin_strlen(s)); +} + +template +constexpr T* construct_at(void* ptr, Args&&... args) + requires constructible_from +{ + return new (ptr) T{ ASL_FWD(args)... }; +} + +template +constexpr void destroy(T* data) +{ + if constexpr (!trivially_destructible) + { + data->~T(); + } +} + +template +constexpr void destroy_n(T* data, isize_t n) +{ + if constexpr (!trivially_destructible) + { + for (isize_t i = 0; i < n; ++i) + { + destroy(data + i); + } + } +} + +template +constexpr void copy_uninit_n(T* to, const T* from, isize_t n) +{ + if constexpr (trivially_copy_constructible) + { + memcpy(to, from, size_of * n); + } + else + { + for (isize_t i = 0; i < n; ++i) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + construct_at(to + i, from[i]); + } + } +} + +template +constexpr void copy_assign_n(T* to, const T* from, isize_t n) +{ + if constexpr (trivially_copy_constructible) + { + memcpy(to, from, size_of * n); + } + else + { + for (isize_t i = 0; i < n; ++i) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + to[i] = from[i]; + } + } +} + +template +constexpr void relocate_uninit_n(T* to, T* from, isize_t n) +{ + if constexpr (trivially_move_constructible) + { + static_assert(trivially_destructible); + memcpy(to, from, size_of * n); + } + else + { + for (isize_t i = 0; i < n; ++i) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + construct_at(to + i, ASL_MOVE(from[i])); + } + destroy_n(from, n); + } +} + +template +constexpr void relocate_assign_n(T* to, T* from, isize_t n) +{ + if constexpr (trivially_move_assignable) + { + static_assert(trivially_destructible); + memcpy(to, from, size_of * n); + } + else + { + for (isize_t i = 0; i < n; ++i) + { + // NOLINTNEXTLINE(*-pointer-arithmetic) + to[i] = ASL_MOVE(from[i]); + } + destroy_n(from, n); + } +} + +} // namespace asl + diff --git a/asl/meta.hpp b/asl/meta.hpp index e77f4a7..9909be6 100644 --- a/asl/meta.hpp +++ b/asl/meta.hpp @@ -1,221 +1,221 @@ -#pragma once - -#include "asl/integers.hpp" - -namespace asl { - -struct source_location -{ - const char* file; - int line; - - explicit source_location( - const char* file_ = __builtin_FILE(), - int line_ = __builtin_LINE()) - : file{file_} - , line{line_} - {} -}; - -struct empty {}; - -template struct id { using type = T; }; - -template static constexpr isize_t types_count = sizeof...(Args); - -template struct integral_constant { static constexpr T value = kValue; }; -template using bool_constant = integral_constant; - -using true_type = bool_constant; -using false_type = bool_constant; - -template struct _select_helper { using type = V; }; -template struct _select_helper { using type = U; }; - -template using select_t = _select_helper::type; - -template struct _same_as_helper : false_type {}; -template struct _same_as_helper : true_type {}; - -template concept same_as = _same_as_helper::value && _same_as_helper::value; - -template auto _as_lref_helper(int) -> id; -template auto _as_lref_helper(...) -> id; - -template auto _as_rref_helper(int) -> id; -template auto _as_rref_helper(...) -> id; - -template using as_lref_t = decltype(_as_lref_helper(0))::type; -template using as_rref_t = decltype(_as_rref_helper(0))::type; - -template consteval as_rref_t declval() {} - -template struct _un_ref_t { using type = T; }; -template struct _un_ref_t { using type = T; }; -template struct _un_ref_t { using type = T; }; - -template using un_ref_t = _un_ref_t::type; - -template concept constructible_from = __is_constructible(T, Args...); - -template concept default_constructible = constructible_from; -template concept copy_constructible = constructible_from>; -template concept move_constructible = constructible_from>; - -template concept trivially_constructible_from = __is_trivially_constructible(T, Args...); - -template concept trivially_default_constructible = trivially_constructible_from; -template concept trivially_copy_constructible = trivially_constructible_from>; -template concept trivially_move_constructible = trivially_constructible_from>; - -template concept assignable_from = __is_assignable(T, Args...); - -template concept copy_assignable = assignable_from, as_lref_t>; -template concept move_assignable = assignable_from, as_rref_t>; - -template concept trivially_assignable_from = __is_trivially_assignable(T, Args...); - -template concept trivially_copy_assignable = trivially_assignable_from, as_lref_t>; -template concept trivially_move_assignable = trivially_assignable_from, as_rref_t>; - -template concept trivially_destructible = __is_trivially_destructible(T); - -template concept copyable = copy_constructible && copy_assignable; -template concept moveable = move_constructible && move_assignable; - -template -concept convertible_from = __is_convertible(From, To); - -template -concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from; - -using nullptr_t = decltype(nullptr); - -template struct _un_const_helper { using type = T; }; -template struct _un_const_helper { using type = T; }; - -template using un_const_t = _un_const_helper::type; - -template struct _is_const_helper : false_type {}; -template struct _is_const_helper : true_type {}; - -template concept is_const = _is_const_helper::value; - -template struct _un_volatile_helper { using type = T; }; -template struct _un_volatile_helper { using type = T; }; - -template using un_volatile_t = _un_volatile_helper::type; - -template using un_cv_t = un_volatile_t>; - -template using un_cvref_t = un_ref_t>; - -template concept is_void = same_as>; - -template struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = false; }; -template struct _is_ref_helper { static constexpr bool l = true; static constexpr bool r = false; }; -template struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = true; }; - -template concept is_ref = _is_ref_helper::l || _is_ref_helper::r; - -template struct _is_ptr_helper : false_type {}; -template struct _is_ptr_helper : true_type {}; - -template concept is_ptr = _is_ptr_helper>::value; - -template struct _tame_helper { using type = T; }; - -#define TAME_HELPER_IMPL(TRAILING) \ - template \ - struct _tame_helper { using type = R(Args...); } - -TAME_HELPER_IMPL(); -TAME_HELPER_IMPL(&); -TAME_HELPER_IMPL(&&); -TAME_HELPER_IMPL(const); -TAME_HELPER_IMPL(const &); -TAME_HELPER_IMPL(const &&); -TAME_HELPER_IMPL(volatile); -TAME_HELPER_IMPL(volatile &); -TAME_HELPER_IMPL(volatile &&); -TAME_HELPER_IMPL(const volatile); -TAME_HELPER_IMPL(const volatile &); -TAME_HELPER_IMPL(const volatile &&); -TAME_HELPER_IMPL(noexcept); -TAME_HELPER_IMPL(& noexcept); -TAME_HELPER_IMPL(&& noexcept); -TAME_HELPER_IMPL(const noexcept); -TAME_HELPER_IMPL(const & noexcept); -TAME_HELPER_IMPL(const && noexcept); -TAME_HELPER_IMPL(volatile noexcept); -TAME_HELPER_IMPL(volatile & noexcept); -TAME_HELPER_IMPL(volatile && noexcept); -TAME_HELPER_IMPL(const volatile noexcept); -TAME_HELPER_IMPL(const volatile & noexcept); -TAME_HELPER_IMPL(const volatile && noexcept); - -#undef TAME_HELPER_IMPL - -template using tame_t = _tame_helper::type; - -template struct _is_func_helper : false_type {}; -template struct _is_func_helper : true_type {}; - -template concept is_func = _is_func_helper>::value; - -template concept is_object = !is_void && !is_ref && !is_func; - -template struct _is_array_helper : false_type {}; -template struct _is_array_helper : true_type {}; -template struct _is_array_helper : true_type {}; - -template concept is_array = _is_array_helper::value; - -template struct _is_floating_point_helper : false_type {}; -template<> struct _is_floating_point_helper : true_type {}; -template<> struct _is_floating_point_helper : true_type {}; - -template concept is_floating_point = _is_floating_point_helper>::value; - -template struct _is_integer_helper : false_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; -template<> struct _is_integer_helper : true_type {}; - -template concept is_integer = _is_integer_helper>::value; - -template concept is_enum = __is_enum(T); - -template struct is_uniquely_represented : false_type {}; -template struct is_uniquely_represented : true_type {}; -template struct is_uniquely_represented : true_type {}; -template<> struct is_uniquely_represented : true_type {}; -template<> struct is_uniquely_represented : true_type {}; - -template concept uniquely_represented = is_uniquely_represented>::value; - -template -concept equality_comparable_with = requires (const un_cvref_t& a, const un_cvref_t& b) -{ - { a == b } -> same_as; - { b == a } -> same_as; - { a != b } -> same_as; - { b != a } -> same_as; -}; - -template concept equality_comparable = equality_comparable_with; - -struct niche_t {}; - -template -concept has_niche = constructible_from && equality_comparable_with; - -template -concept is_niche = same_as, niche_t>; - -} // namespace asl +#pragma once + +#include "asl/integers.hpp" + +namespace asl { + +struct source_location +{ + const char* file; + int line; + + explicit source_location( + const char* file_ = __builtin_FILE(), + int line_ = __builtin_LINE()) + : file{file_} + , line{line_} + {} +}; + +struct empty {}; + +template struct id { using type = T; }; + +template static constexpr isize_t types_count = sizeof...(Args); + +template struct integral_constant { static constexpr T value = kValue; }; +template using bool_constant = integral_constant; + +using true_type = bool_constant; +using false_type = bool_constant; + +template struct _select_helper { using type = V; }; +template struct _select_helper { using type = U; }; + +template using select_t = _select_helper::type; + +template struct _same_as_helper : false_type {}; +template struct _same_as_helper : true_type {}; + +template concept same_as = _same_as_helper::value && _same_as_helper::value; + +template auto _as_lref_helper(int) -> id; +template auto _as_lref_helper(...) -> id; + +template auto _as_rref_helper(int) -> id; +template auto _as_rref_helper(...) -> id; + +template using as_lref_t = decltype(_as_lref_helper(0))::type; +template using as_rref_t = decltype(_as_rref_helper(0))::type; + +template consteval as_rref_t declval() {} + +template struct _un_ref_t { using type = T; }; +template struct _un_ref_t { using type = T; }; +template struct _un_ref_t { using type = T; }; + +template using un_ref_t = _un_ref_t::type; + +template concept constructible_from = __is_constructible(T, Args...); + +template concept default_constructible = constructible_from; +template concept copy_constructible = constructible_from>; +template concept move_constructible = constructible_from>; + +template concept trivially_constructible_from = __is_trivially_constructible(T, Args...); + +template concept trivially_default_constructible = trivially_constructible_from; +template concept trivially_copy_constructible = trivially_constructible_from>; +template concept trivially_move_constructible = trivially_constructible_from>; + +template concept assignable_from = __is_assignable(T, Args...); + +template concept copy_assignable = assignable_from, as_lref_t>; +template concept move_assignable = assignable_from, as_rref_t>; + +template concept trivially_assignable_from = __is_trivially_assignable(T, Args...); + +template concept trivially_copy_assignable = trivially_assignable_from, as_lref_t>; +template concept trivially_move_assignable = trivially_assignable_from, as_rref_t>; + +template concept trivially_destructible = __is_trivially_destructible(T); + +template concept copyable = copy_constructible && copy_assignable; +template concept moveable = move_constructible && move_assignable; + +template +concept convertible_from = __is_convertible(From, To); + +template +concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from; + +using nullptr_t = decltype(nullptr); + +template struct _un_const_helper { using type = T; }; +template struct _un_const_helper { using type = T; }; + +template using un_const_t = _un_const_helper::type; + +template struct _is_const_helper : false_type {}; +template struct _is_const_helper : true_type {}; + +template concept is_const = _is_const_helper::value; + +template struct _un_volatile_helper { using type = T; }; +template struct _un_volatile_helper { using type = T; }; + +template using un_volatile_t = _un_volatile_helper::type; + +template using un_cv_t = un_volatile_t>; + +template using un_cvref_t = un_ref_t>; + +template concept is_void = same_as>; + +template struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = false; }; +template struct _is_ref_helper { static constexpr bool l = true; static constexpr bool r = false; }; +template struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = true; }; + +template concept is_ref = _is_ref_helper::l || _is_ref_helper::r; + +template struct _is_ptr_helper : false_type {}; +template struct _is_ptr_helper : true_type {}; + +template concept is_ptr = _is_ptr_helper>::value; + +template struct _tame_helper { using type = T; }; + +#define TAME_HELPER_IMPL(TRAILING) \ + template \ + struct _tame_helper { using type = R(Args...); } + +TAME_HELPER_IMPL(); +TAME_HELPER_IMPL(&); +TAME_HELPER_IMPL(&&); +TAME_HELPER_IMPL(const); +TAME_HELPER_IMPL(const &); +TAME_HELPER_IMPL(const &&); +TAME_HELPER_IMPL(volatile); +TAME_HELPER_IMPL(volatile &); +TAME_HELPER_IMPL(volatile &&); +TAME_HELPER_IMPL(const volatile); +TAME_HELPER_IMPL(const volatile &); +TAME_HELPER_IMPL(const volatile &&); +TAME_HELPER_IMPL(noexcept); +TAME_HELPER_IMPL(& noexcept); +TAME_HELPER_IMPL(&& noexcept); +TAME_HELPER_IMPL(const noexcept); +TAME_HELPER_IMPL(const & noexcept); +TAME_HELPER_IMPL(const && noexcept); +TAME_HELPER_IMPL(volatile noexcept); +TAME_HELPER_IMPL(volatile & noexcept); +TAME_HELPER_IMPL(volatile && noexcept); +TAME_HELPER_IMPL(const volatile noexcept); +TAME_HELPER_IMPL(const volatile & noexcept); +TAME_HELPER_IMPL(const volatile && noexcept); + +#undef TAME_HELPER_IMPL + +template using tame_t = _tame_helper::type; + +template struct _is_func_helper : false_type {}; +template struct _is_func_helper : true_type {}; + +template concept is_func = _is_func_helper>::value; + +template concept is_object = !is_void && !is_ref && !is_func; + +template struct _is_array_helper : false_type {}; +template struct _is_array_helper : true_type {}; +template struct _is_array_helper : true_type {}; + +template concept is_array = _is_array_helper::value; + +template struct _is_floating_point_helper : false_type {}; +template<> struct _is_floating_point_helper : true_type {}; +template<> struct _is_floating_point_helper : true_type {}; + +template concept is_floating_point = _is_floating_point_helper>::value; + +template struct _is_integer_helper : false_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; +template<> struct _is_integer_helper : true_type {}; + +template concept is_integer = _is_integer_helper>::value; + +template concept is_enum = __is_enum(T); + +template struct is_uniquely_represented : false_type {}; +template struct is_uniquely_represented : true_type {}; +template struct is_uniquely_represented : true_type {}; +template<> struct is_uniquely_represented : true_type {}; +template<> struct is_uniquely_represented : true_type {}; + +template concept uniquely_represented = is_uniquely_represented>::value; + +template +concept equality_comparable_with = requires (const un_cvref_t& a, const un_cvref_t& b) +{ + { a == b } -> same_as; + { b == a } -> same_as; + { a != b } -> same_as; + { b != a } -> same_as; +}; + +template concept equality_comparable = equality_comparable_with; + +struct niche_t {}; + +template +concept has_niche = constructible_from && equality_comparable_with; + +template +concept is_niche = same_as, niche_t>; + +} // namespace asl diff --git a/asl/option.hpp b/asl/option.hpp index f7a3eab..d12d4ce 100644 --- a/asl/option.hpp +++ b/asl/option.hpp @@ -1,509 +1,509 @@ -#pragma once - -#include "asl/assert.hpp" -#include "asl/meta.hpp" -#include "asl/maybe_uninit.hpp" -#include "asl/functional.hpp" -#include "asl/annotations.hpp" -#include "asl/hash.hpp" - -namespace asl -{ - -struct nullopt_t {}; -static constexpr nullopt_t nullopt{}; - -// @Todo(option) Reference -// @Todo(option) Function -// @Todo(option) Arrays - -template class option; - -namespace option_internal -{ - -template -concept not_constructible_from_option = - !constructible_from&> && - !constructible_from&> && - !constructible_from&&> && - !constructible_from&&>; - -template -concept not_assignable_from_option = - !assignable_from&> && - !assignable_from&> && - !assignable_from&&> && - !assignable_from&&>; - -template -concept not_constructible_assignable_from_option = - not_constructible_from_option && - not_assignable_from_option; - -} // namespace option_internal - -template -concept is_option = requires -{ - typename T::type; - requires same_as, option>; -}; - -template -class option -{ - static constexpr bool kHasNiche = has_niche; - - using HasValueMarker = select_t; - - maybe_uninit m_payload{}; - ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{}; - - template - friend class option; - - template - constexpr void construct(Args&&... args) - { - ASL_ASSERT(!has_value()); - - if constexpr (!kHasNiche) - { - m_payload.construct_unsafe(ASL_FWD(args)...); - m_has_value = true; - } - else - { - if constexpr (move_assignable) - { - m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...})); - } - else - { - m_payload.destroy_unsafe(); - m_payload.construct_unsafe(ASL_FWD(args)...); - } - } - } - - template - constexpr void assign(U&& arg) - { - ASL_ASSERT(has_value()); - m_payload.assign_unsafe(ASL_FWD(arg)); - } - -public: - using type = T; - - constexpr option() : option{nullopt} {} - - // NOLINTNEXTLINE(*-explicit-conversions) - constexpr option(nullopt_t) requires (!kHasNiche) {} - - // NOLINTNEXTLINE(*-explicit-conversions) - constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {} - - template - constexpr explicit (!convertible_from) - option(U&& value) - requires ( - kHasNiche && - constructible_from && - !same_as, option> - ) - : m_payload{in_place, ASL_FWD(value)} - {} - - template - constexpr explicit (!convertible_from) - option(U&& value) - requires ( - !kHasNiche && - constructible_from && - !is_option - ) - : m_payload{in_place, ASL_FWD(value)} - , m_has_value{true} - {} - - constexpr option(const option& other) requires trivially_copy_constructible = default; - constexpr option(const option& other) requires (!copy_constructible) = delete; - - constexpr option(const option& other) - requires copy_constructible && (!trivially_copy_constructible) - : option{nullopt} - { - if (other.has_value()) - { - construct(other.m_payload.as_init_unsafe()); - } - } - - constexpr option(option&& other) requires trivially_move_constructible = default; - constexpr option(option&& other) requires (!move_constructible) = delete; - - constexpr option(option&& other) - requires move_constructible && (!trivially_move_constructible) - : option{nullopt} - { - if (other.has_value()) - { - construct(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - } - - template - constexpr explicit (!convertible_from) - option(const option& other) - requires ( - constructible_from && - option_internal::not_constructible_from_option - ) - : option{nullopt} - { - if (other.has_value()) - { - construct(other.m_payload.as_init_unsafe()); - } - } - - template - constexpr explicit (!convertible_from) - option(option&& other) - requires ( - constructible_from && - option_internal::not_constructible_from_option - ) - : option{nullopt} - { - if (other.has_value()) - { - construct(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - } - - constexpr option& operator=(nullopt_t) & - { - reset(); - return *this; - } - - template - constexpr option& operator=(U&& value) & - requires ( - assignable_from && - constructible_from && - !is_option - ) - { - if (has_value()) - { - assign(ASL_FWD(value)); - } - else - { - construct(ASL_FWD(value)); - } - - return *this; - } - - constexpr option& operator=(const option& other) & - requires (!copy_assignable) = delete; - - constexpr option& operator=(const option& other) & - requires trivially_copy_assignable = default; - - constexpr option& operator=(const option& other) & - requires copy_assignable && (!trivially_copy_constructible) - { - if (&other == this) { return *this; } - - if (other.has_value()) - { - if (has_value()) - { - assign(other.m_payload.as_init_unsafe()); - } - else - { - construct(other.m_payload.as_init_unsafe()); - } - } - else if (has_value()) - { - reset(); - } - - return *this; - } - - constexpr option& operator=(option&& other) & - requires (!move_assignable) = delete; - - constexpr option& operator=(option&& other) & - requires trivially_move_assignable = default; - - constexpr option& operator=(option&& other) & - requires move_assignable && (!trivially_move_constructible) - { - if (&other == this) { return *this; } - - if (other.has_value()) - { - if (has_value()) - { - assign(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - else - { - construct(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - } - else if (has_value()) - { - reset(); - } - - return *this; - } - - template - constexpr option& operator=(const option& other) & - requires ( - constructible_from && - assignable_from && +#pragma once + +#include "asl/assert.hpp" +#include "asl/meta.hpp" +#include "asl/maybe_uninit.hpp" +#include "asl/functional.hpp" +#include "asl/annotations.hpp" +#include "asl/hash.hpp" + +namespace asl +{ + +struct nullopt_t {}; +static constexpr nullopt_t nullopt{}; + +// @Todo(option) Reference +// @Todo(option) Function +// @Todo(option) Arrays + +template class option; + +namespace option_internal +{ + +template +concept not_constructible_from_option = + !constructible_from&> && + !constructible_from&> && + !constructible_from&&> && + !constructible_from&&>; + +template +concept not_assignable_from_option = + !assignable_from&> && + !assignable_from&> && + !assignable_from&&> && + !assignable_from&&>; + +template +concept not_constructible_assignable_from_option = + not_constructible_from_option && + not_assignable_from_option; + +} // namespace option_internal + +template +concept is_option = requires +{ + typename T::type; + requires same_as, option>; +}; + +template +class option +{ + static constexpr bool kHasNiche = has_niche; + + using HasValueMarker = select_t; + + maybe_uninit m_payload{}; + ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{}; + + template + friend class option; + + template + constexpr void construct(Args&&... args) + { + ASL_ASSERT(!has_value()); + + if constexpr (!kHasNiche) + { + m_payload.construct_unsafe(ASL_FWD(args)...); + m_has_value = true; + } + else + { + if constexpr (move_assignable) + { + m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...})); + } + else + { + m_payload.destroy_unsafe(); + m_payload.construct_unsafe(ASL_FWD(args)...); + } + } + } + + template + constexpr void assign(U&& arg) + { + ASL_ASSERT(has_value()); + m_payload.assign_unsafe(ASL_FWD(arg)); + } + +public: + using type = T; + + constexpr option() : option{nullopt} {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires (!kHasNiche) {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {} + + template + constexpr explicit (!convertible_from) + option(U&& value) + requires ( + kHasNiche && + constructible_from && + !same_as, option> + ) + : m_payload{in_place, ASL_FWD(value)} + {} + + template + constexpr explicit (!convertible_from) + option(U&& value) + requires ( + !kHasNiche && + constructible_from && + !is_option + ) + : m_payload{in_place, ASL_FWD(value)} + , m_has_value{true} + {} + + constexpr option(const option& other) requires trivially_copy_constructible = default; + constexpr option(const option& other) requires (!copy_constructible) = delete; + + constexpr option(const option& other) + requires copy_constructible && (!trivially_copy_constructible) + : option{nullopt} + { + if (other.has_value()) + { + construct(other.m_payload.as_init_unsafe()); + } + } + + constexpr option(option&& other) requires trivially_move_constructible = default; + constexpr option(option&& other) requires (!move_constructible) = delete; + + constexpr option(option&& other) + requires move_constructible && (!trivially_move_constructible) + : option{nullopt} + { + if (other.has_value()) + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + + template + constexpr explicit (!convertible_from) + option(const option& other) + requires ( + constructible_from && + option_internal::not_constructible_from_option + ) + : option{nullopt} + { + if (other.has_value()) + { + construct(other.m_payload.as_init_unsafe()); + } + } + + template + constexpr explicit (!convertible_from) + option(option&& other) + requires ( + constructible_from && + option_internal::not_constructible_from_option + ) + : option{nullopt} + { + if (other.has_value()) + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + + constexpr option& operator=(nullopt_t) & + { + reset(); + return *this; + } + + template + constexpr option& operator=(U&& value) & + requires ( + assignable_from && + constructible_from && + !is_option + ) + { + if (has_value()) + { + assign(ASL_FWD(value)); + } + else + { + construct(ASL_FWD(value)); + } + + return *this; + } + + constexpr option& operator=(const option& other) & + requires (!copy_assignable) = delete; + + constexpr option& operator=(const option& other) & + requires trivially_copy_assignable = default; + + constexpr option& operator=(const option& other) & + requires copy_assignable && (!trivially_copy_constructible) + { + if (&other == this) { return *this; } + + if (other.has_value()) + { + if (has_value()) + { + assign(other.m_payload.as_init_unsafe()); + } + else + { + construct(other.m_payload.as_init_unsafe()); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + constexpr option& operator=(option&& other) & + requires (!move_assignable) = delete; + + constexpr option& operator=(option&& other) & + requires trivially_move_assignable = default; + + constexpr option& operator=(option&& other) & + requires move_assignable && (!trivially_move_constructible) + { + if (&other == this) { return *this; } + + if (other.has_value()) + { + if (has_value()) + { + assign(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + else + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + template + constexpr option& operator=(const option& other) & + requires ( + constructible_from && + assignable_from && option_internal::not_constructible_assignable_from_option - ) - { - if (other.has_value()) - { - if (has_value()) - { - assign(other.m_payload.as_init_unsafe()); - } - else - { - construct(other.m_payload.as_init_unsafe()); - } - } - else if (has_value()) - { - reset(); - } - - return *this; - } - - template - constexpr option& operator=(option&& other) & - requires ( - constructible_from && - assignable_from && - option_internal::not_constructible_assignable_from_option - ) - { - if (other.has_value()) - { - if (has_value()) - { - assign(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - else - { - construct(ASL_MOVE(other.m_payload.as_init_unsafe())); - } - } - else if (has_value()) - { - reset(); - } - - return *this; - } - - constexpr ~option() requires trivially_destructible = default; - constexpr ~option() requires (!trivially_destructible) - { - reset(); - } - - constexpr void reset() - { - if (!has_value()) { return; } - - if constexpr (kHasNiche) - { - if constexpr (move_assignable) - { - m_payload.assign_unsafe(ASL_MOVE(T{niche_t{}})); - } - else - { - m_payload.destroy_unsafe(); - m_payload.construct_unsafe(niche_t{}); - } - } - else - { - m_has_value = false; - m_payload.destroy_unsafe(); - } - } - - constexpr bool has_value() const - { - if constexpr (kHasNiche) - { - return m_payload.as_init_unsafe() != niche_t{}; - } - else - { - return m_has_value; - } - } - - // @Todo(C++23) Deducing this - constexpr T&& value() && - { - ASL_ASSERT_RELEASE(has_value()); - return ASL_MOVE(m_payload).as_init_unsafe(); - } - - constexpr T& value() & - { - ASL_ASSERT_RELEASE(has_value()); - return m_payload.as_init_unsafe(); - } - - constexpr const T& value() const& - { - ASL_ASSERT_RELEASE(has_value()); - return m_payload.as_init_unsafe(); - } - - template - constexpr T value_or(U&& other_value) const& - requires copy_constructible && convertible_from - { - return has_value() ? value() : static_cast(ASL_FWD(other_value)); - } - - template - constexpr T value_or(U&& other_value) && - requires move_constructible && convertible_from - { - return has_value() ? ASL_MOVE(value()) : static_cast(ASL_FWD(other_value)); - } - - template - constexpr T& emplace(Args&&... args) & - requires constructible_from - { - if (has_value()) { reset(); } - construct(ASL_FWD(args)...); - return value(); - } - - template - constexpr auto and_then(F&& f) & - requires is_option> - { - if (has_value()) - { - return invoke(ASL_FWD(f), value()); - } - return un_cvref_t>{}; - } - - template - constexpr auto and_then(F&& f) const& - requires is_option> - { - if (has_value()) - { - return invoke(ASL_FWD(f), value()); - } - return un_cvref_t>{}; - } - - template - constexpr auto and_then(F&& f) && - requires is_option> - { - if (has_value()) - { - return invoke(ASL_FWD(f), ASL_MOVE(value())); - } - return un_cvref_t>{}; - } - - template - constexpr auto transform(F&& f) & - { - using U = un_cvref_t>; - if (has_value()) - { - return option{ invoke(ASL_FWD(f), value()) }; - } - return option{}; - } - - template - constexpr auto transform(F&& f) const& - { - using U = un_cvref_t>; - if (has_value()) - { - return option{ invoke(ASL_FWD(f), value()) }; - } - return option{}; - } - - template - constexpr auto transform(F&& f) && - { - using U = un_cvref_t>; - if (has_value()) - { - return option{ invoke(ASL_FWD(f), ASL_MOVE(value())) }; - } - return option{}; - } - - template - constexpr option or_else(F&& f) const& - requires same_as>, option> - { - return has_value() ? *this : invoke(ASL_FWD(f)); - } - - template - constexpr option or_else(F&& f) && - requires same_as>, option> - { - return has_value() ? ASL_MOVE(*this) : invoke(ASL_FWD(f)); - } - - template - requires (!uniquely_represented