commit 537c4144a1473fe977e63cecdc6e0518056ab24b Author: Merry Date: Tue Apr 19 10:28:22 2022 +0100 First commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5c5555e --- /dev/null +++ b/.clang-format @@ -0,0 +1,218 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeConceptDeclarations: true +BreakBeforeTernaryOperators: true +BreakBeforeInheritanceComma: false +BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +# EmptyLineAfterAccessModifier: Leave +EmptyLineBeforeAccessModifier: Always +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '(^)|(^)|(^)' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<([^\.])*>$' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 4 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +# IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: AfterHash +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +# InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +NamespaceMacros: +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +# ShortNamespaceLines: 5 +SortIncludes: true +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +# SpacesInLineCommentPrefix: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +TypenameMacros: +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME + - FCODE + - ICODE +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..468d90c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d3cbc38 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +project(mcl CXX) + +# Project options +option(MCL_WARNINGS_AS_ERRORS "Warnings as errors" ON) + +# Default to a Release build +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + message(STATUS "Defaulting to a Release build") +endif() + +# Set hard requirements for C++ +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Warn on CMake API deprecations +set(CMAKE_WARN_DEPRECATED ON) + +# Disable in-source builds +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") + message(SEND_ERROR "In-source builds are not allowed.") +endif() + +# Add the module directory to the list of paths +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules") + +# Compiler flags +if (MSVC) + set(MCL_CXX_FLAGS + /std:c++latest + /experimental:external + /external:W0 + /external:anglebrackets + /W4 + /w44263 # Non-virtual member function hides base class virtual function + /w44265 # Class has virtual functions, but destructor is not virtual + /w44456 # Declaration of 'var' hides previous local declaration + /w44457 # Declaration of 'var' hides function parameter + /w44458 # Declaration of 'var' hides class member + /w44459 # Declaration of 'var' hides global definition + /w44946 # Reinterpret-cast between related types + /wd4592 # Symbol will be dynamically initialized (implementation limitation) + /permissive- # Stricter C++ standards conformance + /MP + /Zi + /Zo + /EHsc + /Zc:externConstexpr # Allows external linkage for variables declared "extern constexpr", as the standard permits. + /Zc:inline # Omits inline functions from object-file output. + /Zc:throwingNew # Assumes new (without std::nothrow) never returns null. + /volatile:iso # Use strict standard-abiding volatile semantics + /bigobj # Increase number of sections in .obj files + /DNOMINMAX) + + if (MCL_WARNINGS_AS_ERRORS) + list(APPEND MCL_CXX_FLAGS /WX) + endif() + + if (CMAKE_VS_PLATFORM_TOOLSET MATCHES "LLVM-vs[0-9]+") + list(APPEND MCL_CXX_FLAGS + -Qunused-arguments + -Wno-missing-braces) + endif() +else() + set(MCL_CXX_FLAGS + -Wall + -Wextra + -Wcast-qual + -pedantic + -pedantic-errors + -Wfatal-errors + -Wno-missing-braces) + + if (MCL_WARNINGS_AS_ERRORS) + list(APPEND MCL_CXX_FLAGS -Werror) + endif() +endif() + +# Dependencies + +if (NOT TARGET Catch2::Catch2) + find_package(Catch2 QUIET) +endif() + +if (NOT TARGET fmt::fmt) + find_package(fmt REQUIRED) +endif() + +# Project files + +add_subdirectory(src) +if (TARGET Catch2::Catch2) + add_subdirectory(tests) +endif() diff --git a/CMakeModules/CreateTargetDirectoryGroups.cmake b/CMakeModules/CreateTargetDirectoryGroups.cmake new file mode 100644 index 0000000..175899e --- /dev/null +++ b/CMakeModules/CreateTargetDirectoryGroups.cmake @@ -0,0 +1,17 @@ +# This function should be passed a name of an existing target. It will automatically generate +# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the +# one in the filesystem. +function(create_target_directory_groups target_name) + # Place any files that aren't in the source list in a separate group so that they don't get in + # the way. + source_group("Other Files" REGULAR_EXPRESSION ".") + + get_target_property(target_sources "${target_name}" SOURCES) + + foreach(file_name IN LISTS target_sources) + get_filename_component(dir_name "${file_name}" PATH) + # Group names use '\' as a separator even though the entire rest of CMake uses '/'... + string(REPLACE "/" "\\" group_name "${dir_name}") + source_group("${group_name}" FILES "${file_name}") + endforeach() +endfunction() diff --git a/CMakeModules/DetectArchitecture.cmake b/CMakeModules/DetectArchitecture.cmake new file mode 100644 index 0000000..1fbd5e9 --- /dev/null +++ b/CMakeModules/DetectArchitecture.cmake @@ -0,0 +1,56 @@ +include(CheckSymbolExists) + +function(detect_architecture symbol arch) + if (NOT DEFINED ARCHITECTURE) + set(CMAKE_REQUIRED_QUIET YES) + check_symbol_exists("${symbol}" "" DETECT_ARCHITECTURE_${arch}) + unset(CMAKE_REQUIRED_QUIET) + + if (DETECT_ARCHITECTURE_${arch}) + set(ARCHITECTURE "${arch}" PARENT_SCOPE) + endif() + + unset(DETECT_ARCHITECTURE_${arch} CACHE) + endif() +endfunction() + +detect_architecture("__ARM64__" arm64) +detect_architecture("__aarch64__" arm64) +detect_architecture("_M_ARM64" arm64) + +detect_architecture("__arm__" arm32) +detect_architecture("__TARGET_ARCH_ARM" arm32) +detect_architecture("_M_ARM" arm32) + +detect_architecture("__x86_64" x86_64) +detect_architecture("__x86_64__" x86_64) +detect_architecture("__amd64" x86_64) +detect_architecture("_M_X64" x86_64) + +detect_architecture("__i386" x86_32) +detect_architecture("__i386__" x86_32) +detect_architecture("_M_IX86" x86_32) + +detect_architecture("__ia64" ia64) +detect_architecture("__ia64__" ia64) +detect_architecture("_M_IA64" ia64) + +detect_architecture("__mips" mips) +detect_architecture("__mips__" mips) +detect_architecture("_M_MRX000" mips) + +detect_architecture("__ppc64__" ppc64) +detect_architecture("__powerpc64__" ppc64) + +detect_architecture("__ppc__" ppc32) +detect_architecture("__ppc" ppc32) +detect_architecture("__powerpc__" ppc32) +detect_architecture("_ARCH_COM" ppc32) +detect_architecture("_ARCH_PWR" ppc32) +detect_architecture("_ARCH_PPC" ppc32) +detect_architecture("_M_MPPC" ppc32) +detect_architecture("_M_PPC" ppc32) + +detect_architecture("__riscv" riscv) + +detect_architecture("__EMSCRIPTEN__" wasm) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..614ef75 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 merryhime + +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. diff --git a/README b/README new file mode 100644 index 0000000..55feace --- /dev/null +++ b/README @@ -0,0 +1,17 @@ + + oooo + `888 + ooo. .oo. .oo. .ooooo. 888 + `888P"Y88bP"Y88b d88' `"Y8 888 + 888 888 888 888 888 + 888 888 888 888 .o8 888 + o888o o888o o888o `Y8bod8P' o888o + + + .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. + / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ +`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-` + +A collection of C++20 utilities which is common to a number of merry's projects. + +MIT licensed. diff --git a/include/mcl/assert.hpp b/include/mcl/assert.hpp new file mode 100644 index 0000000..3b9377a --- /dev/null +++ b/include/mcl/assert.hpp @@ -0,0 +1,61 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include + +#include "mcl/hint/assume.hpp" + +namespace mcl::detail { + +[[noreturn]] void assert_terminate_impl(fmt::string_view msg, fmt::format_args args); + +template +[[noreturn]] void assert_terminate(fmt::string_view msg, Ts... args) { + assert_terminate_impl(msg, fmt::make_format_args(args...)); +} + +} // namespace mcl::detail + +#define UNREACHABLE() ASSERT_FALSE("Unreachable code!") + +#define ASSERT(expr) \ + [&] { \ + if (std::is_constant_evaluated()) { \ + if (!(expr)) { \ + throw std::logic_error{"ASSERT failed at compile time"}; \ + } \ + } else { \ + if (!(expr)) [[unlikely]] { \ + ::mcl::detail::assert_terminate(#expr); \ + } \ + } \ + }() + +#define ASSERT_MSG(expr, ...) \ + [&] { \ + if (std::is_constant_evaluated()) { \ + if (!(expr)) { \ + throw std::logic_error{"ASSERT_MSG failed at compile time"}; \ + } \ + } else { \ + if (!(expr)) [[unlikely]] { \ + ::mcl::detail::assert_terminate(#expr "\nMessage: " __VA_ARGS__); \ + } \ + } \ + }() + +#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false\nMessage: " __VA_ARGS__) + +#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS) +# define DEBUG_ASSERT(expr) ASSUME(expr) +# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr) +#else +# define DEBUG_ASSERT(expr) ASSERT(expr) +# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__) +#endif diff --git a/include/mcl/bit/bit_count.hpp b/include/mcl/bit/bit_count.hpp new file mode 100644 index 0000000..d4ac628 --- /dev/null +++ b/include/mcl/bit/bit_count.hpp @@ -0,0 +1,54 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "mcl/bitsizeof.hpp" +#include "mcl/concepts/bit_integral.hpp" +#include "mcl/stdint.hpp" + +namespace mcl::bit { + +template +inline size_t count_ones(T x) { + return std::bitset>(x).count(); +} + +template +constexpr size_t count_leading_zeros(T x) { + size_t result = bitsizeof; + while (x != 0) { + x >>= 1; + result--; + } + return result; +} + +template +constexpr int highest_set_bit(T x) { + int result = -1; + while (x != 0) { + x >>= 1; + result++; + } + return result; +} + +template +constexpr size_t lowest_set_bit(T x) { + if (x == 0) { + return bitsizeof; + } + + size_t result = 0; + while ((x & 1) == 0) { + x >>= 1; + result++; + } + return result; +} + +} // namespace mcl::bit diff --git a/include/mcl/bit/bit_field.hpp b/include/mcl/bit/bit_field.hpp new file mode 100644 index 0000000..5ea5f9f --- /dev/null +++ b/include/mcl/bit/bit_field.hpp @@ -0,0 +1,195 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include "mcl/assert.hpp" +#include "mcl/bitsizeof.hpp" +#include "mcl/concepts/bit_integral.hpp" +#include "mcl/stdint.hpp" + +namespace mcl::bit { + +/// Create a mask with `count` number of one bits. +template +constexpr T ones() { + static_assert(count <= bitsizeof, "count larger than bitsize of T"); + + if constexpr (count == 0) { + return 0; + } else { + return ~static_cast(0) >> (bitsizeof - count); + } +} + +/// Create a mask with `count` number of one bits. +template +constexpr T ones(size_t count) { + ASSERT_MSG(count <= bitsizeof, "count larger than bitsize of T"); + + if (count == 0) { + return 0; + } + return ~static_cast(0) >> (bitsizeof - count); +} + +/// Create a mask of type T for bits [begin_bit, end_bit] inclusive. +template +constexpr T mask() { + static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); + static_assert(begin_bit < bitsizeof, "begin_bit must be smaller than size of T"); + static_assert(end_bit < bitsizeof, "end_bit must be smaller than size of T"); + + return ones() << begin_bit; +} + +/// Create a mask of type T for bits [begin_bit, end_bit] inclusive. +template +constexpr T mask(size_t begin_bit, size_t end_bit) { + ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); + ASSERT_MSG(begin_bit < bitsizeof, "begin_bit must be smaller than size of T"); + ASSERT_MSG(end_bit < bitsizeof, "end_bit must be smaller than size of T"); + + return ones(end_bit - begin_bit + 1) << begin_bit; +} + +/// Extract bits [begin_bit, end_bit] inclusive from value of type T. +template +constexpr T get_bits(T value) { + constexpr T m = mask(); + return (value & m) >> begin_bit; +} + +/// Extract bits [begin_bit, end_bit] inclusive from value of type T. +template +constexpr T get_bits(size_t begin_bit, size_t end_bit, T value) { + const T m = mask(begin_bit, end_bit); + return (value & m) >> begin_bit; +} + +/// Clears bits [begin_bit, end_bit] inclusive of value of type T. +template +constexpr T clear_bits(T value) { + constexpr T m = mask(); + return value & ~m; +} + +/// Clears bits [begin_bit, end_bit] inclusive of value of type T. +template +constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value) { + const T m = mask(begin_bit, end_bit); + return value & ~m; +} + +/// Modifies bits [begin_bit, end_bit] inclusive of value of type T. +template +constexpr T set_bits(T value, T new_bits) { + constexpr T m = mask(); + return (value & ~m) | ((new_bits << begin_bit) & m); +} + +/// Modifies bits [begin_bit, end_bit] inclusive of value of type T. +template +constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits) { + const T m = mask(begin_bit, end_bit); + return (value & ~m) | ((new_bits << begin_bit) & m); +} + +/// Extract bit at bit_position from value of type T. +template +constexpr bool get_bit(T value) { + constexpr T m = mask(); + return (value & m) != 0; +} + +/// Extract bit at bit_position from value of type T. +template +constexpr bool get_bit(size_t bit_position, T value) { + const T m = mask(bit_position, bit_position); + return (value & m) != 0; +} + +/// Clears bit at bit_position of value of type T. +template +constexpr T clear_bit(T value) { + constexpr T m = mask(); + return value & ~m; +} + +/// Clears bit at bit_position of value of type T. +template +constexpr T clear_bit(size_t bit_position, T value) { + const T m = mask(bit_position, bit_position); + return value & ~m; +} + +/// Modifies bit at bit_position of value of type T. +template +constexpr T set_bit(T value, bool new_bit) { + constexpr T m = mask(); + return (value & ~m) | (new_bit ? m : static_cast(0)); +} + +/// Modifies bit at bit_position of value of type T. +template +constexpr T set_bit(size_t bit_position, T value, bool new_bit) { + const T m = mask(bit_position, bit_position); + return (value & ~m) | (new_bit ? m : static_cast(0)); +} + +/// Sign-extends a value that has bit_count bits to the full bitwidth of type T. +template +constexpr T sign_extend(T value) { + static_assert(bit_count != 0, "cannot sign-extend zero-sized value"); + + constexpr T m = ones(); + if (get_bit(value)) { + return value | ~m; + } + return value; +} + +/// Sign-extends a value that has bit_count bits to the full bitwidth of type T. +template +constexpr T sign_extend(size_t bit_count, T value) { + ASSERT_MSG(bit_count != 0, "cannot sign-extend zero-sized value"); + + const T m = ones(bit_count); + if (get_bit(bit_count - 1, value)) { + return value | ~m; + } + return value; +} + +/// Replicate an element across a value of type T. +template +constexpr T replicate_element(T value) { + static_assert(element_size > bitsizeof, "element_size is too large"); + static_assert(bitsizeof % element_size == 0, "bitsize of T not divisible by element_size"); + + if constexpr (element_size == bitsizeof) { + return value; + } else { + return replicate_element(static_cast(value | (value << element_size))); + } +} + +/// Replicate an element across a value of type T. +template +constexpr T replicate_element(size_t element_size, T value) { + ASSERT_MSG(element_size > bitsizeof, "element_size is too large"); + ASSERT_MSG(bitsizeof % element_size == 0, "bitsize of T not divisible by element_size"); + + if (element_size == bitsizeof) { + return value; + } + return replicate_element(static_cast(value | (value << element_size)), element_size * 2); +} + +template +constexpr bool most_significant_bit(T value) { + return get_bit - 1, T>(value); +} + +} // namespace mcl::bit diff --git a/include/mcl/bit/rotate.hpp b/include/mcl/bit/rotate.hpp new file mode 100644 index 0000000..649e5e2 --- /dev/null +++ b/include/mcl/bit/rotate.hpp @@ -0,0 +1,31 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include "mcl/bitsizeof.hpp" +#include "mcl/concepts/bit_integral.hpp" +#include "mcl/stdint.hpp" + +namespace mcl::bit { + +template +constexpr T rotate_right(T x, size_t amount) { + amount %= bitsizeof; + if (amount == 0) { + return x; + } + return static_cast((x >> amount) | (x << (bitsizeof - amount))); +} + +template +constexpr T rotate_left(T x, size_t amount) { + amount %= bitsizeof; + if (amount == 0) { + return x; + } + return static_cast((x << amount) | (x >> (bitsizeof - amount))); +} + +} // namespace mcl::bit diff --git a/include/mcl/bit/swap.hpp b/include/mcl/bit/swap.hpp new file mode 100644 index 0000000..0df6bac --- /dev/null +++ b/include/mcl/bit/swap.hpp @@ -0,0 +1,50 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include "mcl/concepts/bit_integral.hpp" + +namespace mcl::bit { + +constexpr u16 swap_bytes_16(u16 value) { + return static_cast(u32{value} >> 8 | u32{value} << 8); +} + +constexpr u32 swap_bytes_32(u32 value) { + return ((value & 0xff000000u) >> 24) + | ((value & 0x00ff0000u) >> 8) + | ((value & 0x0000ff00u) << 8) + | ((value & 0x000000ffu) << 24); +} + +constexpr u64 swap_bytes_64(u64 value) { + return ((value & 0xff00000000000000ull) >> 56) + | ((value & 0x00ff000000000000ull) >> 40) + | ((value & 0x0000ff0000000000ull) >> 24) + | ((value & 0x000000ff00000000ull) >> 8) + | ((value & 0x00000000ff000000ull) << 8) + | ((value & 0x0000000000ff0000ull) << 24) + | ((value & 0x000000000000ff00ull) << 40) + | ((value & 0x00000000000000ffull) << 56); +} + +constexpr u32 swap_halves_32(u32 value) { + return ((value & 0xffff0000u) >> 16) + | ((value & 0x0000ffffu) << 16); +} + +constexpr u64 swap_halves_64(u64 value) { + return ((value & 0xffff000000000000ull) >> 48) + | ((value & 0x0000ffff00000000ull) >> 16) + | ((value & 0x00000000ffff0000ull) << 16) + | ((value & 0x000000000000ffffull) << 48); +} + +constexpr u64 swap_words_64(u64 value) { + return ((value & 0xffffffff00000000ull) >> 32) + | ((value & 0x00000000ffffffffull) << 32); +} + +} // namespace mcl::bit diff --git a/include/mcl/bit_cast.hpp b/include/mcl/bit_cast.hpp new file mode 100644 index 0000000..cfa8860 --- /dev/null +++ b/include/mcl/bit_cast.hpp @@ -0,0 +1,36 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +namespace mcl { + +/// Reinterpret objects of one type as another by bit-casting between object representations. +template +inline Dest bit_cast(const Source& source) noexcept { + static_assert(sizeof(Dest) == sizeof(Source), "size of destination and source objects must be equal"); + static_assert(std::is_trivially_copyable_v, "destination type must be trivially copyable."); + static_assert(std::is_trivially_copyable_v, "source type must be trivially copyable"); + + std::aligned_storage_t dest; + std::memcpy(&dest, &source, sizeof(dest)); + return reinterpret_cast(dest); +} + +/// Reinterpret objects of any arbitrary type as another type by bit-casting between object representations. +/// Note that here we do not verify if source pointed to by source_ptr has enough bytes to read from. +template +inline Dest bit_cast_pointee(const SourcePtr source_ptr) noexcept { + static_assert(sizeof(SourcePtr) == sizeof(void*), "source pointer must have size of a pointer"); + static_assert(std::is_trivially_copyable_v, "destination type must be trivially copyable."); + + std::aligned_storage_t dest; + std::memcpy(&dest, bit_cast(source_ptr), sizeof(dest)); + return reinterpret_cast(dest); +} + +} // namespace mcl diff --git a/include/mcl/bitsizeof.hpp b/include/mcl/bitsizeof.hpp new file mode 100644 index 0000000..a5e76bd --- /dev/null +++ b/include/mcl/bitsizeof.hpp @@ -0,0 +1,14 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +namespace mcl { + +template +constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T); + +} // namespace mcl diff --git a/include/mcl/concepts/bit_integral.hpp b/include/mcl/concepts/bit_integral.hpp new file mode 100644 index 0000000..412bb4c --- /dev/null +++ b/include/mcl/concepts/bit_integral.hpp @@ -0,0 +1,16 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include "mcl/concepts/is_any_of.hpp" +#include "mcl/stdint.hpp" + +namespace mcl { + +/// Integral upon which bit operations can be safely performed. +template +concept BitIntegral = IsAnyOf; + +} // namespace mcl diff --git a/include/mcl/concepts/is_any_of.hpp b/include/mcl/concepts/is_any_of.hpp new file mode 100644 index 0000000..0dab6f4 --- /dev/null +++ b/include/mcl/concepts/is_any_of.hpp @@ -0,0 +1,14 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include "mcl/same_as.hpp" + +namespace mcl { + +template +concept IsAnyOf = (SameAs || ...); + +} // namespace mcl diff --git a/include/mcl/concepts/same_as.hpp b/include/mcl/concepts/same_as.hpp new file mode 100644 index 0000000..7aaaa50 --- /dev/null +++ b/include/mcl/concepts/same_as.hpp @@ -0,0 +1,19 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +namespace mcl { + +namespace detail { + +template +concept SameHelper = std::is_same_v; + +} // namespace detail + +template +concept SameAs = detail::SameHelper && detail::SameHelper; + +} // namespace mcl diff --git a/include/mcl/container/intrusive_list.hpp b/include/mcl/container/intrusive_list.hpp new file mode 100644 index 0000000..2d80c85 --- /dev/null +++ b/include/mcl/container/intrusive_list.hpp @@ -0,0 +1,378 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +#include "mcl/assert.hpp" + +namespace mcl { + +template +class intrusive_list; +template +class intrusive_list_iterator; + +template +class intrusive_list_node { +public: + bool is_sentinel() const { + return is_sentinel_; + } + +protected: + intrusive_list_node* next = nullptr; + intrusive_list_node* prev = nullptr; + bool is_sentinel_ = false; + + friend class intrusive_list; + friend class intrusive_list_iterator; + friend class intrusive_list_iterator; +}; + +template +class intrusive_list_sentinel final : public intrusive_list_node { + using intrusive_list_node::next; + using intrusive_list_node::prev; + using intrusive_list_node::is_sentinel; + +public: + intrusive_list_sentinel() { + next = this; + prev = this; + is_sentinel = true; + } +}; + +template +class intrusive_list_iterator { +public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + // If value_type is const, we want "const intrusive_list_node", not "intrusive_list_node" + using node_type = std::conditional_t::value, + const intrusive_list_node>, + intrusive_list_node>; + using node_pointer = node_type*; + using node_reference = node_type&; + + intrusive_list_iterator() = default; + intrusive_list_iterator(const intrusive_list_iterator& other) = default; + intrusive_list_iterator& operator=(const intrusive_list_iterator& other) = default; + + explicit intrusive_list_iterator(node_pointer list_node) + : node(list_node) { + } + explicit intrusive_list_iterator(pointer data) + : node(data) { + } + explicit intrusive_list_iterator(reference data) + : node(&data) { + } + + intrusive_list_iterator& operator++() { + node = node->next; + return *this; + } + intrusive_list_iterator& operator--() { + node = node->prev; + return *this; + } + intrusive_list_iterator operator++(int) { + intrusive_list_iterator it(*this); + ++*this; + return it; + } + intrusive_list_iterator operator--(int) { + intrusive_list_iterator it(*this); + --*this; + return it; + } + + bool operator==(const intrusive_list_iterator& other) const { + return node == other.node; + } + bool operator!=(const intrusive_list_iterator& other) const { + return !operator==(other); + } + + reference operator*() const { + DEBUG_ASSERT(!node->is_sentinel()); + return static_cast(*node); + } + pointer operator->() const { + return std::addressof(operator*()); + } + + node_pointer AsNodePointer() const { + return node; + } + +private: + friend class intrusive_list; + node_pointer node = nullptr; +}; + +template +class intrusive_list { +public: + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + using value_type = T; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = intrusive_list_iterator; + using const_iterator = intrusive_list_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + /** + * Inserts a node at the given location indicated by an iterator. + * + * @param location The location to insert the node. + * @param new_node The node to add. + */ + iterator insert(iterator location, pointer new_node) { + return insert_before(location, new_node); + } + + /** + * Inserts a node at the given location, moving the previous + * node occupant ahead of the one inserted. + * + * @param location The location to insert the new node. + * @param new_node The node to insert into the list. + */ + iterator insert_before(iterator location, pointer new_node) { + auto existing_node = location.AsNodePointer(); + + new_node->next = existing_node; + new_node->prev = existing_node->prev; + existing_node->prev->next = new_node; + existing_node->prev = new_node; + + return iterator(new_node); + } + + /** + * Inserts a new node into the list ahead of the position indicated. + * + * @param position Location to insert the node in front of. + * @param new_node The node to be inserted into the list. + */ + iterator insert_after(iterator position, pointer new_node) { + if (empty()) + return insert(begin(), new_node); + + return insert(++position, new_node); + } + + /** + * Add an entry to the start of the list. + * @param node Node to add to the list. + */ + void push_front(pointer node) { + insert(begin(), node); + } + + /** + * Add an entry to the end of the list + * @param node Node to add to the list. + */ + void push_back(pointer node) { + insert(end(), node); + } + + /** + * Erases the node at the front of the list. + * @note Must not be called on an empty list. + */ + void pop_front() { + DEBUG_ASSERT(!empty()); + erase(begin()); + } + + /** + * Erases the node at the back of the list. + * @note Must not be called on an empty list. + */ + void pop_back() { + DEBUG_ASSERT(!empty()); + erase(--end()); + } + + /** + * Removes a node from this list + * @param it An iterator that points to the node to remove from list. + */ + pointer remove(iterator& it) { + DEBUG_ASSERT(it != end()); + + pointer node = &*it++; + + node->prev->next = node->next; + node->next->prev = node->prev; +#if !defined(NDEBUG) + node->next = nullptr; + node->prev = nullptr; +#endif + + return node; + } + + /** + * Removes a node from this list + * @param it A constant iterator that points to the node to remove from list. + */ + pointer remove(const iterator& it) { + iterator copy = it; + return remove(copy); + } + + /** + * Removes a node from this list. + * @param node A pointer to the node to remove. + */ + pointer remove(pointer node) { + return remove(iterator(node)); + } + + /** + * Removes a node from this list. + * @param node A reference to the node to remove. + */ + pointer remove(reference node) { + return remove(iterator(node)); + } + + /** + * Is this list empty? + * @returns true if there are no nodes in this list. + */ + bool empty() const { + return root->next == root.get(); + } + + /** + * Gets the total number of elements within this list. + * @return the number of elements in this list. + */ + size_type size() const { + return static_cast(std::distance(begin(), end())); + } + + /** + * Retrieves a reference to the node at the front of the list. + * @note Must not be called on an empty list. + */ + reference front() { + DEBUG_ASSERT(!empty()); + return *begin(); + } + + /** + * Retrieves a constant reference to the node at the front of the list. + * @note Must not be called on an empty list. + */ + const_reference front() const { + DEBUG_ASSERT(!empty()); + return *begin(); + } + + /** + * Retrieves a reference to the node at the back of the list. + * @note Must not be called on an empty list. + */ + reference back() { + DEBUG_ASSERT(!empty()); + return *--end(); + } + + /** + * Retrieves a constant reference to the node at the back of the list. + * @note Must not be called on an empty list. + */ + const_reference back() const { + DEBUG_ASSERT(!empty()); + return *--end(); + } + + // Iterator interface + iterator begin() { return iterator(root->next); } + const_iterator begin() const { return const_iterator(root->next); } + const_iterator cbegin() const { return begin(); } + + iterator end() { return iterator(root.get()); } + const_iterator end() const { return const_iterator(root.get()); } + const_iterator cend() const { return end(); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const { return rbegin(); } + + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const { return rend(); } + + /** + * Erases a node from the list, indicated by an iterator. + * @param it The iterator that points to the node to erase. + */ + iterator erase(iterator it) { + remove(it); + return it; + } + + /** + * Erases a node from this list. + * @param node A pointer to the node to erase from this list. + */ + iterator erase(pointer node) { + return erase(iterator(node)); + } + + /** + * Erases a node from this list. + * @param node A reference to the node to erase from this list. + */ + iterator erase(reference node) { + return erase(iterator(node)); + } + + /** + * Exchanges contents of this list with another list instance. + * @param other The other list to swap with. + */ + void swap(intrusive_list& other) noexcept { + root.swap(other.root); + } + +private: + std::shared_ptr> root = std::make_shared>(); +}; + +/** + * Exchanges contents of an intrusive list with another intrusive list. + * @tparam T The type of data being kept track of by the lists. + * @param lhs The first list. + * @param rhs The second list. + */ +template +void swap(intrusive_list& lhs, intrusive_list& rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace mcl diff --git a/include/mcl/hint/assume.hpp b/include/mcl/hint/assume.hpp new file mode 100644 index 0000000..ac1873f --- /dev/null +++ b/include/mcl/hint/assume.hpp @@ -0,0 +1,13 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#if defined(__clang) || defined(__GNUC__) +# define ASSUME(expr) [&] { if (!(expr)) __builtin_unreachable(); }() +#elif defined(_MSC_VER) +# define ASSUME(expr) __assume(expr) +#else +# define ASSUME(expr) +#endif diff --git a/include/mcl/iterator/reverse.hpp b/include/mcl/iterator/reverse.hpp new file mode 100644 index 0000000..eb21b9e --- /dev/null +++ b/include/mcl/iterator/reverse.hpp @@ -0,0 +1,34 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +namespace mcl::iterator { +namespace detail { + +template +struct reverse_adapter { + T& iterable; + + constexpr auto begin() { + using namespace std; + return rbegin(iterable); + } + + constexpr auto end() { + using namespace std; + return rend(iterable); + } +}; + +} // namespace detail + +template +constexpr detail::reverse_adapter reverse(T&& iterable) { + return detail::reverse_adapter{iterable}; +} + +} // namespace mcl::iterator diff --git a/include/mcl/macro/anonymous_variable.hpp b/include/mcl/macro/anonymous_variable.hpp new file mode 100644 index 0000000..eca428d --- /dev/null +++ b/include/mcl/macro/anonymous_variable.hpp @@ -0,0 +1,11 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#ifdef __COUNTER__ +# define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __COUNTER__) +#else +# define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __LINE__) +#endif diff --git a/include/mcl/macro/architecture.hpp b/include/mcl/macro/architecture.hpp new file mode 100644 index 0000000..3251d82 --- /dev/null +++ b/include/mcl/macro/architecture.hpp @@ -0,0 +1,40 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#if defined(__ARM64__) || defined(__aarch64__) || defined(_M_ARM64) +# define MCL_ARCHITECTURE arm64 +# define MCL_ARCHITECTURE_ARM64 1 +#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM) +# define MCL_ARCHITECTURE arm32 +# define MCL_ARCHITECTURE_ARM32 1 +#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) +# define MCL_ARCHITECTURE x86_64 +# define MCL_ARCHITECTURE_X86_64 1 +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) +# define MCL_ARCHITECTURE x86_32 +# define MCL_ARCHITECTURE_X86_32 1 +#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) +# define MCL_ARCHITECTURE ia64 +# define MCL_ARCHITECTURE_IA64 1 +#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000) +# define MCL_ARCHITECTURE mips +# define MCL_ARCHITECTURE_MIPS 1 +#elif defined(__ppc64__) || defined(__powerpc64__) +# define MCL_ARCHITECTURE ppc64 +# define MCL_ARCHITECTURE_PPC64 1 +#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_M_MPPC) || defined(_M_PPC) +# define MCL_ARCHITECTURE ppc32 +# define MCL_ARCHITECTURE_PPC32 1 +#elif defined(__riscv) +# define MCL_ARCHITECTURE riscv +# define MCL_ARCHITECTURE_RISCV 1 +#elif defined(__EMSCRIPTEN__) +# define MCL_ARCHITECTURE wasm +# define MCL_ARCHITECTURE_WASM 1 +#else +# define MCL_ARCHITECTURE generic +# define MCL_ARCHITECTURE_GENERIC 1 +#endif diff --git a/include/mcl/macro/concatenate_tokens.hpp b/include/mcl/macro/concatenate_tokens.hpp new file mode 100644 index 0000000..3555b23 --- /dev/null +++ b/include/mcl/macro/concatenate_tokens.hpp @@ -0,0 +1,8 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#define CONCATENATE_TOKENS(x, y) CONCATENATE_TOKENS_IMPL(x, y) +#define CONCATENATE_TOKENS_IMPL(x, y) x##y diff --git a/include/mcl/scope_exit.hpp b/include/mcl/scope_exit.hpp new file mode 100644 index 0000000..7a121b8 --- /dev/null +++ b/include/mcl/scope_exit.hpp @@ -0,0 +1,85 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +#include "mcl/macro/anonymous_variable.h" + +namespace mcl::detail { + +struct scope_exit_tag {}; +struct scope_fail_tag {}; +struct scope_success_tag {}; + +template +class scope_exit final { +public: + explicit scope_exit(Function&& fn) + : function(std::move(fn)) {} + ~scope_exit() noexcept { + function(); + } + +private: + Function function; +}; + +template +class scope_fail final { +public: + explicit scope_fail(Function&& fn) + : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {} + ~scope_fail() noexcept { + if (std::uncaught_exceptions() > exception_count) { + function(); + } + } + +private: + Function function; + int exception_count; +}; + +template +class scope_success final { +public: + explicit scope_success(Function&& fn) + : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {} + ~scope_success() { + if (std::uncaught_exceptions() <= exception_count) { + function(); + } + } + +private: + Function function; + int exception_count; +}; + +// We use ->* here as it has the highest precedence of the operators we can use. + +template +auto operator->*(scope_exit_tag, Function&& function) { + return scope_exit>{std::forward(function)}; +} + +template +auto operator->*(scope_fail_tag, Function&& function) { + return scope_fail>{std::forward(function)}; +} + +template +auto operator->*(scope_success_tag, Function&& function) { + return scope_success>{std::forward(function)}; +} + +} // namespace mcl::detail + +#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(MCL_SCOPE_EXIT_VAR_) = ::mcl::detail::scope_exit_tag{}->*[&]() noexcept +#define SCOPE_FAIL auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_fail_tag{}->*[&]() noexcept +#define SCOPE_SUCCESS auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_success_tag{}->*[&]() diff --git a/include/mcl/stdint.hpp b/include/mcl/stdint.hpp new file mode 100644 index 0000000..37b82a2 --- /dev/null +++ b/include/mcl/stdint.hpp @@ -0,0 +1,27 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; +using uptr = std::uintptr_t; + +using s8 = std::int8_t; +using s16 = std::int16_t; +using s32 = std::int32_t; +using s64 = std::int64_t; +using sptr = std::intptr_t; + +using size_t = std::size_t; + +using f32 = float; +using f64 = double; +static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide"); +static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide"); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..01b9164 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,26 @@ +add_library(mcl + ../include/mcl/assert.hpp + ../include/mcl/bit/bit_count.hpp + ../include/mcl/bit/bit_field.hpp + ../include/mcl/bit/rotate.hpp + ../include/mcl/bit/swap.hpp + ../include/mcl/bit_cast.hpp + ../include/mcl/concepts/bit_integral.hpp + ../include/mcl/concepts/is_any_of.hpp + ../include/mcl/concepts/same_as.hpp + ../include/mcl/container/intrusive_list.hpp + ../include/mcl/hint/assume.hpp + ../include/mcl/macro/anonymous_variable.hpp + ../include/mcl/macro/architecture.hpp + ../include/mcl/macro/concatenate_tokens.hpp + ../include/mcl/scope_exit.hpp + ../include/mcl/stdint.hpp + ../include/mcl/stdint.hpp + assert.cpp +) +target_include_directories(mcl PUBLIC ../include/) +target_compile_options(mcl PRIVATE ${MCL_CXX_FLAGS}) +target_link_libraries(mcl PUBLIC fmt::fmt) + +include(CreateTargetDirectoryGroups) +create_target_directory_groups(mcl) diff --git a/src/assert.cpp b/src/assert.cpp new file mode 100644 index 0000000..e30697e --- /dev/null +++ b/src/assert.cpp @@ -0,0 +1,20 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#include "mcl/assert.hpp" + +#include +#include + +#include + +namespace mcl::detail { + +[[noreturn]] void assert_terminate_impl(fmt::string_view msg, fmt::format_args args) { + fmt::print(stderr, "assertion failed: "); + fmt::vprint(stderr, msg, args); + std::terminate(); +} + +} // namespace mcl::detail diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..91cb4c6 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(mcl-tests + main.cpp +) +target_include_directories(mcl-tests PUBLIC .) +target_compile_options(mcl-tests PRIVATE ${STAMINA_CXX_FLAGS}) +target_link_libraries(mcl-tests PRIVATE Catch2::Catch2 mcl) + +include(CTest) +include(Catch) +catch_discover_tests(mcl-tests) +enable_testing() diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..14b471b --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,6 @@ +// This file is part of the mcl project. +// Copyright (c) 2022 merryhime +// SPDX-License-Identifier: MIT + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp"