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

View File

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

6
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,17 @@
#pragma once
#include "asl/meta.hpp"
namespace asl
{
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 bool is_infinity(T f) { return __builtin_isinf(f); }
template<is_floating_point T> constexpr bool is_nan(T f) { return __builtin_isnan(f); }
} // namespace asl
#pragma once
#include "asl/meta.hpp"
namespace asl
{
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 bool is_infinity(T f) { return __builtin_isinf(f); }
template<is_floating_point T> constexpr bool is_nan(T f) { return __builtin_isnan(f); }
} // namespace asl

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,221 +1,221 @@
#pragma once
#include "asl/integers.hpp"
namespace asl {
struct source_location
{
const char* file;
int line;
explicit source_location(
const char* file_ = __builtin_FILE(),
int line_ = __builtin_LINE())
: file{file_}
, line{line_}
{}
};
struct empty {};
template<typename T> struct id { using type = T; };
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<bool B> using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
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<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 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 T> auto _as_lref_helper(int) -> 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(...) -> id<T>;
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> consteval as_rref_t<T> declval() {}
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> using un_ref_t = _un_ref_t<T>::type;
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 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, 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_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, 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 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> 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_destructible = __is_trivially_destructible(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 To, typename From>
concept convertible_from = __is_convertible(From, To);
template<typename Derived, class Base>
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from<const volatile Base*, const volatile Derived*>;
using nullptr_t = decltype(nullptr);
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> 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<const T> : true_type {};
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<volatile T> { using type = T; };
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_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> 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 = 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> struct _is_ptr_helper : false_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> struct _tame_helper { using type = T; };
#define TAME_HELPER_IMPL(TRAILING) \
template<typename R, typename... Args> \
struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); }
TAME_HELPER_IMPL();
TAME_HELPER_IMPL(&);
TAME_HELPER_IMPL(&&);
TAME_HELPER_IMPL(const);
TAME_HELPER_IMPL(const &);
TAME_HELPER_IMPL(const &&);
TAME_HELPER_IMPL(volatile);
TAME_HELPER_IMPL(volatile &);
TAME_HELPER_IMPL(volatile &&);
TAME_HELPER_IMPL(const volatile);
TAME_HELPER_IMPL(const volatile &);
TAME_HELPER_IMPL(const volatile &&);
TAME_HELPER_IMPL(noexcept);
TAME_HELPER_IMPL(& noexcept);
TAME_HELPER_IMPL(&& noexcept);
TAME_HELPER_IMPL(const noexcept);
TAME_HELPER_IMPL(const & noexcept);
TAME_HELPER_IMPL(const && noexcept);
TAME_HELPER_IMPL(volatile noexcept);
TAME_HELPER_IMPL(volatile & noexcept);
TAME_HELPER_IMPL(volatile && noexcept);
TAME_HELPER_IMPL(const volatile noexcept);
TAME_HELPER_IMPL(const volatile & noexcept);
TAME_HELPER_IMPL(const volatile && noexcept);
#undef TAME_HELPER_IMPL
template<typename T> using tame_t = _tame_helper<T>::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 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> struct _is_array_helper : false_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> concept is_array = _is_array_helper<T>::value;
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<double> : true_type {};
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<> struct _is_integer_helper<int8_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<int64_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<uint32_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_enum = __is_enum(T);
template<typename T> struct is_uniquely_represented : false_type {};
template<is_integer 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<byte> : true_type {};
template<typename T> concept uniquely_represented = is_uniquely_represented<un_cv_t<T>>::value;
template<typename T, typename U>
concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<U>& b)
{
{ a == b } -> same_as<bool>;
{ b == a } -> same_as<bool>;
{ a != b } -> same_as<bool>;
{ b != a } -> same_as<bool>;
};
template<typename T> concept equality_comparable = equality_comparable_with<T, T>;
struct niche_t {};
template<typename T>
concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T, niche_t>;
template<typename T>
concept is_niche = same_as<un_cvref_t<T>, niche_t>;
} // namespace asl
#pragma once
#include "asl/integers.hpp"
namespace asl {
struct source_location
{
const char* file;
int line;
explicit source_location(
const char* file_ = __builtin_FILE(),
int line_ = __builtin_LINE())
: file{file_}
, line{line_}
{}
};
struct empty {};
template<typename T> struct id { using type = T; };
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<bool B> using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
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<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 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 T> auto _as_lref_helper(int) -> 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(...) -> id<T>;
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> consteval as_rref_t<T> declval() {}
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> using un_ref_t = _un_ref_t<T>::type;
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 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, 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_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, 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 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> 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_destructible = __is_trivially_destructible(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 To, typename From>
concept convertible_from = __is_convertible(From, To);
template<typename Derived, class Base>
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_from<const volatile Base*, const volatile Derived*>;
using nullptr_t = decltype(nullptr);
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> 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<const T> : true_type {};
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<volatile T> { using type = T; };
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_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> 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 = 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> struct _is_ptr_helper : false_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> struct _tame_helper { using type = T; };
#define TAME_HELPER_IMPL(TRAILING) \
template<typename R, typename... Args> \
struct _tame_helper<R(Args...) TRAILING> { using type = R(Args...); }
TAME_HELPER_IMPL();
TAME_HELPER_IMPL(&);
TAME_HELPER_IMPL(&&);
TAME_HELPER_IMPL(const);
TAME_HELPER_IMPL(const &);
TAME_HELPER_IMPL(const &&);
TAME_HELPER_IMPL(volatile);
TAME_HELPER_IMPL(volatile &);
TAME_HELPER_IMPL(volatile &&);
TAME_HELPER_IMPL(const volatile);
TAME_HELPER_IMPL(const volatile &);
TAME_HELPER_IMPL(const volatile &&);
TAME_HELPER_IMPL(noexcept);
TAME_HELPER_IMPL(& noexcept);
TAME_HELPER_IMPL(&& noexcept);
TAME_HELPER_IMPL(const noexcept);
TAME_HELPER_IMPL(const & noexcept);
TAME_HELPER_IMPL(const && noexcept);
TAME_HELPER_IMPL(volatile noexcept);
TAME_HELPER_IMPL(volatile & noexcept);
TAME_HELPER_IMPL(volatile && noexcept);
TAME_HELPER_IMPL(const volatile noexcept);
TAME_HELPER_IMPL(const volatile & noexcept);
TAME_HELPER_IMPL(const volatile && noexcept);
#undef TAME_HELPER_IMPL
template<typename T> using tame_t = _tame_helper<T>::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 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> struct _is_array_helper : false_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> concept is_array = _is_array_helper<T>::value;
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<double> : true_type {};
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<> struct _is_integer_helper<int8_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<int64_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<uint32_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_enum = __is_enum(T);
template<typename T> struct is_uniquely_represented : false_type {};
template<is_integer 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<byte> : true_type {};
template<typename T> concept uniquely_represented = is_uniquely_represented<un_cv_t<T>>::value;
template<typename T, typename U>
concept equality_comparable_with = requires (const un_cvref_t<T>& a, const un_cvref_t<U>& b)
{
{ a == b } -> same_as<bool>;
{ b == a } -> same_as<bool>;
{ a != b } -> same_as<bool>;
{ b != a } -> same_as<bool>;
};
template<typename T> concept equality_comparable = equality_comparable_with<T, T>;
struct niche_t {};
template<typename T>
concept has_niche = constructible_from<T, niche_t> && equality_comparable_with<T, niche_t>;
template<typename T>
concept is_niche = same_as<un_cvref_t<T>, niche_t>;
} // namespace asl

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -49,7 +49,7 @@ class span
{
return size < 0;
}
static constexpr bool kIsDynamic = is_dynamic(kSize);
using SizeType = select_t<kIsDynamic, isize_t, empty>;
@ -102,7 +102,7 @@ public:
constexpr span(const span&) = default;
constexpr span(span&&) = default;
constexpr span& operator=(const span&) = default;
constexpr span& operator=(span&&) = default;
@ -124,7 +124,7 @@ public:
{
return contiguous_iterator{m_data};
}
constexpr contiguous_iterator<T> end() const
{
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)
{
string_view status_str{};
switch (s.code())
{
case status_code::ok: status_str = "ok"_sv; break;

View File

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

View File

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

View File

@ -59,7 +59,7 @@ public:
{
return as_string_view() == other.as_string_view();
}
constexpr bool operator==(string_view other) const
{
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=(StringWriter&&) = default;
void write(span<const byte> str) override
{
m_builder.push(string_view{reinterpret_cast<const char*>(str.data()), str.size()});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,73 +1,73 @@
#include "asl/functional.hpp"
#include "asl/testing/testing.hpp"
struct HasFunction
{
void do_something(int, float) {}
};
struct HasMember
{
int member{};
int member_array[4]{};
void (*member_fn)(){};
};
struct Functor
{
int64_t operator()() { return 35; }
int operator()(int x) { return x; }
};
static int some_func0() { return 1; }
static int some_func1(int 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_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<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(&HasMember::member)(HasMember)>, int>);
ASL_TEST(invoke_member_function)
{
HasFunction c;
asl::invoke(&HasFunction::do_something, c, 5, 5.0F);
asl::invoke(&HasFunction::do_something, &c, 5, 5.0F);
}
ASL_TEST(invoke_member_data)
{
HasMember c;
asl::invoke(&HasMember::member, c);
asl::invoke(&HasMember::member_array, c);
asl::invoke(&HasMember::member_fn, c);
asl::invoke(&HasMember::member, &c);
asl::invoke(&HasMember::member_array, &c);
asl::invoke(&HasMember::member_fn, &c);
}
ASL_TEST(invoke_fn)
{
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(some_func2, 4, 8) == 12);
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(&some_func2, 4, 8) == 12);
}
ASL_TEST(invoke_operator_call)
{
Functor f;
ASL_TEST_EXPECT(asl::invoke(f) == 35);
ASL_TEST_EXPECT(asl::invoke(f, 8) == 8);
}
ASL_TEST(invoke_lambda)
{
ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35);
ASL_TEST_EXPECT(asl::invoke([](int x){ return x + 2; }, 6) == 8);
}
#include "asl/functional.hpp"
#include "asl/testing/testing.hpp"
struct HasFunction
{
void do_something(int, float) {}
};
struct HasMember
{
int member{};
int member_array[4]{};
void (*member_fn)(){};
};
struct Functor
{
int64_t operator()() { return 35; }
int operator()(int x) { return x; }
};
static int some_func0() { return 1; }
static int some_func1(int 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_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<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(&HasMember::member)(HasMember)>, int>);
ASL_TEST(invoke_member_function)
{
HasFunction c;
asl::invoke(&HasFunction::do_something, c, 5, 5.0F);
asl::invoke(&HasFunction::do_something, &c, 5, 5.0F);
}
ASL_TEST(invoke_member_data)
{
HasMember c;
asl::invoke(&HasMember::member, c);
asl::invoke(&HasMember::member_array, c);
asl::invoke(&HasMember::member_fn, c);
asl::invoke(&HasMember::member, &c);
asl::invoke(&HasMember::member_array, &c);
asl::invoke(&HasMember::member_fn, &c);
}
ASL_TEST(invoke_fn)
{
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(some_func2, 4, 8) == 12);
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(&some_func2, 4, 8) == 12);
}
ASL_TEST(invoke_operator_call)
{
Functor f;
ASL_TEST_EXPECT(asl::invoke(f) == 35);
ASL_TEST_EXPECT(asl::invoke(f, 8) == 8);
}
ASL_TEST(invoke_lambda)
{
ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35);
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/hash_map.hpp"
ASL_TEST(default)
{
asl::hash_map<int, int> map;
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(!map.contains(46));
map.insert(45, 5);
map.insert(46, 6);
ASL_TEST_EXPECT(map.size() == 2);
ASL_TEST_EXPECT(map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(*map.get(45) == 5);
ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr);
ASL_TEST_EXPECT(map.remove(45));
ASL_TEST_EXPECT(!map.remove(45));
ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr);
map.insert(46, 460);
ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 460);
ASL_TEST_EXPECT(map.get(47) == nullptr);
}
#include "asl/testing/testing.hpp"
#include "asl/hash_map.hpp"
ASL_TEST(default)
{
asl::hash_map<int, int> map;
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(!map.contains(46));
map.insert(45, 5);
map.insert(46, 6);
ASL_TEST_EXPECT(map.size() == 2);
ASL_TEST_EXPECT(map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(*map.get(45) == 5);
ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr);
ASL_TEST_EXPECT(map.remove(45));
ASL_TEST_EXPECT(!map.remove(45));
ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 6);
ASL_TEST_EXPECT(map.get(47) == nullptr);
map.insert(46, 460);
ASL_TEST_EXPECT(map.size() == 1);
ASL_TEST_EXPECT(!map.contains(45));
ASL_TEST_EXPECT(map.contains(46));
ASL_TEST_EXPECT(!map.contains(47));
ASL_TEST_EXPECT(map.get(45) == nullptr);
ASL_TEST_EXPECT(*map.get(46) == 460);
ASL_TEST_EXPECT(map.get(47) == nullptr);
}

View File

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

View File

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

View File

@ -1,22 +1,22 @@
#include "asl/maybe_uninit.hpp"
#include "asl/tests/test_types.hpp"
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::align_of<int> == asl::align_of<asl::maybe_uninit<int>>);
#define TEST_TYPE_PROPERTIES(PRP) \
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<WithDestructor>> == asl::PRP<WithDestructor>); \
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<Pinned>> == asl::PRP<Pinned>);
TEST_TYPE_PROPERTIES(trivially_default_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_constructible);
TEST_TYPE_PROPERTIES(trivially_move_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_assignable);
TEST_TYPE_PROPERTIES(trivially_move_assignable);
TEST_TYPE_PROPERTIES(trivially_destructible);
#include "asl/maybe_uninit.hpp"
#include "asl/tests/test_types.hpp"
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::align_of<int> == asl::align_of<asl::maybe_uninit<int>>);
#define TEST_TYPE_PROPERTIES(PRP) \
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<WithDestructor>> == asl::PRP<WithDestructor>); \
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<Pinned>> == asl::PRP<Pinned>);
TEST_TYPE_PROPERTIES(trivially_default_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_constructible);
TEST_TYPE_PROPERTIES(trivially_move_constructible);
TEST_TYPE_PROPERTIES(trivially_copy_assignable);
TEST_TYPE_PROPERTIES(trivially_move_assignable);
TEST_TYPE_PROPERTIES(trivially_destructible);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,92 +1,92 @@
#pragma once
#include "asl/utility.hpp"
#include "asl/io.hpp"
#include "asl/allocator.hpp"
#include "asl/string_view.hpp"
struct TrivialType
{
int x;
TrivialType() = default;
TrivialType(const TrivialType&) = default;
TrivialType(TrivialType&&) = default;
TrivialType& operator=(const TrivialType&) = default;
TrivialType& operator=(TrivialType&&) = default;
~TrivialType() = default;
};
struct TrivialTypeDefaultValue
{
int x{};
TrivialTypeDefaultValue() = default;
TrivialTypeDefaultValue(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue(TrivialTypeDefaultValue&&) = default;
TrivialTypeDefaultValue& operator=(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue& operator=(TrivialTypeDefaultValue&&) = default;
~TrivialTypeDefaultValue() = default;
};
struct WithDestructor
{
WithDestructor() = default;
WithDestructor(const WithDestructor&) = default;
WithDestructor(WithDestructor&&) = default;
WithDestructor& operator=(const WithDestructor&) = default;
WithDestructor& operator=(WithDestructor&&) = default;
~WithDestructor() {} // NOLINT
};
struct Copyable // NOLINT
{
Copyable(const Copyable&) {} // NOLINT
Copyable& operator=(const Copyable&); // NOLINT
};
struct MoveableOnly // NOLINT
{
MoveableOnly(const MoveableOnly&) = delete;
MoveableOnly& operator=(const MoveableOnly&) = delete;
MoveableOnly(MoveableOnly&&);
MoveableOnly& operator=(MoveableOnly&&); // NOLINT
};
struct Pinned // NOLINT
{
Pinned(const Pinned&) = delete;
Pinned& operator=(const Pinned&) = delete;
Pinned(Pinned&&) = delete;
Pinned& operator=(Pinned&&) = delete;
};
struct DestructorObserver
{
bool* destroyed;
explicit DestructorObserver(bool* destroyed_) : destroyed{destroyed_} {}
DestructorObserver(const DestructorObserver&) = delete;
DestructorObserver& operator=(const DestructorObserver&) = delete;
DestructorObserver(DestructorObserver&& other)
: destroyed{asl::exchange(other.destroyed, nullptr)}
{}
DestructorObserver& operator=(DestructorObserver&& other)
{
if (this != &other)
{
destroyed = asl::exchange(other.destroyed, nullptr);
}
return *this;
}
~DestructorObserver()
{
if (destroyed != nullptr)
{
ASL_ASSERT_RELEASE(*destroyed == false);
*destroyed = true;
}
#pragma once
#include "asl/utility.hpp"
#include "asl/io.hpp"
#include "asl/allocator.hpp"
#include "asl/string_view.hpp"
struct TrivialType
{
int x;
TrivialType() = default;
TrivialType(const TrivialType&) = default;
TrivialType(TrivialType&&) = default;
TrivialType& operator=(const TrivialType&) = default;
TrivialType& operator=(TrivialType&&) = default;
~TrivialType() = default;
};
struct TrivialTypeDefaultValue
{
int x{};
TrivialTypeDefaultValue() = default;
TrivialTypeDefaultValue(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue(TrivialTypeDefaultValue&&) = default;
TrivialTypeDefaultValue& operator=(const TrivialTypeDefaultValue&) = default;
TrivialTypeDefaultValue& operator=(TrivialTypeDefaultValue&&) = default;
~TrivialTypeDefaultValue() = default;
};
struct WithDestructor
{
WithDestructor() = default;
WithDestructor(const WithDestructor&) = default;
WithDestructor(WithDestructor&&) = default;
WithDestructor& operator=(const WithDestructor&) = default;
WithDestructor& operator=(WithDestructor&&) = default;
~WithDestructor() {} // NOLINT
};
struct Copyable // NOLINT
{
Copyable(const Copyable&) {} // NOLINT
Copyable& operator=(const Copyable&); // NOLINT
};
struct MoveableOnly // NOLINT
{
MoveableOnly(const MoveableOnly&) = delete;
MoveableOnly& operator=(const MoveableOnly&) = delete;
MoveableOnly(MoveableOnly&&);
MoveableOnly& operator=(MoveableOnly&&); // NOLINT
};
struct Pinned // NOLINT
{
Pinned(const Pinned&) = delete;
Pinned& operator=(const Pinned&) = delete;
Pinned(Pinned&&) = delete;
Pinned& operator=(Pinned&&) = delete;
};
struct DestructorObserver
{
bool* destroyed;
explicit DestructorObserver(bool* destroyed_) : destroyed{destroyed_} {}
DestructorObserver(const DestructorObserver&) = delete;
DestructorObserver& operator=(const DestructorObserver&) = delete;
DestructorObserver(DestructorObserver&& other)
: destroyed{asl::exchange(other.destroyed, nullptr)}
{}
DestructorObserver& operator=(DestructorObserver&& other)
{
if (this != &other)
{
destroyed = asl::exchange(other.destroyed, nullptr);
}
return *this;
}
};
~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
#include "asl/meta.hpp"
#include "asl/layout.hpp"
#include "asl/assert.hpp"
#define ASL_MOVE(...) (static_cast<::asl::un_ref_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__))
#define ASL_FWD(expr_) (static_cast<decltype(expr_)&&>(expr_))
namespace asl
{
struct in_place_t {};
static constexpr in_place_t in_place{};
template<moveable T>
constexpr void swap(T& a, T& b)
{
T tmp{ASL_MOVE(a)};
a = ASL_MOVE(b);
b = ASL_MOVE(tmp);
}
template<typename T, typename U>
T exchange(T& obj, U&& new_value)
{
T old_value = ASL_MOVE(obj);
obj = ASL_FWD(new_value);
return old_value;
}
template<trivially_copy_constructible U, trivially_copy_constructible T>
constexpr U bit_cast(T value) requires (size_of<T> == size_of<U>)
{
return __builtin_bit_cast(U, value);
}
template<typename T>
constexpr T min(T a, T b)
{
return (a <= b) ? a : b;
}
template<typename T>
constexpr T max(T a, T b)
{
return (a >= b) ? a : b;
}
constexpr uint64_t round_up_pow2(uint64_t v)
{
ASL_ASSERT(v <= 0x8000'0000'0000'0000);
v -= 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
return v + 1;
}
constexpr bool is_pow2(isize_t v)
{
return v > 0 && ((v - 1) & v) == 0;
}
#define ASL_DELETE_COPY(T) \
T(const T&) = delete; \
T& operator=(const T&) = delete;
#define ASL_DELETE_MOVE(T) \
T(T&&) = delete; \
T& operator=(T&&) = delete;
#define ASL_DELETE_COPY_MOVE(T) \
ASL_DELETE_COPY(T) \
ASL_DELETE_MOVE(T)
} // namespace asl
#pragma once
#include "asl/meta.hpp"
#include "asl/layout.hpp"
#include "asl/assert.hpp"
#define ASL_MOVE(...) (static_cast<::asl::un_ref_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__))
#define ASL_FWD(expr_) (static_cast<decltype(expr_)&&>(expr_))
namespace asl
{
struct in_place_t {};
static constexpr in_place_t in_place{};
template<moveable T>
constexpr void swap(T& a, T& b)
{
T tmp{ASL_MOVE(a)};
a = ASL_MOVE(b);
b = ASL_MOVE(tmp);
}
template<typename T, typename U>
T exchange(T& obj, U&& new_value)
{
T old_value = ASL_MOVE(obj);
obj = ASL_FWD(new_value);
return old_value;
}
template<trivially_copy_constructible U, trivially_copy_constructible T>
constexpr U bit_cast(T value) requires (size_of<T> == size_of<U>)
{
return __builtin_bit_cast(U, value);
}
template<typename T>
constexpr T min(T a, T b)
{
return (a <= b) ? a : b;
}
template<typename T>
constexpr T max(T a, T b)
{
return (a >= b) ? a : b;
}
constexpr uint64_t round_up_pow2(uint64_t v)
{
ASL_ASSERT(v <= 0x8000'0000'0000'0000);
v -= 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
return v + 1;
}
constexpr bool is_pow2(isize_t v)
{
return v > 0 && ((v - 1) & v) == 0;
}
#define ASL_DELETE_COPY(T) \
T(const T&) = delete; \
T& operator=(const T&) = delete;
#define ASL_DELETE_MOVE(T) \
T(T&&) = delete; \
T& operator=(T&&) = delete;
#define ASL_DELETE_COPY_MOVE(T) \
ASL_DELETE_COPY(T) \
ASL_DELETE_MOVE(T)
} // 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.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Copyright (c) 2011 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

View File

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