Fix line endings

This commit is contained in:
2025-01-26 00:40:51 +01:00
parent 79aaec3d7d
commit cf7db48c26
64 changed files with 6490 additions and 6412 deletions

View File

@ -1,44 +1,44 @@
startup --windows_enable_symlinks startup --windows_enable_symlinks
build:windows --enable_runfiles=true build:windows --enable_runfiles=true
build --build_python_zip=false build --build_python_zip=false
build:windows --extra_execution_platforms=//:x64_windows-clang-cl build:windows --extra_execution_platforms=//:x64_windows-clang-cl
# @Todo(bazel) We should be able to use @local_config_cc... # @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:windows --extra_toolchains=@@rules_cc++cc_configure_extension+local_config_cc//:cc-toolchain-x64_windows-clang-cl
build:linux --repo_env=CC=clang build:linux --repo_env=CC=clang
build:windows --cxxopt=-Xclang=-std=c++20 build:windows --cxxopt=-Xclang=-std=c++20
build:linux --cxxopt=-std=c++20 build:linux --cxxopt=-std=c++20
build --cxxopt=-Wall build --cxxopt=-Wall
build --cxxopt=-Wno-c++98-compat build --cxxopt=-Wno-c++98-compat
build --cxxopt=-Wno-c++98-compat-pedantic build --cxxopt=-Wno-c++98-compat-pedantic
build --cxxopt=-Wno-pre-c++17-compat build --cxxopt=-Wno-pre-c++17-compat
build --cxxopt=-Wno-c++20-compat build --cxxopt=-Wno-c++20-compat
build --cxxopt=-Wno-unused-macros build --cxxopt=-Wno-unused-macros
build --cxxopt=-Wno-documentation-unknown-command build --cxxopt=-Wno-documentation-unknown-command
build --cxxopt=-Wno-extra-semi-stmt build --cxxopt=-Wno-extra-semi-stmt
build --cxxopt=-Wno-extra-semi build --cxxopt=-Wno-extra-semi
build --cxxopt=-Wno-global-constructors build --cxxopt=-Wno-global-constructors
build --cxxopt=-Wno-unsafe-buffer-usage build --cxxopt=-Wno-unsafe-buffer-usage
build --cxxopt=-Wno-covered-switch-default build --cxxopt=-Wno-covered-switch-default
build:windows_san --config=windows build:windows_san --config=windows
build:windows_san --copt=-fno-sanitize-ignorelist build:windows_san --copt=-fno-sanitize-ignorelist
build:windows_san --copt=-fsanitize=address build:windows_san --copt=-fsanitize=address
build:windows_san --copt=-fsanitize=undefined build:windows_san --copt=-fsanitize=undefined
build:windows_san --copt=-fno-sanitize-recover=all build:windows_san --copt=-fno-sanitize-recover=all
build:windows_san --linkopt=clang_rt.asan-x86_64.lib build:windows_san --linkopt=clang_rt.asan-x86_64.lib
build:windows_san --copt=/MT build:windows_san --copt=/MT
build:linux_san --config=linux build:linux_san --config=linux
build:linux_san --copt=-fsanitize=address build:linux_san --copt=-fsanitize=address
build:linux_san --linkopt=-fsanitize=address build:linux_san --linkopt=-fsanitize=address
build:linux_san --copt=-fsanitize=undefined build:linux_san --copt=-fsanitize=undefined
build:linux_san --copt=-fno-sanitize-recover=all build:linux_san --copt=-fno-sanitize-recover=all
build:linux_san --linkopt=-fsanitize=undefined build:linux_san --linkopt=-fsanitize=undefined
build:linux_san --linkopt=-fsanitize-link-c++-runtime build:linux_san --linkopt=-fsanitize-link-c++-runtime
test --test_output=errors test --test_output=errors

View File

@ -1,34 +1,34 @@
Checks: Checks:
- "hicpp-*" - "hicpp-*"
- "cppcoreguidelines-*" - "cppcoreguidelines-*"
- "misc-*" - "misc-*"
- "clang-analyzer-*" - "clang-analyzer-*"
- "-misc-include-cleaner" - "-misc-include-cleaner"
- "performance-*" - "performance-*"
- "readability-*" - "readability-*"
- "-*-named-parameter" - "-*-named-parameter"
- "-*-avoid-do-while" - "-*-avoid-do-while"
- "-*-magic-numbers" - "-*-magic-numbers"
- "-*-identifier-length" - "-*-identifier-length"
- "-*-union-access" - "-*-union-access"
- "-*-vararg" - "-*-vararg"
- "-*-macro-usage" - "-*-macro-usage"
- "-*-non-private-member-variables-in-classes" - "-*-non-private-member-variables-in-classes"
- "-*-avoid-non-const-global-variables" - "-*-avoid-non-const-global-variables"
- "-*-missing-std-forward" - "-*-missing-std-forward"
- "-*-owning-memory" - "-*-owning-memory"
- "-*-no-malloc" - "-*-no-malloc"
- "-*-avoid-c-arrays" - "-*-avoid-c-arrays"
- "-*-use-anonymous-namespace" - "-*-use-anonymous-namespace"
- "-*-reinterpret-cast" - "-*-reinterpret-cast"
- "-*-noexcept-swap" - "-*-noexcept-swap"
- "-*-noexcept-move" - "-*-noexcept-move"
- "-*-noexcept-move-constructor" - "-*-noexcept-move-constructor"
- "-*-noexcept-move-operations" - "-*-noexcept-move-operations"
- "-*-bounds-array-to-pointer-decay" - "-*-bounds-array-to-pointer-decay"
- "-*-no-array-decay" - "-*-no-array-decay"
- "-*-signed-bitwise" - "-*-signed-bitwise"
- "-readability-use-anyofallof" - "-readability-use-anyofallof"
- "-readability-function-cognitive-complexity" - "-readability-function-cognitive-complexity"
- "-readability-math-missing-parentheses" - "-readability-math-missing-parentheses"
- "-*-rvalue-reference-param-not-moved" - "-*-rvalue-reference-param-not-moved"

6
.gitignore vendored
View File

@ -1,2 +1,4 @@
bazel-*/ bazel-*/
compile_commands.json compile_commands.json
.cache/
external/

View File

@ -1,18 +1,18 @@
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
refresh_compile_commands( refresh_compile_commands(
name = "refresh_clangd", name = "refresh_clangd",
targets = "//...", targets = "//...",
) )
platform( platform(
name = "x64_windows-clang-cl", name = "x64_windows-clang-cl",
constraint_values = [ constraint_values = [
"@platforms//cpu:x86_64", "@platforms//cpu:x86_64",
"@platforms//os:windows", "@platforms//os:windows",
# @Todo(bazel) Bit weird to use a private thing. # @Todo(bazel) Bit weird to use a private thing.
# We used to use @bazel_tools//tools/cpp:clang-cl but it's deprecated # We used to use @bazel_tools//tools/cpp:clang-cl but it's deprecated
# in favor of... this?... # in favor of... this?...
"@rules_cc//cc/private/toolchain:clang-cl", "@rules_cc//cc/private/toolchain:clang-cl",
], ],
) )

View File

@ -1,15 +1,15 @@
module(name = "asl") module(name = "asl")
bazel_dep(name = "platforms", version = "0.0.10") bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_cc", version = "0.0.17") bazel_dep(name = "rules_cc", version = "0.0.17")
bazel_dep(name = "hedron_compile_commands", dev_dependency = True) bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override( git_override(
module_name = "hedron_compile_commands", module_name = "hedron_compile_commands",
remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
commit = "4f28899228fb3ad0126897876f147ca15026151e", commit = "4f28899228fb3ad0126897876f147ca15026151e",
) )
bazel_dep(name = "rules_python", version = "1.1.0", dev_dependency = True) bazel_dep(name = "rules_python", version = "1.1.0", dev_dependency = True)
python = use_extension("@rules_python//python/extensions:python.bzl", "python") python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.13", is_default = True) python.toolchain(python_version = "3.13", is_default = True)

View File

@ -1,78 +1,78 @@
cc_library( cc_library(
name = "asl", name = "asl",
hdrs = [ hdrs = [
"allocator.hpp", "allocator.hpp",
"annotations.hpp", "annotations.hpp",
"assert.hpp", "assert.hpp",
"atomic.hpp", "atomic.hpp",
"box.hpp", "box.hpp",
"buffer.hpp", "buffer.hpp",
"config.hpp", "config.hpp",
"float.hpp", "float.hpp",
"format.hpp", "format.hpp",
"functional.hpp", "functional.hpp",
"hash.hpp", "hash.hpp",
"hash_map.hpp", "hash_map.hpp",
"hash_set.hpp", "hash_set.hpp",
"integers.hpp", "integers.hpp",
"io.hpp", "io.hpp",
"layout.hpp", "layout.hpp",
"maybe_uninit.hpp", "maybe_uninit.hpp",
"memory.hpp", "memory.hpp",
"meta.hpp", "meta.hpp",
"option.hpp", "option.hpp",
"print.hpp", "print.hpp",
"span.hpp", "span.hpp",
"status.hpp", "status.hpp",
"status_or.hpp", "status_or.hpp",
"string.hpp", "string.hpp",
"string_builder.hpp", "string_builder.hpp",
"string_view.hpp", "string_view.hpp",
"utility.hpp", "utility.hpp",
], ],
srcs = [ srcs = [
"allocator.cpp", "allocator.cpp",
"assert.cpp", "assert.cpp",
"format.cpp", "format.cpp",
"format_float.cpp", "format_float.cpp",
"hash_cityhash.cpp", "hash_cityhash.cpp",
"print.cpp", "print.cpp",
"status.cpp", "status.cpp",
], ],
deps = [ deps = [
"//vendor/dragonbox", "//vendor/dragonbox",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
[cc_test( [cc_test(
name = "%s_tests" % name, name = "%s_tests" % name,
srcs = [ srcs = [
"tests/%s_tests.cpp" % name, "tests/%s_tests.cpp" % name,
"tests/test_types.hpp", "tests/test_types.hpp",
], ],
deps = [ deps = [
":asl", ":asl",
"//asl/testing", "//asl/testing",
], ],
) for name in [ ) for name in [
"box", "box",
"buffer", "buffer",
"float", "float",
"format", "format",
"functional", "functional",
"hash", "hash",
"hash_map", "hash_map",
"hash_set", "hash_set",
"integers", "integers",
"maybe_uninit", "maybe_uninit",
"meta", "meta",
"option", "option",
"span", "span",
"status", "status",
"status_or", "status_or",
"string", "string",
"string_builder", "string_builder",
"string_view", "string_view",
"utility", "utility",
]] ]]

View File

@ -1,56 +1,56 @@
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/assert.hpp" #include "asl/assert.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
#include "asl/print.hpp" #include "asl/print.hpp"
#include <cstdlib> #include <cstdlib>
// @Todo zalloc // @Todo zalloc
// @Todo Cookies // @Todo Cookies
// @Todo Debug values // @Todo Debug values
void* asl::GlobalHeap::alloc(const layout& layout) void* asl::GlobalHeap::alloc(const layout& layout)
{ {
#if ASL_OS_WINDOWS #if ASL_OS_WINDOWS
void* ptr = ::_aligned_malloc( void* ptr = ::_aligned_malloc(
static_cast<size_t>(layout.size), static_cast<size_t>(layout.size),
static_cast<size_t>(layout.align)); static_cast<size_t>(layout.align));
#elif ASL_OS_LINUX #elif ASL_OS_LINUX
void* ptr = ::aligned_alloc( void* ptr = ::aligned_alloc(
static_cast<size_t>(layout.align), static_cast<size_t>(layout.align),
static_cast<size_t>(layout.size)); static_cast<size_t>(layout.size));
#endif #endif
ASL_ASSERT(ptr != nullptr); // @Todo panic ASL_ASSERT(ptr != nullptr); // @Todo panic
return ptr; return ptr;
} }
void* asl::GlobalHeap::realloc(void* old_ptr, [[maybe_unused]] const layout& old_layout, const layout& new_layout) void* asl::GlobalHeap::realloc(void* old_ptr, [[maybe_unused]] const layout& old_layout, const layout& new_layout)
{ {
#if ASL_OS_WINDOWS #if ASL_OS_WINDOWS
return ::_aligned_realloc(old_ptr, return ::_aligned_realloc(old_ptr,
static_cast<size_t>(new_layout.size), static_cast<size_t>(new_layout.size),
static_cast<size_t>(new_layout.align)); static_cast<size_t>(new_layout.align));
#elif ASL_OS_LINUX #elif ASL_OS_LINUX
if (new_layout.align <= old_layout.align) if (new_layout.align <= old_layout.align)
{ {
void* new_ptr = ::realloc(old_ptr, static_cast<size_t>(new_layout.size)); void* new_ptr = ::realloc(old_ptr, static_cast<size_t>(new_layout.size));
ASL_ASSERT(new_ptr != nullptr); // @Todo panic ASL_ASSERT(new_ptr != nullptr); // @Todo panic
return new_ptr; return new_ptr;
} }
void* new_ptr = alloc(new_layout); void* new_ptr = alloc(new_layout);
asl::memcpy(new_ptr, old_ptr, asl::min(old_layout.size, new_layout.size)); asl::memcpy(new_ptr, old_ptr, asl::min(old_layout.size, new_layout.size));
dealloc(old_ptr, old_layout); dealloc(old_ptr, old_layout);
return new_ptr; return new_ptr;
#endif #endif
} }
void asl::GlobalHeap::dealloc(void* ptr, const layout&) void asl::GlobalHeap::dealloc(void* ptr, const layout&)
{ {
#if ASL_OS_WINDOWS #if ASL_OS_WINDOWS
::_aligned_free(ptr); ::_aligned_free(ptr);
#elif ASL_OS_LINUX #elif ASL_OS_LINUX
::free(ptr); ::free(ptr);
#endif #endif
} }

View File

@ -1,58 +1,58 @@
#pragma once #pragma once
#include "asl/layout.hpp" #include "asl/layout.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
namespace asl namespace asl
{ {
template<typename T> template<typename T>
concept allocator = moveable<T> && equality_comparable<T> && concept allocator = moveable<T> && equality_comparable<T> &&
requires(T& alloc, layout layout, void* ptr) requires(T& alloc, layout layout, void* ptr)
{ {
{ alloc.alloc(layout) } -> same_as<void*>; { alloc.alloc(layout) } -> same_as<void*>;
{ alloc.realloc(ptr, layout, layout) } -> same_as<void*>; { alloc.realloc(ptr, layout, layout) } -> same_as<void*>;
alloc.dealloc(ptr, layout); alloc.dealloc(ptr, layout);
}; };
class GlobalHeap class GlobalHeap
{ {
public: public:
static void* alloc(const layout&); static void* alloc(const layout&);
static void* realloc(void* ptr, const layout& old, const layout& new_layout); static void* realloc(void* ptr, const layout& old, const layout& new_layout);
static void dealloc(void* ptr, const layout&); static void dealloc(void* ptr, const layout&);
constexpr bool operator==(const GlobalHeap&) const { return true; } constexpr bool operator==(const GlobalHeap&) const { return true; }
}; };
static_assert(allocator<GlobalHeap>); static_assert(allocator<GlobalHeap>);
using DefaultAllocator = GlobalHeap; using DefaultAllocator = GlobalHeap;
template<typename T> template<typename T>
T* alloc_new(allocator auto& a, auto&&... args) T* alloc_new(allocator auto& a, auto&&... args)
{ {
void* ptr = a.alloc(layout::of<T>()); void* ptr = a.alloc(layout::of<T>());
return construct_at<T>(ptr, ASL_FWD(args)...); return construct_at<T>(ptr, ASL_FWD(args)...);
} }
template<typename T> template<typename T>
void alloc_delete(allocator auto& a, T* ptr) void alloc_delete(allocator auto& a, T* ptr)
{ {
destroy(ptr); destroy(ptr);
a.dealloc(ptr, layout::of<T>()); a.dealloc(ptr, layout::of<T>());
} }
template<typename T> template<typename T>
constexpr T* alloc_new_default(auto&&... args) constexpr T* alloc_new_default(auto&&... args)
{ {
return alloc_new<T>(DefaultAllocator{}, ASL_FWD(args)...); return alloc_new<T>(DefaultAllocator{}, ASL_FWD(args)...);
} }
template<typename T> template<typename T>
void alloc_delete_default(T* ptr) void alloc_delete_default(T* ptr)
{ {
alloc_delete(DefaultAllocator{}, ptr); alloc_delete(DefaultAllocator{}, ptr);
} }
} // namespace asl } // namespace asl

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "asl/config.hpp" #include "asl/config.hpp"
#if ASL_COMPILER_CLANG_CL #if ASL_COMPILER_CLANG_CL
#define ASL_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #define ASL_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#elif ASL_COMPILER_CLANG #elif ASL_COMPILER_CLANG
#define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]] #define ASL_NO_UNIQUE_ADDRESS [[no_unique_address]]
#endif #endif

View File

@ -1,33 +1,33 @@
#pragma once #pragma once
#include "asl/config.hpp" #include "asl/config.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
namespace asl namespace asl
{ {
void report_assert_failure( const char* msg, const source_location& sl = source_location{}); void report_assert_failure( const char* msg, const source_location& sl = source_location{});
} // namespace asl } // namespace asl
#if ASL_COMPILER_CLANG_CL #if ASL_COMPILER_CLANG_CL
#define ASL_DEBUG_BREAK() __debugbreak() #define ASL_DEBUG_BREAK() __debugbreak()
#elif ASL_COMPILER_CLANG #elif ASL_COMPILER_CLANG
#define ASL_DEBUG_BREAK() __builtin_debugtrap() #define ASL_DEBUG_BREAK() __builtin_debugtrap()
#endif #endif
#define ASL_ASSERT(...) \ #define ASL_ASSERT(...) \
if (__VA_ARGS__) {} \ if (__VA_ARGS__) {} \
else \ else \
{ \ { \
::asl::report_assert_failure(#__VA_ARGS__); \ ::asl::report_assert_failure(#__VA_ARGS__); \
ASL_DEBUG_BREAK(); \ ASL_DEBUG_BREAK(); \
} }
#define ASL_ASSERT_RELEASE(...) \ #define ASL_ASSERT_RELEASE(...) \
if (__VA_ARGS__) {} \ if (__VA_ARGS__) {} \
else \ else \
{ \ { \
::asl::report_assert_failure(#__VA_ARGS__); \ ::asl::report_assert_failure(#__VA_ARGS__); \
ASL_DEBUG_BREAK(); \ ASL_DEBUG_BREAK(); \
} }

View File

@ -1,115 +1,115 @@
#pragma once #pragma once
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/assert.hpp" #include "asl/assert.hpp"
#include "asl/annotations.hpp" #include "asl/annotations.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
namespace asl namespace asl
{ {
template<is_object T, allocator Allocator = DefaultAllocator> template<is_object T, allocator Allocator = DefaultAllocator>
class box class box
{ {
T* m_ptr; T* m_ptr;
ASL_NO_UNIQUE_ADDRESS Allocator m_alloc; ASL_NO_UNIQUE_ADDRESS Allocator m_alloc;
public: public:
explicit constexpr box(niche_t) explicit constexpr box(niche_t)
requires default_constructible<Allocator> requires default_constructible<Allocator>
: m_ptr{nullptr} : m_ptr{nullptr}
, m_alloc{} , m_alloc{}
{} {}
constexpr box(T* ptr, Allocator alloc) constexpr box(T* ptr, Allocator alloc)
: m_ptr{ptr} : m_ptr{ptr}
, m_alloc{ASL_MOVE(alloc)} , m_alloc{ASL_MOVE(alloc)}
{ {
ASL_ASSERT(m_ptr != nullptr); ASL_ASSERT(m_ptr != nullptr);
} }
constexpr box(box&& other) constexpr box(box&& other)
: m_ptr{exchange(other.m_ptr, nullptr)} : m_ptr{exchange(other.m_ptr, nullptr)}
, m_alloc{ASL_MOVE(other.m_alloc)} , m_alloc{ASL_MOVE(other.m_alloc)}
{} {}
constexpr box& operator=(box&& other) constexpr box& operator=(box&& other)
{ {
if (this == &other) { return *this; } if (this == &other) { return *this; }
if (m_ptr != nullptr) { reset(); } if (m_ptr != nullptr) { reset(); }
m_ptr = exchange(other.m_ptr, nullptr); m_ptr = exchange(other.m_ptr, nullptr);
m_alloc = ASL_MOVE(other.m_alloc); m_alloc = ASL_MOVE(other.m_alloc);
return *this; return *this;
} }
box(const box&) = delete; box(const box&) = delete;
box& operator=(const box&) = delete; box& operator=(const box&) = delete;
constexpr ~box() constexpr ~box()
{ {
reset(); reset();
} }
constexpr void reset() constexpr void reset()
{ {
if (m_ptr != nullptr) if (m_ptr != nullptr)
{ {
destroy(m_ptr); destroy(m_ptr);
m_alloc.dealloc(m_ptr, layout::of<T>()); m_alloc.dealloc(m_ptr, layout::of<T>());
m_ptr = nullptr; m_ptr = nullptr;
} }
} }
constexpr T* get() const { return m_ptr; } constexpr T* get() const { return m_ptr; }
constexpr T& operator*() const constexpr T& operator*() const
{ {
ASL_ASSERT(m_ptr != nullptr); ASL_ASSERT(m_ptr != nullptr);
return *m_ptr; return *m_ptr;
} }
constexpr T* operator->() const constexpr T* operator->() const
{ {
ASL_ASSERT(m_ptr != nullptr); ASL_ASSERT(m_ptr != nullptr);
return m_ptr; return m_ptr;
} }
constexpr bool operator==(niche_t) const constexpr bool operator==(niche_t) const
{ {
return m_ptr == nullptr; return m_ptr == nullptr;
} }
template<typename H> template<typename H>
requires hashable<T> requires hashable<T>
friend H AslHashValue(H h, const box& b) friend H AslHashValue(H h, const box& b)
{ {
return H::combine(ASL_MOVE(h), *b); return H::combine(ASL_MOVE(h), *b);
} }
}; };
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args> template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
constexpr box<T, Allocator> make_box_in(Allocator allocator, Args&&... args) constexpr box<T, Allocator> make_box_in(Allocator allocator, Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
{ {
void* raw_ptr = allocator.alloc(layout::of<T>()); void* raw_ptr = allocator.alloc(layout::of<T>());
auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...); auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...);
return box(ptr, ASL_MOVE(allocator)); return box(ptr, ASL_MOVE(allocator));
} }
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args> template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
constexpr box<T, Allocator> make_box(Args&&... args) constexpr box<T, Allocator> make_box(Args&&... args)
requires default_constructible<Allocator> && constructible_from<T, Args&&...> requires default_constructible<Allocator> && constructible_from<T, Args&&...>
{ {
Allocator allocator{}; Allocator allocator{};
void* raw_ptr = allocator.alloc(layout::of<T>()); void* raw_ptr = allocator.alloc(layout::of<T>());
auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...); auto* ptr = construct_at<T>(raw_ptr, ASL_FWD(args)...);
return box<T>(ptr, ASL_MOVE(allocator)); return box<T>(ptr, ASL_MOVE(allocator));
} }
} // namespace asl } // namespace asl

View File

@ -1,459 +1,459 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/annotations.hpp" #include "asl/annotations.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
#include "asl/assert.hpp" #include "asl/assert.hpp"
#include "asl/span.hpp" #include "asl/span.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
namespace asl namespace asl
{ {
template<is_object T, allocator Allocator = DefaultAllocator> template<is_object T, allocator Allocator = DefaultAllocator>
class buffer class buffer
{ {
T* m_data{}; T* m_data{};
isize_t m_capacity{}; isize_t m_capacity{};
static constexpr size_t kOnHeapMask = 0x8000'0000'0000'0000ULL; static constexpr size_t kOnHeapMask = 0x8000'0000'0000'0000ULL;
// bit 63 : 1 = on heap, 0 = inline // bit 63 : 1 = on heap, 0 = inline
// bits [62:56] : size when inline // bits [62:56] : size when inline
// bits [62:0] : size when on heap // bits [62:0] : size when on heap
size_t m_size_encoded_{}; size_t m_size_encoded_{};
ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; ASL_NO_UNIQUE_ADDRESS Allocator m_allocator;
static constexpr isize_t kInlineRegionSize = size_of<T*> + size_of<isize_t> + size_of<size_t>; static constexpr isize_t kInlineRegionSize = size_of<T*> + size_of<isize_t> + size_of<size_t>;
public: public:
static constexpr isize_t kInlineCapacity = []() { static constexpr isize_t kInlineCapacity = []() {
// 1 byte is used for size inline in m_size_encoded. // 1 byte is used for size inline in m_size_encoded.
// This is enough because we have at most 24 bytes available, // This is enough because we have at most 24 bytes available,
// so 23 chars of capacity. // so 23 chars of capacity.
const isize_t available_size = kInlineRegionSize - 1; const isize_t available_size = kInlineRegionSize - 1;
return available_size / size_of<T>; return available_size / size_of<T>;
}(); }();
private: private:
static_assert(align_of<T> <= align_of<T*>); static_assert(align_of<T> <= align_of<T*>);
static_assert(align_of<T*> == align_of<isize_t>); static_assert(align_of<T*> == align_of<isize_t>);
static_assert(align_of<T*> == align_of<size_t>); static_assert(align_of<T*> == align_of<size_t>);
constexpr size_t load_size_encoded() const constexpr size_t load_size_encoded() const
{ {
size_t s{}; size_t s{};
asl::memcpy(&s, &m_size_encoded_, sizeof(size_t)); asl::memcpy(&s, &m_size_encoded_, sizeof(size_t));
return s; return s;
} }
constexpr void store_size_encoded(size_t encoded) constexpr void store_size_encoded(size_t encoded)
{ {
asl::memcpy(&m_size_encoded_, &encoded, sizeof(size_t)); asl::memcpy(&m_size_encoded_, &encoded, sizeof(size_t));
} }
static constexpr bool is_on_heap(size_t size_encoded) static constexpr bool is_on_heap(size_t size_encoded)
{ {
return (size_encoded & kOnHeapMask) != 0; return (size_encoded & kOnHeapMask) != 0;
} }
static constexpr size_t encode_size_heap(isize_t size) static constexpr size_t encode_size_heap(isize_t size)
{ {
return static_cast<size_t>(size) | kOnHeapMask; return static_cast<size_t>(size) | kOnHeapMask;
} }
static constexpr isize_t decode_size(size_t size_encoded) static constexpr isize_t decode_size(size_t size_encoded)
{ {
if constexpr (kInlineCapacity == 0) if constexpr (kInlineCapacity == 0)
{ {
return is_on_heap(size_encoded) return is_on_heap(size_encoded)
? static_cast<isize_t>(size_encoded & (~kOnHeapMask)) ? static_cast<isize_t>(size_encoded & (~kOnHeapMask))
: 0; : 0;
} }
else else
{ {
return is_on_heap(size_encoded) return is_on_heap(size_encoded)
? static_cast<isize_t>(size_encoded & (~kOnHeapMask)) ? static_cast<isize_t>(size_encoded & (~kOnHeapMask))
: static_cast<isize_t>(size_encoded >> 56); : static_cast<isize_t>(size_encoded >> 56);
} }
} }
constexpr bool is_on_heap() const constexpr bool is_on_heap() const
{ {
return is_on_heap(load_size_encoded()); return is_on_heap(load_size_encoded());
} }
constexpr T* push_uninit() constexpr T* push_uninit()
{ {
isize_t sz = size(); isize_t sz = size();
resize_uninit_inner(sz + 1); resize_uninit_inner(sz + 1);
return data() + sz; return data() + sz;
} }
constexpr void resize_uninit_inner(isize_t new_size) constexpr void resize_uninit_inner(isize_t new_size)
{ {
isize_t old_size = size(); isize_t old_size = size();
if (!trivially_destructible<T> && new_size < old_size) if (!trivially_destructible<T> && new_size < old_size)
{ {
destroy_n(data() + new_size, old_size - new_size); destroy_n(data() + new_size, old_size - new_size);
} }
reserve_capacity(new_size); reserve_capacity(new_size);
set_size(new_size); set_size(new_size);
} }
constexpr void set_size_inline(isize_t new_size) constexpr void set_size_inline(isize_t new_size)
{ {
ASL_ASSERT(new_size >= 0 && new_size <= kInlineCapacity); ASL_ASSERT(new_size >= 0 && new_size <= kInlineCapacity);
size_t size_encoded = (load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff}) | (bit_cast<size_t>(new_size) << 56); size_t size_encoded = (load_size_encoded() & size_t{0x00ff'ffff'ffff'ffff}) | (bit_cast<size_t>(new_size) << 56);
store_size_encoded(size_encoded); store_size_encoded(size_encoded);
} }
constexpr void set_size(isize_t new_size) constexpr void set_size(isize_t new_size)
{ {
ASL_ASSERT(new_size >= 0 && new_size <= capacity()); ASL_ASSERT(new_size >= 0 && new_size <= capacity());
if (is_on_heap()) if (is_on_heap())
{ {
store_size_encoded(encode_size_heap(new_size)); store_size_encoded(encode_size_heap(new_size));
} }
else else
{ {
set_size_inline(new_size); set_size_inline(new_size);
} }
} }
// NOLINTNEXTLINE(*-rvalue-reference-param-not-moved) // NOLINTNEXTLINE(*-rvalue-reference-param-not-moved)
void move_from_other(buffer&& other, bool assign) void move_from_other(buffer&& other, bool assign)
{ {
if (other.is_on_heap()) if (other.is_on_heap())
{ {
destroy(); destroy();
m_data = other.m_data; m_data = other.m_data;
m_capacity = other.m_capacity; m_capacity = other.m_capacity;
store_size_encoded(other.load_size_encoded()); store_size_encoded(other.load_size_encoded());
} }
else if (trivially_move_constructible<T>) else if (trivially_move_constructible<T>)
{ {
destroy(); destroy();
asl::memcpy(this, &other, kInlineRegionSize); asl::memcpy(this, &other, kInlineRegionSize);
} }
else if (!assign || m_allocator == other.m_allocator) else if (!assign || m_allocator == other.m_allocator)
{ {
isize_t other_n = other.size(); isize_t other_n = other.size();
isize_t this_n = size(); isize_t this_n = size();
resize_uninit_inner(other_n); resize_uninit_inner(other_n);
if (other_n <= this_n) if (other_n <= this_n)
{ {
relocate_assign_n(data(), other.data(), other_n); relocate_assign_n(data(), other.data(), other_n);
} }
else else
{ {
relocate_assign_n(data(), other.data(), this_n); relocate_assign_n(data(), other.data(), this_n);
relocate_uninit_n(data() + this_n, other.data() + this_n, other_n - this_n); relocate_uninit_n(data() + this_n, other.data() + this_n, other_n - this_n);
} }
} }
else else
{ {
destroy(); destroy();
isize_t n = other.size(); isize_t n = other.size();
ASL_ASSERT(n <= kInlineCapacity); ASL_ASSERT(n <= kInlineCapacity);
relocate_uninit_n(data(), other.data(), n); relocate_uninit_n(data(), other.data(), n);
set_size_inline(n); set_size_inline(n);
} }
other.set_size_inline(0); other.set_size_inline(0);
if (assign) if (assign)
{ {
m_allocator = ASL_MOVE(other.m_allocator); m_allocator = ASL_MOVE(other.m_allocator);
} }
} }
void copy_range(span<const T> to_copy) void copy_range(span<const T> to_copy)
{ {
isize_t this_size = size(); isize_t this_size = size();
isize_t new_size = to_copy.size(); isize_t new_size = to_copy.size();
resize_uninit_inner(to_copy.size()); resize_uninit_inner(to_copy.size());
ASL_ASSERT(capacity() >= new_size); ASL_ASSERT(capacity() >= new_size);
ASL_ASSERT(size() == to_copy.size()); ASL_ASSERT(size() == to_copy.size());
if (new_size <= this_size) if (new_size <= this_size)
{ {
copy_assign_n(data(), to_copy.data(), new_size); copy_assign_n(data(), to_copy.data(), new_size);
} }
else else
{ {
copy_assign_n(data(), to_copy.data(), this_size); copy_assign_n(data(), to_copy.data(), this_size);
copy_uninit_n(data() + this_size, to_copy.data() + this_size, new_size - this_size); copy_uninit_n(data() + this_size, to_copy.data() + this_size, new_size - this_size);
} }
} }
template<typename... Args> template<typename... Args>
void resize_inner(isize_t new_size, Args&&... args) void resize_inner(isize_t new_size, Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
{ {
ASL_ASSERT(new_size >= 0); ASL_ASSERT(new_size >= 0);
isize_t old_size = size(); isize_t old_size = size();
resize_uninit_inner(new_size); resize_uninit_inner(new_size);
T* data_ptr = data(); T* data_ptr = data();
T* end = data_ptr + new_size; T* end = data_ptr + new_size;
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
for (T* it = data_ptr + old_size; it < end; ++it) for (T* it = data_ptr + old_size; it < end; ++it)
{ {
construct_at<T>(it, ASL_FWD(args)...); construct_at<T>(it, ASL_FWD(args)...);
} }
} }
public: public:
constexpr buffer() requires default_constructible<Allocator> = default; constexpr buffer() requires default_constructible<Allocator> = default;
explicit constexpr buffer(span<const T> s) explicit constexpr buffer(span<const T> s)
requires default_constructible<Allocator> requires default_constructible<Allocator>
: buffer{} : buffer{}
{ {
copy_range(s); copy_range(s);
} }
explicit constexpr buffer(Allocator allocator) explicit constexpr buffer(Allocator allocator)
: m_allocator{ASL_MOVE(allocator)} : m_allocator{ASL_MOVE(allocator)}
{} {}
explicit constexpr buffer(span<const T> s, Allocator allocator) explicit constexpr buffer(span<const T> s, Allocator allocator)
: m_allocator{ASL_MOVE(allocator)} : m_allocator{ASL_MOVE(allocator)}
{ {
copy_range(s); copy_range(s);
} }
constexpr buffer(const buffer& other) constexpr buffer(const buffer& other)
requires copy_constructible<Allocator> && copyable<T> requires copy_constructible<Allocator> && copyable<T>
: m_allocator{other.m_allocator} : m_allocator{other.m_allocator}
{ {
copy_range(other); copy_range(other);
} }
constexpr buffer(buffer&& other) constexpr buffer(buffer&& other)
requires moveable<T> requires moveable<T>
: buffer(ASL_MOVE(other.m_allocator)) : buffer(ASL_MOVE(other.m_allocator))
{ {
move_from_other(ASL_MOVE(other), false); move_from_other(ASL_MOVE(other), false);
} }
constexpr buffer& operator=(const buffer& other) constexpr buffer& operator=(const buffer& other)
requires copyable<T> requires copyable<T>
{ {
if (&other == this) { return *this; } if (&other == this) { return *this; }
copy_range(other); copy_range(other);
return *this; return *this;
} }
constexpr buffer& operator=(buffer&& other) constexpr buffer& operator=(buffer&& other)
requires moveable<T> requires moveable<T>
{ {
if (&other == this) { return *this; } if (&other == this) { return *this; }
move_from_other(ASL_MOVE(other), true); move_from_other(ASL_MOVE(other), true);
return *this; return *this;
} }
~buffer() ~buffer()
{ {
destroy(); destroy();
} }
constexpr isize_t size() const constexpr isize_t size() const
{ {
return decode_size(load_size_encoded()); return decode_size(load_size_encoded());
} }
constexpr isize_t capacity() const constexpr isize_t capacity() const
{ {
if constexpr (kInlineCapacity == 0) if constexpr (kInlineCapacity == 0)
{ {
return m_capacity; return m_capacity;
} }
else else
{ {
return is_on_heap() ? m_capacity : kInlineCapacity; return is_on_heap() ? m_capacity : kInlineCapacity;
} }
} }
void clear() void clear()
{ {
isize_t current_size = size(); isize_t current_size = size();
if (current_size == 0) { return; } if (current_size == 0) { return; }
destroy_n(data(), current_size); destroy_n(data(), current_size);
set_size(0); set_size(0);
} }
void destroy() void destroy()
{ {
clear(); clear();
if (is_on_heap()) if (is_on_heap())
{ {
if (m_data != nullptr) if (m_data != nullptr)
{ {
auto current_layout = layout::array<T>(m_capacity); auto current_layout = layout::array<T>(m_capacity);
m_allocator.dealloc(m_data, current_layout); m_allocator.dealloc(m_data, current_layout);
} }
set_size_inline(0); set_size_inline(0);
} }
} }
void reserve_capacity(isize_t new_capacity) void reserve_capacity(isize_t new_capacity)
{ {
ASL_ASSERT(new_capacity >= 0); ASL_ASSERT(new_capacity >= 0);
ASL_ASSERT_RELEASE(new_capacity <= 0x4000'0000'0000'0000); ASL_ASSERT_RELEASE(new_capacity <= 0x4000'0000'0000'0000);
if (new_capacity <= capacity()) { return; } if (new_capacity <= capacity()) { return; }
ASL_ASSERT(new_capacity > kInlineCapacity); ASL_ASSERT(new_capacity > kInlineCapacity);
new_capacity = static_cast<isize_t>(round_up_pow2(static_cast<uint64_t>(new_capacity))); new_capacity = static_cast<isize_t>(round_up_pow2(static_cast<uint64_t>(new_capacity)));
T* old_data = data(); T* old_data = data();
const isize_t old_capacity = capacity(); const isize_t old_capacity = capacity();
const isize_t current_size = size(); const isize_t current_size = size();
const bool currently_on_heap = is_on_heap(); const bool currently_on_heap = is_on_heap();
auto old_layout = layout::array<T>(old_capacity); auto old_layout = layout::array<T>(old_capacity);
auto new_layout = layout::array<T>(new_capacity); auto new_layout = layout::array<T>(new_capacity);
if (currently_on_heap && trivially_move_constructible<T>) if (currently_on_heap && trivially_move_constructible<T>)
{ {
m_data = reinterpret_cast<T*>(m_allocator.realloc(m_data, old_layout, new_layout)); m_data = reinterpret_cast<T*>(m_allocator.realloc(m_data, old_layout, new_layout));
m_capacity = new_capacity; m_capacity = new_capacity;
return; return;
} }
T* new_data = reinterpret_cast<T*>(m_allocator.alloc(new_layout)); T* new_data = reinterpret_cast<T*>(m_allocator.alloc(new_layout));
relocate_uninit_n(new_data, old_data, current_size); relocate_uninit_n(new_data, old_data, current_size);
if (currently_on_heap) if (currently_on_heap)
{ {
m_allocator.dealloc(old_data, old_layout); m_allocator.dealloc(old_data, old_layout);
} }
m_data = new_data; m_data = new_data;
m_capacity = new_capacity; m_capacity = new_capacity;
store_size_encoded(encode_size_heap(current_size)); store_size_encoded(encode_size_heap(current_size));
} }
constexpr void resize_uninit(isize_t new_size) constexpr void resize_uninit(isize_t new_size)
requires trivially_default_constructible<T> && trivially_destructible<T> requires trivially_default_constructible<T> && trivially_destructible<T>
{ {
reserve_capacity(new_size); reserve_capacity(new_size);
set_size(new_size); set_size(new_size);
} }
constexpr void resize_zero(isize_t new_size) constexpr void resize_zero(isize_t new_size)
requires trivially_default_constructible<T> && trivially_destructible<T> requires trivially_default_constructible<T> && trivially_destructible<T>
{ {
isize_t old_size = size(); isize_t old_size = size();
resize_uninit(new_size); resize_uninit(new_size);
if (new_size > old_size) if (new_size > old_size)
{ {
memzero(data() + old_size, (new_size - old_size) * size_of<T>); memzero(data() + old_size, (new_size - old_size) * size_of<T>);
} }
} }
void resize(isize_t new_size) void resize(isize_t new_size)
requires default_constructible<T> requires default_constructible<T>
{ {
resize_inner(new_size); resize_inner(new_size);
} }
void resize(isize_t new_size, const T& value) void resize(isize_t new_size, const T& value)
{ {
resize_inner(new_size, value); resize_inner(new_size, value);
} }
constexpr T& push(auto&&... args) constexpr T& push(auto&&... args)
requires constructible_from<T, decltype(args)&&...> requires constructible_from<T, decltype(args)&&...>
{ {
T* uninit = push_uninit(); T* uninit = push_uninit();
T* init = construct_at<T>(uninit, ASL_FWD(args)...); T* init = construct_at<T>(uninit, ASL_FWD(args)...);
return *init; return *init;
} }
// @Todo(C++23) Use deducing this // @Todo(C++23) Use deducing this
const T* data() const const T* data() const
{ {
if constexpr (kInlineCapacity == 0) if constexpr (kInlineCapacity == 0)
{ {
return m_data; return m_data;
} }
else else
{ {
return is_on_heap() ? m_data : reinterpret_cast<const T*>(this); return is_on_heap() ? m_data : reinterpret_cast<const T*>(this);
} }
} }
T* data() T* data()
{ {
if constexpr (kInlineCapacity == 0) if constexpr (kInlineCapacity == 0)
{ {
return m_data; return m_data;
} }
else else
{ {
return is_on_heap() ? m_data : reinterpret_cast<T*>(this); return is_on_heap() ? m_data : reinterpret_cast<T*>(this);
} }
} }
// @Todo(C++23) Use deducing this // @Todo(C++23) Use deducing this
constexpr contiguous_iterator<const T> begin() const { return contiguous_iterator{data()}; } constexpr contiguous_iterator<const T> begin() const { return contiguous_iterator{data()}; }
constexpr contiguous_iterator<const T> end() const { return contiguous_iterator{data() + size()}; } constexpr contiguous_iterator<const T> end() const { return contiguous_iterator{data() + size()}; }
constexpr contiguous_iterator<T> begin() { return contiguous_iterator{data()}; } constexpr contiguous_iterator<T> begin() { return contiguous_iterator{data()}; }
constexpr contiguous_iterator<T> end() { return contiguous_iterator{data() + size()}; } constexpr contiguous_iterator<T> end() { return contiguous_iterator{data() + size()}; }
// @Todo(C++23) Deducing this // @Todo(C++23) Deducing this
constexpr operator span<const T>() const // NOLINT(*-explicit-conversions) constexpr operator span<const T>() const // NOLINT(*-explicit-conversions)
{ {
return as_span(); return as_span();
} }
constexpr operator span<T>() // NOLINT(*-explicit-conversions) constexpr operator span<T>() // NOLINT(*-explicit-conversions)
{ {
return as_span(); return as_span();
} }
constexpr span<const T> as_span() const constexpr span<const T> as_span() const
{ {
return span<const T>{data(), size()}; return span<const T>{data(), size()};
} }
constexpr span<T> as_span() constexpr span<T> as_span()
{ {
return span<T>{data(), size()}; return span<T>{data(), size()};
} }
// @Todo(C++23) Use deducing this // @Todo(C++23) Use deducing this
constexpr T& operator[](isize_t i) constexpr T& operator[](isize_t i)
{ {
ASL_ASSERT(i >= 0 && i <= size()); ASL_ASSERT(i >= 0 && i <= size());
return data()[i]; return data()[i];
} }
constexpr const T& operator[](isize_t i) const constexpr const T& operator[](isize_t i) const
{ {
ASL_ASSERT(i >= 0 && i <= size()); ASL_ASSERT(i >= 0 && i <= size());
return data()[i]; return data()[i];
} }
template<typename H> template<typename H>
requires hashable<T> requires hashable<T>
friend H AslHashValue(H h, const buffer& b) friend H AslHashValue(H h, const buffer& b)
{ {
return H::combine_contiguous(ASL_MOVE(h), b.as_span()); return H::combine_contiguous(ASL_MOVE(h), b.as_span());
} }
}; };
} // namespace asl } // namespace asl

View File

@ -1,17 +1,17 @@
#pragma once #pragma once
#if defined(_WIN32) #if defined(_WIN32)
#define ASL_OS_WINDOWS 1 #define ASL_OS_WINDOWS 1
#elif defined(__linux__) #elif defined(__linux__)
#define ASL_OS_LINUX 1 #define ASL_OS_LINUX 1
#else #else
#error Unknown OS #error Unknown OS
#endif #endif
#if defined(__clang__) && defined(_MSC_VER) #if defined(__clang__) && defined(_MSC_VER)
#define ASL_COMPILER_CLANG_CL 1 #define ASL_COMPILER_CLANG_CL 1
#elif defined(__clang__) #elif defined(__clang__)
#define ASL_COMPILER_CLANG 1 #define ASL_COMPILER_CLANG 1
#else #else
#error Unknown compiler #error Unknown compiler
#endif #endif

View File

@ -1,17 +1,17 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
namespace asl namespace asl
{ {
template<is_floating_point T> constexpr T infinity() { return __builtin_inf(); } template<is_floating_point T> constexpr T infinity() { return __builtin_inf(); }
template<is_floating_point T> constexpr T nan() { return static_cast<T>(__builtin_nanf("")); } template<is_floating_point T> constexpr T nan() { return static_cast<T>(__builtin_nanf("")); }
template<is_floating_point T> constexpr bool is_infinity(T f) { return __builtin_isinf(f); } template<is_floating_point T> constexpr bool is_infinity(T f) { return __builtin_isinf(f); }
template<is_floating_point T> constexpr bool is_nan(T f) { return __builtin_isnan(f); } template<is_floating_point T> constexpr bool is_nan(T f) { return __builtin_isnan(f); }
} // namespace asl } // namespace asl

View File

@ -1,201 +1,201 @@
#include "asl/format.hpp" #include "asl/format.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/assert.hpp" #include "asl/assert.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
void asl::format_internals::format( void asl::format_internals::format(
Writer* writer, Writer* writer,
string_view fmt, string_view fmt,
span<const type_erased_arg> args) span<const type_erased_arg> args)
{ {
Formatter f(writer); Formatter f(writer);
auto arg_it = args.begin(); auto arg_it = args.begin();
auto arg_end = args.end(); auto arg_end = args.end();
isize_t i = 0; isize_t i = 0;
while (i < fmt.size()) while (i < fmt.size())
{ {
if (fmt[i] == '{') if (fmt[i] == '{')
{ {
if (i + 1 < fmt.size()) if (i + 1 < fmt.size())
{ {
if (fmt[i + 1] == '}') if (fmt[i + 1] == '}')
{ {
f.write(fmt.substr(0, i)); f.write(fmt.substr(0, i));
fmt = fmt.substr(i + 2); fmt = fmt.substr(i + 2);
i = 0; i = 0;
if (arg_it == arg_end) if (arg_it == arg_end)
{ {
f.write("<ERROR>"); f.write("<ERROR>");
} }
else else
{ {
arg_it->fn(f, arg_it->data); arg_it->fn(f, arg_it->data);
arg_it++; arg_it++;
} }
continue; continue;
} }
if (fmt[i + 1] == '{') if (fmt[i + 1] == '{')
{ {
f.write(fmt.substr(0, i + 1)); f.write(fmt.substr(0, i + 1));
fmt = fmt.substr(i + 2); fmt = fmt.substr(i + 2);
i = 0; i = 0;
continue; continue;
} }
} }
f.write(fmt.substr(0, i)); f.write(fmt.substr(0, i));
fmt = fmt.substr(i + 1); fmt = fmt.substr(i + 1);
i = 0; i = 0;
f.write("<ERROR>"); f.write("<ERROR>");
} }
else if (i + 1 < fmt.size() && fmt[i] == '}' && fmt[i + 1] == '}') else if (i + 1 < fmt.size() && fmt[i] == '}' && fmt[i + 1] == '}')
{ {
f.write(fmt.substr(0, i + 1)); f.write(fmt.substr(0, i + 1));
fmt = fmt.substr(i + 2); fmt = fmt.substr(i + 2);
i = 0; i = 0;
} }
else else
{ {
i += 1; i += 1;
} }
} }
f.write(fmt); f.write(fmt);
} }
void asl::AslFormat(Formatter& f, const char* str) void asl::AslFormat(Formatter& f, const char* str)
{ {
f.write({str, asl::strlen(str)}); f.write({str, asl::strlen(str)});
} }
void asl::AslFormat(Formatter& f, bool v) void asl::AslFormat(Formatter& f, bool v)
{ {
if (v) if (v)
{ {
f.write("true"); f.write("true");
} }
else else
{ {
f.write("false"); f.write("false");
} }
} }
void asl::AslFormat(Formatter& f, uint8_t v) void asl::AslFormat(Formatter& f, uint8_t v)
{ {
AslFormat(f, static_cast<uint64_t>(v)); AslFormat(f, static_cast<uint64_t>(v));
} }
void asl::AslFormat(Formatter& f, uint16_t v) void asl::AslFormat(Formatter& f, uint16_t v)
{ {
AslFormat(f, static_cast<uint64_t>(v)); AslFormat(f, static_cast<uint64_t>(v));
} }
void asl::AslFormat(Formatter& f, uint32_t v) void asl::AslFormat(Formatter& f, uint32_t v)
{ {
AslFormat(f, static_cast<uint64_t>(v)); AslFormat(f, static_cast<uint64_t>(v));
} }
static constexpr int32_t kMaxUint64Digits = 20; static constexpr int32_t kMaxUint64Digits = 20;
asl::string_view asl::format_uint64(uint64_t v, asl::span<char, kMaxUint64Digits> buffer) asl::string_view asl::format_uint64(uint64_t v, asl::span<char, kMaxUint64Digits> buffer)
{ {
static constexpr char s_pairs_storage[] = { static constexpr char s_pairs_storage[] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '0', '0', '1', '0', '2', '0', '3', '0', '4',
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4',
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4',
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4',
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9',
}; };
static constexpr span s_pairs = s_pairs_storage; static constexpr span s_pairs = s_pairs_storage;
int32_t cursor = kMaxUint64Digits; int32_t cursor = kMaxUint64Digits;
auto write_two = [&buffer, &cursor](span<const char, 2> str) auto write_two = [&buffer, &cursor](span<const char, 2> str)
{ {
ASL_ASSERT(cursor >= 2); ASL_ASSERT(cursor >= 2);
buffer[--cursor] = str[1]; buffer[--cursor] = str[1];
buffer[--cursor] = str[0]; buffer[--cursor] = str[0];
}; };
auto write_one = [&buffer, &cursor](char c) auto write_one = [&buffer, &cursor](char c)
{ {
ASL_ASSERT(cursor >= 1); ASL_ASSERT(cursor >= 1);
buffer[--cursor] = c; buffer[--cursor] = c;
}; };
while (v >= 100) while (v >= 100)
{ {
uint64_t x = v % 100; uint64_t x = v % 100;
v /= 100; v /= 100;
write_two(s_pairs.subspan(static_cast<isize_t>(x * 2)).first<2>()); write_two(s_pairs.subspan(static_cast<isize_t>(x * 2)).first<2>());
} }
if (v >= 10) if (v >= 10)
{ {
write_two(s_pairs.subspan(static_cast<isize_t>(v * 2)).first<2>()); write_two(s_pairs.subspan(static_cast<isize_t>(v * 2)).first<2>());
} }
else if (v > 0 || cursor == kMaxUint64Digits) else if (v > 0 || cursor == kMaxUint64Digits)
{ {
ASL_ASSERT(v < 10); ASL_ASSERT(v < 10);
write_one(static_cast<char>('0' + v)); write_one(static_cast<char>('0' + v));
} }
return string_view(buffer.data(), kMaxUint64Digits).substr(cursor); return string_view(buffer.data(), kMaxUint64Digits).substr(cursor);
} }
void asl::AslFormat(Formatter& f, uint64_t v) void asl::AslFormat(Formatter& f, uint64_t v)
{ {
char buffer[kMaxUint64Digits]; char buffer[kMaxUint64Digits];
f.write(format_uint64(v, buffer)); f.write(format_uint64(v, buffer));
} }
void asl::AslFormat(Formatter& f, int8_t v) void asl::AslFormat(Formatter& f, int8_t v)
{ {
AslFormat(f, static_cast<int64_t>(v)); AslFormat(f, static_cast<int64_t>(v));
} }
void asl::AslFormat(Formatter& f, int16_t v) void asl::AslFormat(Formatter& f, int16_t v)
{ {
AslFormat(f, static_cast<int64_t>(v)); AslFormat(f, static_cast<int64_t>(v));
} }
void asl::AslFormat(Formatter& f, int32_t v) void asl::AslFormat(Formatter& f, int32_t v)
{ {
AslFormat(f, static_cast<int64_t>(v)); AslFormat(f, static_cast<int64_t>(v));
} }
void asl::AslFormat(Formatter& f, int64_t v) void asl::AslFormat(Formatter& f, int64_t v)
{ {
if (v < 0) if (v < 0)
{ {
f.write("-"); f.write("-");
uint64_t absolute_value = ~(bit_cast<uint64_t>(v) - 1); uint64_t absolute_value = ~(bit_cast<uint64_t>(v) - 1);
AslFormat(f, absolute_value); AslFormat(f, absolute_value);
} }
else else
{ {
AslFormat(f, static_cast<uint64_t>(v)); AslFormat(f, static_cast<uint64_t>(v));
} }
} }

View File

@ -1,109 +1,109 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/io.hpp" #include "asl/io.hpp"
#include "asl/span.hpp" #include "asl/span.hpp"
#include "asl/string_view.hpp" #include "asl/string_view.hpp"
namespace asl namespace asl
{ {
class Formatter; class Formatter;
template<typename T> template<typename T>
concept formattable = requires (Formatter& f, const T& value) concept formattable = requires (Formatter& f, const T& value)
{ {
AslFormat(f, value); AslFormat(f, value);
}; };
namespace format_internals namespace format_internals
{ {
struct type_erased_arg struct type_erased_arg
{ {
const void* data; const void* data;
void (*fn)(Formatter&, const void*); void (*fn)(Formatter&, const void*);
template<formattable T> template<formattable T>
static constexpr void erased_fn(Formatter& f, const void* data) static constexpr void erased_fn(Formatter& f, const void* data)
{ {
AslFormat(f, *reinterpret_cast<const T*>(data)); AslFormat(f, *reinterpret_cast<const T*>(data));
} }
template<formattable T> template<formattable T>
explicit constexpr type_erased_arg(const T& arg) explicit constexpr type_erased_arg(const T& arg)
: data{&arg} : data{&arg}
, fn{erased_fn<T>} , fn{erased_fn<T>}
{} {}
}; };
void format(Writer*, string_view fmt, span<const type_erased_arg> args); void format(Writer*, string_view fmt, span<const type_erased_arg> args);
} // namespace internals } // namespace internals
class Formatter class Formatter
{ {
Writer* m_writer; Writer* m_writer;
public: public:
explicit constexpr Formatter(Writer* writer) explicit constexpr Formatter(Writer* writer)
: m_writer{writer} : m_writer{writer}
{} {}
constexpr void write(string_view s) constexpr void write(string_view s)
{ {
m_writer->write(as_bytes(s.as_span())); m_writer->write(as_bytes(s.as_span()));
} }
constexpr Writer* writer() const { return m_writer; } constexpr Writer* writer() const { return m_writer; }
}; };
template<formattable... Args> template<formattable... Args>
void format(Writer* w, string_view fmt, const Args&... args) void format(Writer* w, string_view fmt, const Args&... args)
{ {
if constexpr (types_count<Args...> > 0) if constexpr (types_count<Args...> > 0)
{ {
format_internals::type_erased_arg type_erased_args[] = { format_internals::type_erased_arg type_erased_args[] = {
format_internals::type_erased_arg(args)... format_internals::type_erased_arg(args)...
}; };
format_internals::format(w, fmt, type_erased_args); format_internals::format(w, fmt, type_erased_args);
} }
else else
{ {
format_internals::format(w, fmt, {}); format_internals::format(w, fmt, {});
} }
} }
template<isize_t N> template<isize_t N>
void AslFormat(Formatter& f, const char (&str)[N]) void AslFormat(Formatter& f, const char (&str)[N])
{ {
f.write(string_view(str, N - 1)); f.write(string_view(str, N - 1));
} }
void AslFormat(Formatter& f, const char* str); void AslFormat(Formatter& f, const char* str);
inline void AslFormat(Formatter& f, string_view sv) inline void AslFormat(Formatter& f, string_view sv)
{ {
f.write(sv); f.write(sv);
} }
void AslFormat(Formatter& f, float); void AslFormat(Formatter& f, float);
void AslFormat(Formatter& f, double); void AslFormat(Formatter& f, double);
void AslFormat(Formatter& f, bool); void AslFormat(Formatter& f, bool);
void AslFormat(Formatter& f, uint8_t); void AslFormat(Formatter& f, uint8_t);
void AslFormat(Formatter& f, uint16_t); void AslFormat(Formatter& f, uint16_t);
void AslFormat(Formatter& f, uint32_t); void AslFormat(Formatter& f, uint32_t);
void AslFormat(Formatter& f, uint64_t); void AslFormat(Formatter& f, uint64_t);
void AslFormat(Formatter& f, int8_t); void AslFormat(Formatter& f, int8_t);
void AslFormat(Formatter& f, int16_t); void AslFormat(Formatter& f, int16_t);
void AslFormat(Formatter& f, int32_t); void AslFormat(Formatter& f, int32_t);
void AslFormat(Formatter& f, int64_t); void AslFormat(Formatter& f, int64_t);
string_view format_uint64(uint64_t value, span<char, 20> buffer); string_view format_uint64(uint64_t value, span<char, 20> buffer);
} // namespace asl } // namespace asl

View File

@ -1,98 +1,98 @@
#include "asl/format.hpp" #include "asl/format.hpp"
#include "asl/float.hpp" #include "asl/float.hpp"
#define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0 #define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0
#define JKJ_STATIC_DATA_SECTION_DEFINED 0 #define JKJ_STATIC_DATA_SECTION_DEFINED 0
#include <dragonbox.h> #include <dragonbox.h>
static constexpr isize_t kZeroCount = 100; static constexpr isize_t kZeroCount = 100;
static constexpr char kZeros[kZeroCount] = { 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',
'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<asl::is_floating_point T> template<asl::is_floating_point T>
static void format_float(asl::Formatter& f, T value) static void format_float(asl::Formatter& f, T value)
{ {
if (asl::is_infinity(value)) if (asl::is_infinity(value))
{ {
if (value > 0) if (value > 0)
{ {
f.write("Infinity"_sv); f.write("Infinity"_sv);
} }
else else
{ {
f.write("-Infinity"_sv); f.write("-Infinity"_sv);
} }
return; return;
} }
if (value == static_cast<T>(0)) if (value == static_cast<T>(0))
{ {
f.write("0"_sv); f.write("0"_sv);
return; return;
} }
if (asl::is_nan(value)) if (asl::is_nan(value))
{ {
f.write("NaN"_sv); f.write("NaN"_sv);
return; return;
} }
auto decimal = jkj::dragonbox::to_decimal(value); auto decimal = jkj::dragonbox::to_decimal(value);
if (decimal.is_negative) { f.write("-"); } if (decimal.is_negative) { f.write("-"); }
char buffer[20]; char buffer[20];
asl::string_view digits = asl::format_uint64(decimal.significand, buffer); asl::string_view digits = asl::format_uint64(decimal.significand, buffer);
if (decimal.exponent >= 0) if (decimal.exponent >= 0)
{ {
f.write(digits); f.write(digits);
while (decimal.exponent > 0) while (decimal.exponent > 0)
{ {
isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount); isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
f.write(asl::string_view(kZeros, to_write)); f.write(asl::string_view(kZeros, to_write));
decimal.exponent -= to_write; decimal.exponent -= to_write;
} }
} }
else else
{ {
if (digits.size() <= -decimal.exponent) if (digits.size() <= -decimal.exponent)
{ {
f.write("0."); f.write("0.");
decimal.exponent = -decimal.exponent - static_cast<int>(digits.size()); decimal.exponent = -decimal.exponent - static_cast<int>(digits.size());
while (decimal.exponent > 0) while (decimal.exponent > 0)
{ {
isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount); isize_t to_write = asl::min(static_cast<isize_t>(decimal.exponent), kZeroCount);
f.write(asl::string_view(kZeros, to_write)); f.write(asl::string_view(kZeros, to_write));
decimal.exponent -= to_write; decimal.exponent -= to_write;
} }
f.write(digits); f.write(digits);
} }
else else
{ {
f.write(digits.first(digits.size() + decimal.exponent)); f.write(digits.first(digits.size() + decimal.exponent));
f.write("."); f.write(".");
f.write(digits.last(-decimal.exponent)); f.write(digits.last(-decimal.exponent));
} }
} }
} }
void asl::AslFormat(Formatter& f, float value) void asl::AslFormat(Formatter& f, float value)
{ {
format_float(f, value); format_float(f, value);
} }
void asl::AslFormat(Formatter& f, double value) void asl::AslFormat(Formatter& f, double value)
{ {
format_float(f, value); format_float(f, value);
} }

View File

@ -1,65 +1,65 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
namespace asl { namespace asl {
template<typename... Args, typename C> template<typename... Args, typename C>
constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args) constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args)
requires requires { requires requires {
(self.*f)(ASL_FWD(args)...); (self.*f)(ASL_FWD(args)...);
} }
{ {
return (ASL_FWD(self).*f)(ASL_FWD(args)...); return (ASL_FWD(self).*f)(ASL_FWD(args)...);
} }
template<typename... Args, typename C> template<typename... Args, typename C>
constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args) constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args)
requires requires { requires requires {
(self->*f)(ASL_FWD(args)...); (self->*f)(ASL_FWD(args)...);
} }
{ {
return (self->*f)(ASL_FWD(args)...); return (self->*f)(ASL_FWD(args)...);
} }
template<typename... Args, typename C> template<typename... Args, typename C>
constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...) constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...)
requires ( requires (
sizeof...(Args) == 0 && sizeof...(Args) == 0 &&
requires { self.*m; } requires { self.*m; }
) )
{ {
return ASL_FWD(self).*m; return ASL_FWD(self).*m;
} }
template<typename... Args, typename C> template<typename... Args, typename C>
constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...) constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...)
requires ( requires (
sizeof...(Args) == 0 && sizeof...(Args) == 0 &&
requires { self->*m; } requires { self->*m; }
) )
{ {
return self->*m; return self->*m;
} }
template<typename... Args> template<typename... Args>
constexpr auto invoke(auto&& f, Args&&... args) constexpr auto invoke(auto&& f, Args&&... args)
requires requires { requires requires {
f(ASL_FWD(args)...); f(ASL_FWD(args)...);
} }
{ {
return ASL_FWD(f)(ASL_FWD(args)...); return ASL_FWD(f)(ASL_FWD(args)...);
} }
template<typename F> struct _result_of_helper; template<typename F> struct _result_of_helper;
template<typename R, typename... Args> template<typename R, typename... Args>
struct _result_of_helper<R(Args...)> struct _result_of_helper<R(Args...)>
{ {
using type = decltype(invoke(declval<R>(), declval<Args>()...)); using type = decltype(invoke(declval<R>(), declval<Args>()...));
}; };
template<typename F> using result_of_t = _result_of_helper<F>::type; template<typename F> using result_of_t = _result_of_helper<F>::type;
} // namespace asl } // namespace asl

View File

@ -1,138 +1,138 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/span.hpp" #include "asl/span.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
namespace asl::city_hash namespace asl::city_hash
{ {
// Hash function for a byte array. // Hash function for a byte array.
uint64_t CityHash64(const char *s, size_t len); uint64_t CityHash64(const char *s, size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also // Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result. // hashed into the result.
uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed); uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed);
// Hash function for a byte array. For convenience, two seeds are also // Hash function for a byte array. For convenience, two seeds are also
// hashed into the result. // hashed into the result.
uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t CityHash64WithSeeds(const char *s, size_t len,
uint64_t seed0, uint64_t seed1); uint64_t seed0, uint64_t seed1);
// Hash function for a byte array. // Hash function for a byte array.
uint128_t CityHash128(const char *s, size_t len); uint128_t CityHash128(const char *s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also // Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result. // hashed into the result.
uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed); uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed);
// Hash function for a byte array. Most useful in 32-bit binaries. // Hash function for a byte array. Most useful in 32-bit binaries.
uint32_t CityHash32(const char *s, size_t len); uint32_t CityHash32(const char *s, size_t len);
// Hash 128 input bits down to 64 bits of output. // Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function. // This is intended to be a reasonably good hash function.
constexpr uint64_t Hash128to64(uint64_t high, uint64_t low) constexpr uint64_t Hash128to64(uint64_t high, uint64_t low)
{ {
// Murmur-inspired hashing. // Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL; const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (low ^ high) * kMul; uint64_t a = (low ^ high) * kMul;
a ^= (a >> 47); a ^= (a >> 47);
uint64_t b = (high ^ a) * kMul; uint64_t b = (high ^ a) * kMul;
b ^= (b >> 47); b ^= (b >> 47);
b *= kMul; b *= kMul;
return b; return b;
} }
// Hash 128 input bits down to 64 bits of output. // Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function. // This is intended to be a reasonably good hash function.
constexpr uint64_t Hash128to64(const uint128_t& x) constexpr uint64_t Hash128to64(const uint128_t& x)
{ {
return Hash128to64(x.high, x.low); return Hash128to64(x.high, x.low);
} }
} // namespace asl::city_hash } // namespace asl::city_hash
namespace asl namespace asl
{ {
template<typename T, typename H> template<typename T, typename H>
concept hashable_generic = requires(const T& value, H h) concept hashable_generic = requires(const T& value, H h)
{ {
{ AslHashValue(h, value) } -> same_as<H>; { AslHashValue(h, value) } -> same_as<H>;
}; };
struct HashState struct HashState
{ {
uint128_t state{}; uint128_t state{};
constexpr HashState() = default; constexpr HashState() = default;
explicit constexpr HashState(uint128_t s) : state{s} {} explicit constexpr HashState(uint128_t s) : state{s} {}
template<typename T> template<typename T>
static HashState combine_contiguous(HashState h, span<const T> s) static HashState combine_contiguous(HashState h, span<const T> s)
{ {
if constexpr (uniquely_represented<T>) if constexpr (uniquely_represented<T>)
{ {
auto bytes = as_bytes(s); auto bytes = as_bytes(s);
auto hashed = city_hash::CityHash128WithSeed( auto hashed = city_hash::CityHash128WithSeed(
reinterpret_cast<const char*>(bytes.data()), reinterpret_cast<const char*>(bytes.data()),
static_cast<size_t>(bytes.size()), static_cast<size_t>(bytes.size()),
h.state); h.state);
return HashState{hashed}; return HashState{hashed};
} }
else else
{ {
for (const auto& value: s) for (const auto& value: s)
{ {
h = AslHashValue(ASL_MOVE(h), value); h = AslHashValue(ASL_MOVE(h), value);
} }
return h; return h;
} }
} }
static constexpr HashState combine(HashState h) static constexpr HashState combine(HashState h)
{ {
return h; return h;
} }
template<hashable_generic<HashState> Arg, hashable_generic<HashState>... Remaining> template<hashable_generic<HashState> Arg, hashable_generic<HashState>... Remaining>
static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining) static constexpr HashState combine(HashState h, const Arg& arg, const Remaining&... remaining)
{ {
return combine(AslHashValue(ASL_MOVE(h), arg), remaining...); return combine(AslHashValue(ASL_MOVE(h), arg), remaining...);
} }
}; };
template<typename T> template<typename T>
concept hashable = hashable_generic<T, HashState>; concept hashable = hashable_generic<T, HashState>;
template<typename H, uniquely_represented T> template<typename H, uniquely_represented T>
constexpr H AslHashValue(H h, const T& value) constexpr H AslHashValue(H h, const T& value)
{ {
return H::combine_contiguous(ASL_MOVE(h), span<const T>{&value, 1}); return H::combine_contiguous(ASL_MOVE(h), span<const T>{&value, 1});
} }
template<typename H> template<typename H>
constexpr H AslHashValue(H h, bool value) constexpr H AslHashValue(H h, bool value)
{ {
return AslHashValue(ASL_MOVE(h), value ? 1 : 0); return AslHashValue(ASL_MOVE(h), value ? 1 : 0);
} }
template<typename H, typename T> template<typename H, typename T>
constexpr void AslHashValue(H h, T*); // Don't hash pointers constexpr void AslHashValue(H h, T*); // Don't hash pointers
template<typename H, hashable T> template<typename H, hashable T>
constexpr H AslHashValue(H h, const span<T>& s) constexpr H AslHashValue(H h, const span<T>& s)
{ {
return H::combine_contiguous(ASL_MOVE(h), span<const T>{s.data(), s.size()}); return H::combine_contiguous(ASL_MOVE(h), span<const T>{s.data(), s.size()});
} }
template<hashable T> template<hashable T>
constexpr uint64_t hash_value(const T& value) constexpr uint64_t hash_value(const T& value)
{ {
auto result = AslHashValue(HashState{}, value).state; auto result = AslHashValue(HashState{}, value).state;
return city_hash::Hash128to64(result); return city_hash::Hash128to64(result);
} }
} // namespace asl } // namespace asl

File diff suppressed because it is too large Load Diff

View File

@ -1,178 +1,178 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/hash_set.hpp" #include "asl/hash_set.hpp"
namespace asl namespace asl
{ {
namespace hash_map_internal namespace hash_map_internal
{ {
template<typename K, typename V> template<typename K, typename V>
struct Slot struct Slot
{ {
K key; K key;
V value; V value;
}; };
template<hashable K, typename V, key_hasher<K> KeyHasher> template<hashable K, typename V, key_hasher<K> KeyHasher>
struct SlotHasher : public KeyHasher struct SlotHasher : public KeyHasher
{ {
using KeyHasher::hash; using KeyHasher::hash;
constexpr static uint64_t hash(const Slot<K, V>& slot) constexpr static uint64_t hash(const Slot<K, V>& slot)
{ {
return KeyHasher::hash(slot.key); return KeyHasher::hash(slot.key);
} }
}; };
template<equality_comparable K, typename V, key_comparator<K> KeyComparator> template<equality_comparable K, typename V, key_comparator<K> KeyComparator>
struct SlotComparator : public KeyComparator struct SlotComparator : public KeyComparator
{ {
using KeyComparator::eq; using KeyComparator::eq;
constexpr static bool eq(const Slot<K, V>& a, const Slot<K, V>& b) constexpr static bool eq(const Slot<K, V>& a, const Slot<K, V>& b)
{ {
return KeyComparator::eq(a.key, b.key); return KeyComparator::eq(a.key, b.key);
} }
constexpr static bool eq(const Slot<K, V>& a, const K& b) constexpr static bool eq(const Slot<K, V>& a, const K& b)
{ {
return KeyComparator::eq(a.key, b); return KeyComparator::eq(a.key, b);
} }
}; };
} // namespace hash_map_internal } // namespace hash_map_internal
template< template<
is_object K, is_object K,
is_object V, is_object V,
allocator Allocator = DefaultAllocator, allocator Allocator = DefaultAllocator,
key_hasher<K> KeyHasher = default_key_hasher<K>, key_hasher<K> KeyHasher = default_key_hasher<K>,
key_comparator<K> KeyComparator = default_key_comparator<K> key_comparator<K> KeyComparator = default_key_comparator<K>
> >
requires moveable<K> && moveable<V> requires moveable<K> && moveable<V>
class hash_map : protected hash_set< class hash_map : protected hash_set<
hash_map_internal::Slot<K, V>, hash_map_internal::Slot<K, V>,
Allocator, Allocator,
hash_map_internal::SlotHasher<K, V, KeyHasher>, hash_map_internal::SlotHasher<K, V, KeyHasher>,
hash_map_internal::SlotComparator<K, V, KeyComparator>> hash_map_internal::SlotComparator<K, V, KeyComparator>>
{ {
using Base = using Base =
hash_set< hash_set<
hash_map_internal::Slot<K, V>, hash_map_internal::Slot<K, V>,
Allocator, Allocator,
hash_map_internal::SlotHasher<K, V, KeyHasher>, hash_map_internal::SlotHasher<K, V, KeyHasher>,
hash_map_internal::SlotComparator<K, V, KeyComparator>>; hash_map_internal::SlotComparator<K, V, KeyComparator>>;
public: public:
constexpr hash_map() requires default_constructible<Allocator> = default; constexpr hash_map() requires default_constructible<Allocator> = default;
explicit constexpr hash_map(Allocator allocator) explicit constexpr hash_map(Allocator allocator)
: Base{ASL_MOVE(allocator)} : Base{ASL_MOVE(allocator)}
{} {}
hash_map(const hash_map&) requires copyable<K> && copyable<V> = default; hash_map(const hash_map&) requires copyable<K> && copyable<V> = default;
hash_map& operator=(const hash_map&) requires copyable<K> && copyable<V> = default; hash_map& operator=(const hash_map&) requires copyable<K> && copyable<V> = default;
hash_map(hash_map&&) = default; hash_map(hash_map&&) = default;
hash_map& operator=(hash_map&&) = default; hash_map& operator=(hash_map&&) = default;
~hash_map() = default; ~hash_map() = default;
using Base::destroy; using Base::destroy;
using Base::clear; using Base::clear;
using Base::size; using Base::size;
using Base::remove; using Base::remove;
using Base::contains; using Base::contains;
template<typename U, typename Arg0, typename... Args1> template<typename U, typename Arg0, typename... Args1>
requires requires
key_hasher<KeyHasher, U> && key_hasher<KeyHasher, U> &&
key_comparator<KeyComparator, K, U> && key_comparator<KeyComparator, K, U> &&
constructible_from<K, U&&> && constructible_from<K, U&&> &&
constructible_from<V, Arg0&&, Args1&&...> constructible_from<V, Arg0&&, Args1&&...>
void insert(U&& key, Arg0&& arg0, Args1&&... args1) void insert(U&& key, Arg0&& arg0, Args1&&... args1)
{ {
Base::maybe_grow_to_fit_one_more(); Base::maybe_grow_to_fit_one_more();
auto result = Base::find_slot_insert(key); auto result = Base::find_slot_insert(key);
// NOLINTBEGIN(*-pointer-arithmetic) // NOLINTBEGIN(*-pointer-arithmetic)
ASL_ASSERT(result.first_available_index >= 0); ASL_ASSERT(result.first_available_index >= 0);
if (result.already_present_index >= 0) if (result.already_present_index >= 0)
{ {
if (result.already_present_index != result.first_available_index) if (result.already_present_index != result.first_available_index)
{ {
ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); 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.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_values[result.already_present_index].destroy_unsafe();
Base::m_tags[result.first_available_index] = result.tag; Base::m_tags[result.first_available_index] = result.tag;
Base::m_tags[result.already_present_index] = Base::kTombstone; Base::m_tags[result.already_present_index] = Base::kTombstone;
} }
ASL_ASSERT(Base::m_tags[result.first_available_index] == result.tag); ASL_ASSERT(Base::m_tags[result.first_available_index] == result.tag);
if constexpr (sizeof...(Args1) == 0 && assignable_from<V&, Arg0&&>) if constexpr (sizeof...(Args1) == 0 && assignable_from<V&, Arg0&&>)
{ {
Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_FWD(arg0); Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_FWD(arg0);
} }
else else
{ {
Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_MOVE(V{ASL_FWD(arg0), ASL_FWD(args1)...}); Base::m_values[result.first_available_index].as_init_unsafe().value = ASL_MOVE(V{ASL_FWD(arg0), ASL_FWD(args1)...});
} }
} }
else else
{ {
ASL_ASSERT((Base::m_tags[result.first_available_index] & Base::kHasValue) == 0); 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_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_tags[result.first_available_index] = result.tag;
Base::m_size += 1; Base::m_size += 1;
} }
// NOLINTEND(*-pointer-arithmetic) // NOLINTEND(*-pointer-arithmetic)
} }
// @Todo(C++23) Deducing this // @Todo(C++23) Deducing this
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, K, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, K, U>
const V* get(const U& value) const const V* get(const U& value) const
{ {
isize_t index = Base::find_slot_lookup(value); isize_t index = Base::find_slot_lookup(value);
if (index >= 0) if (index >= 0)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return &Base::m_values[index].as_init_unsafe().value; return &Base::m_values[index].as_init_unsafe().value;
} }
return nullptr; return nullptr;
} }
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, K, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, K, U>
V* get(const U& value) V* get(const U& value)
{ {
isize_t index = Base::find_slot_lookup(value); isize_t index = Base::find_slot_lookup(value);
if (index >= 0) if (index >= 0)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return &Base::m_values[index].as_init_unsafe().value; return &Base::m_values[index].as_init_unsafe().value;
} }
return nullptr; return nullptr;
} }
}; };
} // namespace asl } // namespace asl

View File

@ -1,418 +1,418 @@
#pragma once #pragma once
#include "asl/annotations.hpp" #include "asl/annotations.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/maybe_uninit.hpp" #include "asl/maybe_uninit.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
namespace asl namespace asl
{ {
template<typename H, typename T> template<typename H, typename T>
concept key_hasher = requires (const T& value) concept key_hasher = requires (const T& value)
{ {
{ H::hash(value) } -> same_as<uint64_t>; { H::hash(value) } -> same_as<uint64_t>;
}; };
template<hashable T> template<hashable T>
struct default_key_hasher struct default_key_hasher
{ {
constexpr static uint64_t hash(const T& value) constexpr static uint64_t hash(const T& value)
{ {
return hash_value(value); return hash_value(value);
} }
}; };
template<typename C, typename U, typename V = U> template<typename C, typename U, typename V = U>
concept key_comparator = requires(const U& a, const V& b) concept key_comparator = requires(const U& a, const V& b)
{ {
{ C::eq(a, b) } -> same_as<bool>; { C::eq(a, b) } -> same_as<bool>;
}; };
template<equality_comparable T> template<equality_comparable T>
struct default_key_comparator struct default_key_comparator
{ {
constexpr static bool eq(const T& a, const T& b) constexpr static bool eq(const T& a, const T& b)
{ {
return a == b; return a == b;
} }
}; };
template< template<
is_object T, is_object T,
allocator Allocator = DefaultAllocator, allocator Allocator = DefaultAllocator,
key_hasher<T> KeyHasher = default_key_hasher<T>, key_hasher<T> KeyHasher = default_key_hasher<T>,
key_comparator<T> KeyComparator = default_key_comparator<T> key_comparator<T> KeyComparator = default_key_comparator<T>
> >
requires moveable<T> requires moveable<T>
class hash_set class hash_set
{ {
protected: protected:
static constexpr uint8_t kHasValue = 0x80; static constexpr uint8_t kHasValue = 0x80;
static constexpr uint8_t kHashMask = 0x7f; static constexpr uint8_t kHashMask = 0x7f;
static constexpr uint8_t kEmpty = 0x00; static constexpr uint8_t kEmpty = 0x00;
static constexpr uint8_t kTombstone = 0x01; static constexpr uint8_t kTombstone = 0x01;
static constexpr isize_t kMinCapacity = 8; static constexpr isize_t kMinCapacity = 8;
// Important so we can memzero the tags // Important so we can memzero the tags
static_assert(kEmpty == 0); static_assert(kEmpty == 0);
uint8_t* m_tags{}; uint8_t* m_tags{};
maybe_uninit<T>* m_values{}; maybe_uninit<T>* m_values{};
isize_t m_capacity{}; isize_t m_capacity{};
isize_t m_size{}; isize_t m_size{};
ASL_NO_UNIQUE_ADDRESS Allocator m_allocator; ASL_NO_UNIQUE_ADDRESS Allocator m_allocator;
constexpr isize_t max_size() const constexpr isize_t max_size() const
{ {
// Max load factor is 75% // Max load factor is 75%
return (m_capacity >> 1) + (m_capacity >> 2); return (m_capacity >> 1) + (m_capacity >> 2);
} }
static isize_t size_to_capacity(isize_t size) static isize_t size_to_capacity(isize_t size)
{ {
ASL_ASSERT(size > 0); ASL_ASSERT(size > 0);
return max<isize_t>( return max<isize_t>(
kMinCapacity, kMinCapacity,
static_cast<isize_t>(round_up_pow2((static_cast<uint64_t>(size) * 4 + 2) / 3))); static_cast<isize_t>(round_up_pow2((static_cast<uint64_t>(size) * 4 + 2) / 3)));
} }
static void insert_inner( static void insert_inner(
T&& value, T&& value,
uint8_t* tags, uint8_t* tags,
maybe_uninit<T>* values, maybe_uninit<T>* values,
isize_t capacity, isize_t capacity,
isize_t* size) isize_t* size)
{ {
ASL_ASSERT(*size < capacity); ASL_ASSERT(*size < capacity);
const auto result = find_slot_insert(value, tags, values, capacity); const auto result = find_slot_insert(value, tags, values, capacity);
// NOLINTBEGIN(*-pointer-arithmetic) // NOLINTBEGIN(*-pointer-arithmetic)
ASL_ASSERT(result.first_available_index >= 0); ASL_ASSERT(result.first_available_index >= 0);
if (result.already_present_index != result.first_available_index) if (result.already_present_index != result.first_available_index)
{ {
ASL_ASSERT((tags[result.first_available_index] & kHasValue) == 0); ASL_ASSERT((tags[result.first_available_index] & kHasValue) == 0);
if (result.already_present_index >= 0) if (result.already_present_index >= 0)
{ {
ASL_ASSERT((tags[result.already_present_index] & kHasValue) != 0); ASL_ASSERT((tags[result.already_present_index] & kHasValue) != 0);
values[result.already_present_index].destroy_unsafe(); values[result.already_present_index].destroy_unsafe();
tags[result.already_present_index] = kTombstone; tags[result.already_present_index] = kTombstone;
} }
else else
{ {
*size += 1; *size += 1;
} }
values[result.first_available_index].construct_unsafe(ASL_MOVE(value)); values[result.first_available_index].construct_unsafe(ASL_MOVE(value));
tags[result.first_available_index] = result.tag; tags[result.first_available_index] = result.tag;
} }
// NOLINTEND(*-pointer-arithmetic) // NOLINTEND(*-pointer-arithmetic)
} }
void grow_and_rehash() void grow_and_rehash()
{ {
grow_and_rehash(max(kMinCapacity, m_capacity * 2)); grow_and_rehash(max(kMinCapacity, m_capacity * 2));
} }
void grow_and_rehash(isize_t new_capacity) void grow_and_rehash(isize_t new_capacity)
{ {
ASL_ASSERT(new_capacity >= kMinCapacity && is_pow2(new_capacity) && new_capacity > m_capacity); ASL_ASSERT(new_capacity >= kMinCapacity && is_pow2(new_capacity) && new_capacity > m_capacity);
auto* new_tags = reinterpret_cast<uint8_t*>(m_allocator.alloc(layout::array<uint8_t>(new_capacity))); auto* new_tags = reinterpret_cast<uint8_t*>(m_allocator.alloc(layout::array<uint8_t>(new_capacity)));
auto* new_values = reinterpret_cast<maybe_uninit<T>*>(m_allocator.alloc(layout::array<maybe_uninit<T>>(new_capacity))); auto* new_values = reinterpret_cast<maybe_uninit<T>*>(m_allocator.alloc(layout::array<maybe_uninit<T>>(new_capacity)));
asl::memzero(new_tags, new_capacity); asl::memzero(new_tags, new_capacity);
isize_t new_size = 0; isize_t new_size = 0;
if (m_size > 0) if (m_size > 0)
{ {
// NOLINTBEGIN(*-pointer-arithmetic) // NOLINTBEGIN(*-pointer-arithmetic)
for (isize_t i = 0; i < m_capacity; ++i) for (isize_t i = 0; i < m_capacity; ++i)
{ {
if ((m_tags[i] & kHasValue) == 0) { continue; } 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); 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 // Destroy now so that destroy() has less things to do
m_values[i].destroy_unsafe(); m_values[i].destroy_unsafe();
m_tags[i] = kTombstone; m_tags[i] = kTombstone;
} }
// NOLINTEND(*-pointer-arithmetic) // NOLINTEND(*-pointer-arithmetic)
} }
ASL_ASSERT(new_size == m_size); ASL_ASSERT(new_size == m_size);
m_size = 0; m_size = 0;
destroy(); destroy();
m_tags = new_tags; m_tags = new_tags;
m_values = new_values; m_values = new_values;
m_capacity = new_capacity; m_capacity = new_capacity;
m_size = new_size; m_size = new_size;
} }
void clear_values() void clear_values()
{ {
if constexpr (!trivially_destructible<T>) if constexpr (!trivially_destructible<T>)
{ {
if (m_size > 0) if (m_size > 0)
{ {
for (isize_t i = 0; i < m_capacity; ++i) for (isize_t i = 0; i < m_capacity; ++i)
{ {
if ((m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) if ((m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic)
{ {
m_values[i].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) m_values[i].destroy_unsafe(); // NOLINT(*-pointer-arithmetic)
} }
} }
} }
} }
} }
void copy_from(const hash_set& other) void copy_from(const hash_set& other)
{ {
if (other.size() > 0) if (other.size() > 0)
{ {
isize_t min_capacity = size_to_capacity(other.size()); isize_t min_capacity = size_to_capacity(other.size());
if (m_capacity < min_capacity) if (m_capacity < min_capacity)
{ {
grow_and_rehash(min_capacity); grow_and_rehash(min_capacity);
} }
ASL_ASSERT(m_capacity >= min_capacity); ASL_ASSERT(m_capacity >= min_capacity);
for (isize_t i = 0; i < other.m_capacity; ++i) for (isize_t i = 0; i < other.m_capacity; ++i)
{ {
if ((other.m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic) if ((other.m_tags[i] & kHasValue) != 0) // NOLINT(*-pointer-arithmetic)
{ {
insert(other.m_values[i].as_init_unsafe()); // NOLINT(*-pointer-arithmetic) insert(other.m_values[i].as_init_unsafe()); // NOLINT(*-pointer-arithmetic)
} }
} }
} }
} }
struct FindSlotResult struct FindSlotResult
{ {
uint8_t tag{}; uint8_t tag{};
isize_t first_available_index = -1; isize_t first_available_index = -1;
isize_t already_present_index = -1; isize_t already_present_index = -1;
}; };
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U>
static FindSlotResult find_slot_insert( static FindSlotResult find_slot_insert(
const U& value, const U& value,
const uint8_t* tags, const uint8_t* tags,
const maybe_uninit<T>* values, const maybe_uninit<T>* values,
isize_t capacity) isize_t capacity)
{ {
ASL_ASSERT(is_pow2(capacity)); ASL_ASSERT(is_pow2(capacity));
FindSlotResult result{}; FindSlotResult result{};
const isize_t capacity_mask = capacity - 1; const isize_t capacity_mask = capacity - 1;
const uint64_t hash = KeyHasher::hash(value); const uint64_t hash = KeyHasher::hash(value);
const auto starting_index = static_cast<isize_t>(hash >> 7) & capacity_mask; const auto starting_index = static_cast<isize_t>(hash >> 7) & capacity_mask;
result.tag = static_cast<uint8_t>(hash & kHashMask) | kHasValue; result.tag = static_cast<uint8_t>(hash & kHashMask) | kHasValue;
// NOLINTBEGIN(*-pointer-arithmetic) // NOLINTBEGIN(*-pointer-arithmetic)
for ( for (
isize_t i = starting_index; isize_t i = starting_index;
i != starting_index || result.first_available_index < 0; i != starting_index || result.first_available_index < 0;
i = (i + 1) & capacity_mask) i = (i + 1) & capacity_mask)
{ {
uint8_t t = tags[i]; uint8_t t = tags[i];
if ((t & kHasValue) == 0 && result.first_available_index < 0) if ((t & kHasValue) == 0 && result.first_available_index < 0)
{ {
result.first_available_index = i; result.first_available_index = i;
} }
if (t == result.tag && KeyComparator::eq(values[i].as_init_unsafe(), value)) if (t == result.tag && KeyComparator::eq(values[i].as_init_unsafe(), value))
{ {
ASL_ASSERT(result.already_present_index < 0); ASL_ASSERT(result.already_present_index < 0);
result.already_present_index = i; result.already_present_index = i;
if (result.first_available_index < 0) if (result.first_available_index < 0)
{ {
result.first_available_index = i; result.first_available_index = i;
} }
break; break;
} }
if (t == kEmpty) { break; } if (t == kEmpty) { break; }
} }
// NOLINTEND(*-pointer-arithmetic) // NOLINTEND(*-pointer-arithmetic)
return result; return result;
} }
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U>
isize_t find_slot_lookup(const U& value) const isize_t find_slot_lookup(const U& value) const
{ {
if (m_size <= 0) { return -1; }; if (m_size <= 0) { return -1; };
ASL_ASSERT(is_pow2(m_capacity)); ASL_ASSERT(is_pow2(m_capacity));
const isize_t capacity_mask = m_capacity - 1; const isize_t capacity_mask = m_capacity - 1;
const uint64_t hash = KeyHasher::hash(value); const uint64_t hash = KeyHasher::hash(value);
const uint8_t tag = static_cast<uint8_t>(hash & kHashMask) | kHasValue; const uint8_t tag = static_cast<uint8_t>(hash & kHashMask) | kHasValue;
const auto starting_index = static_cast<isize_t>(hash >> 7) & capacity_mask; const auto starting_index = static_cast<isize_t>(hash >> 7) & capacity_mask;
// NOLINTBEGIN(*-pointer-arithmetic) // NOLINTBEGIN(*-pointer-arithmetic)
isize_t i = starting_index; isize_t i = starting_index;
do do
{ {
const uint8_t t = m_tags[i]; const uint8_t t = m_tags[i];
if (t == tag && KeyComparator::eq(m_values[i].as_init_unsafe(), value)) { return i; } if (t == tag && KeyComparator::eq(m_values[i].as_init_unsafe(), value)) { return i; }
if (t == kEmpty) { break; } if (t == kEmpty) { break; }
i = (i + 1) & capacity_mask; i = (i + 1) & capacity_mask;
} while (i != starting_index); } while (i != starting_index);
// NOLINTEND(*-pointer-arithmetic) // NOLINTEND(*-pointer-arithmetic)
return -1; return -1;
} }
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U>
FindSlotResult find_slot_insert(const U& value) FindSlotResult find_slot_insert(const U& value)
{ {
return find_slot_insert(value, m_tags, m_values, m_capacity); return find_slot_insert(value, m_tags, m_values, m_capacity);
} }
void maybe_grow_to_fit_one_more() void maybe_grow_to_fit_one_more()
{ {
if (m_size >= max_size()) if (m_size >= max_size())
{ {
grow_and_rehash(); grow_and_rehash();
} }
} }
public: public:
constexpr hash_set() requires default_constructible<Allocator> constexpr hash_set() requires default_constructible<Allocator>
: m_allocator{} : m_allocator{}
{} {}
explicit constexpr hash_set(Allocator allocator) explicit constexpr hash_set(Allocator allocator)
: m_allocator{ASL_MOVE(allocator)} : m_allocator{ASL_MOVE(allocator)}
{} {}
hash_set(const hash_set& other) hash_set(const hash_set& other)
requires copy_constructible<Allocator> && copyable<T> requires copy_constructible<Allocator> && copyable<T>
: hash_set{other.m_allocator} : hash_set{other.m_allocator}
{ {
copy_from(other); copy_from(other);
} }
hash_set& operator=(const hash_set& other) hash_set& operator=(const hash_set& other)
requires copyable<T> requires copyable<T>
{ {
if (&other != this) if (&other != this)
{ {
clear(); clear();
copy_from(other); copy_from(other);
} }
return *this; return *this;
} }
hash_set(hash_set&& other) hash_set(hash_set&& other)
requires move_constructible<Allocator> requires move_constructible<Allocator>
: m_tags{exchange(other.m_tags, nullptr)} : m_tags{exchange(other.m_tags, nullptr)}
, m_values{exchange(other.m_values, nullptr)} , m_values{exchange(other.m_values, nullptr)}
, m_capacity{exchange(other.m_capacity, 0)} , m_capacity{exchange(other.m_capacity, 0)}
, m_size{exchange(other.m_size, 0)} , m_size{exchange(other.m_size, 0)}
, m_allocator{ASL_MOVE(other.m_allocator)} , m_allocator{ASL_MOVE(other.m_allocator)}
{} {}
hash_set& operator=(hash_set&& other) hash_set& operator=(hash_set&& other)
{ {
if (&other != this) if (&other != this)
{ {
destroy(); destroy();
m_tags = exchange(other.m_tags, nullptr); m_tags = exchange(other.m_tags, nullptr);
m_values = exchange(other.m_values, nullptr); m_values = exchange(other.m_values, nullptr);
m_capacity = exchange(other.m_capacity, 0); m_capacity = exchange(other.m_capacity, 0);
m_size = exchange(other.m_size, 0); m_size = exchange(other.m_size, 0);
m_allocator = ASL_MOVE(other.m_allocator); m_allocator = ASL_MOVE(other.m_allocator);
} }
return *this; return *this;
} }
~hash_set() ~hash_set()
{ {
destroy(); destroy();
} }
void destroy() void destroy()
{ {
clear_values(); clear_values();
m_size = 0; m_size = 0;
if (m_capacity > 0) if (m_capacity > 0)
{ {
m_allocator.dealloc(m_tags, layout::array<uint8_t>(m_capacity)); m_allocator.dealloc(m_tags, layout::array<uint8_t>(m_capacity));
m_allocator.dealloc(m_values, layout::array<maybe_uninit<T>>(m_capacity)); m_allocator.dealloc(m_values, layout::array<maybe_uninit<T>>(m_capacity));
m_capacity = 0; m_capacity = 0;
} }
} }
void clear() void clear()
{ {
clear_values(); clear_values();
m_size = 0; m_size = 0;
if (m_capacity > 0) if (m_capacity > 0)
{ {
asl::memzero(m_tags, m_capacity); asl::memzero(m_tags, m_capacity);
} }
} }
constexpr isize_t size() const { return m_size; } constexpr isize_t size() const { return m_size; }
template<typename... Args> template<typename... Args>
void insert(Args&&... args) void insert(Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
{ {
maybe_grow_to_fit_one_more(); maybe_grow_to_fit_one_more();
ASL_ASSERT(m_size < max_size()); ASL_ASSERT(m_size < max_size());
insert_inner(ASL_MOVE(T{ASL_FWD(args)...}), m_tags, m_values, m_capacity, &m_size); insert_inner(ASL_MOVE(T{ASL_FWD(args)...}), m_tags, m_values, m_capacity, &m_size);
} }
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U>
bool contains(const U& value) const bool contains(const U& value) const
{ {
return find_slot_lookup(value) >= 0; return find_slot_lookup(value) >= 0;
} }
template<typename U> template<typename U>
requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U> requires key_hasher<KeyHasher, U> && key_comparator<KeyComparator, T, U>
bool remove(const U& value) bool remove(const U& value)
{ {
isize_t slot = find_slot_lookup(value); isize_t slot = find_slot_lookup(value);
if (slot < 0) { return false; } if (slot < 0) { return false; }
m_values[slot].destroy_unsafe(); // NOLINT(*-pointer-arithmetic) m_values[slot].destroy_unsafe(); // NOLINT(*-pointer-arithmetic)
m_tags[slot] = kTombstone; // NOLINT(*-pointer-arithmetic) m_tags[slot] = kTombstone; // NOLINT(*-pointer-arithmetic)
m_size -= 1; m_size -= 1;
return true; return true;
} }
}; };
} // namespace asl } // namespace asl

View File

@ -1,40 +1,40 @@
#pragma once #pragma once
#include "asl/config.hpp" #include "asl/config.hpp"
using int8_t = signed char; using int8_t = signed char;
using int16_t = signed short; using int16_t = signed short;
using int32_t = signed int; using int32_t = signed int;
#if ASL_OS_WINDOWS #if ASL_OS_WINDOWS
using int64_t = signed long long; using int64_t = signed long long;
#elif ASL_OS_LINUX #elif ASL_OS_LINUX
using int64_t = signed long; using int64_t = signed long;
#endif #endif
using uint8_t = unsigned char; using uint8_t = unsigned char;
using uint16_t = unsigned short; using uint16_t = unsigned short;
using uint32_t = unsigned int; using uint32_t = unsigned int;
#if ASL_OS_WINDOWS #if ASL_OS_WINDOWS
using uint64_t = unsigned long long; using uint64_t = unsigned long long;
#elif ASL_OS_LINUX #elif ASL_OS_LINUX
using uint64_t = unsigned long; using uint64_t = unsigned long;
#endif #endif
struct uint128_t struct uint128_t
{ {
uint64_t high; uint64_t high;
uint64_t low; uint64_t low;
}; };
using size_t = uint64_t; using size_t = uint64_t;
using isize_t = int64_t; using isize_t = int64_t;
using uintptr_t = size_t; using uintptr_t = size_t;
namespace asl namespace asl
{ {
enum class byte : uint8_t {}; enum class byte : uint8_t {};
} // namespace asl } // namespace asl

View File

@ -1,20 +1,20 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/span.hpp" #include "asl/span.hpp"
namespace asl namespace asl
{ {
class Writer class Writer
{ {
public: public:
Writer() = default; Writer() = default;
ASL_DELETE_COPY_MOVE(Writer); ASL_DELETE_COPY_MOVE(Writer);
virtual ~Writer() = default; virtual ~Writer() = default;
virtual void write(span<const byte>) = 0; virtual void write(span<const byte>) = 0;
}; };
} // namespace asl } // namespace asl

View File

@ -1,37 +1,37 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
namespace asl namespace asl
{ {
template<typename T> template<typename T>
inline constexpr isize_t size_of = static_cast<isize_t>(sizeof(T)); inline constexpr isize_t size_of = static_cast<isize_t>(sizeof(T));
template<typename T> template<typename T>
inline constexpr isize_t align_of = static_cast<isize_t>(alignof(T)); inline constexpr isize_t align_of = static_cast<isize_t>(alignof(T));
struct layout struct layout
{ {
isize_t size; isize_t size;
isize_t align; isize_t align;
constexpr bool operator==(const layout&) const = default; constexpr bool operator==(const layout&) const = default;
template<is_object T> template<is_object T>
static constexpr layout of() static constexpr layout of()
{ {
return layout{ size_of<T>, align_of<T> }; return layout{ size_of<T>, align_of<T> };
} }
template<is_object T> template<is_object T>
static constexpr layout array(isize_t size) static constexpr layout array(isize_t size)
{ {
return layout{ size_of<T> * size, align_of<T> }; return layout{ size_of<T> * size, align_of<T> };
} }
}; };
} // namespace asl } // namespace asl
#define AslOffsetOf(S, M) (static_cast<isize_t>(__builtin_offsetof(S, M))) #define AslOffsetOf(S, M) (static_cast<isize_t>(__builtin_offsetof(S, M)))

View File

@ -1,26 +1,26 @@
cc_library( cc_library(
name = "log", name = "log",
srcs = [ srcs = [
"log.hpp", "log.hpp",
], ],
hdrs = [ hdrs = [
"log.cpp", "log.cpp",
], ],
deps = [ deps = [
"//asl", "//asl",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
cc_test( cc_test(
name = "tests", name = "tests",
srcs = [ srcs = [
"log_tests.cpp" "log_tests.cpp"
], ],
deps = [ deps = [
":log", ":log",
"//asl/testing", "//asl/testing",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

View File

@ -1,72 +1,72 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/memory.hpp" #include "asl/memory.hpp"
namespace asl namespace asl
{ {
template<is_object T> template<is_object T>
union maybe_uninit union maybe_uninit
{ {
private: private:
T m_value; T m_value;
public: public:
constexpr maybe_uninit() requires trivially_default_constructible<T> = default; constexpr maybe_uninit() requires trivially_default_constructible<T> = default;
constexpr maybe_uninit() requires (!trivially_default_constructible<T>) {} // NOLINT constexpr maybe_uninit() requires (!trivially_default_constructible<T>) {} // NOLINT
template<typename... Args> template<typename... Args>
explicit constexpr maybe_uninit(in_place_t, Args&&... args) explicit constexpr maybe_uninit(in_place_t, Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
: m_value{ASL_FWD(args)...} : m_value{ASL_FWD(args)...}
{} {}
constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible<T> = default; constexpr maybe_uninit(const maybe_uninit&) requires trivially_copy_constructible<T> = default;
constexpr maybe_uninit(const maybe_uninit&) requires (!trivially_copy_constructible<T>) {} // NOLINT constexpr maybe_uninit(const maybe_uninit&) requires (!trivially_copy_constructible<T>) {} // NOLINT
constexpr maybe_uninit(maybe_uninit&&) requires trivially_move_constructible<T> = default; constexpr maybe_uninit(maybe_uninit&&) requires trivially_move_constructible<T> = default;
constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible<T>) {} // NOLINT constexpr maybe_uninit(maybe_uninit&&) requires (!trivially_move_constructible<T>) {} // NOLINT
constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable<T> = default; constexpr maybe_uninit& operator=(const maybe_uninit&) requires trivially_copy_assignable<T> = default;
constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable<T>) {} constexpr maybe_uninit& operator=(const maybe_uninit&) requires (!trivially_copy_assignable<T>) {}
constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable<T> = default; constexpr maybe_uninit& operator=(maybe_uninit&&) requires trivially_move_assignable<T> = default;
constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable<T>) {} constexpr maybe_uninit& operator=(maybe_uninit&&) requires (!trivially_move_assignable<T>) {}
constexpr ~maybe_uninit() requires trivially_destructible<T> = default; constexpr ~maybe_uninit() requires trivially_destructible<T> = default;
constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {} // NOLINT constexpr ~maybe_uninit() requires (!trivially_destructible<T>) {} // NOLINT
// @Safety Value must not have been initialized yet // @Safety Value must not have been initialized yet
template<typename... Args> template<typename... Args>
constexpr void construct_unsafe(Args&&... args) constexpr void construct_unsafe(Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
{ {
construct_at<T>(&m_value, ASL_FWD(args)...); construct_at<T>(&m_value, ASL_FWD(args)...);
} }
// @Safety Value must have been initialized // @Safety Value must have been initialized
template<typename U> template<typename U>
constexpr void assign_unsafe(U&& value) constexpr void assign_unsafe(U&& value)
requires assignable_from<T&, U&&> requires assignable_from<T&, U&&>
{ {
m_value = ASL_FWD(value); m_value = ASL_FWD(value);
} }
// @Safety Value must have been initialized // @Safety Value must have been initialized
constexpr void destroy_unsafe() constexpr void destroy_unsafe()
{ {
if constexpr (!trivially_destructible<T>) if constexpr (!trivially_destructible<T>)
{ {
destroy(&m_value); destroy(&m_value);
} }
} }
// @Safety Value must have been initialized // @Safety Value must have been initialized
constexpr const T& as_init_unsafe() const& { return m_value; } constexpr const T& as_init_unsafe() const& { return m_value; }
constexpr T& as_init_unsafe() & { return m_value; } constexpr T& as_init_unsafe() & { return m_value; }
constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); } constexpr T&& as_init_unsafe() && { return ASL_MOVE(m_value); }
}; };
} // namespace asl } // namespace asl

View File

@ -1,137 +1,137 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/layout.hpp" #include "asl/layout.hpp"
#include "asl/utility.hpp" #include "asl/utility.hpp"
constexpr void* operator new(size_t, void* ptr) constexpr void* operator new(size_t, void* ptr)
{ {
return ptr; return ptr;
} }
namespace asl namespace asl
{ {
constexpr isize_t memcmp(const void* a, const void* b, isize_t size) constexpr isize_t memcmp(const void* a, const void* b, isize_t size)
{ {
return __builtin_memcmp(a, b, static_cast<size_t>(size)); return __builtin_memcmp(a, b, static_cast<size_t>(size));
} }
constexpr void memcpy(void* dst, const void* src, isize_t size) constexpr void memcpy(void* dst, const void* src, isize_t size)
{ {
__builtin_memcpy(dst, src, static_cast<size_t>(size)); __builtin_memcpy(dst, src, static_cast<size_t>(size));
} }
inline void memzero(void* dst, isize_t size) inline void memzero(void* dst, isize_t size)
{ {
__builtin_memset(dst, 0, static_cast<size_t>(size)); __builtin_memset(dst, 0, static_cast<size_t>(size));
} }
constexpr isize_t strlen(const char* s) constexpr isize_t strlen(const char* s)
{ {
return static_cast<isize_t>(__builtin_strlen(s)); return static_cast<isize_t>(__builtin_strlen(s));
} }
template<typename T, typename... Args> template<typename T, typename... Args>
constexpr T* construct_at(void* ptr, Args&&... args) constexpr T* construct_at(void* ptr, Args&&... args)
requires constructible_from<T, Args&&...> requires constructible_from<T, Args&&...>
{ {
return new (ptr) T{ ASL_FWD(args)... }; return new (ptr) T{ ASL_FWD(args)... };
} }
template<typename T> template<typename T>
constexpr void destroy(T* data) constexpr void destroy(T* data)
{ {
if constexpr (!trivially_destructible<T>) if constexpr (!trivially_destructible<T>)
{ {
data->~T(); data->~T();
} }
} }
template<typename T> template<typename T>
constexpr void destroy_n(T* data, isize_t n) constexpr void destroy_n(T* data, isize_t n)
{ {
if constexpr (!trivially_destructible<T>) if constexpr (!trivially_destructible<T>)
{ {
for (isize_t i = 0; i < n; ++i) for (isize_t i = 0; i < n; ++i)
{ {
destroy(data + i); destroy(data + i);
} }
} }
} }
template<copy_constructible T> template<copy_constructible T>
constexpr void copy_uninit_n(T* to, const T* from, isize_t n) constexpr void copy_uninit_n(T* to, const T* from, isize_t n)
{ {
if constexpr (trivially_copy_constructible<T>) if constexpr (trivially_copy_constructible<T>)
{ {
memcpy(to, from, size_of<T> * n); memcpy(to, from, size_of<T> * n);
} }
else else
{ {
for (isize_t i = 0; i < n; ++i) for (isize_t i = 0; i < n; ++i)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
construct_at<T>(to + i, from[i]); construct_at<T>(to + i, from[i]);
} }
} }
} }
template<copy_assignable T> template<copy_assignable T>
constexpr void copy_assign_n(T* to, const T* from, isize_t n) constexpr void copy_assign_n(T* to, const T* from, isize_t n)
{ {
if constexpr (trivially_copy_constructible<T>) if constexpr (trivially_copy_constructible<T>)
{ {
memcpy(to, from, size_of<T> * n); memcpy(to, from, size_of<T> * n);
} }
else else
{ {
for (isize_t i = 0; i < n; ++i) for (isize_t i = 0; i < n; ++i)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
to[i] = from[i]; to[i] = from[i];
} }
} }
} }
template<move_constructible T> template<move_constructible T>
constexpr void relocate_uninit_n(T* to, T* from, isize_t n) constexpr void relocate_uninit_n(T* to, T* from, isize_t n)
{ {
if constexpr (trivially_move_constructible<T>) if constexpr (trivially_move_constructible<T>)
{ {
static_assert(trivially_destructible<T>); static_assert(trivially_destructible<T>);
memcpy(to, from, size_of<T> * n); memcpy(to, from, size_of<T> * n);
} }
else else
{ {
for (isize_t i = 0; i < n; ++i) for (isize_t i = 0; i < n; ++i)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
construct_at<T>(to + i, ASL_MOVE(from[i])); construct_at<T>(to + i, ASL_MOVE(from[i]));
} }
destroy_n(from, n); destroy_n(from, n);
} }
} }
template<move_assignable T> template<move_assignable T>
constexpr void relocate_assign_n(T* to, T* from, isize_t n) constexpr void relocate_assign_n(T* to, T* from, isize_t n)
{ {
if constexpr (trivially_move_assignable<T>) if constexpr (trivially_move_assignable<T>)
{ {
static_assert(trivially_destructible<T>); static_assert(trivially_destructible<T>);
memcpy(to, from, size_of<T> * n); memcpy(to, from, size_of<T> * n);
} }
else else
{ {
for (isize_t i = 0; i < n; ++i) for (isize_t i = 0; i < n; ++i)
{ {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
to[i] = ASL_MOVE(from[i]); to[i] = ASL_MOVE(from[i]);
} }
destroy_n(from, n); destroy_n(from, n);
} }
} }
} // namespace asl } // namespace asl

View File

@ -1,221 +1,221 @@
#pragma once #pragma once
#include "asl/integers.hpp" #include "asl/integers.hpp"
namespace asl { namespace asl {
struct source_location struct source_location
{ {
const char* file; const char* file;
int line; int line;
explicit source_location( explicit source_location(
const char* file_ = __builtin_FILE(), const char* file_ = __builtin_FILE(),
int line_ = __builtin_LINE()) int line_ = __builtin_LINE())
: file{file_} : file{file_}
, line{line_} , line{line_}
{} {}
}; };
struct empty {}; struct empty {};
template<typename T> struct id { using type = T; }; template<typename T> struct id { using type = T; };
template<typename... Args> static constexpr isize_t types_count = sizeof...(Args); template<typename... Args> static constexpr isize_t types_count = sizeof...(Args);
template<typename T, T kValue> struct integral_constant { static constexpr T value = kValue; }; template<typename T, T kValue> struct integral_constant { static constexpr T value = kValue; };
template<bool B> using bool_constant = integral_constant<bool, B>; template<bool B> using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>; using true_type = bool_constant<true>;
using false_type = bool_constant<false>; using false_type = bool_constant<false>;
template<bool kSelect, typename U, typename V> struct _select_helper { using type = V; }; template<bool kSelect, typename U, typename V> struct _select_helper { using type = V; };
template<typename U, typename V> struct _select_helper<true, U, V> { using type = U; }; template<typename U, typename V> struct _select_helper<true, U, V> { using type = U; };
template<bool kSelect, typename U, typename V> using select_t = _select_helper<kSelect, U, V>::type; template<bool kSelect, typename U, typename V> using select_t = _select_helper<kSelect, U, V>::type;
template<typename U, typename V> struct _same_as_helper : false_type {}; template<typename U, typename V> struct _same_as_helper : false_type {};
template<typename T> struct _same_as_helper<T, T> : true_type {}; template<typename T> struct _same_as_helper<T, T> : true_type {};
template<typename U, typename V> concept same_as = _same_as_helper<U, V>::value && _same_as_helper<V, U>::value; template<typename U, typename V> concept same_as = _same_as_helper<U, V>::value && _same_as_helper<V, U>::value;
template<typename T> auto _as_lref_helper(int) -> id<T&>; template<typename T> auto _as_lref_helper(int) -> id<T&>;
template<typename T> auto _as_lref_helper(...) -> id<T>; template<typename T> auto _as_lref_helper(...) -> id<T>;
template<typename T> auto _as_rref_helper(int) -> id<T&&>; template<typename T> auto _as_rref_helper(int) -> id<T&&>;
template<typename T> auto _as_rref_helper(...) -> id<T>; template<typename T> auto _as_rref_helper(...) -> id<T>;
template<typename T> using as_lref_t = decltype(_as_lref_helper<T>(0))::type; template<typename T> using as_lref_t = decltype(_as_lref_helper<T>(0))::type;
template<typename T> using as_rref_t = decltype(_as_rref_helper<T>(0))::type; template<typename T> using as_rref_t = decltype(_as_rref_helper<T>(0))::type;
template<typename T> consteval as_rref_t<T> declval() {} template<typename T> consteval as_rref_t<T> declval() {}
template<typename T> struct _un_ref_t { using type = T; }; template<typename T> struct _un_ref_t { using type = T; };
template<typename T> struct _un_ref_t<T&> { using type = T; }; template<typename T> struct _un_ref_t<T&> { using type = T; };
template<typename T> struct _un_ref_t<T&&> { using type = T; }; template<typename T> struct _un_ref_t<T&&> { using type = T; };
template<typename T> using un_ref_t = _un_ref_t<T>::type; template<typename T> using un_ref_t = _un_ref_t<T>::type;
template<typename T, typename... Args> concept constructible_from = __is_constructible(T, Args...); template<typename T, typename... Args> concept constructible_from = __is_constructible(T, Args...);
template<typename T> concept default_constructible = constructible_from<T>; template<typename T> concept default_constructible = constructible_from<T>;
template<typename T> concept copy_constructible = constructible_from<T, as_lref_t<const T>>; template<typename T> concept copy_constructible = constructible_from<T, as_lref_t<const T>>;
template<typename T> concept move_constructible = constructible_from<T, as_rref_t<T>>; template<typename T> concept move_constructible = constructible_from<T, as_rref_t<T>>;
template<typename T, typename... Args> concept trivially_constructible_from = __is_trivially_constructible(T, Args...); template<typename T, typename... Args> concept trivially_constructible_from = __is_trivially_constructible(T, Args...);
template<typename T> concept trivially_default_constructible = trivially_constructible_from<T>; template<typename T> concept trivially_default_constructible = trivially_constructible_from<T>;
template<typename T> concept trivially_copy_constructible = trivially_constructible_from<T, as_lref_t<const T>>; template<typename T> concept trivially_copy_constructible = trivially_constructible_from<T, as_lref_t<const T>>;
template<typename T> concept trivially_move_constructible = trivially_constructible_from<T, as_rref_t<T>>; template<typename T> concept trivially_move_constructible = trivially_constructible_from<T, as_rref_t<T>>;
template<typename T, typename... Args> concept assignable_from = __is_assignable(T, Args...); template<typename T, typename... Args> concept assignable_from = __is_assignable(T, Args...);
template<typename T> concept copy_assignable = assignable_from<as_lref_t<T>, as_lref_t<const T>>; template<typename T> concept copy_assignable = assignable_from<as_lref_t<T>, as_lref_t<const T>>;
template<typename T> concept move_assignable = assignable_from<as_lref_t<T>, as_rref_t<T>>; template<typename T> concept move_assignable = assignable_from<as_lref_t<T>, as_rref_t<T>>;
template<typename T, typename... Args> concept trivially_assignable_from = __is_trivially_assignable(T, Args...); template<typename T, typename... Args> concept trivially_assignable_from = __is_trivially_assignable(T, Args...);
template<typename T> concept trivially_copy_assignable = trivially_assignable_from<as_lref_t<T>, as_lref_t<const T>>; template<typename T> concept trivially_copy_assignable = trivially_assignable_from<as_lref_t<T>, as_lref_t<const T>>;
template<typename T> concept trivially_move_assignable = trivially_assignable_from<as_lref_t<T>, as_rref_t<T>>; template<typename T> concept trivially_move_assignable = trivially_assignable_from<as_lref_t<T>, as_rref_t<T>>;
template<typename T> concept trivially_destructible = __is_trivially_destructible(T); template<typename T> concept trivially_destructible = __is_trivially_destructible(T);
template<typename T> concept copyable = copy_constructible<T> && copy_assignable<T>; template<typename T> concept copyable = copy_constructible<T> && copy_assignable<T>;
template<typename T> concept moveable = move_constructible<T> && move_assignable<T>; template<typename T> concept moveable = move_constructible<T> && move_assignable<T>;
template<typename To, typename From> template<typename To, typename From>
concept convertible_from = __is_convertible(From, To); concept convertible_from = __is_convertible(From, To);
template<typename Derived, class Base> template<typename Derived, class Base>
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from<const volatile Base*, const volatile Derived*>; concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from<const volatile Base*, const volatile Derived*>;
using nullptr_t = decltype(nullptr); using nullptr_t = decltype(nullptr);
template<typename T> struct _un_const_helper { using type = T; }; template<typename T> struct _un_const_helper { using type = T; };
template<typename T> struct _un_const_helper<const T> { using type = T; }; template<typename T> struct _un_const_helper<const T> { using type = T; };
template<typename T> using un_const_t = _un_const_helper<T>::type; template<typename T> using un_const_t = _un_const_helper<T>::type;
template<typename T> struct _is_const_helper : false_type {}; template<typename T> struct _is_const_helper : false_type {};
template<typename T> struct _is_const_helper<const T> : true_type {}; template<typename T> struct _is_const_helper<const T> : true_type {};
template<typename T> concept is_const = _is_const_helper<T>::value; template<typename T> concept is_const = _is_const_helper<T>::value;
template<typename T> struct _un_volatile_helper { using type = T; }; template<typename T> struct _un_volatile_helper { using type = T; };
template<typename T> struct _un_volatile_helper<volatile T> { using type = T; }; template<typename T> struct _un_volatile_helper<volatile T> { using type = T; };
template<typename T> using un_volatile_t = _un_volatile_helper<T>::type; template<typename T> using un_volatile_t = _un_volatile_helper<T>::type;
template<typename T> using un_cv_t = un_volatile_t<un_const_t<T>>; template<typename T> using un_cv_t = un_volatile_t<un_const_t<T>>;
template<typename T> using un_cvref_t = un_ref_t<un_cv_t<T>>; template<typename T> using un_cvref_t = un_ref_t<un_cv_t<T>>;
template<typename T> concept is_void = same_as<void, un_cv_t<T>>; template<typename T> concept is_void = same_as<void, un_cv_t<T>>;
template<typename T> struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = false; }; template<typename T> struct _is_ref_helper { static constexpr bool l = false; static constexpr bool r = false; };
template<typename T> struct _is_ref_helper<T&> { static constexpr bool l = true; static constexpr bool r = false; }; template<typename T> struct _is_ref_helper<T&> { static constexpr bool l = true; static constexpr bool r = false; };
template<typename T> struct _is_ref_helper<T&&> { static constexpr bool l = false; static constexpr bool r = true; }; template<typename T> struct _is_ref_helper<T&&> { static constexpr bool l = false; static constexpr bool r = true; };
template<typename T> concept is_ref = _is_ref_helper<T>::l || _is_ref_helper<T>::r; template<typename T> concept is_ref = _is_ref_helper<T>::l || _is_ref_helper<T>::r;
template<typename T> struct _is_ptr_helper : false_type {}; template<typename T> struct _is_ptr_helper : false_type {};
template<typename T> struct _is_ptr_helper<T*> : true_type {}; template<typename T> struct _is_ptr_helper<T*> : true_type {};
template<typename T> concept is_ptr = _is_ptr_helper<un_cv_t<T>>::value; template<typename T> concept is_ptr = _is_ptr_helper<un_cv_t<T>>::value;
template<typename T> struct _tame_helper { using type = T; }; template<typename T> struct _tame_helper { using type = T; };
#define TAME_HELPER_IMPL(TRAILING) \ #define TAME_HELPER_IMPL(TRAILING) \
template<typename R, typename... Args> \ template<typename R, typename... Args> \
struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); } struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); }
TAME_HELPER_IMPL(); TAME_HELPER_IMPL();
TAME_HELPER_IMPL(&); TAME_HELPER_IMPL(&);
TAME_HELPER_IMPL(&&); TAME_HELPER_IMPL(&&);
TAME_HELPER_IMPL(const); TAME_HELPER_IMPL(const);
TAME_HELPER_IMPL(const &); 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(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(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(& 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(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(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); TAME_HELPER_IMPL(const volatile & noexcept);
TAME_HELPER_IMPL(const volatile && noexcept); TAME_HELPER_IMPL(const volatile && noexcept);
#undef TAME_HELPER_IMPL #undef TAME_HELPER_IMPL
template<typename T> using tame_t = _tame_helper<T>::type; template<typename T> using tame_t = _tame_helper<T>::type;
template<typename T> struct _is_func_helper : false_type {}; template<typename T> struct _is_func_helper : false_type {};
template<typename R, typename... Args> struct _is_func_helper<R(Args...)> : true_type {}; template<typename R, typename... Args> struct _is_func_helper<R(Args...)> : true_type {};
template<typename T> concept is_func = _is_func_helper<tame_t<T>>::value; template<typename T> concept is_func = _is_func_helper<tame_t<T>>::value;
template<typename T> concept is_object = !is_void<T> && !is_ref<T> && !is_func<T>; template<typename T> concept is_object = !is_void<T> && !is_ref<T> && !is_func<T>;
template<typename T> struct _is_array_helper : false_type {}; template<typename T> struct _is_array_helper : false_type {};
template<typename T> struct _is_array_helper<T[]> : true_type {}; template<typename T> struct _is_array_helper<T[]> : true_type {};
template<typename T, int N> struct _is_array_helper<T[N]> : true_type {}; template<typename T, int N> struct _is_array_helper<T[N]> : true_type {};
template<typename T> concept is_array = _is_array_helper<T>::value; template<typename T> concept is_array = _is_array_helper<T>::value;
template<typename T> struct _is_floating_point_helper : false_type {}; template<typename T> struct _is_floating_point_helper : false_type {};
template<> struct _is_floating_point_helper<float> : true_type {}; template<> struct _is_floating_point_helper<float> : true_type {};
template<> struct _is_floating_point_helper<double> : true_type {}; template<> struct _is_floating_point_helper<double> : true_type {};
template<typename T> concept is_floating_point = _is_floating_point_helper<un_cv_t<T>>::value; template<typename T> concept is_floating_point = _is_floating_point_helper<un_cv_t<T>>::value;
template<typename T> struct _is_integer_helper : false_type {}; template<typename T> struct _is_integer_helper : false_type {};
template<> struct _is_integer_helper<int8_t> : true_type {}; template<> struct _is_integer_helper<int8_t> : true_type {};
template<> struct _is_integer_helper<int16_t> : true_type {}; template<> struct _is_integer_helper<int16_t> : true_type {};
template<> struct _is_integer_helper<int32_t> : true_type {}; template<> struct _is_integer_helper<int32_t> : true_type {};
template<> struct _is_integer_helper<int64_t> : true_type {}; template<> struct _is_integer_helper<int64_t> : true_type {};
template<> struct _is_integer_helper<uint8_t> : true_type {}; template<> struct _is_integer_helper<uint8_t> : true_type {};
template<> struct _is_integer_helper<uint16_t> : true_type {}; template<> struct _is_integer_helper<uint16_t> : true_type {};
template<> struct _is_integer_helper<uint32_t> : true_type {}; template<> struct _is_integer_helper<uint32_t> : true_type {};
template<> struct _is_integer_helper<uint64_t> : true_type {}; template<> struct _is_integer_helper<uint64_t> : true_type {};
template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value; template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value;
template<typename T> concept is_enum = __is_enum(T); template<typename T> concept is_enum = __is_enum(T);
template<typename T> struct is_uniquely_represented : false_type {}; template<typename T> struct is_uniquely_represented : false_type {};
template<is_integer T> struct is_uniquely_represented<T> : true_type {}; template<is_integer T> struct is_uniquely_represented<T> : true_type {};
template<is_enum T> struct is_uniquely_represented<T> : true_type {}; template<is_enum T> struct is_uniquely_represented<T> : true_type {};
template<> struct is_uniquely_represented<uint128_t> : true_type {}; template<> struct is_uniquely_represented<uint128_t> : true_type {};
template<> struct is_uniquely_represented<byte> : true_type {}; template<> struct is_uniquely_represented<byte> : true_type {};
template<typename T> concept uniquely_represented = is_uniquely_represented<un_cv_t<T>>::value; template<typename T> concept uniquely_represented = is_uniquely_represented<un_cv_t<T>>::value;
template<typename T, typename U> template<typename T, typename U>
concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<U>& b) concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<U>& b)
{ {
{ a == b } -> same_as<bool>; { a == b } -> same_as<bool>;
{ b == a } -> same_as<bool>; { b == a } -> same_as<bool>;
{ a != b } -> same_as<bool>; { a != b } -> same_as<bool>;
{ b != a } -> same_as<bool>; { b != a } -> same_as<bool>;
}; };
template<typename T> concept equality_comparable = equality_comparable_with<T, T>; template<typename T> concept equality_comparable = equality_comparable_with<T, T>;
struct niche_t {}; struct niche_t {};
template<typename T> template<typename T>
concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T, niche_t>; concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T, niche_t>;
template<typename T> template<typename T>
concept is_niche = same_as<un_cvref_t<T>, niche_t>; concept is_niche = same_as<un_cvref_t<T>, niche_t>;
} // namespace asl } // namespace asl

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,31 @@
#include "asl/print.hpp" #include "asl/print.hpp"
#include <cstdio> #include <cstdio>
// @Todo Optimize this, maybe make buffered // @Todo Optimize this, maybe make buffered
class ConsoleWriter : public asl::Writer class ConsoleWriter : public asl::Writer
{ {
FILE* m_handle; FILE* m_handle;
public: public:
explicit ConsoleWriter(FILE* handle) explicit ConsoleWriter(FILE* handle)
: m_handle{handle} : m_handle{handle}
{} {}
void write(asl::span<const asl::byte> s) override void write(asl::span<const asl::byte> s) override
{ {
fwrite(s.data(), 1, static_cast<size_t>(s.size()), m_handle); fwrite(s.data(), 1, static_cast<size_t>(s.size()), m_handle);
} }
}; };
asl::Writer* asl::print_internals::get_stdout_writer() asl::Writer* asl::print_internals::get_stdout_writer()
{ {
static ConsoleWriter s_writer{stdout}; static ConsoleWriter s_writer{stdout};
return &s_writer; return &s_writer;
} }
asl::Writer* asl::print_internals::get_stderr_writer() asl::Writer* asl::print_internals::get_stderr_writer()
{ {
static ConsoleWriter s_writer{stderr}; static ConsoleWriter s_writer{stderr};
return &s_writer; return &s_writer;
} }

View File

@ -1,29 +1,29 @@
#pragma once #pragma once
#include "asl/format.hpp" #include "asl/format.hpp"
namespace asl namespace asl
{ {
namespace print_internals namespace print_internals
{ {
// @Todo Make print writers thread safe // @Todo Make print writers thread safe
Writer* get_stdout_writer(); Writer* get_stdout_writer();
Writer* get_stderr_writer(); Writer* get_stderr_writer();
} // namespace print_internals } // namespace print_internals
template<formattable... Args> template<formattable... Args>
void print(string_view fmt, const Args&... args) void print(string_view fmt, const Args&... args)
{ {
format(print_internals::get_stdout_writer(), fmt, args...); format(print_internals::get_stdout_writer(), fmt, args...);
} }
template<formattable... Args> template<formattable... Args>
void eprint(string_view fmt, const Args&... args) void eprint(string_view fmt, const Args&... args)
{ {
format(print_internals::get_stderr_writer(), fmt, args...); format(print_internals::get_stderr_writer(), fmt, args...);
} }
} // namespace asl } // namespace asl

View File

@ -49,7 +49,7 @@ class span
{ {
return size < 0; return size < 0;
} }
static constexpr bool kIsDynamic = is_dynamic(kSize); static constexpr bool kIsDynamic = is_dynamic(kSize);
using SizeType = select_t<kIsDynamic, isize_t, empty>; using SizeType = select_t<kIsDynamic, isize_t, empty>;
@ -102,7 +102,7 @@ public:
constexpr span(const span&) = default; constexpr span(const span&) = default;
constexpr span(span&&) = default; constexpr span(span&&) = default;
constexpr span& operator=(const span&) = default; constexpr span& operator=(const span&) = default;
constexpr span& operator=(span&&) = default; constexpr span& operator=(span&&) = default;
@ -124,7 +124,7 @@ public:
{ {
return contiguous_iterator{m_data}; return contiguous_iterator{m_data};
} }
constexpr contiguous_iterator<T> end() const constexpr contiguous_iterator<T> end() const
{ {
return contiguous_iterator{m_data + size()}; return contiguous_iterator{m_data + size()};

View File

@ -73,7 +73,7 @@ void asl::status::unref()
void asl::AslFormat(asl::Formatter& f, const asl::status& s) void asl::AslFormat(asl::Formatter& f, const asl::status& s)
{ {
string_view status_str{}; string_view status_str{};
switch (s.code()) switch (s.code())
{ {
case status_code::ok: status_str = "ok"_sv; break; case status_code::ok: status_str = "ok"_sv; break;

View File

@ -54,13 +54,13 @@ class status
void ref(); void ref();
void unref(); void unref();
public: public:
constexpr ~status() constexpr ~status()
{ {
if (!is_inline()) { unref(); } if (!is_inline()) { unref(); }
} }
explicit constexpr status(status_code code) explicit constexpr status(status_code code)
: m_payload{status_to_payload(code)} : m_payload{status_to_payload(code)}
{} {}

View File

@ -1,179 +1,179 @@
#pragma once #pragma once
#include "asl/status.hpp" #include "asl/status.hpp"
#include "asl/maybe_uninit.hpp" #include "asl/maybe_uninit.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
namespace asl namespace asl
{ {
template<is_object T> template<is_object T>
class status_or class status_or
{ {
status m_status; status m_status;
maybe_uninit<T> m_value{}; maybe_uninit<T> m_value{};
public: public:
// @Todo Convert copy // @Todo Convert copy
// @Todo Convert move // @Todo Convert move
constexpr status_or(const status_or& other) constexpr status_or(const status_or& other)
requires copy_constructible<T> requires copy_constructible<T>
: m_status{other.m_status} : m_status{other.m_status}
{ {
if (other.ok()) if (other.ok())
{ {
m_value.construct_unsafe(other.m_value.as_init_unsafe()); m_value.construct_unsafe(other.m_value.as_init_unsafe());
} }
} }
constexpr status_or(status_or&& other) constexpr status_or(status_or&& other)
requires move_constructible<T> requires move_constructible<T>
: m_status{ASL_MOVE(other.m_status)} : m_status{ASL_MOVE(other.m_status)}
{ {
if (other.ok()) if (other.ok())
{ {
m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
} }
} }
constexpr status_or& operator=(const status_or& other) constexpr status_or& operator=(const status_or& other)
requires copyable<T> requires copyable<T>
{ {
if (&other != this) if (&other != this)
{ {
if (ok()) if (ok())
{ {
if (other.ok()) if (other.ok())
{ {
m_value.assign_unsafe(other.m_value.as_init_unsafe()); m_value.assign_unsafe(other.m_value.as_init_unsafe());
} }
else else
{ {
m_value.destroy_unsafe(); m_value.destroy_unsafe();
} }
} }
else if (other.ok()) else if (other.ok())
{ {
m_value.construct_unsafe(other.m_value.as_init_unsafe()); m_value.construct_unsafe(other.m_value.as_init_unsafe());
} }
m_status = other.m_status; m_status = other.m_status;
} }
return *this; return *this;
} }
constexpr status_or& operator=(status_or&& other) constexpr status_or& operator=(status_or&& other)
requires moveable<T> requires moveable<T>
{ {
if (&other != this) if (&other != this)
{ {
if (ok()) if (ok())
{ {
if (other.ok()) if (other.ok())
{ {
m_value.assign_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); m_value.assign_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
} }
else else
{ {
m_value.destroy_unsafe(); m_value.destroy_unsafe();
} }
} }
else if (other.ok()) else if (other.ok())
{ {
m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
} }
m_status = ASL_MOVE(other.m_status); m_status = ASL_MOVE(other.m_status);
} }
return *this; return *this;
} }
constexpr ~status_or() constexpr ~status_or()
{ {
if (m_status.ok()) if (m_status.ok())
{ {
m_value.destroy_unsafe(); m_value.destroy_unsafe();
} }
} }
// NOLINTNEXTLINE(*-explicit-conversions) // NOLINTNEXTLINE(*-explicit-conversions)
constexpr status_or(const status& status) : m_status{status} constexpr status_or(const status& status) : m_status{status}
{ {
ASL_ASSERT_RELEASE(!m_status.ok()); ASL_ASSERT_RELEASE(!m_status.ok());
} }
// NOLINTNEXTLINE(*-explicit-conversions) // NOLINTNEXTLINE(*-explicit-conversions)
constexpr status_or(status&& status) : m_status{ASL_MOVE(status)} constexpr status_or(status&& status) : m_status{ASL_MOVE(status)}
{ {
ASL_ASSERT_RELEASE(!m_status.ok()); ASL_ASSERT_RELEASE(!m_status.ok());
} }
status_or& operator=(status status) = delete; status_or& operator=(status status) = delete;
template<typename U = T> template<typename U = T>
constexpr explicit (!convertible_from<T, U&&>) constexpr explicit (!convertible_from<T, U&&>)
status_or(U&& value) status_or(U&& value)
requires ( requires (
constructible_from<T, U&&> && constructible_from<T, U&&> &&
!same_as<un_cvref_t<U>, status_or> && !same_as<un_cvref_t<U>, status_or> &&
!same_as<un_cvref_t<U>, status> !same_as<un_cvref_t<U>, status>
) )
: m_status{status_code::ok} : m_status{status_code::ok}
, m_value{in_place, ASL_FWD(value)} , m_value{in_place, ASL_FWD(value)}
{} {}
constexpr bool ok() const { return m_status.ok(); } constexpr bool ok() const { return m_status.ok(); }
constexpr status_code code() const { return m_status.code(); } constexpr status_code code() const { return m_status.code(); }
constexpr string_view message() const { return m_status.message(); } constexpr string_view message() const { return m_status.message(); }
// @Todo(C++23) Deducing this // @Todo(C++23) Deducing this
constexpr const T& value() const& constexpr const T& value() const&
{ {
ASL_ASSERT_RELEASE(ok()); ASL_ASSERT_RELEASE(ok());
return m_value.as_init_unsafe(); return m_value.as_init_unsafe();
} }
constexpr T& value() & constexpr T& value() &
{ {
ASL_ASSERT_RELEASE(ok()); ASL_ASSERT_RELEASE(ok());
return m_value.as_init_unsafe(); return m_value.as_init_unsafe();
} }
constexpr T&& value() && constexpr T&& value() &&
{ {
ASL_ASSERT_RELEASE(ok()); ASL_ASSERT_RELEASE(ok());
return ASL_MOVE(m_value.as_init_unsafe()); return ASL_MOVE(m_value.as_init_unsafe());
} }
template<typename U> template<typename U>
constexpr T value_or(U&& other_value) const& constexpr T value_or(U&& other_value) const&
requires copy_constructible<T> && convertible_from<T, U&&> requires copy_constructible<T> && convertible_from<T, U&&>
{ {
return ok() ? value() : static_cast<T>(ASL_FWD(other_value)); return ok() ? value() : static_cast<T>(ASL_FWD(other_value));
} }
template<typename U> template<typename U>
constexpr T value_or(U&& other_value) && constexpr T value_or(U&& other_value) &&
requires move_constructible<T> && convertible_from<T, U&&> requires move_constructible<T> && convertible_from<T, U&&>
{ {
return ok() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value)); return ok() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value));
} }
template<typename H> template<typename H>
requires hashable<T> requires hashable<T>
friend H AslHashValue(H h, const status_or& s) friend H AslHashValue(H h, const status_or& s)
{ {
if (s.ok()) if (s.ok())
{ {
return H::combine(ASL_MOVE(h), s.m_status, s.value()); return H::combine(ASL_MOVE(h), s.m_status, s.value());
} }
return H::combine(ASL_MOVE(h), s.m_status); return H::combine(ASL_MOVE(h), s.m_status);
} }
}; };
template<typename T> template<typename T>
status_or(T) -> status_or<T>; status_or(T) -> status_or<T>;
} // namespace asl } // namespace asl

View File

@ -59,7 +59,7 @@ public:
{ {
return as_string_view() == other.as_string_view(); return as_string_view() == other.as_string_view();
} }
constexpr bool operator==(string_view other) const constexpr bool operator==(string_view other) const
{ {
return as_string_view() == other; return as_string_view() == other;

View File

@ -104,7 +104,7 @@ public:
constexpr StringWriter& operator=(const StringWriter&) requires copy_assignable<Allocator> = default; constexpr StringWriter& operator=(const StringWriter&) requires copy_assignable<Allocator> = default;
constexpr StringWriter& operator=(StringWriter&&) = default; constexpr StringWriter& operator=(StringWriter&&) = default;
void write(span<const byte> str) override void write(span<const byte> str) override
{ {
m_builder.push(string_view{reinterpret_cast<const char*>(str.data()), str.size()}); m_builder.push(string_view{reinterpret_cast<const char*>(str.data()), str.size()});

View File

@ -41,7 +41,7 @@ public:
~string_view() = default; ~string_view() = default;
constexpr isize_t size() const { return m_size; } constexpr isize_t size() const { return m_size; }
constexpr bool is_empty() const { return m_size == 0; } constexpr bool is_empty() const { return m_size == 0; }
constexpr const char* data() const { return m_data; } constexpr const char* data() const { return m_data; }

View File

@ -1,13 +1,13 @@
cc_library( cc_library(
name = "testing", name = "testing",
hdrs = [ hdrs = [
"testing.hpp", "testing.hpp",
], ],
srcs = [ srcs = [
"testing.cpp", "testing.cpp",
], ],
deps = [ deps = [
"//asl", "//asl",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

View File

@ -1,81 +1,81 @@
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/print.hpp" #include "asl/print.hpp"
static asl::testing::Test* g_head = nullptr; static asl::testing::Test* g_head = nullptr;
static asl::testing::Test* g_tail = nullptr; static asl::testing::Test* g_tail = nullptr;
void asl::testing::register_test(Test* test) void asl::testing::register_test(Test* test)
{ {
if (g_head == nullptr && g_tail == nullptr) if (g_head == nullptr && g_tail == nullptr)
{ {
g_head = test; g_head = test;
g_tail = test; g_tail = test;
} }
else else
{ {
g_tail->m_next = test; g_tail->m_next = test;
test->m_prev = asl::exchange(g_tail, test); test->m_prev = asl::exchange(g_tail, test);
} }
} }
static bool g_current_test_fail = false; static bool g_current_test_fail = false;
void asl::testing::report_failure(const char* msg, const char* file, int line) void asl::testing::report_failure(const char* msg, const char* file, int line)
{ {
asl::eprint("--------------------------------------------------------------\n"); asl::eprint("--------------------------------------------------------------\n");
asl::eprint("Test assertion failed at {}, line {}:\n", file, line); asl::eprint("Test assertion failed at {}, line {}:\n", file, line);
asl::eprint(" {}\n", msg); asl::eprint(" {}\n", msg);
asl::eprint("--------------------------------------------------------------\n"); asl::eprint("--------------------------------------------------------------\n");
g_current_test_fail = true; g_current_test_fail = true;
} }
#define RESET "\x1b[0m" #define RESET "\x1b[0m"
#define RED(S) "\x1b[0;31m" S RESET #define RED(S) "\x1b[0;31m" S RESET
#define GREEN(S) "\x1b[0;32m" S RESET #define GREEN(S) "\x1b[0;32m" S RESET
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{ {
int fail = 0; int fail = 0;
int pass = 0; int pass = 0;
asl::testing::Test* failed_head = nullptr; asl::testing::Test* failed_head = nullptr;
for (auto* it = g_head; it != nullptr; it = it->m_next) for (auto* it = g_head; it != nullptr; it = it->m_next)
{ {
asl::eprint(GREEN("[ RUN ]") " {}\n", it->m_case_name); asl::eprint(GREEN("[ RUN ]") " {}\n", it->m_case_name);
g_current_test_fail = false; g_current_test_fail = false;
it->m_fn(); it->m_fn();
if (!g_current_test_fail) if (!g_current_test_fail)
{ {
asl::eprint(GREEN("[ OK ]") " {}\n", it->m_case_name); asl::eprint(GREEN("[ OK ]") " {}\n", it->m_case_name);
pass += 1; pass += 1;
} }
else else
{ {
asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name); asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name);
fail += 1; fail += 1;
it->m_next = asl::exchange(failed_head, it); it->m_next = asl::exchange(failed_head, it);
} }
} }
asl::eprint(GREEN("[----------]") " {} test(s) run\n", fail + pass); asl::eprint(GREEN("[----------]") " {} test(s) run\n", fail + pass);
if (fail == 0) if (fail == 0)
{ {
asl::eprint(GREEN("[ PASSED ]") " Good job!\n"); asl::eprint(GREEN("[ PASSED ]") " Good job!\n");
} }
else else
{ {
asl::eprint(RED("[ FAILED ]") " {} test(s) failed\n", fail); asl::eprint(RED("[ FAILED ]") " {} test(s) failed\n", fail);
for (auto* it = failed_head; it != nullptr; it = it->m_next) for (auto* it = failed_head; it != nullptr; it = it->m_next)
{ {
asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name); asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name);
} }
} }
return fail; return fail;
} }

View File

@ -1,46 +1,46 @@
#pragma once #pragma once
#include "asl/utility.hpp" #include "asl/utility.hpp"
namespace asl::testing namespace asl::testing
{ {
struct Test; struct Test;
void register_test(Test*); void register_test(Test*);
void report_failure(const char* msg, const char* file, int line); void report_failure(const char* msg, const char* file, int line);
using TestFunction = void(); using TestFunction = void();
struct Test struct Test
{ {
const char* m_case_name; const char* m_case_name;
TestFunction* m_fn; TestFunction* m_fn;
Test* m_next{}; Test* m_next{};
Test* m_prev{}; Test* m_prev{};
constexpr explicit Test(const char* case_name, TestFunction* fn) constexpr explicit Test(const char* case_name, TestFunction* fn)
: m_case_name{case_name} : m_case_name{case_name}
, m_fn{fn} , m_fn{fn}
{ {
register_test(this); register_test(this);
} }
}; };
} // namespace asl::testing } // namespace asl::testing
#define ASL_TEST(CASE) \ #define ASL_TEST(CASE) \
static void asl_test_fn_##CASE(); \ static void asl_test_fn_##CASE(); \
static ::asl::testing::Test asl_test_##CASE( \ static ::asl::testing::Test asl_test_##CASE( \
#CASE, \ #CASE, \
asl_test_fn_##CASE); \ asl_test_fn_##CASE); \
void asl_test_fn_##CASE() void asl_test_fn_##CASE()
#define ASL_TEST_ASSERT(EXPR) \ #define ASL_TEST_ASSERT(EXPR) \
if (EXPR) {} \ if (EXPR) {} \
else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); return; } else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); return; }
#define ASL_TEST_EXPECT(EXPR) \ #define ASL_TEST_EXPECT(EXPR) \
if (EXPR) {} \ if (EXPR) {} \
else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); } else { ::asl::testing::report_failure(#EXPR, __FILE__, __LINE__); }

View File

@ -1,78 +1,78 @@
#include "asl/box.hpp" #include "asl/box.hpp"
#include "asl/option.hpp" #include "asl/option.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
static_assert(sizeof(asl::box<int>) == sizeof(int*)); static_assert(sizeof(asl::box<int>) == sizeof(int*));
static_assert(!asl::copyable<asl::box<int>>); static_assert(!asl::copyable<asl::box<int>>);
static_assert(asl::moveable<asl::box<int>>); static_assert(asl::moveable<asl::box<int>>);
static_assert(asl::has_niche<asl::box<int>>); static_assert(asl::has_niche<asl::box<int>>);
static_assert(sizeof(asl::option<asl::box<int>>) == sizeof(int*)); static_assert(sizeof(asl::option<asl::box<int>>) == sizeof(int*));
ASL_TEST(destructor) ASL_TEST(destructor)
{ {
bool d = false; bool d = false;
{ {
auto box = asl::make_box<DestructorObserver>(&d); auto box = asl::make_box<DestructorObserver>(&d);
ASL_TEST_ASSERT(!d); ASL_TEST_ASSERT(!d);
auto box3 = ASL_MOVE(box); auto box3 = ASL_MOVE(box);
ASL_TEST_ASSERT(!d); ASL_TEST_ASSERT(!d);
} }
ASL_TEST_ASSERT(d); ASL_TEST_ASSERT(d);
} }
ASL_TEST(value) ASL_TEST(value)
{ {
auto b = asl::make_box<int>(24); auto b = asl::make_box<int>(24);
ASL_TEST_EXPECT(*b == 24); ASL_TEST_EXPECT(*b == 24);
auto b2 = ASL_MOVE(b); auto b2 = ASL_MOVE(b);
ASL_TEST_EXPECT(*b2 == 24); ASL_TEST_EXPECT(*b2 == 24);
} }
ASL_TEST(ptr) ASL_TEST(ptr)
{ {
auto b = asl::make_box<int>(24); auto b = asl::make_box<int>(24);
auto* ptr1 = b.get(); auto* ptr1 = b.get();
auto b2 = ASL_MOVE(b); auto b2 = ASL_MOVE(b);
auto* ptr2 = b2.get(); auto* ptr2 = b2.get();
ASL_TEST_EXPECT(ptr1 == ptr2); ASL_TEST_EXPECT(ptr1 == ptr2);
} }
struct Struct { int a; }; struct Struct { int a; };
ASL_TEST(arrow) ASL_TEST(arrow)
{ {
auto b = asl::make_box<Struct>(45); auto b = asl::make_box<Struct>(45);
ASL_TEST_EXPECT(b->a == 45); ASL_TEST_EXPECT(b->a == 45);
} }
ASL_TEST(niche) ASL_TEST(niche)
{ {
static_assert(sizeof(asl::box<int>) == sizeof(asl::option<asl::box<int>>)); static_assert(sizeof(asl::box<int>) == sizeof(asl::option<asl::box<int>>));
asl::option<asl::box<int>> opt; asl::option<asl::box<int>> opt;
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
opt = asl::make_box<int>(66); opt = asl::make_box<int>(66);
ASL_TEST_EXPECT(opt.has_value()); ASL_TEST_EXPECT(opt.has_value());
ASL_TEST_EXPECT(*opt.value() == 66); ASL_TEST_EXPECT(*opt.value() == 66);
opt = asl::nullopt; opt = asl::nullopt;
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
bool destroyed = false; bool destroyed = false;
asl::option opt2 = asl::make_box<DestructorObserver>(&destroyed); asl::option opt2 = asl::make_box<DestructorObserver>(&destroyed);
ASL_TEST_EXPECT(opt2.has_value()); ASL_TEST_EXPECT(opt2.has_value());
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
opt2.reset(); opt2.reset();
ASL_TEST_EXPECT(!opt2.has_value()); ASL_TEST_EXPECT(!opt2.has_value());
ASL_TEST_EXPECT(destroyed); ASL_TEST_EXPECT(destroyed);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,23 @@
#include "asl/float.hpp" #include "asl/float.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
ASL_TEST(is_infinity) ASL_TEST(is_infinity)
{ {
ASL_TEST_EXPECT(!asl::is_infinity(0.0F)); ASL_TEST_EXPECT(!asl::is_infinity(0.0F));
ASL_TEST_EXPECT(!asl::is_infinity(-25.0F)); ASL_TEST_EXPECT(!asl::is_infinity(-25.0F));
ASL_TEST_EXPECT(asl::is_infinity(45.0F / 0.0F)); ASL_TEST_EXPECT(asl::is_infinity(45.0F / 0.0F));
ASL_TEST_EXPECT(asl::is_infinity(-45.0F / 0.0F)); ASL_TEST_EXPECT(asl::is_infinity(-45.0F / 0.0F));
ASL_TEST_EXPECT(asl::is_infinity(asl::infinity<float>())); ASL_TEST_EXPECT(asl::is_infinity(asl::infinity<float>()));
ASL_TEST_EXPECT(asl::is_infinity(-asl::infinity<double>())); ASL_TEST_EXPECT(asl::is_infinity(-asl::infinity<double>()));
} }
ASL_TEST(is_nan) ASL_TEST(is_nan)
{ {
ASL_TEST_EXPECT(!asl::is_nan(0.0F)); ASL_TEST_EXPECT(!asl::is_nan(0.0F));
ASL_TEST_EXPECT(!asl::is_nan(-25.0F)); ASL_TEST_EXPECT(!asl::is_nan(-25.0F));
ASL_TEST_EXPECT(!asl::is_nan(45.0F / 0.0F)); ASL_TEST_EXPECT(!asl::is_nan(45.0F / 0.0F));
ASL_TEST_EXPECT(asl::is_nan(asl::nan<float>())); ASL_TEST_EXPECT(asl::is_nan(asl::nan<float>()));
ASL_TEST_EXPECT(asl::is_nan(asl::nan<double>())); ASL_TEST_EXPECT(asl::is_nan(asl::nan<double>()));
} }

View File

@ -1,110 +1,110 @@
#include "asl/format.hpp" #include "asl/format.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/float.hpp" #include "asl/float.hpp"
#include "asl/string_builder.hpp" #include "asl/string_builder.hpp"
static_assert(asl::formattable<decltype("Hello")>); static_assert(asl::formattable<decltype("Hello")>);
ASL_TEST(format_args) ASL_TEST(format_args)
{ {
// @Todo Introduce ASL_TEST_EXPECT_EQ, or ASL_TEST_EXPECT_STREQ // @Todo Introduce ASL_TEST_EXPECT_EQ, or ASL_TEST_EXPECT_STREQ
auto s = asl::format_to_string("Hello, world!"); auto s = asl::format_to_string("Hello, world!");
ASL_TEST_EXPECT(s == "Hello, world!"_sv); ASL_TEST_EXPECT(s == "Hello, world!"_sv);
s = asl::format_to_string(""); s = asl::format_to_string("");
ASL_TEST_EXPECT(s == ""_sv); ASL_TEST_EXPECT(s == ""_sv);
s = asl::format_to_string("Hello, {}!", "world"); s = asl::format_to_string("Hello, {}!", "world");
ASL_TEST_EXPECT(s == "Hello, world!"_sv); ASL_TEST_EXPECT(s == "Hello, world!"_sv);
s = asl::format_to_string("Hello, {}! {}", "world"); s = asl::format_to_string("Hello, {}! {}", "world");
ASL_TEST_EXPECT(s == "Hello, world! <ERROR>"_sv); ASL_TEST_EXPECT(s == "Hello, world! <ERROR>"_sv);
s = asl::format_to_string("Hello, pup!", "world"); s = asl::format_to_string("Hello, pup!", "world");
ASL_TEST_EXPECT(s == "Hello, pup!"_sv); ASL_TEST_EXPECT(s == "Hello, pup!"_sv);
s = asl::format_to_string("{}", "CHEESE"); s = asl::format_to_string("{}", "CHEESE");
ASL_TEST_EXPECT(s == "CHEESE"_sv); ASL_TEST_EXPECT(s == "CHEESE"_sv);
s = asl::format_to_string("{ ", "CHEESE"); s = asl::format_to_string("{ ", "CHEESE");
ASL_TEST_EXPECT(s == "<ERROR> "_sv); ASL_TEST_EXPECT(s == "<ERROR> "_sv);
s = asl::format_to_string("{", "CHEESE"); s = asl::format_to_string("{", "CHEESE");
ASL_TEST_EXPECT(s == "<ERROR>"_sv); ASL_TEST_EXPECT(s == "<ERROR>"_sv);
s = asl::format_to_string("a{{b"); s = asl::format_to_string("a{{b");
ASL_TEST_EXPECT(s == "a{b"_sv); ASL_TEST_EXPECT(s == "a{b"_sv);
s = asl::format_to_string("{{{}}} }", "CHEESE"); s = asl::format_to_string("{{{}}} }", "CHEESE");
ASL_TEST_EXPECT(s == "{CHEESE} }"_sv); ASL_TEST_EXPECT(s == "{CHEESE} }"_sv);
} }
ASL_TEST(format_integers) ASL_TEST(format_integers)
{ {
auto s = asl::format_to_string("{} {} {}", 0, 1, 2); auto s = asl::format_to_string("{} {} {}", 0, 1, 2);
ASL_TEST_EXPECT(s == "0 1 2"_sv); ASL_TEST_EXPECT(s == "0 1 2"_sv);
s = asl::format_to_string("{} {} {}", 10, 11, 12); s = asl::format_to_string("{} {} {}", 10, 11, 12);
ASL_TEST_EXPECT(s == "10 11 12"_sv); ASL_TEST_EXPECT(s == "10 11 12"_sv);
s = asl::format_to_string("{} {} {}", 100, 101, 102); s = asl::format_to_string("{} {} {}", 100, 101, 102);
ASL_TEST_EXPECT(s == "100 101 102"_sv); ASL_TEST_EXPECT(s == "100 101 102"_sv);
s = asl::format_to_string("{} {} {}", 1000, 1001, 1002); s = asl::format_to_string("{} {} {}", 1000, 1001, 1002);
ASL_TEST_EXPECT(s == "1000 1001 1002"_sv); ASL_TEST_EXPECT(s == "1000 1001 1002"_sv);
s = asl::format_to_string("{} {} {} {}", -1, -23, -456, -7890); s = asl::format_to_string("{} {} {} {}", -1, -23, -456, -7890);
ASL_TEST_EXPECT(s == "-1 -23 -456 -7890"_sv); ASL_TEST_EXPECT(s == "-1 -23 -456 -7890"_sv);
} }
ASL_TEST(format_floats) ASL_TEST(format_floats)
{ {
auto s = asl::format_to_string("{} {} {}", 0.0F, 1.0, 2.0F); auto s = asl::format_to_string("{} {} {}", 0.0F, 1.0, 2.0F);
ASL_TEST_EXPECT(s == "0 1 2"_sv); ASL_TEST_EXPECT(s == "0 1 2"_sv);
s = asl::format_to_string("{} {} {}", 0.1F, 0.001F, 0.123F); s = asl::format_to_string("{} {} {}", 0.1F, 0.001F, 0.123F);
ASL_TEST_EXPECT(s == "0.1 0.001 0.123"_sv); ASL_TEST_EXPECT(s == "0.1 0.001 0.123"_sv);
s = asl::format_to_string("{} {}", 1.25F, -22.3); s = asl::format_to_string("{} {}", 1.25F, -22.3);
ASL_TEST_EXPECT(s == "1.25 -22.3"_sv); ASL_TEST_EXPECT(s == "1.25 -22.3"_sv);
s = asl::format_to_string("{}", 1e32); s = asl::format_to_string("{}", 1e32);
ASL_TEST_EXPECT(s == "100000000000000000000000000000000"_sv); ASL_TEST_EXPECT(s == "100000000000000000000000000000000"_sv);
s = asl::format_to_string("{}", 123e-8); s = asl::format_to_string("{}", 123e-8);
ASL_TEST_EXPECT(s == "0.00000123"_sv); ASL_TEST_EXPECT(s == "0.00000123"_sv);
s = asl::format_to_string("{} {}", asl::infinity<float>(), -asl::infinity<double>()); s = asl::format_to_string("{} {}", asl::infinity<float>(), -asl::infinity<double>());
ASL_TEST_EXPECT(s == "Infinity -Infinity"_sv); ASL_TEST_EXPECT(s == "Infinity -Infinity"_sv);
s = asl::format_to_string("{}", asl::nan<float>()); s = asl::format_to_string("{}", asl::nan<float>());
ASL_TEST_EXPECT(s == "NaN"_sv); ASL_TEST_EXPECT(s == "NaN"_sv);
} }
ASL_TEST(format_boolean) ASL_TEST(format_boolean)
{ {
auto s = asl::format_to_string("{} {}", true, false); auto s = asl::format_to_string("{} {}", true, false);
ASL_TEST_EXPECT(s == "true false"_sv); ASL_TEST_EXPECT(s == "true false"_sv);
} }
struct CustomFormat struct CustomFormat
{ {
int x; int x;
friend void AslFormat(asl::Formatter&, const CustomFormat&); friend void AslFormat(asl::Formatter&, const CustomFormat&);
}; };
void AslFormat(asl::Formatter& f, const CustomFormat& c) void AslFormat(asl::Formatter& f, const CustomFormat& c)
{ {
f.write("("_sv); f.write("("_sv);
AslFormat(f, c.x); AslFormat(f, c.x);
f.write(")"_sv); f.write(")"_sv);
} }
static_assert(asl::formattable<CustomFormat>); static_assert(asl::formattable<CustomFormat>);
ASL_TEST(format_custom) ASL_TEST(format_custom)
{ {
auto s = asl::format_to_string("{}", CustomFormat{37}); auto s = asl::format_to_string("{}", CustomFormat{37});
ASL_TEST_EXPECT(s == "(37)"_sv); ASL_TEST_EXPECT(s == "(37)"_sv);
} }

View File

@ -1,73 +1,73 @@
#include "asl/functional.hpp" #include "asl/functional.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
struct HasFunction struct HasFunction
{ {
void do_something(int, float) {} void do_something(int, float) {}
}; };
struct HasMember struct HasMember
{ {
int member{}; int member{};
int member_array[4]{}; int member_array[4]{};
void (*member_fn)(){}; void (*member_fn)(){};
}; };
struct Functor struct Functor
{ {
int64_t operator()() { return 35; } int64_t operator()() { return 35; }
int operator()(int x) { return x; } int operator()(int x) { return x; }
}; };
static int some_func0() { return 1; } static int some_func0() { return 1; }
static int some_func1(int x) { return x + 1; } static int some_func1(int x) { return x + 1; }
[[maybe_unused]] static float some_func1(float x) { return x + 1; } [[maybe_unused]] static float some_func1(float x) { return x + 1; }
static int some_func2(int x, int b) { return x + b; } static int some_func2(int x, int b) { return x + b; }
static_assert(asl::same_as<asl::result_of_t<Functor()>, int64_t>); static_assert(asl::same_as<asl::result_of_t<Functor()>, int64_t>);
static_assert(asl::same_as<asl::result_of_t<Functor(int)>, int>); static_assert(asl::same_as<asl::result_of_t<Functor(int)>, int>);
static_assert(asl::same_as<asl::result_of_t<decltype(static_cast<float(*)(float)>(some_func1))(float)>, float>); static_assert(asl::same_as<asl::result_of_t<decltype(static_cast<float(*)(float)>(some_func1))(float)>, float>);
static_assert(asl::same_as<asl::result_of_t<decltype(&HasFunction::do_something)(HasFunction, int, float)>, void>); static_assert(asl::same_as<asl::result_of_t<decltype(&HasFunction::do_something)(HasFunction, int, float)>, void>);
static_assert(asl::same_as<asl::result_of_t<decltype(&HasMember::member)(HasMember)>, int>); static_assert(asl::same_as<asl::result_of_t<decltype(&HasMember::member)(HasMember)>, int>);
ASL_TEST(invoke_member_function) ASL_TEST(invoke_member_function)
{ {
HasFunction c; HasFunction c;
asl::invoke(&HasFunction::do_something, c, 5, 5.0F); asl::invoke(&HasFunction::do_something, c, 5, 5.0F);
asl::invoke(&HasFunction::do_something, &c, 5, 5.0F); asl::invoke(&HasFunction::do_something, &c, 5, 5.0F);
} }
ASL_TEST(invoke_member_data) ASL_TEST(invoke_member_data)
{ {
HasMember c; HasMember c;
asl::invoke(&HasMember::member, c); asl::invoke(&HasMember::member, c);
asl::invoke(&HasMember::member_array, c); asl::invoke(&HasMember::member_array, c);
asl::invoke(&HasMember::member_fn, c); asl::invoke(&HasMember::member_fn, c);
asl::invoke(&HasMember::member, &c); asl::invoke(&HasMember::member, &c);
asl::invoke(&HasMember::member_array, &c); asl::invoke(&HasMember::member_array, &c);
asl::invoke(&HasMember::member_fn, &c); asl::invoke(&HasMember::member_fn, &c);
} }
ASL_TEST(invoke_fn) ASL_TEST(invoke_fn)
{ {
ASL_TEST_EXPECT(asl::invoke(some_func0) == 1); ASL_TEST_EXPECT(asl::invoke(some_func0) == 1);
ASL_TEST_EXPECT(asl::invoke(static_cast<int(*)(int)>(some_func1), 8) == 9); ASL_TEST_EXPECT(asl::invoke(static_cast<int(*)(int)>(some_func1), 8) == 9);
ASL_TEST_EXPECT(asl::invoke(some_func2, 4, 8) == 12); ASL_TEST_EXPECT(asl::invoke(some_func2, 4, 8) == 12);
ASL_TEST_EXPECT(asl::invoke(&some_func0) == 1); ASL_TEST_EXPECT(asl::invoke(&some_func0) == 1);
ASL_TEST_EXPECT(asl::invoke(static_cast<int(*)(int)>(&some_func1), 8) == 9); ASL_TEST_EXPECT(asl::invoke(static_cast<int(*)(int)>(&some_func1), 8) == 9);
ASL_TEST_EXPECT(asl::invoke(&some_func2, 4, 8) == 12); ASL_TEST_EXPECT(asl::invoke(&some_func2, 4, 8) == 12);
} }
ASL_TEST(invoke_operator_call) ASL_TEST(invoke_operator_call)
{ {
Functor f; Functor f;
ASL_TEST_EXPECT(asl::invoke(f) == 35); ASL_TEST_EXPECT(asl::invoke(f) == 35);
ASL_TEST_EXPECT(asl::invoke(f, 8) == 8); ASL_TEST_EXPECT(asl::invoke(f, 8) == 8);
} }
ASL_TEST(invoke_lambda) ASL_TEST(invoke_lambda)
{ {
ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35); ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35);
ASL_TEST_EXPECT(asl::invoke([](int x){ return x + 2; }, 6) == 8); ASL_TEST_EXPECT(asl::invoke([](int x){ return x + 2; }, 6) == 8);
} }

View File

@ -1,48 +1,48 @@
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/hash_map.hpp" #include "asl/hash_map.hpp"
ASL_TEST(default) ASL_TEST(default)
{ {
asl::hash_map<int, int> map; asl::hash_map<int, int> map;
ASL_TEST_EXPECT(!map.contains(45)); ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(!map.contains(46)); ASL_TEST_EXPECT(!map.contains(46));
map.insert(45, 5); map.insert(45, 5);
map.insert(46, 6); map.insert(46, 6);
ASL_TEST_EXPECT(map.size() == 2); ASL_TEST_EXPECT(map.size() == 2);
ASL_TEST_EXPECT(map.contains(45)); ASL_TEST_EXPECT(map.contains(45));
ASL_TEST_EXPECT(map.contains(46)); ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47)); ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(*map.get(45) == 5); ASL_TEST_EXPECT(*map.get(45) == 5);
ASL_TEST_EXPECT(*map.get(46) == 6); ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr); ASL_TEST_EXPECT(map.get(47) == nullptr);
ASL_TEST_EXPECT(map.remove(45)); ASL_TEST_EXPECT(map.remove(45));
ASL_TEST_EXPECT(!map.remove(45)); ASL_TEST_EXPECT(!map.remove(45));
ASL_TEST_EXPECT(map.size() == 1); ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45)); ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46)); ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47)); ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr); ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 6); ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr); ASL_TEST_EXPECT(map.get(47) == nullptr);
map.insert(46, 460); map.insert(46, 460);
ASL_TEST_EXPECT(map.size() == 1); ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45)); ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46)); ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47)); ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr); ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 460); ASL_TEST_EXPECT(*map.get(46) == 460);
ASL_TEST_EXPECT(map.get(47) == nullptr); ASL_TEST_EXPECT(map.get(47) == nullptr);
} }

View File

@ -1,185 +1,185 @@
#include "asl/hash_set.hpp" #include "asl/hash_set.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
#include "asl/string.hpp" #include "asl/string.hpp"
#include "asl/string_view.hpp" #include "asl/string_view.hpp"
ASL_TEST(empty) ASL_TEST(empty)
{ {
asl::hash_set<int> set; asl::hash_set<int> set;
ASL_TEST_EXPECT(set.size() == 0); ASL_TEST_EXPECT(set.size() == 0);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
ASL_TEST_EXPECT(!set.contains(i)); ASL_TEST_EXPECT(!set.contains(i));
} }
} }
ASL_TEST(a_bunch_of_strings) ASL_TEST(a_bunch_of_strings)
{ {
asl::hash_set<asl::string<>> set; asl::hash_set<asl::string<>> set;
set.insert("Hello, world!"); set.insert("Hello, world!");
ASL_TEST_EXPECT(set.size() == 1); ASL_TEST_EXPECT(set.size() == 1);
set.insert("Hello, puppy!"); set.insert("Hello, puppy!");
ASL_TEST_EXPECT(set.size() == 2); ASL_TEST_EXPECT(set.size() == 2);
set.insert("Hello, puppy!"); set.insert("Hello, puppy!");
ASL_TEST_EXPECT(set.size() == 2); ASL_TEST_EXPECT(set.size() == 2);
ASL_TEST_EXPECT(set.contains("Hello, world!"_sv)); ASL_TEST_EXPECT(set.contains("Hello, world!"_sv));
ASL_TEST_EXPECT(set.contains("Hello, puppy!"_sv)); ASL_TEST_EXPECT(set.contains("Hello, puppy!"_sv));
ASL_TEST_EXPECT(!set.contains("Hello, Steven!"_sv)); ASL_TEST_EXPECT(!set.contains("Hello, Steven!"_sv));
} }
ASL_TEST(a_bunch_of_ints) ASL_TEST(a_bunch_of_ints)
{ {
asl::hash_set<int> set; asl::hash_set<int> set;
int count = 3000; int count = 3000;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
set.insert(i); set.insert(i);
} }
ASL_TEST_EXPECT(set.size() == count); ASL_TEST_EXPECT(set.size() == count);
for (int i = 0; i < count * 2; ++i) for (int i = 0; i < count * 2; ++i)
{ {
ASL_TEST_EXPECT(set.contains(i) == (i < count)); ASL_TEST_EXPECT(set.contains(i) == (i < count));
} }
} }
struct HashWithDestructor: public DestructorObserver struct HashWithDestructor: public DestructorObserver
{ {
int x; int x;
HashWithDestructor(int x_, bool* ptr) HashWithDestructor(int x_, bool* ptr)
: DestructorObserver{ptr} : DestructorObserver{ptr}
, x{x_} , x{x_}
{} {}
constexpr bool operator==(const HashWithDestructor& other) const constexpr bool operator==(const HashWithDestructor& other) const
{ {
return x == other.x; return x == other.x;
} }
}; };
struct CustomComparator struct CustomComparator
{ {
static bool eq(const HashWithDestructor& a, const HashWithDestructor& b) static bool eq(const HashWithDestructor& a, const HashWithDestructor& b)
{ {
return a.x == b.x; return a.x == b.x;
} }
static bool eq(const HashWithDestructor& a, int b) static bool eq(const HashWithDestructor& a, int b)
{ {
return a.x == b; return a.x == b;
} }
}; };
struct CustomHasher struct CustomHasher
{ {
static uint64_t hash(const HashWithDestructor& b) static uint64_t hash(const HashWithDestructor& b)
{ {
return asl::hash_value(b.x); return asl::hash_value(b.x);
} }
static uint64_t hash(int x) static uint64_t hash(int x)
{ {
return asl::hash_value(x); return asl::hash_value(x);
} }
}; };
ASL_TEST(destructor_and_remove) ASL_TEST(destructor_and_remove)
{ {
static constexpr int kCount = 200; static constexpr int kCount = 200;
bool destroyed[kCount]{}; bool destroyed[kCount]{};
{ {
asl::hash_set<HashWithDestructor, asl::DefaultAllocator, CustomHasher, CustomComparator> set; asl::hash_set<HashWithDestructor, asl::DefaultAllocator, CustomHasher, CustomComparator> set;
for (int i = 0; i < kCount; ++i) for (int i = 0; i < kCount; ++i)
{ {
set.insert(i, &destroyed[i]); // NOLINT set.insert(i, &destroyed[i]); // NOLINT
} }
ASL_TEST_EXPECT(set.size() == kCount); ASL_TEST_EXPECT(set.size() == kCount);
for (int i = 0; i < kCount; ++i) for (int i = 0; i < kCount; ++i)
{ {
ASL_TEST_EXPECT(!destroyed[i]); // NOLINT ASL_TEST_EXPECT(!destroyed[i]); // NOLINT
} }
for (int i = 0; i < kCount; i += 2) for (int i = 0; i < kCount; i += 2)
{ {
ASL_TEST_EXPECT(set.remove(i)); ASL_TEST_EXPECT(set.remove(i));
} }
for (int i = 0; i < kCount; i += 2) for (int i = 0; i < kCount; i += 2)
{ {
ASL_TEST_EXPECT(!set.contains(i)); ASL_TEST_EXPECT(!set.contains(i));
ASL_TEST_EXPECT(set.contains(i+1)); ASL_TEST_EXPECT(set.contains(i+1));
ASL_TEST_EXPECT(destroyed[i]); // NOLINT ASL_TEST_EXPECT(destroyed[i]); // NOLINT
ASL_TEST_EXPECT(!destroyed[i + 1]); // NOLINT ASL_TEST_EXPECT(!destroyed[i + 1]); // NOLINT
} }
} }
for (int i = 0; i < kCount; ++i) for (int i = 0; i < kCount; ++i)
{ {
ASL_TEST_EXPECT(destroyed[i]); // NOLINT ASL_TEST_EXPECT(destroyed[i]); // NOLINT
} }
} }
ASL_TEST(copy) ASL_TEST(copy)
{ {
asl::hash_set<int> set1; asl::hash_set<int> set1;
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
set1.insert(i); set1.insert(i);
} }
asl::hash_set<int> set2 = set1; asl::hash_set<int> set2 = set1;
asl::hash_set<int> set3; asl::hash_set<int> set3;
set3 = set1; set3 = set1;
ASL_TEST_EXPECT(set2.size() == 100); ASL_TEST_EXPECT(set2.size() == 100);
ASL_TEST_EXPECT(set3.size() == 100); ASL_TEST_EXPECT(set3.size() == 100);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
ASL_TEST_EXPECT(set2.contains(i)); ASL_TEST_EXPECT(set2.contains(i));
ASL_TEST_EXPECT(set3.contains(i)); ASL_TEST_EXPECT(set3.contains(i));
} }
} }
ASL_TEST(move) ASL_TEST(move)
{ {
asl::hash_set<int> set1; asl::hash_set<int> set1;
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
set1.insert(i); set1.insert(i);
} }
asl::hash_set<int> set2 = ASL_MOVE(set1); asl::hash_set<int> set2 = ASL_MOVE(set1);
ASL_TEST_EXPECT(set2.size() == 100); ASL_TEST_EXPECT(set2.size() == 100);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
ASL_TEST_EXPECT(set2.contains(i)); ASL_TEST_EXPECT(set2.contains(i));
} }
set1 = ASL_MOVE(set2); set1 = ASL_MOVE(set2);
ASL_TEST_EXPECT(set1.size() == 100); ASL_TEST_EXPECT(set1.size() == 100);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
ASL_TEST_EXPECT(set1.contains(i)); ASL_TEST_EXPECT(set1.contains(i));
} }
} }

View File

@ -1,260 +1,260 @@
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/hash.hpp" #include "asl/hash.hpp"
#include "asl/string_view.hpp" #include "asl/string_view.hpp"
#include "asl/string.hpp" #include "asl/string.hpp"
#include "asl/buffer.hpp" #include "asl/buffer.hpp"
#include "asl/box.hpp" #include "asl/box.hpp"
#include "asl/option.hpp" #include "asl/option.hpp"
#include "asl/status.hpp" #include "asl/status.hpp"
#include "asl/status_or.hpp" #include "asl/status_or.hpp"
static_assert(!asl::hashable<int*>); static_assert(!asl::hashable<int*>);
static_assert(!asl::hashable<int[]>); static_assert(!asl::hashable<int[]>);
static_assert(!asl::hashable<int[9]>); static_assert(!asl::hashable<int[9]>);
static_assert(asl::hashable<uint8_t>); static_assert(asl::hashable<uint8_t>);
static_assert(asl::hashable<uint16_t>); static_assert(asl::hashable<uint16_t>);
static_assert(asl::hashable<uint32_t>); static_assert(asl::hashable<uint32_t>);
static_assert(asl::hashable<uint64_t>); static_assert(asl::hashable<uint64_t>);
static_assert(asl::hashable<uint128_t>); static_assert(asl::hashable<uint128_t>);
static_assert(asl::hashable<int8_t>); static_assert(asl::hashable<int8_t>);
static_assert(asl::hashable<int16_t>); static_assert(asl::hashable<int16_t>);
static_assert(asl::hashable<int32_t>); static_assert(asl::hashable<int32_t>);
static_assert(asl::hashable<int64_t>); static_assert(asl::hashable<int64_t>);
ASL_TEST(integers) ASL_TEST(integers)
{ {
uint64_t a = asl::hash_value<uint16_t>(45); uint64_t a = asl::hash_value<uint16_t>(45);
uint64_t b = asl::hash_value<uint16_t>(45); uint64_t b = asl::hash_value<uint16_t>(45);
uint64_t c = asl::hash_value<uint16_t>(46); uint64_t c = asl::hash_value<uint16_t>(46);
uint64_t d = asl::hash_value<uint32_t>(45); uint64_t d = asl::hash_value<uint32_t>(45);
ASL_TEST_EXPECT(a == b); ASL_TEST_EXPECT(a == b);
ASL_TEST_EXPECT(a != c); ASL_TEST_EXPECT(a != c);
ASL_TEST_EXPECT(a != d); ASL_TEST_EXPECT(a != d);
} }
static_assert(asl::hashable<bool>); static_assert(asl::hashable<bool>);
ASL_TEST(bool) ASL_TEST(bool)
{ {
ASL_TEST_EXPECT(asl::hash_value(true) == asl::hash_value(true)); ASL_TEST_EXPECT(asl::hash_value(true) == asl::hash_value(true));
ASL_TEST_EXPECT(asl::hash_value(false) == asl::hash_value(false)); ASL_TEST_EXPECT(asl::hash_value(false) == asl::hash_value(false));
ASL_TEST_EXPECT(asl::hash_value(true) != asl::hash_value(false)); ASL_TEST_EXPECT(asl::hash_value(true) != asl::hash_value(false));
} }
static_assert(asl::hashable<asl::string_view>); static_assert(asl::hashable<asl::string_view>);
static_assert(asl::hashable<asl::string<>>); static_assert(asl::hashable<asl::string<>>);
ASL_TEST(strings) ASL_TEST(strings)
{ {
ASL_TEST_EXPECT(asl::hash_value("hello"_sv) == asl::hash_value("hello"_sv)); ASL_TEST_EXPECT(asl::hash_value("hello"_sv) == asl::hash_value("hello"_sv));
ASL_TEST_EXPECT(asl::hash_value("hello"_sv) != asl::hash_value("hello "_sv)); ASL_TEST_EXPECT(asl::hash_value("hello"_sv) != asl::hash_value("hello "_sv));
ASL_TEST_EXPECT(asl::hash_value("hello"_sv) != asl::hash_value("HELLO"_sv)); ASL_TEST_EXPECT(asl::hash_value("hello"_sv) != asl::hash_value("HELLO"_sv));
ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) == asl::hash_value(asl::string("hello"_sv))); ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) == asl::hash_value(asl::string("hello"_sv)));
ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) != asl::hash_value(asl::string("hello "_sv))); ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) != asl::hash_value(asl::string("hello "_sv)));
ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) != asl::hash_value(asl::string("HELLO"_sv))); ASL_TEST_EXPECT(asl::hash_value(asl::string("hello"_sv)) != asl::hash_value(asl::string("HELLO"_sv)));
ASL_TEST_EXPECT(asl::hash_value("hello"_sv) == asl::hash_value(asl::string("hello"_sv))); ASL_TEST_EXPECT(asl::hash_value("hello"_sv) == asl::hash_value(asl::string("hello"_sv)));
} }
static_assert(asl::hashable<asl::span<const int>>); static_assert(asl::hashable<asl::span<const int>>);
static_assert(!asl::hashable<asl::span<const int*>>); static_assert(!asl::hashable<asl::span<const int*>>);
static_assert(asl::hashable<asl::span<asl::string_view>>); static_assert(asl::hashable<asl::span<asl::string_view>>);
ASL_TEST(span) ASL_TEST(span)
{ {
int ints1[] = {1, 2, 3}; int ints1[] = {1, 2, 3};
int ints2[] = {1, 2, 3}; int ints2[] = {1, 2, 3};
int ints3[] = {1, 2}; int ints3[] = {1, 2};
int ints4[] = {3, 2, 1}; int ints4[] = {3, 2, 1};
ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) == asl::hash_value(asl::span<int>(ints2))); ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) == asl::hash_value(asl::span<int>(ints2)));
ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) != asl::hash_value(asl::span<int>(ints3))); ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) != asl::hash_value(asl::span<int>(ints3)));
ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) != asl::hash_value(asl::span<int>(ints4))); ASL_TEST_EXPECT(asl::hash_value(asl::span<int>(ints1)) != asl::hash_value(asl::span<int>(ints4)));
asl::string_view strs1[] = {"a", "abc", "hello"}; asl::string_view strs1[] = {"a", "abc", "hello"};
asl::string_view strs2[] = {"a", "abc", "hello"}; asl::string_view strs2[] = {"a", "abc", "hello"};
asl::string_view strs3[] = {"a", "abc"}; asl::string_view strs3[] = {"a", "abc"};
asl::string_view strs4[] = {"a", "abc", "hello", "what"}; asl::string_view strs4[] = {"a", "abc", "hello", "what"};
ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) == asl::hash_value(asl::span<asl::string_view>(strs2))); ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) == asl::hash_value(asl::span<asl::string_view>(strs2)));
ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) != asl::hash_value(asl::span<asl::string_view>(strs3))); ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) != asl::hash_value(asl::span<asl::string_view>(strs3)));
ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) != asl::hash_value(asl::span<asl::string_view>(strs4))); ASL_TEST_EXPECT(asl::hash_value(asl::span<asl::string_view>(strs1)) != asl::hash_value(asl::span<asl::string_view>(strs4)));
} }
static_assert(asl::hashable<asl::buffer<int>>); static_assert(asl::hashable<asl::buffer<int>>);
static_assert(!asl::hashable<asl::buffer<int*>>); static_assert(!asl::hashable<asl::buffer<int*>>);
ASL_TEST(buffer) ASL_TEST(buffer)
{ {
asl::buffer<int> ints1; asl::buffer<int> ints1;
ints1.push(1); ints1.push(1);
ints1.push(2); ints1.push(2);
ints1.push(3); ints1.push(3);
asl::buffer<int> ints2; asl::buffer<int> ints2;
ints2.push(1); ints2.push(1);
ints2.push(2); ints2.push(2);
ints2.push(3); ints2.push(3);
asl::buffer<int> ints3; asl::buffer<int> ints3;
ints3.push(1); ints3.push(1);
ints3.push(2); ints3.push(2);
asl::buffer<int> ints4; asl::buffer<int> ints4;
ints4.push(1); ints4.push(1);
ints4.push(2); ints4.push(2);
ints4.push(4); ints4.push(4);
ASL_TEST_EXPECT(asl::hash_value(ints1) == asl::hash_value(ints2)); ASL_TEST_EXPECT(asl::hash_value(ints1) == asl::hash_value(ints2));
ASL_TEST_EXPECT(asl::hash_value(ints1) != asl::hash_value(ints3)); ASL_TEST_EXPECT(asl::hash_value(ints1) != asl::hash_value(ints3));
ASL_TEST_EXPECT(asl::hash_value(ints1) != asl::hash_value(ints4)); ASL_TEST_EXPECT(asl::hash_value(ints1) != asl::hash_value(ints4));
ASL_TEST_EXPECT(asl::hash_value(ints1) == asl::hash_value(ints1.as_span())); ASL_TEST_EXPECT(asl::hash_value(ints1) == asl::hash_value(ints1.as_span()));
asl::buffer<asl::string_view> strs1; asl::buffer<asl::string_view> strs1;
strs1.push("Hello"); strs1.push("Hello");
strs1.push("World"); strs1.push("World");
asl::buffer<asl::string_view> strs2; asl::buffer<asl::string_view> strs2;
strs2.push("Hello"); strs2.push("Hello");
strs2.push("World"); strs2.push("World");
asl::buffer<asl::string_view> strs3; asl::buffer<asl::string_view> strs3;
strs3.push("Hello"); strs3.push("Hello");
strs3.push("world"); strs3.push("world");
asl::buffer<asl::string_view> strs4; asl::buffer<asl::string_view> strs4;
strs4.push("Hello"); strs4.push("Hello");
strs4.push("World"); strs4.push("World");
strs4.push("World"); strs4.push("World");
ASL_TEST_EXPECT(asl::hash_value(strs1) == asl::hash_value(strs2)); ASL_TEST_EXPECT(asl::hash_value(strs1) == asl::hash_value(strs2));
ASL_TEST_EXPECT(asl::hash_value(strs1) != asl::hash_value(strs3)); ASL_TEST_EXPECT(asl::hash_value(strs1) != asl::hash_value(strs3));
ASL_TEST_EXPECT(asl::hash_value(strs1) != asl::hash_value(strs4)); ASL_TEST_EXPECT(asl::hash_value(strs1) != asl::hash_value(strs4));
ASL_TEST_EXPECT(asl::hash_value(strs1) == asl::hash_value(strs1.as_span())); ASL_TEST_EXPECT(asl::hash_value(strs1) == asl::hash_value(strs1.as_span()));
} }
enum Enum1 {}; enum Enum1 {};
enum class Enum2 {}; enum class Enum2 {};
static_assert(asl::hashable<Enum1>); static_assert(asl::hashable<Enum1>);
static_assert(asl::hashable<Enum2>); static_assert(asl::hashable<Enum2>);
static_assert(!asl::hashable<asl::box<int*>>); static_assert(!asl::hashable<asl::box<int*>>);
static_assert(asl::hashable<asl::box<asl::string_view>>); static_assert(asl::hashable<asl::box<asl::string_view>>);
ASL_TEST(box) ASL_TEST(box)
{ {
auto b1 = asl::make_box<asl::string_view>("Hello, world!"); auto b1 = asl::make_box<asl::string_view>("Hello, world!");
auto b2 = asl::make_box<asl::string_view>("Hello, world!"); auto b2 = asl::make_box<asl::string_view>("Hello, world!");
auto b3 = asl::make_box<asl::string_view>("Hello, world! 2"); auto b3 = asl::make_box<asl::string_view>("Hello, world! 2");
ASL_TEST_EXPECT(asl::hash_value(b1) == asl::hash_value(b2)); ASL_TEST_EXPECT(asl::hash_value(b1) == asl::hash_value(b2));
ASL_TEST_EXPECT(asl::hash_value(b1) != asl::hash_value(b3)); ASL_TEST_EXPECT(asl::hash_value(b1) != asl::hash_value(b3));
ASL_TEST_EXPECT(asl::hash_value(b1) == asl::hash_value("Hello, world!"_sv)); ASL_TEST_EXPECT(asl::hash_value(b1) == asl::hash_value("Hello, world!"_sv));
} }
struct NonZero struct NonZero
{ {
int value; int value;
constexpr explicit NonZero(int x) : value(x) constexpr explicit NonZero(int x) : value(x)
{ {
ASL_ASSERT(x != 0); ASL_ASSERT(x != 0);
} }
constexpr explicit NonZero(asl::niche_t) : value(0) {} constexpr explicit NonZero(asl::niche_t) : value(0) {}
constexpr bool operator==(asl::niche_t) const { return value == 0; } constexpr bool operator==(asl::niche_t) const { return value == 0; }
}; };
namespace asl { template<> struct is_uniquely_represented<NonZero> : true_type {}; } namespace asl { template<> struct is_uniquely_represented<NonZero> : true_type {}; }
static_assert(asl::has_niche<NonZero>); static_assert(asl::has_niche<NonZero>);
static_assert(asl::uniquely_represented<NonZero>); static_assert(asl::uniquely_represented<NonZero>);
static_assert(asl::hashable<asl::option<int>>); static_assert(asl::hashable<asl::option<int>>);
static_assert(!asl::hashable<asl::option<int*>>); static_assert(!asl::hashable<asl::option<int*>>);
static_assert(asl::hashable<asl::option<asl::string_view>>); static_assert(asl::hashable<asl::option<asl::string_view>>);
static_assert(asl::hashable<asl::option<NonZero>>); static_assert(asl::hashable<asl::option<NonZero>>);
static_assert(asl::uniquely_represented<asl::option<NonZero>>); static_assert(asl::uniquely_represented<asl::option<NonZero>>);
ASL_TEST(option) ASL_TEST(option)
{ {
asl::option<int> int1 = 0; asl::option<int> int1 = 0;
asl::option<int> int2 = 0; asl::option<int> int2 = 0;
asl::option<int> int3 = 1; asl::option<int> int3 = 1;
asl::option<int> int4 = asl::nullopt; asl::option<int> int4 = asl::nullopt;
ASL_TEST_EXPECT(asl::hash_value(int1) == asl::hash_value(int2)); ASL_TEST_EXPECT(asl::hash_value(int1) == asl::hash_value(int2));
ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int3)); ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int3));
ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int4)); ASL_TEST_EXPECT(asl::hash_value(int1) != asl::hash_value(int4));
asl::option<NonZero> noz1{8}; asl::option<NonZero> noz1{8};
asl::option<NonZero> noz2{8}; asl::option<NonZero> noz2{8};
asl::option<NonZero> noz3{9}; asl::option<NonZero> noz3{9};
asl::option<NonZero> noz4 = asl::nullopt; asl::option<NonZero> noz4 = asl::nullopt;
ASL_TEST_EXPECT(asl::hash_value(noz1) == asl::hash_value(noz2)); ASL_TEST_EXPECT(asl::hash_value(noz1) == asl::hash_value(noz2));
ASL_TEST_EXPECT(asl::hash_value(noz1) != asl::hash_value(noz3)); ASL_TEST_EXPECT(asl::hash_value(noz1) != asl::hash_value(noz3));
ASL_TEST_EXPECT(asl::hash_value(noz1) != asl::hash_value(noz4)); ASL_TEST_EXPECT(asl::hash_value(noz1) != asl::hash_value(noz4));
} }
static_assert(asl::hashable<asl::status>); static_assert(asl::hashable<asl::status>);
ASL_TEST(status) ASL_TEST(status)
{ {
asl::status s1 = asl::ok(); asl::status s1 = asl::ok();
asl::status s2 = asl::ok(); asl::status s2 = asl::ok();
asl::status s3 = asl::internal_error(); asl::status s3 = asl::internal_error();
asl::status s4 = asl::internal_error(); asl::status s4 = asl::internal_error();
asl::status s5 = asl::runtime_error(); asl::status s5 = asl::runtime_error();
asl::status s6 = asl::internal_error("Oh, no!"); asl::status s6 = asl::internal_error("Oh, no!");
asl::status s7 = asl::internal_error("Oh, no!"); asl::status s7 = asl::internal_error("Oh, no!");
asl::status s8 = asl::internal_error("Oh, no"); asl::status s8 = asl::internal_error("Oh, no");
asl::status s9 = asl::runtime_error("Oh, no!"); asl::status s9 = asl::runtime_error("Oh, no!");
ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2)); ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2));
ASL_TEST_EXPECT(asl::hash_value(s3) == asl::hash_value(s4)); ASL_TEST_EXPECT(asl::hash_value(s3) == asl::hash_value(s4));
ASL_TEST_EXPECT(asl::hash_value(s6) == asl::hash_value(s7)); ASL_TEST_EXPECT(asl::hash_value(s6) == asl::hash_value(s7));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s3)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s3));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s5)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s5));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s6)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s6));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s9)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s9));
ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s5)); ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s5));
ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s6)); ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s6));
ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s8)); ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s8));
ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s9)); ASL_TEST_EXPECT(asl::hash_value(s3) != asl::hash_value(s9));
ASL_TEST_EXPECT(asl::hash_value(s6) != asl::hash_value(s8)); ASL_TEST_EXPECT(asl::hash_value(s6) != asl::hash_value(s8));
ASL_TEST_EXPECT(asl::hash_value(s6) != asl::hash_value(s9)); ASL_TEST_EXPECT(asl::hash_value(s6) != asl::hash_value(s9));
} }
static_assert(asl::hashable<asl::status_or<int>>); static_assert(asl::hashable<asl::status_or<int>>);
static_assert(asl::hashable<asl::status_or<asl::string_view>>); static_assert(asl::hashable<asl::status_or<asl::string_view>>);
static_assert(!asl::hashable<asl::status_or<int*>>); static_assert(!asl::hashable<asl::status_or<int*>>);
ASL_TEST(status_or) ASL_TEST(status_or)
{ {
asl::status_or<int> s1 = 42; asl::status_or<int> s1 = 42;
asl::status_or<int> s2 = 42; asl::status_or<int> s2 = 42;
asl::status_or<int> s3 = 43; asl::status_or<int> s3 = 43;
asl::status_or<int> s4 = asl::runtime_error(); asl::status_or<int> s4 = asl::runtime_error();
asl::status_or<int> s5 = asl::runtime_error(); asl::status_or<int> s5 = asl::runtime_error();
asl::status_or<int> s6 = asl::runtime_error("Hello"); asl::status_or<int> s6 = asl::runtime_error("Hello");
asl::status_or<int> s7 = asl::runtime_error("Hello"); asl::status_or<int> s7 = asl::runtime_error("Hello");
ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2)); ASL_TEST_EXPECT(asl::hash_value(s1) == asl::hash_value(s2));
ASL_TEST_EXPECT(asl::hash_value(s4) == asl::hash_value(s5)); ASL_TEST_EXPECT(asl::hash_value(s4) == asl::hash_value(s5));
ASL_TEST_EXPECT(asl::hash_value(s6) == asl::hash_value(s7)); ASL_TEST_EXPECT(asl::hash_value(s6) == asl::hash_value(s7));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s3)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s3));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s4)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s4));
ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s6)); ASL_TEST_EXPECT(asl::hash_value(s1) != asl::hash_value(s6));
ASL_TEST_EXPECT(asl::hash_value(s4) != asl::hash_value(s6)); ASL_TEST_EXPECT(asl::hash_value(s4) != asl::hash_value(s6));
} }

View File

@ -1,15 +1,15 @@
#include "asl/integers.hpp" #include "asl/integers.hpp"
static_assert(sizeof(int8_t) == 1); static_assert(sizeof(int8_t) == 1);
static_assert(sizeof(int16_t) == 2); static_assert(sizeof(int16_t) == 2);
static_assert(sizeof(int32_t) == 4); static_assert(sizeof(int32_t) == 4);
static_assert(sizeof(int64_t) == 8); static_assert(sizeof(int64_t) == 8);
static_assert(sizeof(uint8_t) == 1); static_assert(sizeof(uint8_t) == 1);
static_assert(sizeof(uint16_t) == 2); static_assert(sizeof(uint16_t) == 2);
static_assert(sizeof(uint32_t) == 4); static_assert(sizeof(uint32_t) == 4);
static_assert(sizeof(uint64_t) == 8); static_assert(sizeof(uint64_t) == 8);
static_assert(sizeof(asl::byte) == 1); static_assert(sizeof(asl::byte) == 1);
static_assert(sizeof(uintptr_t) == sizeof(void*)); static_assert(sizeof(uintptr_t) == sizeof(void*));

View File

@ -1,22 +1,22 @@
#include "asl/maybe_uninit.hpp" #include "asl/maybe_uninit.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
static_assert(asl::layout::of<int>() == asl::layout::of<asl::maybe_uninit<int>>()); static_assert(asl::layout::of<int>() == asl::layout::of<asl::maybe_uninit<int>>());
static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>); static_assert(asl::size_of<int> == asl::size_of<asl::maybe_uninit<int>>);
static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>); static_assert(asl::align_of<int> == asl::align_of<asl::maybe_uninit<int>>);
#define TEST_TYPE_PROPERTIES(PRP) \ #define TEST_TYPE_PROPERTIES(PRP) \
static_assert(asl::PRP<asl::maybe_uninit<TrivialType>> == asl::PRP<TrivialType>); \ static_assert(asl::PRP<asl::maybe_uninit<TrivialType>> == asl::PRP<TrivialType>); \
static_assert(asl::PRP<asl::maybe_uninit<TrivialTypeDefaultValue>> == asl::PRP<TrivialTypeDefaultValue>); \ static_assert(asl::PRP<asl::maybe_uninit<TrivialTypeDefaultValue>> == asl::PRP<TrivialTypeDefaultValue>); \
static_assert(asl::PRP<asl::maybe_uninit<WithDestructor>> == asl::PRP<WithDestructor>); \ static_assert(asl::PRP<asl::maybe_uninit<WithDestructor>> == asl::PRP<WithDestructor>); \
static_assert(asl::PRP<asl::maybe_uninit<Copyable>> == asl::PRP<Copyable>); \ static_assert(asl::PRP<asl::maybe_uninit<Copyable>> == asl::PRP<Copyable>); \
static_assert(asl::PRP<asl::maybe_uninit<MoveableOnly>> == asl::PRP<MoveableOnly>); \ static_assert(asl::PRP<asl::maybe_uninit<MoveableOnly>> == asl::PRP<MoveableOnly>); \
static_assert(asl::PRP<asl::maybe_uninit<Pinned>> == asl::PRP<Pinned>); static_assert(asl::PRP<asl::maybe_uninit<Pinned>> == asl::PRP<Pinned>);
TEST_TYPE_PROPERTIES(trivially_default_constructible); TEST_TYPE_PROPERTIES(trivially_default_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_constructible); TEST_TYPE_PROPERTIES(trivially_copy_constructible);
TEST_TYPE_PROPERTIES(trivially_move_constructible); TEST_TYPE_PROPERTIES(trivially_move_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_assignable); TEST_TYPE_PROPERTIES(trivially_copy_assignable);
TEST_TYPE_PROPERTIES(trivially_move_assignable); TEST_TYPE_PROPERTIES(trivially_move_assignable);
TEST_TYPE_PROPERTIES(trivially_destructible); TEST_TYPE_PROPERTIES(trivially_destructible);

View File

@ -1,249 +1,249 @@
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
struct Struct {}; struct Struct {};
union Union {}; union Union {};
enum Enum : uint8_t { EnumVariant = 0, }; enum Enum : uint8_t { EnumVariant = 0, };
enum class EnumClass : uint8_t { Variant = 0, }; enum class EnumClass : uint8_t { Variant = 0, };
static_assert(!asl::same_as<long, short>); static_assert(!asl::same_as<long, short>);
static_assert(asl::same_as<int, int>); static_assert(asl::same_as<int, int>);
static_assert(asl::same_as<asl::select_t<false, int, float>, float>); static_assert(asl::same_as<asl::select_t<false, int, float>, float>);
static_assert(asl::same_as<asl::select_t<true, int, float>, int>); static_assert(asl::same_as<asl::select_t<true, int, float>, int>);
static_assert(asl::default_constructible<int>); static_assert(asl::default_constructible<int>);
static_assert(asl::default_constructible<TrivialType>); static_assert(asl::default_constructible<TrivialType>);
static_assert(asl::default_constructible<TrivialTypeDefaultValue>); static_assert(asl::default_constructible<TrivialTypeDefaultValue>);
static_assert(asl::trivially_default_constructible<int>); static_assert(asl::trivially_default_constructible<int>);
static_assert(asl::trivially_default_constructible<TrivialType>); static_assert(asl::trivially_default_constructible<TrivialType>);
static_assert(!asl::trivially_default_constructible<TrivialTypeDefaultValue>); static_assert(!asl::trivially_default_constructible<TrivialTypeDefaultValue>);
static_assert(asl::copy_constructible<int>); static_assert(asl::copy_constructible<int>);
static_assert(asl::copy_constructible<TrivialType>); static_assert(asl::copy_constructible<TrivialType>);
static_assert(asl::copy_constructible<Copyable>); static_assert(asl::copy_constructible<Copyable>);
static_assert(!asl::copy_constructible<MoveableOnly>); static_assert(!asl::copy_constructible<MoveableOnly>);
static_assert(!asl::copy_constructible<Pinned>); static_assert(!asl::copy_constructible<Pinned>);
static_assert(asl::trivially_copy_constructible<int>); static_assert(asl::trivially_copy_constructible<int>);
static_assert(asl::trivially_copy_constructible<TrivialType>); static_assert(asl::trivially_copy_constructible<TrivialType>);
static_assert(asl::trivially_copy_constructible<TrivialTypeDefaultValue>); static_assert(asl::trivially_copy_constructible<TrivialTypeDefaultValue>);
static_assert(!asl::trivially_copy_constructible<WithDestructor>); static_assert(!asl::trivially_copy_constructible<WithDestructor>);
static_assert(!asl::trivially_copy_constructible<Copyable>); static_assert(!asl::trivially_copy_constructible<Copyable>);
static_assert(!asl::trivially_copy_constructible<MoveableOnly>); static_assert(!asl::trivially_copy_constructible<MoveableOnly>);
static_assert(!asl::trivially_copy_constructible<Pinned>); static_assert(!asl::trivially_copy_constructible<Pinned>);
static_assert(asl::move_constructible<int>); static_assert(asl::move_constructible<int>);
static_assert(asl::move_constructible<TrivialType>); static_assert(asl::move_constructible<TrivialType>);
static_assert(asl::move_constructible<Copyable>); static_assert(asl::move_constructible<Copyable>);
static_assert(asl::move_constructible<MoveableOnly>); static_assert(asl::move_constructible<MoveableOnly>);
static_assert(!asl::move_constructible<Pinned>); static_assert(!asl::move_constructible<Pinned>);
static_assert(asl::trivially_move_constructible<int>); static_assert(asl::trivially_move_constructible<int>);
static_assert(asl::trivially_move_constructible<TrivialType>); static_assert(asl::trivially_move_constructible<TrivialType>);
static_assert(asl::trivially_move_constructible<TrivialTypeDefaultValue>); static_assert(asl::trivially_move_constructible<TrivialTypeDefaultValue>);
static_assert(!asl::trivially_move_constructible<WithDestructor>); static_assert(!asl::trivially_move_constructible<WithDestructor>);
static_assert(!asl::trivially_move_constructible<Copyable>); static_assert(!asl::trivially_move_constructible<Copyable>);
static_assert(!asl::trivially_move_constructible<MoveableOnly>); static_assert(!asl::trivially_move_constructible<MoveableOnly>);
static_assert(!asl::trivially_move_constructible<Pinned>); static_assert(!asl::trivially_move_constructible<Pinned>);
static_assert(asl::copy_assignable<int>); static_assert(asl::copy_assignable<int>);
static_assert(asl::copy_assignable<TrivialType>); static_assert(asl::copy_assignable<TrivialType>);
static_assert(asl::copy_assignable<Copyable>); static_assert(asl::copy_assignable<Copyable>);
static_assert(!asl::copy_assignable<MoveableOnly>); static_assert(!asl::copy_assignable<MoveableOnly>);
static_assert(!asl::copy_assignable<Pinned>); static_assert(!asl::copy_assignable<Pinned>);
static_assert(asl::trivially_copy_assignable<int>); static_assert(asl::trivially_copy_assignable<int>);
static_assert(asl::trivially_copy_assignable<TrivialType>); static_assert(asl::trivially_copy_assignable<TrivialType>);
static_assert(asl::trivially_copy_assignable<TrivialTypeDefaultValue>); static_assert(asl::trivially_copy_assignable<TrivialTypeDefaultValue>);
static_assert(asl::trivially_copy_assignable<WithDestructor>); static_assert(asl::trivially_copy_assignable<WithDestructor>);
static_assert(!asl::trivially_copy_assignable<Copyable>); static_assert(!asl::trivially_copy_assignable<Copyable>);
static_assert(!asl::trivially_copy_assignable<MoveableOnly>); static_assert(!asl::trivially_copy_assignable<MoveableOnly>);
static_assert(!asl::trivially_copy_assignable<Pinned>); static_assert(!asl::trivially_copy_assignable<Pinned>);
static_assert(asl::copyable<int>); static_assert(asl::copyable<int>);
static_assert(asl::copyable<TrivialType>); static_assert(asl::copyable<TrivialType>);
static_assert(asl::copyable<Copyable>); static_assert(asl::copyable<Copyable>);
static_assert(!asl::copyable<MoveableOnly>); static_assert(!asl::copyable<MoveableOnly>);
static_assert(!asl::copyable<Pinned>); static_assert(!asl::copyable<Pinned>);
static_assert(asl::moveable<int>); static_assert(asl::moveable<int>);
static_assert(asl::moveable<TrivialType>); static_assert(asl::moveable<TrivialType>);
static_assert(asl::moveable<Copyable>); static_assert(asl::moveable<Copyable>);
static_assert(asl::moveable<MoveableOnly>); static_assert(asl::moveable<MoveableOnly>);
static_assert(!asl::moveable<Pinned>); static_assert(!asl::moveable<Pinned>);
static_assert(asl::move_assignable<int>); static_assert(asl::move_assignable<int>);
static_assert(asl::move_assignable<TrivialType>); static_assert(asl::move_assignable<TrivialType>);
static_assert(asl::move_assignable<Copyable>); static_assert(asl::move_assignable<Copyable>);
static_assert(asl::move_assignable<MoveableOnly>); static_assert(asl::move_assignable<MoveableOnly>);
static_assert(!asl::move_assignable<Pinned>); static_assert(!asl::move_assignable<Pinned>);
static_assert(asl::trivially_move_assignable<int>); static_assert(asl::trivially_move_assignable<int>);
static_assert(asl::trivially_move_assignable<TrivialType>); static_assert(asl::trivially_move_assignable<TrivialType>);
static_assert(asl::trivially_move_assignable<TrivialTypeDefaultValue>); static_assert(asl::trivially_move_assignable<TrivialTypeDefaultValue>);
static_assert(asl::trivially_move_assignable<WithDestructor>); static_assert(asl::trivially_move_assignable<WithDestructor>);
static_assert(!asl::trivially_move_assignable<Copyable>); static_assert(!asl::trivially_move_assignable<Copyable>);
static_assert(!asl::trivially_move_assignable<MoveableOnly>); static_assert(!asl::trivially_move_assignable<MoveableOnly>);
static_assert(!asl::trivially_move_assignable<Pinned>); static_assert(!asl::trivially_move_assignable<Pinned>);
static_assert(asl::trivially_destructible<int>); static_assert(asl::trivially_destructible<int>);
static_assert(asl::trivially_destructible<TrivialType>); static_assert(asl::trivially_destructible<TrivialType>);
static_assert(asl::trivially_destructible<TrivialTypeDefaultValue>); static_assert(asl::trivially_destructible<TrivialTypeDefaultValue>);
static_assert(!asl::trivially_destructible<WithDestructor>); static_assert(!asl::trivially_destructible<WithDestructor>);
static_assert(asl::trivially_destructible<Copyable>); static_assert(asl::trivially_destructible<Copyable>);
static_assert(asl::trivially_destructible<MoveableOnly>); static_assert(asl::trivially_destructible<MoveableOnly>);
static_assert(asl::trivially_destructible<Pinned>); static_assert(asl::trivially_destructible<Pinned>);
static_assert(asl::same_as<int, asl::un_const_t<int>>); static_assert(asl::same_as<int, asl::un_const_t<int>>);
static_assert(asl::same_as<int, asl::un_const_t<const int>>); static_assert(asl::same_as<int, asl::un_const_t<const int>>);
static_assert(asl::same_as<const int&, asl::un_const_t<const int&>>); static_assert(asl::same_as<const int&, asl::un_const_t<const int&>>);
static_assert(asl::same_as<int, asl::un_volatile_t<int>>); static_assert(asl::same_as<int, asl::un_volatile_t<int>>);
static_assert(asl::same_as<int, asl::un_volatile_t<volatile int>>); static_assert(asl::same_as<int, asl::un_volatile_t<volatile int>>);
static_assert(asl::same_as<volatile int&, asl::un_volatile_t<volatile int&>>); static_assert(asl::same_as<volatile int&, asl::un_volatile_t<volatile int&>>);
static_assert(asl::same_as<int, asl::un_cv_t<int>>); static_assert(asl::same_as<int, asl::un_cv_t<int>>);
static_assert(asl::same_as<int, asl::un_cv_t<const int>>); static_assert(asl::same_as<int, asl::un_cv_t<const int>>);
static_assert(asl::same_as<int, asl::un_cv_t<const volatile int>>); static_assert(asl::same_as<int, asl::un_cv_t<const volatile int>>);
static_assert(asl::same_as<int, asl::un_cv_t<volatile int>>); static_assert(asl::same_as<int, asl::un_cv_t<volatile int>>);
static_assert(asl::is_void<void>); static_assert(asl::is_void<void>);
static_assert(asl::is_void<const void>); static_assert(asl::is_void<const void>);
static_assert(asl::is_void<const volatile void>); static_assert(asl::is_void<const volatile void>);
static_assert(asl::is_void<volatile void>); static_assert(asl::is_void<volatile void>);
static_assert(!asl::is_void<int>); static_assert(!asl::is_void<int>);
static_assert(!asl::is_void<Struct>); static_assert(!asl::is_void<Struct>);
static_assert(!asl::is_void<int&>); static_assert(!asl::is_void<int&>);
static_assert(!asl::is_void<int&&>); static_assert(!asl::is_void<int&&>);
static_assert(!asl::is_void<void()>); static_assert(!asl::is_void<void()>);
static_assert(!asl::is_void<void() const &&>); static_assert(!asl::is_void<void() const &&>);
static_assert(asl::is_ref<int&>); static_assert(asl::is_ref<int&>);
static_assert(asl::is_ref<const int&>); static_assert(asl::is_ref<const int&>);
static_assert(asl::is_ref<const volatile int&>); static_assert(asl::is_ref<const volatile int&>);
static_assert(asl::is_ref<int&&>); static_assert(asl::is_ref<int&&>);
static_assert(!asl::is_ref<int>); static_assert(!asl::is_ref<int>);
static_assert(!asl::is_ref<void>); static_assert(!asl::is_ref<void>);
static_assert(!asl::is_ref<void()>); static_assert(!asl::is_ref<void()>);
static_assert(!asl::is_ref<void() const &&>); static_assert(!asl::is_ref<void() const &&>);
static_assert(asl::is_ptr<int*>); static_assert(asl::is_ptr<int*>);
static_assert(asl::is_ptr<const int* const>); static_assert(asl::is_ptr<const int* const>);
static_assert(asl::is_ptr<const volatile int*>); static_assert(asl::is_ptr<const volatile int*>);
static_assert(!asl::is_ptr<int>); static_assert(!asl::is_ptr<int>);
static_assert(!asl::is_ptr<void>); static_assert(!asl::is_ptr<void>);
static_assert(!asl::is_ptr<void()>); static_assert(!asl::is_ptr<void()>);
static_assert(!asl::is_ptr<void() const &&>); static_assert(!asl::is_ptr<void() const &&>);
static_assert(asl::same_as<int, asl::tame_t<int>>); static_assert(asl::same_as<int, asl::tame_t<int>>);
static_assert(asl::same_as<int(), asl::tame_t<int()>>); static_assert(asl::same_as<int(), asl::tame_t<int()>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float)>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float)>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float) &>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float) &>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float) const &&>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float) const &&>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float) volatile noexcept>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float) volatile noexcept>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float) && noexcept>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float) && noexcept>>);
static_assert(asl::same_as<int(float), asl::tame_t<int(float) const>>); static_assert(asl::same_as<int(float), asl::tame_t<int(float) const>>);
static_assert(asl::is_func<void()>); static_assert(asl::is_func<void()>);
static_assert(asl::is_func<void(int)>); static_assert(asl::is_func<void(int)>);
static_assert(asl::is_func<void(int, float)>); static_assert(asl::is_func<void(int, float)>);
static_assert(asl::is_func<void() &>); static_assert(asl::is_func<void() &>);
static_assert(asl::is_func<void() const &&>); static_assert(asl::is_func<void() const &&>);
static_assert(asl::is_func<void() volatile noexcept>); static_assert(asl::is_func<void() volatile noexcept>);
static_assert(!asl::is_func<void(*)()>); static_assert(!asl::is_func<void(*)()>);
static_assert(!asl::is_func<int>); static_assert(!asl::is_func<int>);
static_assert(!asl::is_func<int&>); static_assert(!asl::is_func<int&>);
static_assert(!asl::is_func<void>); static_assert(!asl::is_func<void>);
static_assert(asl::is_object<Struct>); static_assert(asl::is_object<Struct>);
static_assert(asl::is_object<int>); static_assert(asl::is_object<int>);
static_assert(asl::is_object<int*>); static_assert(asl::is_object<int*>);
static_assert(asl::is_object<int Struct::*>); static_assert(asl::is_object<int Struct::*>);
static_assert(asl::is_object<int (Struct::*)(float)>); static_assert(asl::is_object<int (Struct::*)(float)>);
static_assert(asl::is_object<int[]>); static_assert(asl::is_object<int[]>);
static_assert(asl::is_object<int[45]>); static_assert(asl::is_object<int[45]>);
static_assert(asl::is_object<Enum>); static_assert(asl::is_object<Enum>);
static_assert(!asl::is_object<int&>); static_assert(!asl::is_object<int&>);
static_assert(!asl::is_object<void>); static_assert(!asl::is_object<void>);
static_assert(!asl::is_object<void(int)>); static_assert(!asl::is_object<void(int)>);
static_assert(!asl::is_object<int(float) const && noexcept>); static_assert(!asl::is_object<int(float) const && noexcept>);
static_assert(!asl::is_array<Struct>); static_assert(!asl::is_array<Struct>);
static_assert(!asl::is_array<int>); static_assert(!asl::is_array<int>);
static_assert(!asl::is_array<int*>); static_assert(!asl::is_array<int*>);
static_assert(!asl::is_array<int Struct::*>); static_assert(!asl::is_array<int Struct::*>);
static_assert(!asl::is_array<int (Struct::*)(float)>); static_assert(!asl::is_array<int (Struct::*)(float)>);
static_assert(asl::is_array<int[]>); static_assert(asl::is_array<int[]>);
static_assert(asl::is_array<int[45]>); static_assert(asl::is_array<int[45]>);
static_assert(!asl::is_array<Enum>); static_assert(!asl::is_array<Enum>);
static_assert(!asl::is_array<int&>); static_assert(!asl::is_array<int&>);
static_assert(!asl::is_array<void>); static_assert(!asl::is_array<void>);
static_assert(!asl::is_array<void(int)>); static_assert(!asl::is_array<void(int)>);
static_assert(!asl::is_array<int(float) const && noexcept>); static_assert(!asl::is_array<int(float) const && noexcept>);
static_assert(asl::same_as<int, asl::un_ref_t<int>>); static_assert(asl::same_as<int, asl::un_ref_t<int>>);
static_assert(asl::same_as<int, asl::un_ref_t<int&>>); static_assert(asl::same_as<int, asl::un_ref_t<int&>>);
static_assert(asl::same_as<int, asl::un_ref_t<int&&>>); static_assert(asl::same_as<int, asl::un_ref_t<int&&>>);
static_assert(asl::same_as<int() &, asl::un_ref_t<int() &>>); static_assert(asl::same_as<int() &, asl::un_ref_t<int() &>>);
static_assert(asl::types_count<int, float> == 2); static_assert(asl::types_count<int, float> == 2);
static_assert(asl::types_count<int, int> == 2); static_assert(asl::types_count<int, int> == 2);
static_assert(asl::types_count<int> == 1); static_assert(asl::types_count<int> == 1);
static_assert(asl::types_count<> == 0); static_assert(asl::types_count<> == 0);
class Base {}; class Base {};
class Derived : public Base {}; class Derived : public Base {};
class C {}; class C {};
class D { public: operator C() { return c; } C c; }; // NOLINT class D { public: operator C() { return c; } C c; }; // NOLINT
class E { public: template<class T> E(T&&) {} }; // NOLINT class E { public: template<class T> E(T&&) {} }; // NOLINT
static_assert(asl::convertible_from<Base*, Derived*>); static_assert(asl::convertible_from<Base*, Derived*>);
static_assert(!asl::convertible_from<Derived*, Base*>); static_assert(!asl::convertible_from<Derived*, Base*>);
static_assert(asl::convertible_from<C, D>); static_assert(asl::convertible_from<C, D>);
static_assert(!asl::convertible_from<C*, Derived*>); static_assert(!asl::convertible_from<C*, Derived*>);
static_assert(asl::convertible_from<E, Base>); static_assert(asl::convertible_from<E, Base>);
static_assert(!asl::convertible_from<int16_t(&)[], int32_t(&)[]>); static_assert(!asl::convertible_from<int16_t(&)[], int32_t(&)[]>);
static_assert(asl::convertible_from<const int16_t(&)[], int16_t(&)[]>); static_assert(asl::convertible_from<const int16_t(&)[], int16_t(&)[]>);
static_assert(asl::convertible_from<const int16_t(&)[], const int16_t(&)[]>); static_assert(asl::convertible_from<const int16_t(&)[], const int16_t(&)[]>);
static_assert(asl::convertible_from<int16_t(&)[], int16_t(&)[]>); static_assert(asl::convertible_from<int16_t(&)[], int16_t(&)[]>);
static_assert(!asl::convertible_from<int32_t(&)[], int16_t(&)[]>); static_assert(!asl::convertible_from<int32_t(&)[], int16_t(&)[]>);
static_assert(!asl::convertible_from<int16_t(&)[], const int16_t(&)[]>); static_assert(!asl::convertible_from<int16_t(&)[], const int16_t(&)[]>);
static_assert(!asl::convertible_from<C(&)[], D(&)[]>); static_assert(!asl::convertible_from<C(&)[], D(&)[]>);
static_assert(asl::derived_from<Derived, Base>); static_assert(asl::derived_from<Derived, Base>);
static_assert(!asl::derived_from<Base, Derived>); static_assert(!asl::derived_from<Base, Derived>);
static_assert(!asl::derived_from<D, C>); static_assert(!asl::derived_from<D, C>);
static_assert(!asl::derived_from<C, D>); static_assert(!asl::derived_from<C, D>);
static_assert(!asl::derived_from<uint8_t, uint16_t>); static_assert(!asl::derived_from<uint8_t, uint16_t>);
static_assert(!asl::derived_from<uint16_t, uint8_t>); static_assert(!asl::derived_from<uint16_t, uint8_t>);
static_assert(!asl::derived_from<int, int>); static_assert(!asl::derived_from<int, int>);
static_assert(!asl::is_const<int>); static_assert(!asl::is_const<int>);
static_assert(asl::is_const<const int>); static_assert(asl::is_const<const int>);
static_assert(!asl::is_const<const int*>); static_assert(!asl::is_const<const int*>);
static_assert(asl::is_const<int* const>); static_assert(asl::is_const<int* const>);
static_assert(asl::is_floating_point<float>); static_assert(asl::is_floating_point<float>);
static_assert(asl::is_floating_point<const float>); static_assert(asl::is_floating_point<const float>);
static_assert(asl::is_floating_point<volatile double>); static_assert(asl::is_floating_point<volatile double>);
static_assert(!asl::is_floating_point<const float&>); static_assert(!asl::is_floating_point<const float&>);
static_assert(!asl::is_floating_point<int>); static_assert(!asl::is_floating_point<int>);
static_assert(!asl::is_floating_point<C>); static_assert(!asl::is_floating_point<C>);
static_assert(asl::uniquely_represented<int>); static_assert(asl::uniquely_represented<int>);
static_assert(asl::uniquely_represented<uint128_t>); static_assert(asl::uniquely_represented<uint128_t>);
static_assert(!asl::uniquely_represented<bool>); static_assert(!asl::uniquely_represented<bool>);
enum Enum1 {}; enum Enum1 {};
enum class Enum2 {}; enum class Enum2 {};
static_assert(asl::uniquely_represented<Enum1>); static_assert(asl::uniquely_represented<Enum1>);
static_assert(asl::uniquely_represented<Enum2>); static_assert(asl::uniquely_represented<Enum2>);
static_assert(!asl::is_enum<int>); static_assert(!asl::is_enum<int>);
static_assert(asl::is_enum<Enum1>); static_assert(asl::is_enum<Enum1>);
static_assert(asl::is_enum<Enum2>); static_assert(asl::is_enum<Enum2>);

View File

@ -1,330 +1,330 @@
#include "asl/option.hpp" #include "asl/option.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
class Base {}; class Base {};
class Derived : public Base {}; class Derived : public Base {};
struct NonZero struct NonZero
{ {
int value; int value;
constexpr explicit NonZero(int x) : value(x) constexpr explicit NonZero(int x) : value(x)
{ {
ASL_ASSERT(x != 0); ASL_ASSERT(x != 0);
} }
constexpr explicit NonZero(asl::niche_t) : value(0) {} constexpr explicit NonZero(asl::niche_t) : value(0) {}
constexpr bool operator==(asl::niche_t) const { return value == 0; } constexpr bool operator==(asl::niche_t) const { return value == 0; }
}; };
static_assert(asl::has_niche<NonZero>); static_assert(asl::has_niche<NonZero>);
static_assert(!asl::has_niche<int>); static_assert(!asl::has_niche<int>);
static_assert(sizeof(asl::option<int>) >= sizeof(int)); static_assert(sizeof(asl::option<int>) >= sizeof(int));
static_assert(sizeof(asl::option<NonZero>) == sizeof(NonZero)); static_assert(sizeof(asl::option<NonZero>) == sizeof(NonZero));
static_assert(!asl::is_option<int>); static_assert(!asl::is_option<int>);
static_assert(asl::is_option<asl::option<int>>); static_assert(asl::is_option<asl::option<int>>);
static_assert(asl::is_option<const asl::option<int>>); static_assert(asl::is_option<const asl::option<int>>);
static_assert(asl::trivially_destructible<asl::option<TrivialType>>); static_assert(asl::trivially_destructible<asl::option<TrivialType>>);
static_assert(!asl::trivially_destructible<asl::option<WithDestructor>>); static_assert(!asl::trivially_destructible<asl::option<WithDestructor>>);
static_assert(asl::trivially_copy_constructible<asl::option<int>>); static_assert(asl::trivially_copy_constructible<asl::option<int>>);
static_assert(asl::trivially_copy_constructible<asl::option<NonZero>>); static_assert(asl::trivially_copy_constructible<asl::option<NonZero>>);
static_assert(asl::copy_constructible<asl::option<Copyable>>); static_assert(asl::copy_constructible<asl::option<Copyable>>);
static_assert(!asl::copy_constructible<asl::option<MoveableOnly>>); static_assert(!asl::copy_constructible<asl::option<MoveableOnly>>);
static_assert(!asl::copy_constructible<asl::option<Pinned>>); static_assert(!asl::copy_constructible<asl::option<Pinned>>);
static_assert(asl::trivially_move_constructible<asl::option<int>>); static_assert(asl::trivially_move_constructible<asl::option<int>>);
static_assert(asl::trivially_move_constructible<asl::option<NonZero>>); static_assert(asl::trivially_move_constructible<asl::option<NonZero>>);
static_assert(asl::move_constructible<asl::option<Copyable>>); static_assert(asl::move_constructible<asl::option<Copyable>>);
static_assert(asl::move_constructible<asl::option<MoveableOnly>>); static_assert(asl::move_constructible<asl::option<MoveableOnly>>);
static_assert(!asl::move_constructible<asl::option<Pinned>>); static_assert(!asl::move_constructible<asl::option<Pinned>>);
static_assert(asl::trivially_copy_assignable<asl::option<int>>); static_assert(asl::trivially_copy_assignable<asl::option<int>>);
static_assert(asl::trivially_copy_assignable<asl::option<NonZero>>); static_assert(asl::trivially_copy_assignable<asl::option<NonZero>>);
static_assert(asl::copy_assignable<asl::option<Copyable>>); static_assert(asl::copy_assignable<asl::option<Copyable>>);
static_assert(!asl::copy_assignable<asl::option<MoveableOnly>>); static_assert(!asl::copy_assignable<asl::option<MoveableOnly>>);
static_assert(!asl::copy_assignable<asl::option<Pinned>>); static_assert(!asl::copy_assignable<asl::option<Pinned>>);
static_assert(asl::trivially_move_assignable<asl::option<int>>); static_assert(asl::trivially_move_assignable<asl::option<int>>);
static_assert(asl::trivially_move_assignable<asl::option<NonZero>>); static_assert(asl::trivially_move_assignable<asl::option<NonZero>>);
static_assert(asl::move_assignable<asl::option<Copyable>>); static_assert(asl::move_assignable<asl::option<Copyable>>);
static_assert(asl::move_assignable<asl::option<MoveableOnly>>); static_assert(asl::move_assignable<asl::option<MoveableOnly>>);
static_assert(!asl::move_assignable<asl::option<Pinned>>); static_assert(!asl::move_assignable<asl::option<Pinned>>);
static_assert(asl::assignable_from<asl::option<Base*>&, asl::option<Derived*>>); static_assert(asl::assignable_from<asl::option<Base*>&, asl::option<Derived*>>);
static_assert(!asl::assignable_from<asl::option<Derived*>&, asl::option<Base*>>); static_assert(!asl::assignable_from<asl::option<Derived*>&, asl::option<Base*>>);
static_assert(asl::convertible_from<asl::option<Base*>, asl::option<Derived*>>); static_assert(asl::convertible_from<asl::option<Base*>, asl::option<Derived*>>);
static_assert(!asl::convertible_from<asl::option<Derived*>, asl::option<Base*>>); static_assert(!asl::convertible_from<asl::option<Derived*>, asl::option<Base*>>);
class ExplicitConversion { public: explicit ExplicitConversion(int) {} }; class ExplicitConversion { public: explicit ExplicitConversion(int) {} };
class ImplicitConversion { public: ImplicitConversion(int) {} }; // NOLINT class ImplicitConversion { public: ImplicitConversion(int) {} }; // NOLINT
static_assert(!asl::convertible_from<ExplicitConversion, int>); static_assert(!asl::convertible_from<ExplicitConversion, int>);
static_assert(asl::convertible_from<ImplicitConversion, int>); static_assert(asl::convertible_from<ImplicitConversion, int>);
static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, int>); static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, int>);
static_assert(asl::convertible_from<asl::option<ImplicitConversion>, int>); static_assert(asl::convertible_from<asl::option<ImplicitConversion>, int>);
static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, asl::option<int>>); static_assert(!asl::convertible_from<asl::option<ExplicitConversion>, asl::option<int>>);
static_assert(asl::convertible_from<asl::option<ImplicitConversion>, asl::option<int>>); static_assert(asl::convertible_from<asl::option<ImplicitConversion>, asl::option<int>>);
static_assert(asl::trivially_copy_constructible<asl::option<int>>); static_assert(asl::trivially_copy_constructible<asl::option<int>>);
static_assert(asl::trivially_copy_constructible<asl::option<TrivialType>>); static_assert(asl::trivially_copy_constructible<asl::option<TrivialType>>);
static_assert(asl::trivially_copy_constructible<asl::option<TrivialTypeDefaultValue>>); static_assert(asl::trivially_copy_constructible<asl::option<TrivialTypeDefaultValue>>);
static_assert(!asl::trivially_copy_constructible<asl::option<Copyable>>); static_assert(!asl::trivially_copy_constructible<asl::option<Copyable>>);
static_assert(asl::trivially_move_constructible<asl::option<int>>); static_assert(asl::trivially_move_constructible<asl::option<int>>);
static_assert(asl::trivially_move_constructible<asl::option<TrivialType>>); static_assert(asl::trivially_move_constructible<asl::option<TrivialType>>);
static_assert(asl::trivially_move_constructible<asl::option<TrivialTypeDefaultValue>>); static_assert(asl::trivially_move_constructible<asl::option<TrivialTypeDefaultValue>>);
static_assert(!asl::trivially_move_constructible<asl::option<MoveableOnly>>); static_assert(!asl::trivially_move_constructible<asl::option<MoveableOnly>>);
static_assert(asl::trivially_copy_assignable<asl::option<int>>); static_assert(asl::trivially_copy_assignable<asl::option<int>>);
static_assert(asl::trivially_copy_assignable<asl::option<TrivialType>>); static_assert(asl::trivially_copy_assignable<asl::option<TrivialType>>);
static_assert(asl::trivially_copy_assignable<asl::option<TrivialTypeDefaultValue>>); static_assert(asl::trivially_copy_assignable<asl::option<TrivialTypeDefaultValue>>);
static_assert(!asl::trivially_copy_assignable<asl::option<Copyable>>); static_assert(!asl::trivially_copy_assignable<asl::option<Copyable>>);
static_assert(asl::trivially_move_assignable<asl::option<int>>); static_assert(asl::trivially_move_assignable<asl::option<int>>);
static_assert(asl::trivially_move_assignable<asl::option<TrivialType>>); static_assert(asl::trivially_move_assignable<asl::option<TrivialType>>);
static_assert(asl::trivially_move_assignable<asl::option<TrivialTypeDefaultValue>>); static_assert(asl::trivially_move_assignable<asl::option<TrivialTypeDefaultValue>>);
static_assert(!asl::trivially_move_assignable<asl::option<MoveableOnly>>); static_assert(!asl::trivially_move_assignable<asl::option<MoveableOnly>>);
ASL_TEST(make_null) ASL_TEST(make_null)
{ {
asl::option<int> a; asl::option<int> a;
asl::option<int> b = asl::nullopt; asl::option<int> b = asl::nullopt;
ASL_TEST_EXPECT(!a.has_value()); ASL_TEST_EXPECT(!a.has_value());
ASL_TEST_EXPECT(!b.has_value()); ASL_TEST_EXPECT(!b.has_value());
} }
ASL_TEST(make_value) ASL_TEST(make_value)
{ {
asl::option<int> a = 48; asl::option<int> a = 48;
ASL_TEST_EXPECT(a.has_value()); ASL_TEST_EXPECT(a.has_value());
} }
ASL_TEST(reset) ASL_TEST(reset)
{ {
asl::option<int> b = 48; asl::option<int> b = 48;
ASL_TEST_EXPECT(b.has_value()); ASL_TEST_EXPECT(b.has_value());
b.reset(); b.reset();
ASL_TEST_EXPECT(!b.has_value()); ASL_TEST_EXPECT(!b.has_value());
} }
ASL_TEST(call_destructor) ASL_TEST(call_destructor)
{ {
bool destroyed = false; bool destroyed = false;
{ {
DestructorObserver obs(&destroyed); DestructorObserver obs(&destroyed);
asl::option<DestructorObserver> opt(ASL_MOVE(obs)); asl::option<DestructorObserver> opt(ASL_MOVE(obs));
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
asl::option<DestructorObserver> opt2 = ASL_MOVE(opt); asl::option<DestructorObserver> opt2 = ASL_MOVE(opt);
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
} }
ASL_TEST_EXPECT(destroyed); ASL_TEST_EXPECT(destroyed);
} }
ASL_TEST(call_destructor_on_reset) ASL_TEST(call_destructor_on_reset)
{ {
bool destroyed = false; bool destroyed = false;
asl::option<DestructorObserver> opt(&destroyed); asl::option<DestructorObserver> opt(&destroyed);
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
opt.reset(); opt.reset();
ASL_TEST_EXPECT(destroyed); ASL_TEST_EXPECT(destroyed);
} }
ASL_TEST(value) ASL_TEST(value)
{ {
asl::option<int> a = 1; asl::option<int> a = 1;
asl::option<int> b = 2; asl::option<int> b = 2;
asl::option<int> c = a; asl::option<int> c = a;
ASL_TEST_EXPECT(a.value() == 1); ASL_TEST_EXPECT(a.value() == 1);
ASL_TEST_EXPECT(b.value() == 2); ASL_TEST_EXPECT(b.value() == 2);
ASL_TEST_EXPECT(c.value() == 1); ASL_TEST_EXPECT(c.value() == 1);
c = b; c = b;
ASL_TEST_EXPECT(c.value() == 2); ASL_TEST_EXPECT(c.value() == 2);
} }
ASL_TEST(value_move) ASL_TEST(value_move)
{ {
bool destroyed = false; bool destroyed = false;
asl::option<DestructorObserver> opt(&destroyed); asl::option<DestructorObserver> opt(&destroyed);
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
{ {
auto x = ASL_MOVE(opt).value(); auto x = ASL_MOVE(opt).value();
ASL_TEST_EXPECT(!destroyed); ASL_TEST_EXPECT(!destroyed);
} }
ASL_TEST_EXPECT(destroyed); ASL_TEST_EXPECT(destroyed);
} }
ASL_TEST(deduction_guide) ASL_TEST(deduction_guide)
{ {
asl::option opt(45); asl::option opt(45);
ASL_TEST_EXPECT(opt.value() == 45); ASL_TEST_EXPECT(opt.value() == 45);
} }
ASL_TEST(convert_copy) ASL_TEST(convert_copy)
{ {
asl::option<uint8_t> opt8 = uint8_t{8}; asl::option<uint8_t> opt8 = uint8_t{8};
asl::option<uint16_t> opt16 = opt8; asl::option<uint16_t> opt16 = opt8;
ASL_TEST_ASSERT(opt16.has_value()); ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 8); ASL_TEST_EXPECT(opt16.value() == 8);
opt8 = uint8_t{10}; opt8 = uint8_t{10};
ASL_TEST_ASSERT(opt8.has_value()); ASL_TEST_ASSERT(opt8.has_value());
ASL_TEST_EXPECT(opt8.value() == 10); ASL_TEST_EXPECT(opt8.value() == 10);
opt16 = asl::nullopt; opt16 = asl::nullopt;
ASL_TEST_EXPECT(!opt16.has_value()); ASL_TEST_EXPECT(!opt16.has_value());
opt16 = opt8; opt16 = opt8;
ASL_TEST_ASSERT(opt16.has_value()); ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 10); ASL_TEST_EXPECT(opt16.value() == 10);
} }
ASL_TEST(convert_move) ASL_TEST(convert_move)
{ {
asl::option<uint8_t> opt8 = uint8_t{8}; asl::option<uint8_t> opt8 = uint8_t{8};
asl::option<uint16_t> opt16 = ASL_MOVE(opt8); asl::option<uint16_t> opt16 = ASL_MOVE(opt8);
ASL_TEST_ASSERT(opt16.has_value()); ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 8); ASL_TEST_EXPECT(opt16.value() == 8);
opt8 = ASL_MOVE(uint8_t{10}); opt8 = ASL_MOVE(uint8_t{10});
ASL_TEST_ASSERT(opt8.has_value()); ASL_TEST_ASSERT(opt8.has_value());
ASL_TEST_EXPECT(opt8.value() == 10); ASL_TEST_EXPECT(opt8.value() == 10);
opt16 = asl::nullopt; opt16 = asl::nullopt;
ASL_TEST_EXPECT(!opt16.has_value()); ASL_TEST_EXPECT(!opt16.has_value());
opt16 = ASL_MOVE(opt8); opt16 = ASL_MOVE(opt8);
ASL_TEST_ASSERT(opt16.has_value()); ASL_TEST_ASSERT(opt16.has_value());
ASL_TEST_EXPECT(opt16.value() == 10); ASL_TEST_EXPECT(opt16.value() == 10);
} }
ASL_TEST(value_or) ASL_TEST(value_or)
{ {
asl::option<int> a = asl::nullopt; asl::option<int> a = asl::nullopt;
asl::option<int> b = 2; asl::option<int> b = 2;
ASL_TEST_EXPECT(a.value_or(5) == 5); ASL_TEST_EXPECT(a.value_or(5) == 5);
ASL_TEST_EXPECT(b.value_or(5) == 2); ASL_TEST_EXPECT(b.value_or(5) == 2);
} }
ASL_TEST(emplace) ASL_TEST(emplace)
{ {
asl::option<int> a = asl::nullopt; asl::option<int> a = asl::nullopt;
a.emplace(42); a.emplace(42);
ASL_TEST_ASSERT(a.has_value()); ASL_TEST_ASSERT(a.has_value());
ASL_TEST_EXPECT(a.value() == 42); ASL_TEST_EXPECT(a.value() == 42);
} }
ASL_TEST(emplace_destroys_previous) ASL_TEST(emplace_destroys_previous)
{ {
bool b1 = false; bool b1 = false;
bool b2 = false; bool b2 = false;
{ {
asl::option<DestructorObserver> a{&b1}; asl::option<DestructorObserver> a{&b1};
ASL_TEST_EXPECT(!b1); ASL_TEST_EXPECT(!b1);
a.emplace(&b2); a.emplace(&b2);
ASL_TEST_EXPECT(b1); ASL_TEST_EXPECT(b1);
ASL_TEST_EXPECT(!b2); ASL_TEST_EXPECT(!b2);
} }
ASL_TEST_EXPECT(b2); ASL_TEST_EXPECT(b2);
} }
ASL_TEST(and_then) ASL_TEST(and_then)
{ {
asl::option<int> a = 5; asl::option<int> a = 5;
asl::option<int> b; asl::option<int> b;
auto fn = [](int x) -> asl::option<float> { return static_cast<float>(x) + 0.5F; }; auto fn = [](int x) -> asl::option<float> { return static_cast<float>(x) + 0.5F; };
auto a2 = a.and_then(fn); auto a2 = a.and_then(fn);
static_assert(asl::same_as<decltype(a2), asl::option<float>>); static_assert(asl::same_as<decltype(a2), asl::option<float>>);
ASL_TEST_ASSERT(a2.has_value()); ASL_TEST_ASSERT(a2.has_value());
ASL_TEST_EXPECT(a2.value() == 5.5F); ASL_TEST_EXPECT(a2.value() == 5.5F);
auto b2 = b.and_then(fn); auto b2 = b.and_then(fn);
static_assert(asl::same_as<decltype(b2), asl::option<float>>); static_assert(asl::same_as<decltype(b2), asl::option<float>>);
ASL_TEST_ASSERT(!b2.has_value()); ASL_TEST_ASSERT(!b2.has_value());
} }
ASL_TEST(transform) ASL_TEST(transform)
{ {
asl::option<int> a = 5; asl::option<int> a = 5;
asl::option<int> b; asl::option<int> b;
auto fn = [](int x) -> float { return static_cast<float>(x) + 0.5F; }; auto fn = [](int x) -> float { return static_cast<float>(x) + 0.5F; };
auto a2 = a.transform(fn); auto a2 = a.transform(fn);
static_assert(asl::same_as<decltype(a2), asl::option<float>>); static_assert(asl::same_as<decltype(a2), asl::option<float>>);
ASL_TEST_ASSERT(a2.has_value()); ASL_TEST_ASSERT(a2.has_value());
ASL_TEST_EXPECT(a2.value() == 5.5F); ASL_TEST_EXPECT(a2.value() == 5.5F);
auto b2 = b.transform(fn); auto b2 = b.transform(fn);
static_assert(asl::same_as<decltype(b2), asl::option<float>>); static_assert(asl::same_as<decltype(b2), asl::option<float>>);
ASL_TEST_ASSERT(!b2.has_value()); ASL_TEST_ASSERT(!b2.has_value());
} }
ASL_TEST(or_else) ASL_TEST(or_else)
{ {
asl::option<int> a = 5; asl::option<int> a = 5;
asl::option<int> b; asl::option<int> b;
auto fn = []() -> asl::option<int> { return 12; }; auto fn = []() -> asl::option<int> { return 12; };
auto a2 = a.or_else(fn); auto a2 = a.or_else(fn);
static_assert(asl::same_as<decltype(a2), asl::option<int>>); static_assert(asl::same_as<decltype(a2), asl::option<int>>);
ASL_TEST_ASSERT(a2.has_value()); ASL_TEST_ASSERT(a2.has_value());
ASL_TEST_EXPECT(a2.value() == 5); ASL_TEST_EXPECT(a2.value() == 5);
auto b2 = b.or_else(fn); auto b2 = b.or_else(fn);
static_assert(asl::same_as<decltype(b2), asl::option<int>>); static_assert(asl::same_as<decltype(b2), asl::option<int>>);
ASL_TEST_ASSERT(b2.has_value()); ASL_TEST_ASSERT(b2.has_value());
ASL_TEST_EXPECT(b2.value() == 12); ASL_TEST_EXPECT(b2.value() == 12);
} }
ASL_TEST(niche) ASL_TEST(niche)
{ {
asl::option<NonZero> opt; asl::option<NonZero> opt;
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
asl::option<NonZero> opt2(2); asl::option<NonZero> opt2(2);
ASL_TEST_EXPECT(opt2.has_value()); ASL_TEST_EXPECT(opt2.has_value());
ASL_TEST_EXPECT(opt2.value().value == 2); ASL_TEST_EXPECT(opt2.value().value == 2);
opt = opt2; opt = opt2;
ASL_TEST_EXPECT(opt2.has_value()); ASL_TEST_EXPECT(opt2.has_value());
ASL_TEST_EXPECT(opt2.value().value == 2); ASL_TEST_EXPECT(opt2.value().value == 2);
opt.reset(); opt.reset();
ASL_TEST_EXPECT(!opt.has_value()); ASL_TEST_EXPECT(!opt.has_value());
opt = NonZero(87); opt = NonZero(87);
ASL_TEST_EXPECT(opt.has_value()); ASL_TEST_EXPECT(opt.has_value());
ASL_TEST_EXPECT(opt.value().value == 87); ASL_TEST_EXPECT(opt.value().value == 87);
} }

View File

@ -1,84 +1,84 @@
#include "asl/status_or.hpp" #include "asl/status_or.hpp"
#include "asl/testing/testing.hpp" #include "asl/testing/testing.hpp"
#include "asl/tests/test_types.hpp" #include "asl/tests/test_types.hpp"
static_assert(asl::copyable<asl::status_or<TrivialType>>); static_assert(asl::copyable<asl::status_or<TrivialType>>);
static_assert(asl::moveable<asl::status_or<TrivialType>>); static_assert(asl::moveable<asl::status_or<TrivialType>>);
static_assert(asl::copyable<asl::status_or<Copyable>>); static_assert(asl::copyable<asl::status_or<Copyable>>);
static_assert(asl::moveable<asl::status_or<Copyable>>); static_assert(asl::moveable<asl::status_or<Copyable>>);
static_assert(!asl::copyable<asl::status_or<MoveableOnly>>); static_assert(!asl::copyable<asl::status_or<MoveableOnly>>);
static_assert(asl::moveable<asl::status_or<MoveableOnly>>); static_assert(asl::moveable<asl::status_or<MoveableOnly>>);
static_assert(!asl::copyable<asl::status_or<Pinned>>); static_assert(!asl::copyable<asl::status_or<Pinned>>);
static_assert(!asl::moveable<asl::status_or<Pinned>>); static_assert(!asl::moveable<asl::status_or<Pinned>>);
ASL_TEST(ok) ASL_TEST(ok)
{ {
asl::status_or<int> s = 6; asl::status_or<int> s = 6;
ASL_TEST_EXPECT(s.ok()); ASL_TEST_EXPECT(s.ok());
ASL_TEST_EXPECT(s.code() == asl::status_code::ok); ASL_TEST_EXPECT(s.code() == asl::status_code::ok);
} }
ASL_TEST(from_status) ASL_TEST(from_status)
{ {
asl::status_or<char> s = asl::internal_error(); asl::status_or<char> s = asl::internal_error();
ASL_TEST_EXPECT(!s.ok()); ASL_TEST_EXPECT(!s.ok());
ASL_TEST_EXPECT(s.code() == asl::status_code::internal); ASL_TEST_EXPECT(s.code() == asl::status_code::internal);
ASL_TEST_EXPECT(s.message() == ""_sv); ASL_TEST_EXPECT(s.message() == ""_sv);
asl::status_or<int> s2 = asl::internal_error("oh no"); asl::status_or<int> s2 = asl::internal_error("oh no");
ASL_TEST_EXPECT(!s2.ok()); ASL_TEST_EXPECT(!s2.ok());
ASL_TEST_EXPECT(s2.code() == asl::status_code::internal); ASL_TEST_EXPECT(s2.code() == asl::status_code::internal);
ASL_TEST_EXPECT(s2.message() == "oh no"_sv); ASL_TEST_EXPECT(s2.message() == "oh no"_sv);
asl::status_or<int> s3 = asl::internal_error("{} {}", 1, 2); asl::status_or<int> s3 = asl::internal_error("{} {}", 1, 2);
ASL_TEST_EXPECT(!s3.ok()); ASL_TEST_EXPECT(!s3.ok());
ASL_TEST_EXPECT(s3.code() == asl::status_code::internal); ASL_TEST_EXPECT(s3.code() == asl::status_code::internal);
ASL_TEST_EXPECT(s3.message() == "1 2"_sv); ASL_TEST_EXPECT(s3.message() == "1 2"_sv);
} }
ASL_TEST(destructor) ASL_TEST(destructor)
{ {
bool d = false; bool d = false;
{ {
asl::status_or<DestructorObserver> s{&d}; asl::status_or<DestructorObserver> s{&d};
ASL_TEST_EXPECT(s.ok()); ASL_TEST_EXPECT(s.ok());
ASL_TEST_EXPECT(!d); ASL_TEST_EXPECT(!d);
asl::status_or s2 = ASL_MOVE(s); asl::status_or s2 = ASL_MOVE(s);
ASL_TEST_EXPECT(s.ok()); ASL_TEST_EXPECT(s.ok());
ASL_TEST_EXPECT(!d); ASL_TEST_EXPECT(!d);
s = ASL_MOVE(s2); s = ASL_MOVE(s2);
ASL_TEST_EXPECT(s.ok()); ASL_TEST_EXPECT(s.ok());
ASL_TEST_EXPECT(!d); ASL_TEST_EXPECT(!d);
} }
ASL_TEST_EXPECT(d); ASL_TEST_EXPECT(d);
} }
ASL_TEST(copy) ASL_TEST(copy)
{ {
asl::status_or<int> s = 7; asl::status_or<int> s = 7;
asl::status_or s2 = s; asl::status_or s2 = s;
s = s2; s = s2;
ASL_TEST_EXPECT(s.ok()); ASL_TEST_EXPECT(s.ok());
ASL_TEST_EXPECT(s2.ok()); ASL_TEST_EXPECT(s2.ok());
ASL_TEST_EXPECT(s.value() == 7); ASL_TEST_EXPECT(s.value() == 7);
ASL_TEST_EXPECT(s2.value() == 7); ASL_TEST_EXPECT(s2.value() == 7);
} }
ASL_TEST(value_or) ASL_TEST(value_or)
{ {
asl::status_or<int> s = 7; asl::status_or<int> s = 7;
asl::status_or<int> s2 = asl::internal_error(); asl::status_or<int> s2 = asl::internal_error();
ASL_TEST_EXPECT(s.value_or(45) == 7); ASL_TEST_EXPECT(s.value_or(45) == 7);
ASL_TEST_EXPECT(s2.value_or(45) == 45); ASL_TEST_EXPECT(s2.value_or(45) == 45);
} }

View File

@ -30,10 +30,10 @@ ASL_TEST(copy_inline)
{ {
asl::status s = asl::ok(); asl::status s = asl::ok();
asl::status s2 = asl::internal_error(); asl::status s2 = asl::internal_error();
asl::status s3 = s; asl::status s3 = s;
ASL_TEST_ASSERT(s3.code() == asl::status_code::ok); ASL_TEST_ASSERT(s3.code() == asl::status_code::ok);
s3 = s2; s3 = s2;
ASL_TEST_ASSERT(s3.code() == asl::status_code::internal); ASL_TEST_ASSERT(s3.code() == asl::status_code::internal);
} }

View File

@ -26,7 +26,7 @@ ASL_TEST(from_literal)
ASL_TEST(substr1) ASL_TEST(substr1)
{ {
asl::string_view s1 = "abcd"; asl::string_view s1 = "abcd";
asl::string_view s2 = s1.substr(0); asl::string_view s2 = s1.substr(0);
ASL_TEST_ASSERT(s2.size() == 4); ASL_TEST_ASSERT(s2.size() == 4);
ASL_TEST_EXPECT(asl::memcmp(s2.data(), "abcd", 4) == 0); ASL_TEST_EXPECT(asl::memcmp(s2.data(), "abcd", 4) == 0);
@ -42,7 +42,7 @@ ASL_TEST(substr1)
ASL_TEST(substr2) ASL_TEST(substr2)
{ {
asl::string_view s1 = "abcd"; asl::string_view s1 = "abcd";
asl::string_view s2 = s1.substr(0, 4); asl::string_view s2 = s1.substr(0, 4);
ASL_TEST_ASSERT(s2.size() == 4); ASL_TEST_ASSERT(s2.size() == 4);
ASL_TEST_EXPECT(asl::memcmp(s2.data(), "abcd", 4) == 0); ASL_TEST_EXPECT(asl::memcmp(s2.data(), "abcd", 4) == 0);
@ -61,7 +61,7 @@ ASL_TEST(substr2)
ASL_TEST(first) ASL_TEST(first)
{ {
asl::string_view s1 = "abcd"; asl::string_view s1 = "abcd";
asl::string_view s2 = s1.first(0); asl::string_view s2 = s1.first(0);
ASL_TEST_ASSERT(s2.size() == 0); ASL_TEST_ASSERT(s2.size() == 0);
@ -77,7 +77,7 @@ ASL_TEST(first)
ASL_TEST(last) ASL_TEST(last)
{ {
asl::string_view s1 = "abcd"; asl::string_view s1 = "abcd";
asl::string_view s2 = s1.last(0); asl::string_view s2 = s1.last(0);
ASL_TEST_ASSERT(s2.size() == 0); ASL_TEST_ASSERT(s2.size() == 0);

View File

@ -1,92 +1,92 @@
#pragma once #pragma once
#include "asl/utility.hpp" #include "asl/utility.hpp"
#include "asl/io.hpp" #include "asl/io.hpp"
#include "asl/allocator.hpp" #include "asl/allocator.hpp"
#include "asl/string_view.hpp" #include "asl/string_view.hpp"
struct TrivialType struct TrivialType
{ {
int x; int x;
TrivialType() = default; TrivialType() = default;
TrivialType(const TrivialType&) = default; TrivialType(const TrivialType&) = default;
TrivialType(TrivialType&&) = default; TrivialType(TrivialType&&) = default;
TrivialType& operator=(const TrivialType&) = default; TrivialType& operator=(const TrivialType&) = default;
TrivialType& operator=(TrivialType&&) = default; TrivialType& operator=(TrivialType&&) = default;
~TrivialType() = default; ~TrivialType() = default;
}; };
struct TrivialTypeDefaultValue struct TrivialTypeDefaultValue
{ {
int x{}; int x{};
TrivialTypeDefaultValue() = default; TrivialTypeDefaultValue() = default;
TrivialTypeDefaultValue(const TrivialTypeDefaultValue&) = default; TrivialTypeDefaultValue(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue(TrivialTypeDefaultValue&&) = default; TrivialTypeDefaultValue(TrivialTypeDefaultValue&&) = default;
TrivialTypeDefaultValue& operator=(const TrivialTypeDefaultValue&) = default; TrivialTypeDefaultValue& operator=(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue& operator=(TrivialTypeDefaultValue&&) = default; TrivialTypeDefaultValue& operator=(TrivialTypeDefaultValue&&) = default;
~TrivialTypeDefaultValue() = default; ~TrivialTypeDefaultValue() = default;
}; };
struct WithDestructor struct WithDestructor
{ {
WithDestructor() = default; WithDestructor() = default;
WithDestructor(const WithDestructor&) = default; WithDestructor(const WithDestructor&) = default;
WithDestructor(WithDestructor&&) = default; WithDestructor(WithDestructor&&) = default;
WithDestructor& operator=(const WithDestructor&) = default; WithDestructor& operator=(const WithDestructor&) = default;
WithDestructor& operator=(WithDestructor&&) = default; WithDestructor& operator=(WithDestructor&&) = default;
~WithDestructor() {} // NOLINT ~WithDestructor() {} // NOLINT
}; };
struct Copyable // NOLINT struct Copyable // NOLINT
{ {
Copyable(const Copyable&) {} // NOLINT Copyable(const Copyable&) {} // NOLINT
Copyable& operator=(const Copyable&); // NOLINT Copyable& operator=(const Copyable&); // NOLINT
}; };
struct MoveableOnly // NOLINT struct MoveableOnly // NOLINT
{ {
MoveableOnly(const MoveableOnly&) = delete; MoveableOnly(const MoveableOnly&) = delete;
MoveableOnly& operator=(const MoveableOnly&) = delete; MoveableOnly& operator=(const MoveableOnly&) = delete;
MoveableOnly(MoveableOnly&&); MoveableOnly(MoveableOnly&&);
MoveableOnly& operator=(MoveableOnly&&); // NOLINT MoveableOnly& operator=(MoveableOnly&&); // NOLINT
}; };
struct Pinned // NOLINT struct Pinned // NOLINT
{ {
Pinned(const Pinned&) = delete; Pinned(const Pinned&) = delete;
Pinned& operator=(const Pinned&) = delete; Pinned& operator=(const Pinned&) = delete;
Pinned(Pinned&&) = delete; Pinned(Pinned&&) = delete;
Pinned& operator=(Pinned&&) = delete; Pinned& operator=(Pinned&&) = delete;
}; };
struct DestructorObserver struct DestructorObserver
{ {
bool* destroyed; bool* destroyed;
explicit DestructorObserver(bool* destroyed_) : destroyed{destroyed_} {} explicit DestructorObserver(bool* destroyed_) : destroyed{destroyed_} {}
DestructorObserver(const DestructorObserver&) = delete; DestructorObserver(const DestructorObserver&) = delete;
DestructorObserver& operator=(const DestructorObserver&) = delete; DestructorObserver& operator=(const DestructorObserver&) = delete;
DestructorObserver(DestructorObserver&& other) DestructorObserver(DestructorObserver&& other)
: destroyed{asl::exchange(other.destroyed, nullptr)} : destroyed{asl::exchange(other.destroyed, nullptr)}
{} {}
DestructorObserver& operator=(DestructorObserver&& other) DestructorObserver& operator=(DestructorObserver&& other)
{ {
if (this != &other) if (this != &other)
{ {
destroyed = asl::exchange(other.destroyed, nullptr); destroyed = asl::exchange(other.destroyed, nullptr);
} }
return *this; return *this;
}
~DestructorObserver()
{
if (destroyed != nullptr)
{
ASL_ASSERT_RELEASE(*destroyed == false);
*destroyed = true;
}
} }
};
~DestructorObserver()
{
if (destroyed != nullptr)
{
ASL_ASSERT_RELEASE(*destroyed == false);
*destroyed = true;
}
}
};

View File

@ -1 +1 @@
#include "asl/utility.hpp" #include "asl/utility.hpp"

View File

@ -1,84 +1,84 @@
#pragma once #pragma once
#include "asl/meta.hpp" #include "asl/meta.hpp"
#include "asl/layout.hpp" #include "asl/layout.hpp"
#include "asl/assert.hpp" #include "asl/assert.hpp"
#define ASL_MOVE(...) (static_cast<::asl::un_ref_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)) #define ASL_MOVE(...) (static_cast<::asl::un_ref_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__))
#define ASL_FWD(expr_) (static_cast<decltype(expr_)&&>(expr_)) #define ASL_FWD(expr_) (static_cast<decltype(expr_)&&>(expr_))
namespace asl namespace asl
{ {
struct in_place_t {}; struct in_place_t {};
static constexpr in_place_t in_place{}; static constexpr in_place_t in_place{};
template<moveable T> template<moveable T>
constexpr void swap(T& a, T& b) constexpr void swap(T& a, T& b)
{ {
T tmp{ASL_MOVE(a)}; T tmp{ASL_MOVE(a)};
a = ASL_MOVE(b); a = ASL_MOVE(b);
b = ASL_MOVE(tmp); b = ASL_MOVE(tmp);
} }
template<typename T, typename U> template<typename T, typename U>
T exchange(T& obj, U&& new_value) T exchange(T& obj, U&& new_value)
{ {
T old_value = ASL_MOVE(obj); T old_value = ASL_MOVE(obj);
obj = ASL_FWD(new_value); obj = ASL_FWD(new_value);
return old_value; return old_value;
} }
template<trivially_copy_constructible U, trivially_copy_constructible T> template<trivially_copy_constructible U, trivially_copy_constructible T>
constexpr U bit_cast(T value) requires (size_of<T> == size_of<U>) constexpr U bit_cast(T value) requires (size_of<T> == size_of<U>)
{ {
return __builtin_bit_cast(U, value); return __builtin_bit_cast(U, value);
} }
template<typename T> template<typename T>
constexpr T min(T a, T b) constexpr T min(T a, T b)
{ {
return (a <= b) ? a : b; return (a <= b) ? a : b;
} }
template<typename T> template<typename T>
constexpr T max(T a, T b) constexpr T max(T a, T b)
{ {
return (a >= b) ? a : b; return (a >= b) ? a : b;
} }
constexpr uint64_t round_up_pow2(uint64_t v) constexpr uint64_t round_up_pow2(uint64_t v)
{ {
ASL_ASSERT(v <= 0x8000'0000'0000'0000); ASL_ASSERT(v <= 0x8000'0000'0000'0000);
v -= 1; v -= 1;
v |= v >> 1; v |= v >> 1;
v |= v >> 2; v |= v >> 2;
v |= v >> 4; v |= v >> 4;
v |= v >> 8; v |= v >> 8;
v |= v >> 16; v |= v >> 16;
v |= v >> 32; v |= v >> 32;
return v + 1; return v + 1;
} }
constexpr bool is_pow2(isize_t v) constexpr bool is_pow2(isize_t v)
{ {
return v > 0 && ((v - 1) & v) == 0; return v > 0 && ((v - 1) & v) == 0;
} }
#define ASL_DELETE_COPY(T) \ #define ASL_DELETE_COPY(T) \
T(const T&) = delete; \ T(const T&) = delete; \
T& operator=(const T&) = delete; T& operator=(const T&) = delete;
#define ASL_DELETE_MOVE(T) \ #define ASL_DELETE_MOVE(T) \
T(T&&) = delete; \ T(T&&) = delete; \
T& operator=(T&&) = delete; T& operator=(T&&) = delete;
#define ASL_DELETE_COPY_MOVE(T) \ #define ASL_DELETE_COPY_MOVE(T) \
ASL_DELETE_COPY(T) \ ASL_DELETE_COPY(T) \
ASL_DELETE_MOVE(T) ASL_DELETE_MOVE(T)
} // namespace asl } // namespace asl

8
tools/BUILD.bazel Normal file
View File

@ -0,0 +1,8 @@
load("@rules_python//python:py_binary.bzl", "py_binary")
py_binary(
name = "fix_line_endings",
srcs = [
"fix_line_endings.py",
],
)

68
tools/fix_line_endings.py Normal file
View File

@ -0,0 +1,68 @@
import os
import subprocess
import glob
import re
import sys
import time
TO_FIX = [
".bazelrc",
".clang-tidy",
".gitignore",
"**/*.hpp",
"**/*.h",
"**/*.cpp",
"**/*.py",
"**/*.bzl",
"**/*.bazel",
"**/*.txt",
"**/*.bat",
"**/*.sh",
]
TO_IGNORE = [
"MODULE.bazel.lock",
]
TO_FIX = [re.compile(glob.translate(p, recursive=True, include_hidden=True)) for p in TO_FIX]
TO_IGNORE = [re.compile(glob.translate(p, recursive=True, include_hidden=True)) for p in TO_IGNORE]
def get_git_files():
return subprocess.check_output(["git", "ls-files"]).decode().splitlines()
def fix_file(file):
lines = []
with open(file, "rb") as fp:
lines = [l.rstrip() + b"\n" for l in fp.readlines()]
with open(file, "wb+") as fp:
fp.writelines(lines)
if __name__ == "__main__":
os.chdir(os.getenv("BUILD_WORKSPACE_DIRECTORY"))
files = get_git_files()
files_to_fix = []
unhandled_files = []
for f in files:
for pattern in TO_FIX:
if pattern.match(f):
files_to_fix.append(f)
break
else:
for pattern in TO_IGNORE:
if pattern.match(f):
break
else:
unhandled_files.append(f)
if len(unhandled_files):
print("Some files were not handled:")
for f in unhandled_files:
print(" ", f)
sys.exit(1)
for i, f in enumerate(files):
print(f"\033[K{i}/{len(files_to_fix)} {f}", end="\r", flush=True)
fix_file(f)
print("")

View File

@ -1,19 +1,19 @@
// Copyright (c) 2011 Google, Inc. // Copyright (c) 2011 Google, Inc.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.

View File

@ -1,6 +1,6 @@
cc_library( cc_library(
name = "dragonbox", name = "dragonbox",
hdrs = ["dragonbox.h"], hdrs = ["dragonbox.h"],
includes = ["."], includes = ["."],
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
) )