Compare commits
126 Commits
2541c8c637
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ed381c4178 | |||
| 450f725cab | |||
| 93b58ea197 | |||
| 6c78ecbeb1 | |||
| 7c2f89b331 | |||
| c1be5385d3 | |||
| ecf1cfc29c | |||
| 184bc7fcdf | |||
| 1f6637408d | |||
| 9f499c933d | |||
| 88e33bdcc8 | |||
| 9e6f00eb60 | |||
| e7503ed92f | |||
| aee4e340dd | |||
| 520a0e1363 | |||
| 97f5bbfe00 | |||
| b64ce44d4e | |||
| 1acf00138a | |||
| 5dcb58f53c | |||
| a1bdc077b1 | |||
| 9553f9b662 | |||
| 9645856554 | |||
| a2abb58705 | |||
| 7f1dd245dc | |||
| 6d58105734 | |||
| d928d86014 | |||
| 0b76b06a1b | |||
| 6f09c3f7fe | |||
| fe8c3a4602 | |||
| b9026ec8da | |||
| 7b87828f06 | |||
| 8925b3f2f0 | |||
| f636feb4f1 | |||
| 1a9a80e37f | |||
| 18c8171847 | |||
| d546519180 | |||
| f208141b5b | |||
| 52d6c62f76 | |||
| 037e2554a1 | |||
| 8867576a2e | |||
| 788f63d092 | |||
| a35f2a699d | |||
| 80925965d4 | |||
| f2a45aa913 | |||
| 3565bbbc52 | |||
| 375492ef7b | |||
| dbcb50349d | |||
| 992a02db3e | |||
| e1eaf97961 | |||
| 086c73f058 | |||
| 339f5c8cd8 | |||
| 18c0a7099d | |||
| 540c7fbce8 | |||
| cbcd699ab0 | |||
| ff27caab4f | |||
| fe4c49d092 | |||
| 037c62bf12 | |||
| 494d766741 | |||
| 83f0c01e29 | |||
| 4ff739d625 | |||
| 7cd38604a7 | |||
| 733fca41ef | |||
| 55a8c54119 | |||
| 27754a56d7 | |||
| fcf9c6adcb | |||
| e6c0a60ea9 | |||
| 5252ba84c9 | |||
| 73041e994d | |||
| 3ddc2b3d97 | |||
| e91c2aa9f1 | |||
| 38b7221fa0 | |||
| 8bfb59cd20 | |||
| 2535e1ac4b | |||
| f173c3e7cd | |||
| cc4d85c393 | |||
| d6e31a89b0 | |||
| 74fb525453 | |||
| b9de039a10 | |||
| 9f96155856 | |||
| d2be083a8f | |||
| efe56b3699 | |||
| b7d8426e86 | |||
| 2cb41e1437 | |||
| 0f721f57ea | |||
| 4a3639ecb4 | |||
| ff4d6efedc | |||
| 9dc9ed4ed1 | |||
| 5e04eb0ca6 | |||
| 3d42dea9eb | |||
| 3d4ea4398a | |||
| 7aafa4c9aa | |||
| 8124ea2ae5 | |||
| d02a51fd8d | |||
| 7493b5252a | |||
| 7ea2710ee0 | |||
| f9de242b87 | |||
| 2117e4347c | |||
| 5ab2952e83 | |||
| 65573f28e4 | |||
| 73333b4c67 | |||
| ab1c7d94be | |||
| 86286e84d7 | |||
| c72d1afe32 | |||
| f1552b89b1 | |||
| 89f59c75f3 | |||
| 6ae682aff6 | |||
| 5e0dc78210 | |||
| 4c0d36c933 | |||
| cc20af7504 | |||
| 649e39c70e | |||
| 2573de0904 | |||
| 17d8218124 | |||
| 012052641d | |||
| a33bf5206f | |||
| 0afaae72ac | |||
| 0eeb7ae3cf | |||
| e2ea22f12d | |||
| 9ea63478e5 | |||
| 9010650ceb | |||
| 516d9f4977 | |||
| f38cf5fb51 | |||
| f2ff863b3a | |||
| 1573033b52 | |||
| 37fba8faad | |||
| cbaf69310e | |||
| d8954eafe5 |
7
.gdbinit
Normal file
7
.gdbinit
Normal file
@@ -0,0 +1,7 @@
|
||||
python
|
||||
import sys, os.path
|
||||
print(os.path.abspath("./gdb"))
|
||||
sys.path.insert(0, os.path.abspath("./gdb"))
|
||||
import fennec
|
||||
fennec.register_printers(gdb.current_objfile())
|
||||
end
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@
|
||||
/docs/
|
||||
/bin/
|
||||
/lib/
|
||||
/doxy/README.md
|
||||
/doxy/Doxyfile.out
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "external/sdl"]
|
||||
path = external/sdl
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "external/cpptrace"]
|
||||
path = external/cpptrace
|
||||
url = https://github.com/jeremy-rifkin/cpptrace.git
|
||||
|
||||
333
CMakeLists.txt
333
CMakeLists.txt
@@ -1,79 +1,218 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
cmake_minimum_required(VERSION 3.28...3.31)
|
||||
|
||||
project(fennec)
|
||||
set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
macro(fennec_add_sources)
|
||||
list(APPEND FENNEC_SOURCES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_definitions)
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_link_libraries)
|
||||
list(APPEND FENNEC_LINK_LIBRARIES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_shared_libraries)
|
||||
list(APPEND FENNEC_SHARED_LIBRARIES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_compile_options)
|
||||
list(APPEND FENNEC_PRIVATE_COMPILE_OPTIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_link_options)
|
||||
list(APPEND FENNEC_PRIVATE_LINK_OPTIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
# External dependencies should be loaded here
|
||||
add_subdirectory(external/cpptrace)
|
||||
|
||||
# SDL is a dependency of the project, added as a git submodule
|
||||
set(SDL_STATIC 1)
|
||||
add_subdirectory(external/sdl)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_C_STANDARD 26)
|
||||
add_custom_target(fennec-dependencies
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Running dependencies."
|
||||
COMMENT "Running dependencies."
|
||||
)
|
||||
|
||||
# include scripts
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/version.cmake")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/platform.cmake")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/build.cmake")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/compiler.cmake")
|
||||
|
||||
# common defines
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS "NULL=0")
|
||||
|
||||
# find dependencies
|
||||
find_package(Doxygen)
|
||||
fennec_check_platform()
|
||||
|
||||
# any necessary include directories
|
||||
include_directories(include)
|
||||
include_directories(${FENNEC_SOURCE_DIR}/include)
|
||||
|
||||
# Metaprogramming is a dependency for generating various type info before compilation of the engine.
|
||||
add_subdirectory(metaprogramming)
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} FENNEC_BUILD_NAME)
|
||||
|
||||
message(STATUS "OS: ${CMAKE_SYSTEM_NAME}")
|
||||
message(STATUS "Build: ${FENNEC_BUILD_NAME}")
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
||||
|
||||
add_library(fennec STATIC
|
||||
# Specify where to send libraries and executables
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
||||
|
||||
# Core Files
|
||||
fennec_add_sources(
|
||||
# CORE =================================================================================================================
|
||||
include/fennec/core/engine.h source/core/engine.cpp
|
||||
include/fennec/core/event.h source/core/event.cpp
|
||||
include/fennec/core/logger.h source/core/logger.cpp
|
||||
|
||||
include/fennec/core/version.h
|
||||
include/fennec/core/system.h
|
||||
|
||||
|
||||
|
||||
# SCENE ================================================================================================================
|
||||
include/fennec/scene/scene.h source/scene/scene.cpp
|
||||
include/fennec/scene/component.h
|
||||
include/fennec/scene/scene_node.h
|
||||
include/fennec/scene/node2d.h
|
||||
include/fennec/scene/forward.h
|
||||
|
||||
|
||||
|
||||
|
||||
# RENDERERS ============================================================================================================
|
||||
include/fennec/renderers/interface/forward.h
|
||||
|
||||
include/fennec/renderers/interface/gfxcontext.h
|
||||
include/fennec/renderers/interface/gfxsurface.h
|
||||
|
||||
|
||||
|
||||
# CONTAINERS ===========================================================================================================
|
||||
include/fennec/containers/containers.h
|
||||
|
||||
include/fennec/containers/array.h
|
||||
include/fennec/containers/bintree.h
|
||||
include/fennec/containers/bitfield.h
|
||||
include/fennec/containers/deque.h
|
||||
include/fennec/containers/dynarray.h
|
||||
include/fennec/containers/generic.h
|
||||
include/fennec/containers/graph.h
|
||||
include/fennec/containers/initializer_list.h
|
||||
include/fennec/containers/list.h
|
||||
include/fennec/containers/map.h
|
||||
include/fennec/containers/object_pool.h
|
||||
include/fennec/containers/optional.h
|
||||
include/fennec/containers/pair.h
|
||||
include/fennec/containers/priority_queue.h
|
||||
include/fennec/containers/rdtree.h
|
||||
include/fennec/containers/sequence.h
|
||||
include/fennec/containers/set.h
|
||||
include/fennec/containers/traversal.h
|
||||
include/fennec/containers/tuple.h
|
||||
include/fennec/containers/variant.h
|
||||
|
||||
|
||||
# LANG =================================================================================================================
|
||||
include/fennec/containers/detail/_tuple.h
|
||||
|
||||
|
||||
|
||||
# lang =================================================================================================================
|
||||
include/fennec/lang/lang.h
|
||||
include/fennec/lang/metaprogramming.h
|
||||
|
||||
|
||||
include/fennec/lang/bits.h
|
||||
include/fennec/lang/constants.h
|
||||
include/fennec/lang/conditional_types.h
|
||||
include/fennec/lang/declval.h
|
||||
include/fennec/lang/function.h
|
||||
include/fennec/lang/hashing.h
|
||||
include/fennec/lang/intrinsics.h
|
||||
include/fennec/lang/limits.h
|
||||
include/fennec/lang/numeric_transforms.h
|
||||
include/fennec/lang/sequences.h
|
||||
include/fennec/lang/metasequences.h
|
||||
include/fennec/lang/ranges.h
|
||||
include/fennec/lang/static_constructor.h
|
||||
include/fennec/lang/type_identity.h
|
||||
include/fennec/lang/type_operators.h
|
||||
include/fennec/lang/type_sequences.h
|
||||
include/fennec/lang/type_traits.h
|
||||
include/fennec/lang/type_transforms.h
|
||||
include/fennec/lang/types.h
|
||||
include/fennec/lang/utility.h
|
||||
include/fennec/lang/type_sequences.h
|
||||
include/fennec/lang/integer.h
|
||||
|
||||
include/fennec/lang/detail/__bits.h
|
||||
include/fennec/lang/detail/__numeric_transforms.h
|
||||
include/fennec/lang/detail/__type_traits.h
|
||||
include/fennec/lang/detail/__type_sequences.h
|
||||
|
||||
include/fennec/lang/assert.h source/lang/assert.cpp
|
||||
|
||||
include/fennec/lang/detail/_bits.h
|
||||
include/fennec/lang/detail/_declval.h
|
||||
include/fennec/lang/detail/_function.h
|
||||
include/fennec/lang/detail/_int.h
|
||||
include/fennec/lang/detail/_numeric_transforms.h
|
||||
include/fennec/lang/detail/_stdlib.h
|
||||
include/fennec/lang/detail/_type_traits.h
|
||||
include/fennec/lang/detail/_type_transforms.h
|
||||
include/fennec/lang/detail/_type_sequences.h
|
||||
|
||||
|
||||
# RTTI =================================================================================================================
|
||||
include/fennec/rtti/typeid.h
|
||||
include/fennec/rtti/type_data.h
|
||||
include/fennec/rtti/type.h
|
||||
include/fennec/rtti/enable.h
|
||||
include/fennec/rtti/forward.h
|
||||
include/fennec/rtti/typelist.h
|
||||
include/fennec/rtti/type_registry.h
|
||||
include/fennec/rtti/singleton.h
|
||||
|
||||
|
||||
include/fennec/rtti/detail/_constants.h
|
||||
include/fennec/rtti/detail/_this_t.h
|
||||
include/fennec/rtti/detail/_typeid.h
|
||||
include/fennec/rtti/detail/_type_name.h
|
||||
|
||||
|
||||
|
||||
# MEMORY ===============================================================================================================
|
||||
include/fennec/memory/new.h source/memory/new.cpp
|
||||
|
||||
include/fennec/memory/allocator.h
|
||||
include/fennec/memory/bytes.h
|
||||
include/fennec/memory/common.h
|
||||
include/fennec/memory/memory.h
|
||||
include/fennec/memory/pointers.h
|
||||
include/fennec/memory/ptr_traits.h
|
||||
include/fennec/memory/pointer_traits.h
|
||||
|
||||
include/fennec/memory/detail/_ptr_traits.h
|
||||
|
||||
|
||||
|
||||
# DEBUG ================================================================================================================
|
||||
source/debug/assert_impl.cpp
|
||||
|
||||
include/fennec/memory/detail/__ptr_traits.h
|
||||
|
||||
|
||||
# MATH =================================================================================================================
|
||||
@@ -97,37 +236,111 @@ add_library(fennec STATIC
|
||||
include/fennec/math/trigonometric.h
|
||||
include/fennec/math/relational.h
|
||||
|
||||
include/fennec/math/detail/__fwd.h
|
||||
include/fennec/math/detail/__types.h
|
||||
include/fennec/math/detail/__vector_traits.h
|
||||
include/fennec/math/ext/common.h
|
||||
include/fennec/math/ext/constants.h
|
||||
include/fennec/math/ext/primes.h
|
||||
include/fennec/math/ext/quaternion.h
|
||||
include/fennec/math/ext/rect.h
|
||||
include/fennec/math/ext/transform.h
|
||||
|
||||
|
||||
include/fennec/math/detail/_forward.h
|
||||
include/fennec/math/detail/_math.h
|
||||
include/fennec/math/detail/_matrix.h
|
||||
include/fennec/math/detail/_types.h
|
||||
include/fennec/math/detail/_vector_traits.h
|
||||
|
||||
|
||||
|
||||
# threading ============================================================================================================
|
||||
|
||||
include/fennec/threading/atomic.h
|
||||
include/fennec/threading/lock_guard.h
|
||||
include/fennec/threading/mpscq.h
|
||||
include/fennec/threading/mutex.h
|
||||
include/fennec/threading/thread.h
|
||||
|
||||
include/fennec/threading/detail/_thread.h
|
||||
|
||||
|
||||
|
||||
# string ===============================================================================================================
|
||||
include/fennec/string/locale.h
|
||||
include/fennec/string/cstring.h
|
||||
include/fennec/string/string.h
|
||||
|
||||
include/fennec/string/detail/_ctype.h
|
||||
|
||||
|
||||
|
||||
# format ===============================================================================================================
|
||||
include/fennec/format/format.h
|
||||
include/fennec/format/format_arg.h
|
||||
include/fennec/format/formatter.h
|
||||
include/fennec/format/charconv.h
|
||||
|
||||
include/fennec/format/detail/_format.h
|
||||
|
||||
source/format/charconv.cpp
|
||||
|
||||
|
||||
|
||||
# filesystem ===========================================================================================================
|
||||
include/fennec/filesystem/file.h source/filesystem/file.cpp
|
||||
include/fennec/filesystem/path.h source/filesystem/path.cpp
|
||||
|
||||
|
||||
|
||||
# interpret ============================================================================================================
|
||||
include/fennec/interpret/tokenizer.h
|
||||
|
||||
|
||||
|
||||
# PLATFORM =============================================================================================================
|
||||
include/fennec/platform/interface/forward.h
|
||||
include/fennec/platform/interface/display_server.h
|
||||
include/fennec/platform/interface/platform.h source/platform/interface/platform.cpp
|
||||
include/fennec/platform/interface/window.h source/platform/interface/window.cpp
|
||||
include/fennec/platform/window_manager.h source/platform/window_manager.cpp
|
||||
|
||||
|
||||
|
||||
# GRAPHICS =============================================================================================================
|
||||
|
||||
source/debug/assert_impl.cpp
|
||||
)
|
||||
|
||||
# add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled
|
||||
if(DOXYGEN_FOUND)
|
||||
add_dependencies(fennec fennecdocs metaprogramming SDL3-static)
|
||||
else()
|
||||
add_dependencies(fennec metaprogramming SDL3-static)
|
||||
endif()
|
||||
|
||||
# Compiler Warning Flags
|
||||
if(MSVC)
|
||||
add_compile_options("/W4" "/WX") # All MSVC Warnings throw as Errors
|
||||
else()
|
||||
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror") # All gcc/etc. Warnings throw as errors
|
||||
endif()
|
||||
|
||||
#target_compile_options(fennec PUBLIC "-mavx" "-mavx2" "-mavx512f") # SIMD Instructions, currently unused
|
||||
|
||||
target_link_options(fennec PRIVATE "-nostdlib") # Do not compile base fennec library with c++ stdlib
|
||||
# fennec does not use the C++ stdlib because it is bloated, difficult to read, and implementation defined.
|
||||
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
||||
|
||||
|
||||
# add the test suite as a sub-project
|
||||
add_subdirectory(test)
|
||||
|
||||
add_library(fennec STATIC
|
||||
${FENNEC_SOURCES}
|
||||
include/fennec/rtti/this_t.h
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming fennec-dependencies)
|
||||
|
||||
target_compile_definitions(fennec PUBLIC ${FENNEC_COMPILE_DEFINITIONS})
|
||||
target_compile_options(fennec PRIVATE ${FENNEC_PRIVATE_COMPILE_OPTIONS})
|
||||
|
||||
# Do not compile base fennec library with c++ stdlib
|
||||
# fennec does not use the C++ stdlib because it is bloated, difficult to read, and implementation defined.
|
||||
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
||||
target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_OPTIONS})
|
||||
|
||||
|
||||
|
||||
target_link_libraries(fennec PRIVATE
|
||||
${FENNEC_LINK_LIBRARIES}
|
||||
${FENNEC_SHARED_LIBRARIES}
|
||||
|
||||
cpptrace::cpptrace
|
||||
)
|
||||
|
||||
foreach (_LIB IN ITEMS ${FENNEC_SHARED_LIBRARIES})
|
||||
add_custom_command(TARGET fennec POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_LINKER_FILE:${_LIB}> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
)
|
||||
endforeach ()
|
||||
|
||||
|
||||
# DOXYGEN ==============================================================================================================
|
||||
@@ -136,10 +349,12 @@ add_subdirectory(test)
|
||||
file(COPY logo DESTINATION docs/logo)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
set(DOXY_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/docs")
|
||||
get_filename_component(DOXYGEN_PROJECT_NAME ${PROJECT_SOURCE_DIR} NAME) # Set Doxy Project name to the name of the root dir
|
||||
set(DOXYGEN_CONFIG_IN "${PROJECT_SOURCE_DIR}/doxy/Doxyfile.in") # Input config file with preprocessor arguments
|
||||
set(DOXYGEN_CONFIG_OUT "${PROJECT_SOURCE_DIR}/doxy/Doxyfile") # Generated config file from input
|
||||
add_dependencies(fennec fennecdocs)
|
||||
set(DOXY_OUTPUT_DIR "${FENNEC_SOURCE_DIR}/docs")
|
||||
set(DOXY_EXAMPLES_DIR "${FENNEC_SOURCE_DIR}/examples")
|
||||
get_filename_component(DOXYGEN_PROJECT_NAME ${FENNEC_SOURCE_DIR} NAME) # Set Doxy Project name to the name of the root dir
|
||||
set(DOXYGEN_CONFIG_IN "${FENNEC_SOURCE_DIR}/doxy/Doxyfile.in") # Input config file with preprocessor arguments
|
||||
set(DOXYGEN_CONFIG_OUT "${FENNEC_SOURCE_DIR}/doxy/Doxyfile.out") # Generated config file from input
|
||||
|
||||
configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY) # Execute preprocessing step
|
||||
message("Doxygen Build Started.")
|
||||
@@ -148,8 +363,8 @@ if(DOXYGEN_FOUND)
|
||||
# Target for building docs
|
||||
add_custom_target(fennecdocs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/logo/raster.png
|
||||
WORKING_DIRECTORY ${FENNEC_SOURCE_DIR}/include/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${FENNEC_SOURCE_DIR}/logo/raster.png
|
||||
${DOXY_OUTPUT_DIR}/logo/raster.png
|
||||
COMMENT "Generating Doxygen Documentation"
|
||||
VERBATIM)
|
||||
@@ -158,7 +373,7 @@ if(DOXYGEN_FOUND)
|
||||
|
||||
# Target for cleaning docs
|
||||
add_custom_target(fennecdocs-clean ALL
|
||||
COMMAND rm -r "${PROJECT_SOURCE_DIR}/docs/"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f "${FENNEC_SOURCE_DIR}/docs"
|
||||
COMMENT "Cleaning Doxygen Documentation"
|
||||
VERBATIM)
|
||||
else()
|
||||
|
||||
526
PLANNING.md
526
PLANNING.md
@@ -1,526 +0,0 @@
|
||||
|
||||
|
||||
# Planning Documentation for fennec
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [TODO](#todo)
|
||||
3. [C++ Language](#c-language-library-lang)
|
||||
4. [Math Library](#math-library-math)
|
||||
5. [Memory Library](#memory-library-memory)
|
||||
6. [Containers Library](#containers-library-containers)
|
||||
7. [Format Processing](#format-processing-fproc)
|
||||
8. [Core](#core-core)
|
||||
1. [Tick](#tick)
|
||||
2. [Frame](#frame)
|
||||
9. [Application Layer](#application-layer-app)
|
||||
10. [Scene](#scene-scene)
|
||||
11. [2D Graphics](#2d-graphics-gfx2d)
|
||||
12. [3D Graphics](#3d-graphics-gfx3d)
|
||||
1. [Structures](#structures-gfx3d)
|
||||
2. [Stages](#stages-gfx3d)
|
||||
13. [3D Physics](#3d-physics-physics3d)
|
||||
14. [Artificial Intelligence](#artificial-intelligence-ai)
|
||||
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
This file serves as a general planning document for engine structure, systems, pipelines, and implementation.
|
||||
|
||||
|
||||
Implementations of core engine systems should strive to be `O(1)` in implementations,
|
||||
both in terms of runtime and memory performance. This is obviously not a realistic goal,
|
||||
so rather than the goal requiring the entire engine to be `O(1)`, we should more specifically look
|
||||
at achieving `O(1)` performance on hot paths. I distinctly use 'strive' and 'goal' as different concepts, where designs
|
||||
should *strive* to accommodate function implementations for `O(1)`, however the specifics of the implementation might not always
|
||||
be able to achieve that, so the end *goal* is that hot paths should be `O(1)`.
|
||||
|
||||
Functions should be highly verbose and any bugprone or erroneous behaviour should throw
|
||||
assertions. **DO NOT USE EXCEPTIONS**.
|
||||
|
||||
System implementations should be independent of architecture or platforms. i.e. the code of the graphics system should
|
||||
not care if OpenGL or Vulkan is used and should not use any direct calls to OpenGL or Vulkan.
|
||||
|
||||
The engine should not care about the types of objects loaded from a so/dll. In fact, most of the code should
|
||||
be type independent. Any shared information among a collection of objects should be held either implicitly or explicitly
|
||||
in the super-class. It will be the responsibility of the linked code to initialize and cleanup the objects related to it.
|
||||
This principle should extend to the submodules of the engine.
|
||||
|
||||
It is also best to avoid objects having behaviour that is not defined by the system they are in. There are some exceptions
|
||||
in extensions or mods and should be given configurability and programmability within those systems and their stages.
|
||||
This however can be achieved using events at different stages of those engines that are on-demand.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- 2D Graphics (`gfx2d`)
|
||||
- 2D Physics (`physics2d`)
|
||||
- 2D & 3D Audio (`audio`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## C++ Language Library (`lang`)
|
||||
|
||||
Implement header files for standard functions relating to the C++ Language.
|
||||
So far this is implemented on an as-needed basis. A full implementation should be worked on continuously.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Math Library (`math`)
|
||||
|
||||
Implement math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf).
|
||||
|
||||
"Extensions" has a different meaning here. Extensions for the math library are any functions that are not defined within
|
||||
the Specification.
|
||||
|
||||
Additional extensions should be implemented to provide standard definitions for functions predominantly related
|
||||
to Linear Algebra, Mathematical Analysis, and more specifically Discrete Analysis. Additional extensions will be
|
||||
implemented on an as-needed basis.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Memory Library (`memory`)
|
||||
|
||||
Implement headers related to memory allocation in C++.
|
||||
|
||||
* Smart Pointers
|
||||
* Unique Pointer
|
||||
* Shared Pointer
|
||||
|
||||
- Memory Allocation
|
||||
- Allocation
|
||||
-
|
||||
|
||||
|
||||
|
||||
|
||||
## Containers Library (`containers`)
|
||||
|
||||
All containers of the [C++ Standard Library](https://cppreference.com/w/cpp/container.html) should be implemented.
|
||||
|
||||
Here are essential data-structures not specified in the C++ stdlib:
|
||||
- Graph → AI `graph`
|
||||
- Necessary for 2D and 3D navigation.
|
||||
- Rooted Directed Tree → Scene `rd_tree`
|
||||
- Defines the scene structure.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Format Processing (`fproc`)
|
||||
|
||||
fennec should be able to use Doxygen and LaTeX externally. Consider including binaries with releases.
|
||||
|
||||
* String Analysis (`fproc/strings`)
|
||||
* Search
|
||||
* Manipulation
|
||||
* Delimiting
|
||||
* Regex
|
||||
|
||||
- File Formats (`fproc/formats`)
|
||||
- Serialization
|
||||
- JSON
|
||||
- HTML
|
||||
- XML
|
||||
- YAML
|
||||
- Configuration
|
||||
- INI
|
||||
- TOML
|
||||
- Documents
|
||||
- ODF
|
||||
- Markdown
|
||||
- PDF
|
||||
- Spreadsheets & Tables
|
||||
- ODS
|
||||
- CSV
|
||||
- Graphics Formats
|
||||
- Textures
|
||||
- BMP
|
||||
- DDS
|
||||
- JPG
|
||||
- PNG
|
||||
- TIFF
|
||||
- Vectors
|
||||
- OTF
|
||||
- SVG
|
||||
- TTF
|
||||
- Models
|
||||
- FBX
|
||||
- Wavefront OBJ
|
||||
|
||||
**MAYBE**
|
||||
* Compilation (`fproc/code`)
|
||||
* Lexical Analysis
|
||||
* Syntax Analysis
|
||||
* Semantic Analysis
|
||||
* Intermediate Code Generation
|
||||
* Optimization
|
||||
* Target Code Generation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Core (`core`)
|
||||
|
||||
This will be the core of the engine.
|
||||
|
||||
- Event System
|
||||
- Most events will fire at the start of the next tick, especially those related to physics and input.
|
||||
- Events for graphics or audio should propagate immediately.
|
||||
- Events for stages should also propagate immediately, this is to support extensions and mods.
|
||||
- Core Engine Loop
|
||||
- System Manager
|
||||
- Ticks vs. Frames
|
||||
|
||||
The following systems are not essential to the core engine, but are instead major systems that should be defined
|
||||
in their operation order:
|
||||
|
||||
### Tick
|
||||
- **Update**
|
||||
- Events
|
||||
- Scripts
|
||||
- AI
|
||||
- **Physics**
|
||||
- Newtonian Commit
|
||||
- Apply Forces (Updates Acceleration and Torque)
|
||||
- Apply Torque & Acceleration (Updates Velocities)
|
||||
- Apply Velocities (Updates Position and Rotation)
|
||||
- Constraint Resolution
|
||||
- Collision Detection
|
||||
- Collision Resolution
|
||||
- Collision Response
|
||||
- Calculate Forces & Velocities
|
||||
- Queue events for next tick
|
||||
|
||||
|
||||
### Frame
|
||||
- **Physics**
|
||||
- Physics Interpolation
|
||||
- **Graphics**
|
||||
- [2D Graphics](#2d-graphics-gfx2d)
|
||||
- Generate 3D Mask
|
||||
- [3D Graphics](#3d-graphics-gfx3d)
|
||||
- **Audio**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Application Layer (`app`)
|
||||
|
||||
This is the core windowing system of fennec. The implementation will initially be an SDL3 wrapper.
|
||||
Custom implementation may be further down the roadmap, however this is extremely complicated and there are better
|
||||
implementations than I could write alone.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Scene (`scene`)
|
||||
|
||||
* In-Array Directed Tree
|
||||
* Elegant method for providing `O(1)` insertions and `O(log(n))` deletions.
|
||||
* Bounding Volume Hierarchy
|
||||
* Octree
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2D Graphics (`gfx2d`)
|
||||
|
||||
Links:
|
||||
- https://en.wikipedia.org/wiki/Quadtree
|
||||
- https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-25-rendering-vector-art-gpu
|
||||
|
||||
Object Structure. The mesh is implicit data.
|
||||
|
||||
|
||||
### Structures (`gfx2d`)
|
||||
|
||||
For the 2d rendering framework, Materials need to be rendered independently because we have
|
||||
no size constraints for images. This disallows us from using a meta-shader like in
|
||||
the 3d rendering framework.
|
||||
|
||||
```c++
|
||||
struct Object
|
||||
{
|
||||
vec2 location, scale; // A matrix would be 36 bytes, this is instead 20 bytes
|
||||
float rotation;
|
||||
}
|
||||
```
|
||||
|
||||
- BVH
|
||||
- Quadtree
|
||||
- Leaf Size and Tree Depth should be calculated by the scene, constraints are as follows:
|
||||
- Min Object Size
|
||||
- Max Object Size
|
||||
- Scene Center
|
||||
- Scene Edge
|
||||
- Insertions and Updates are done on the CPU
|
||||
- Nodes
|
||||
- Start Index 32-bits
|
||||
- Object Count 32-bits
|
||||
- Objects
|
||||
- Buffer of Object IDs grouped by Octree Node
|
||||
- Culling
|
||||
- Starting at each Octree Leaf, traverse upwards.
|
||||
- Insert Visible Leaf IDs
|
||||
- Track using atomic buffer
|
||||
- Generate the Command Buffer for Culled Meshes from the Visible Leaf Buffer
|
||||
- Count Materials
|
||||
- Count Meshes per Material
|
||||
- Generate the Culled Object Buffer by copying objects from the Object Buffer
|
||||
- Adjust Buffer Size using the counts
|
||||
- Insert using another atomic buffer
|
||||
|
||||
- Translucent objects will be sorted. We can cheat by using a z-index instead of a z-coordinate.
|
||||
This will allow us to sort objects as they are created. We can still bulk render each z-index,
|
||||
with meshes and objects being grouped by material.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3D Graphics (`gfx3d`)
|
||||
|
||||
Links:
|
||||
- https://en.wikipedia.org/wiki/Octree
|
||||
- https://www.adriancourreges.com/blog/2015/11/02/gta-v-graphics-study/
|
||||
- https://learnopengl.com/PBR/Lighting
|
||||
- https://learnopengl.com/PBR/IBL/Diffuse-irradiance
|
||||
- https://en.wikipedia.org/wiki/Schlick%27s_approximation
|
||||
- https://pixelandpoly.com/ior.html
|
||||
- https://developer.download.nvidia.com/SDK/10/opengl/screenshots/samples/dual_depth_peeling.html
|
||||
|
||||
**DirectX will never have official support.**
|
||||
If you would like to make a fork, have at it, but know that I will hold a deep disdain for you.
|
||||
|
||||
The graphics pipeline will have a buffer with a list of objects and their rendering data.
|
||||
This will be referred to as the Object Buffer. There will be two, for both the Deferred and Forward Passes.
|
||||
|
||||
The buffers will be optimized by scene prediction.
|
||||
This involves tracking the meshes and textures directly and indirectly used by a scene.
|
||||
A callback function in the graphics system for scene loading can do this.
|
||||
|
||||
|
||||
Materials and Lighting models will be run via a shader metaprogram to make the pipeline independent of this aspect.
|
||||
This allows the GPU to draw every single deferred rendered mesh in a single draw call for each stage of the renderer.
|
||||
|
||||
Specifications for debugging views via early breaks are included in the stages.
|
||||
|
||||
|
||||
|
||||
### Structures (`gfx3d`)
|
||||
|
||||
Object Structure. The mesh is implicit data.
|
||||
|
||||
```c++
|
||||
struct Object
|
||||
{
|
||||
vec3 location, scale; // A matrix would be 64 bytes, this is instead 28 bytes
|
||||
quat rotation;
|
||||
int material;
|
||||
}
|
||||
```
|
||||
|
||||
Textures for 3D rendering are stored in various buffers with sizes of powers of 2.
|
||||
Ratios of `1:1` and `2:1` are allowed. The `2:1` ratio is specifically for spherical and cylindrical projection.
|
||||
UVs may be transformed to use a `2:1` as if it were `1:2`.
|
||||
Cubemaps may only be `1:1`, I would be concerned if you are using any other ratio.
|
||||
|
||||
- 8-Bit R Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RG Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGB Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGBA Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGB Cubemap `1024, 512, 256, 128` (4)
|
||||
|
||||
* 16-Bit HDR RGB Texture `4096, 2048, 1024, 512` (8)
|
||||
* 16-Bit HDR RGBA Texture `4096, 2048, 1024, 512` (8)
|
||||
* 16-Bit HDR RGB Cubemap `1024, 512, 256, 128` (4)
|
||||
|
||||
- 16-Bit Shadow Texture `4096, 2048, 1024, 512` (8)
|
||||
- 16-Bit Shadow Cubemap `2048, 1024, 512, 256` (4)
|
||||
|
||||
Documentation should provide guidelines on categories of Art Assets and the resolution of textures to use.
|
||||
|
||||
Textures are identified by an 8-bit integer and 16-bit integer.
|
||||
- `int8` → the texture buffer
|
||||
- `int16` → the layer in the buffer
|
||||
|
||||
Artists should be informed on the texture structure of the engine and its limitations.
|
||||
However, these principles should be followed in other game engines as these are
|
||||
guided by what is most efficient for typical GPU hardware.
|
||||
|
||||
|
||||
Materials are, for the most part, user-defined. Documentation should make the user aware of this.
|
||||
Material buffers will be a sequence of the Material Struct instances.
|
||||
They will at the very least contain the id of their shader.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Stages (`gfx3d`)
|
||||
|
||||
This is the set of stages for the graphics pipeline that runs every frame:
|
||||
Unless otherwise specified, each stage will be run on the GPU.
|
||||
|
||||
- BVH
|
||||
- Octree `(8 Bpn, 64 bpn) [6-Layers ≈ 2.1MB]`
|
||||
- Leaf Size and Tree Depth should be calculated by the scene, constraints are as follows:
|
||||
- Min Object Size
|
||||
- Max Object Size
|
||||
- Scene Center
|
||||
- Scene Edge
|
||||
- Buffer has implicit locations due to the tree having 8 children.
|
||||
- Insertions and Updates are done on the CPU
|
||||
- Nodes
|
||||
- Start Index `int32`
|
||||
- Object Count `int32`
|
||||
- Objects
|
||||
- Buffer of Object IDs grouped by Octree Node
|
||||
- Leaf Culling
|
||||
- Starting at each Octree Leaf, traverse upwards.
|
||||
- Insert Visible Leaf IDs
|
||||
- Track using atomic buffer
|
||||
- Generate the Command Buffer for Culled Mesh LODs from the Visible Leaf Buffer
|
||||
- Track counts using atomic buffers
|
||||
- To avoid double counting due to the construction of the Octree output, we have some options
|
||||
- Ignore Leaf Instances based on occurrences of the mesh in the surrounding 8 Quadtree Leaves. This would require
|
||||
a bias towards a specific corner of the filter.
|
||||
- Perform a preprocessing step on the CPU to erase duplicate elements and fix the buffer continuity.
|
||||
- Let the duplicates be rendered.
|
||||
- Generate the Culled Object Buffer with the respective object IDs
|
||||
- Adjust Buffer Size using the counts
|
||||
- Insert by reusing the count buffer, clipped to only contain used meshes
|
||||
|
||||
Debug View: Object ID, Mesh ID, LOD
|
||||
|
||||
- Visibility
|
||||
- Buffer `(15 Bpp, 120 bpp) [1920x1080] ≈ 39.4MB`
|
||||
- Depth Buffer → `D24`
|
||||
- Visibility Info → `RGB32I`
|
||||
- R = Object ID
|
||||
- G = Mesh ID
|
||||
- B = Material ID
|
||||
- Regenerate the Command Buffer for Visible Mesh LODs
|
||||
- Regenerate the Culled Object Buffer
|
||||
|
||||
Debug View: Visibility Buffer
|
||||
|
||||
* G-Buffer Pass `(17 Bpp, 136 bpp) [1920x1080] ≈ 35.3MB`
|
||||
* Depth - Stencil → `D24_S8`
|
||||
* S → used to represent the lighting model.
|
||||
* Diffuse → `RGBA8`
|
||||
* A → Ambient Occlusion
|
||||
* Emission → `RGB8`
|
||||
* Normal → `RGB8`
|
||||
* Specular → `RGB8`
|
||||
* R → Roughness
|
||||
* G → Specularity (sometimes called the Metallicness)
|
||||
* B → Index of Refraction (IOR)
|
||||
|
||||
Debug View: Depth, Stencil, Diffuse, Emission, Normal, Specularity
|
||||
|
||||
- Deferred Lighting Pass `(10 Bpp, 80 bpp) [1920x1080] ≈ 2 x 16.3MB + 8.3MB ≈ 24.6MB`
|
||||
- Depth Buffer → `D24`
|
||||
- Lighting Buffer → `RGB16` (w/ Mipmapping when Bloom or DoF are enabled)
|
||||
- Stencil Buffer $rarr; `S8`
|
||||
- Generate Dynamic Shadows
|
||||
- Generate Dynamic Reflections (Optional)
|
||||
- SSAO (Optional)
|
||||
- Apply Lighting Model
|
||||
|
||||
Debug View: Shadows, Reflections, SSAO, Deferred Lighting
|
||||
|
||||
* Forward Pass
|
||||
* BVH, Same as Above
|
||||
* LOD Selection, Same as Above
|
||||
* Translucent Materials
|
||||
* Dual Depth Peeling
|
||||
|
||||
Debug View: Forward Mask
|
||||
|
||||
- Post Processing
|
||||
- Depth of Field (Optional)
|
||||
- When enabled, the Visiblity Buffer, G-Buffer, and Deferred Lighting Pass will be double layered.
|
||||
- At this point the Lighting Buffers will be Flattened
|
||||
- Bloom (Optional) → Mipmap Blurring `(6Bpp, 48bpp) [1920x1080] ≈ 16.3MB`
|
||||
- Tonemapping (Optional)
|
||||
- HDR Correction
|
||||
|
||||
|
||||
|
||||
|
||||
## 3D Physics `(physics3d)`
|
||||
|
||||
Links:
|
||||
- https://www.researchgate.net/publication/264839743_Simulating_Ocean_Water
|
||||
- https://arxiv.org/pdf/2109.00104
|
||||
- https://www.youtube.com/watch?v=rSKMYc1CQHE
|
||||
- https://tflsguoyu.github.io/webpage/pdf/2013ICIA.pdf
|
||||
- https://animation.rwth-aachen.de/publication/0557/
|
||||
- https://github.com/InteractiveComputerGraphics/PositionBasedDynamics?tab=readme-ov-file
|
||||
- https://www.cs.umd.edu/class/fall2019/cmsc828X/LEC/PBD.pdf
|
||||
|
||||
Systems
|
||||
|
||||
* Rigid Body Physics
|
||||
* Newtonian Physics and Collision Resolution
|
||||
* Articulated Skeletal Systems
|
||||
* Inverse Kinematics
|
||||
* Stiff Rods
|
||||
- Particle Physics
|
||||
* Soft Body Physics
|
||||
* Elastics → Finite Element Simulation
|
||||
* Cloth → Position-Based Dynamics
|
||||
* Water
|
||||
* Oceans → iWave
|
||||
* Reasoning: iWave provides interactive lightweight fluid dynamics suitable for flat planes of water.
|
||||
<br><br>
|
||||
|
||||
* 3D Fluid Dynamics → Smoothed-Particle Hydrodynamics
|
||||
* Reasoning: This is the simplest method for simulating 3D bodies of water. This should exclusively be
|
||||
used for small scale simulations where self-interactive fluids are necessary. I.E. pouring water into
|
||||
a glass.
|
||||
<br><br>
|
||||
|
||||
* 2D Fluid Dynamics → Force-Based Dynamics
|
||||
* Reasoning: This model, like iWave, provides lightweight interactive fluid dynamics, but is more easily
|
||||
adapted to flowing surfaces such as streams and rivers.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Artificial Intelligence (`ai`)
|
||||
|
||||
This artificial intelligence method only differs in static generation between 2D and 3D.
|
||||
The solvers are dimension independent since they work on a graph.
|
||||
|
||||
The general process is;
|
||||
|
||||
Static:
|
||||
- generate a static navigation graph (sometimes called a NavMesh)
|
||||
|
||||
Update:
|
||||
* resolve dynamic blockers
|
||||
* update paths using dijkstra's algorithm
|
||||
* apply rigid-body forces with constraints
|
||||
|
||||
The update loop for artificial intelligence should only update every `n` ticks. Where `n <= k`, with `k` being the
|
||||
tick rate of the physics engine.
|
||||
16
READINGS.md
Normal file
16
READINGS.md
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
|
||||
# Readings
|
||||
|
||||
Here is a list of relevant books and articles on various concepts related to
|
||||
developing a game engine and its subsystems.
|
||||
|
||||
- Game Engine Architecture, Ed. 3 – Jason Gregory
|
||||
- https://www.gameenginebook.com/
|
||||
- OpenGL 4 Shading Language Cookbook, Ed. 3 – David A. Wolff
|
||||
- https://www.packtpub.com/en-us/product/opengl-4-shading-language-cookbook-9781789340662
|
||||
- Design Patterns: Elements of Reusable Object-Oriented Software – Erich Gamma, Richard Helm, Ralph Johnson, John Vilssides
|
||||
- https://www.oreilly.com/library/view/design-patterns-elements/0201633612/
|
||||
- Head First Design Patterns – Eric FReeman, Elisabeth Robson, Bert Bates, Kathy Sierra
|
||||
- https://www.oreilly.com/library/view/head-first-design/0596007124/
|
||||
298
README.md
298
README.md
@@ -8,17 +8,24 @@
|
||||
|
||||
<br><br>
|
||||
|
||||
## Table of Contents
|
||||
<a id="table-of-contents"></a>
|
||||
<h2>Table of Contents</h2>
|
||||
|
||||
* [Table of Contents](#table-of-contents)
|
||||
* [Introduction](#introduction)
|
||||
* [Coding Standards](#coding-standards)
|
||||
* [Building from Source](#building-from-source)
|
||||
* [Building from Terminal](#building-from-terminal)
|
||||
* [Git](#git) → `git`
|
||||
* [Debian](#debian) → `apt`
|
||||
* [Arch](#arch) → `pacman`
|
||||
* [Fedora](#fedora) → `dnf`
|
||||
* [Building on Windows](#building-on-windows)
|
||||
* [Running the Test Suite](#running-the-test-suite)
|
||||
* [Usage](#usage)
|
||||
* [Licensing](#licensing)
|
||||
* [Contribution](#contribution)
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
1. [Coding Standards](#coding-standards)
|
||||
2. [Building from Source](#building-from-source)
|
||||
1. [Building from Terminal](#building-from-terminal)
|
||||
2. [Building on Windows](#building-on-windows)
|
||||
3. [Running the Test Suite](#running-the-test-suite)
|
||||
4. [Usage](#usage)
|
||||
5. [Contribution](#contribution)
|
||||
6. [Documentation](./documentation.html)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -27,61 +34,84 @@
|
||||
<a id="introduction"></a>
|
||||
## Introduction
|
||||
|
||||
  fennec is designed to be a general purpose, educational game engine. fennec
|
||||
may be used through the provided editor application, or as a standalone library to
|
||||
  fennec is designed to be a general purpose, educational game engine. fennec
|
||||
may be used through the provided editor application, or as a standalone library to
|
||||
link against your application.
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
<a id="coding-standards"></a>
|
||||
### Coding Standards
|
||||
|
||||
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
Some main areas where the engine strays from the GNU standard includes the following:
|
||||
|
||||
* [Section 4.7, Standards for Graphical Interfaces](https://www.gnu.org/prep/standards/html_node/Graphical-Interfaces.html).
|
||||
fennec provides an implementation for X11, however it does not use the GTK toolkit.
|
||||
- [Section 6.1, GNU Manuals](https://www.gnu.org/prep/standards/html_node/GNU-Manuals.html)
|
||||
- [Section 5.1, Formatting Your Source Code](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
fennec uses Allman (BSD) for namespaces, otherwise K&R.
|
||||
* [Section 6.1, GNU Manuals](https://www.gnu.org/prep/standards/html_node/GNU-Manuals.html)
|
||||
fennec does not use Texinfo and instead uses Doxygen. Otherwise, it follows the other standards of this section.
|
||||
* [Section 7, The Release Process](https://www.gnu.org/prep/standards/html_node/Managing-Releases.html)
|
||||
fennec follows most of the conventions in this section, however the build system used is CMake and not
|
||||
- [Section 7, The Release Process](https://www.gnu.org/prep/standards/html_node/Managing-Releases.html)
|
||||
fennec follows most of the conventions in this section, however the build system used is CMake and not
|
||||
Makefile. CMake, although overwhelming at first, is much more friendly to those who are learning build systems for the first time.
|
||||
|
||||
<br>
|
||||
|
||||
fennec Standards:
|
||||
|
||||
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by `<APP_NAME>_`.
|
||||
Macros that wrap C-Style functions may use normal `snake_case`.
|
||||
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
|
||||
`<APP_NAME>_`. Macros that wrap C-Style functions may use normal `snake_case`.
|
||||
|
||||
- Header Guards should be implemented using `#ifndef`, `#define`, and `#endif` for portability.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`.
|
||||
I.E. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
- Header Guards should be implemented using `#ifndef`, `#define`, and `#endif` for portability.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`,
|
||||
e.g. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
|
||||
* Helper Functions, in the case of classes, should be private.
|
||||
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
||||
* Helper Functions, in the case of classes, should be private.
|
||||
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
||||
Doxygen documentation.
|
||||
|
||||
- **DO NOT USE C++ EXCEPTIONS** they will not be supported because they are shit. No, I won't elaborate.[[1]](#f1)
|
||||
- **DO NOT USE C++ EXCEPTIONS** they will not be supported because they are shit.<sup>[[1]](#f1)</sup>
|
||||
|
||||
* Most behaviours should be type independent. Specifically interactions with the core systems of the engine.
|
||||
|
||||
- Classes should have banners between each distinct group of definitions, with the access specifier on the following
|
||||
line. E.G.
|
||||
```c++
|
||||
class foo {
|
||||
// Definitions ============================
|
||||
public:
|
||||
using bar = int;
|
||||
|
||||
// Constructors & Destructor ==============
|
||||
public:
|
||||
foo();
|
||||
~foo();
|
||||
};
|
||||
```
|
||||
This helps significantly with readability, and it also serves to reduce bugs related to mistakes involving access
|
||||
specifiers. E.G. in the example above, bar is changed to private, the constructors are still labeled public in the
|
||||
case the person forgets to update the succeeding access specifiers.
|
||||
|
||||
<br><br>
|
||||
|
||||
<a id="f1"></a>
|
||||
[1] Okay, I will elaborate. If we were to use the exception paradigm for all erroneous behaviour, we couldn't
|
||||
guarantee that the state will not be corrupted when an exception is thrown. The behaviour afterward is
|
||||
undefined because of this, and we also don't really know when, how, or where that exception will be handled.
|
||||
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the code and how
|
||||
it is handled. In a debug build we can immediately halt the program, we don't care about the state afterward, only
|
||||
beforehand. Now for a release build, this is first and foremost a game engine, so we want to crash as gracefully as
|
||||
possible, prevent data loss, and get some debug information for it. fennec defines its own `assert` macro to
|
||||
be used, defining a hook in the private version of the function. This hook is used to clean up any state information
|
||||
within the engine and may be used to send immediate events to listeners so that outside functionality may decide
|
||||
how to handle the impending crash. There is nothing that can be done to stop the crash, as soon as the branch
|
||||
finishes, `abort()` will be called.
|
||||
|
||||
<sup>[[1]](#f1)</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
|
||||
that the state will not be corrupted when an exception is thrown. The behaviour afterward is undefined
|
||||
because of this, and we also don't really know when, how, or where that exception will be handled.
|
||||
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
|
||||
code and how it is handled. In a debug build we can immediately halt the program, we don't care about
|
||||
the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
|
||||
so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
|
||||
fennec defines its own `assert` macro to be used, defining a hook in the private version of the
|
||||
function. This hook is used to clean up any state information within the engine and may be used to send
|
||||
immediate events to listeners so that outside functionality may decide how to handle the impending crash.
|
||||
In Debug Mode there is nothing that can be done to stop the crash, as soon as the branch finishes,
|
||||
`abort()` will be called.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
@@ -101,11 +131,11 @@ There are a few reasons for this:
|
||||
<a id="building-from-source"></a>
|
||||
## Building from Source
|
||||
|
||||
  fennec uses the CMake build manager. The CMake build script provides several
|
||||
  fennec uses the CMake build manager. The CMake build script provides several
|
||||
targets for building parts of the engine.
|
||||
|
||||
  Using an IDE will streamline the build process for you and add additional configuration
|
||||
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
||||
  Using an IDE will streamline the build process for you and add additional configuration
|
||||
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
||||
is also a viable IDE but involves some extra setup.
|
||||
|
||||
| Target | Description |
|
||||
@@ -116,16 +146,24 @@ is also a viable IDE but involves some extra setup.
|
||||
| fennecdocs-clean | Cleans the generated html documentation files. |
|
||||
| fennec-test | Test suite for verifying engine functionality. |
|
||||
|
||||
<a id="dependencies"></a>
|
||||
<br>
|
||||
|
||||
| Dependency | Notes |
|
||||
|-------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| C/C++ Compiler | GCC/G++ is the compiler that fennec is designed around, however, Clang, MSVC, and MinGW may also be used |
|
||||
| CMake | The build manager used by the engine |
|
||||
| A build system | Any build system will work, however, `build.sh` uses Ninja by default. |
|
||||
| A memory debugger | Any memory debugger will work, however, `test.sh` uses Valgrind by default. |
|
||||
| Doxygen | Doxygen is required for building the documentation for fennec. This is an optional dependency |
|
||||
| Graphviz | Graphviz is a required dependency for Doxygen |
|
||||
<a id="dependencies"></a>
|
||||
### Dependencies
|
||||
|
||||
| Dependency | Notes |
|
||||
|------------------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| C/C++ Compiler | GCC/G++ is the compiler that fennec is designed around, however, Clang, MSVC, and MinGW may also be used |
|
||||
| CMake | The build manager used by the engine |
|
||||
| Volk<sup>[*](#opt)</sup> | The Vulkan loader Volk, includes necessary headers for Vulkan. |
|
||||
| A build system | Any build system will work, however, `build.sh` uses Ninja by default. |
|
||||
| A memory debugger | Any memory debugger will work, however, `test.sh` uses Valgrind by default. |
|
||||
| Doxygen<sup>[*](#opt)</sup> | Doxygen is required for building the documentation for fennec. |
|
||||
| Graphviz<sup>[*](#opt)</sup> | Graphviz is a required dependency for Doxygen |
|
||||
|
||||
|
||||
<a id="opt"></a>
|
||||
<sup>[*](#opt)</sup> Optional Dependency
|
||||
|
||||
<br>
|
||||
|
||||
@@ -135,19 +173,70 @@ is also a viable IDE but involves some extra setup.
|
||||
`build.sh` provides profiles for building the main engine. Run `./build.sh --help`
|
||||
for more info.
|
||||
|
||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
||||
build scripts to use another build manager, see the [CMake documentation for available generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
|
||||
|
||||
  I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
|
||||
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
|
||||
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
|
||||
|
||||
<a id="git"></a>
|
||||
#### Git
|
||||
|
||||
Install git dependencies with the following command:
|
||||
```shell
|
||||
git submodule update --force --init --recursive --remote
|
||||
```
|
||||
|
||||
<a id="debian"></a>
|
||||
#### Debian
|
||||
|
||||
On Debian-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo apt install build-essential cmake ninja-build valgrind libvulkan-volk-dev vulkan-validationlayers
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo apt install doxygen graphviz
|
||||
```
|
||||
|
||||
<a id="arch"></a>
|
||||
#### Arch
|
||||
|
||||
On Arch-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo pacman -S base-devel cmake ninja valgrind vulkan-devel
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo pacman -S doxygen graphviz
|
||||
```
|
||||
|
||||
<a id="fedora"></a>
|
||||
#### Fedora
|
||||
|
||||
On Fedora-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo dnf install build-essential g++ cmake ninja-build valgrind vulkan-volk-devel vulkan-validation-layers
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo dnf install doxygen graphviz
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<a id="building-on-windows"></a>
|
||||
### Building on Windows
|
||||
|
||||
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
||||
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
||||
the build [dependencies](#dependencies) installed and configured to be available on the `PATH` environment variable.
|
||||
|
||||
Fore more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
For more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
|
||||
Otherwise, follow the sequence of commands provided in the bash script.
|
||||
|
||||
@@ -170,8 +259,8 @@ The value of `<profile>` may be one of the following:
|
||||
|
||||
<br>
|
||||
|
||||
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
||||
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
||||
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||
of the CMake docs. Running the following command will generate the Visual Studio project.
|
||||
|
||||
```commandline
|
||||
@@ -186,9 +275,9 @@ cmake -G "Visual Studio 17 2022" -A x64
|
||||
|
||||
`test.sh` provides profiles for building the test suite and executes them.
|
||||
|
||||
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
||||
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
||||
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
||||
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
||||
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
||||
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
||||
information displayed is correct.
|
||||
|
||||
<br>
|
||||
@@ -197,6 +286,95 @@ information displayed is correct.
|
||||
<a id="usage"></a>
|
||||
## Usage
|
||||
|
||||
|
||||
<a id="licensing"></a>
|
||||
### Licensing
|
||||
|
||||
The following content of this section is not legal advice, nor is it legally binding, and nor does it change the terms
|
||||
of the license. Please seek legal council if you have any concerns.
|
||||
|
||||
TLDR; You may license your game under whichever license you please. Any C++ code that is by definition a derivative work
|
||||
must be licensed under GPLv3 and freely available, everything else; assets, scripts, design documents, etc. may be under
|
||||
the license of your choosing and remain proprietary.
|
||||
|
||||
  fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from
|
||||
modifying fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially,
|
||||
however it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
|
||||
fennec however you wish according to the terms of the license, which does not bar you from commercializing software based
|
||||
on fennec.
|
||||
|
||||
  If you wish to protect your game files, assets and generated content do not constitute a covered work and may be
|
||||
copyrighted under a non-compliant license. Think of it in terms of using Blender to create a mesh for a game, then
|
||||
licensing that mesh under another license.
|
||||
|
||||
  Later down the line, I plan on implementing scripts in a manner that allows the script itself to remain proprietary.
|
||||
The scripts will likely be trans-compiled to another language before being compiled to binary, but this is only an
|
||||
intermediate step and will be erased when no longer needed.
|
||||
|
||||
  As long as you use the official editor, it will properly include licenses in project content when provided a license
|
||||
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
|
||||
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
|
||||
licensed under GPLv3 must be available upon request.
|
||||
|
||||
  A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
|
||||
The executable and shared library are under the GPLv3 license, while the archive pak and streamable assets are under your license.
|
||||
|
||||
It is to my discretion whether I enforce the terms of the license on a party.
|
||||
|
||||
The following practices are more likely to get my attention and enforcement:
|
||||
- Redistributing a modified version of fennec that is not licensed under GPLv3
|
||||
- Distributing an engine that is, by definition, a derivative work of fennec that is not licensed under GPLv3
|
||||
- Distributing an editor that runs on fennec or its derivatives that is not licensed under GPLv3
|
||||
- Using fennec to train a Machine Learning or Artificial Intelligence algorithm that is not licensed under GPLv3
|
||||
- Non-compliance of GPLv3 in games with the following mechanics:
|
||||
- Gacha Mechanics
|
||||
- Gambling with real currency
|
||||
- Subscription-Based Sales Model
|
||||
- NFTs or Nonfungible Tokens
|
||||
|
||||
I encourage those who wish to commercialize derivative works crowdfund rather than use a sales model. I also ask
|
||||
that you kindly support me as a developer, I will set up a Buy Me a Coffee link at some point.
|
||||
|
||||
GPLv3 is bound by fair use; here is the clause, 17 U.S. Code § 107:
|
||||
|
||||
```
|
||||
107. Limitations on exclusive rights: Fair use
|
||||
|
||||
Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by
|
||||
reproduction in copies or phonorecords or by any other means specified by that section, for purposes such as criticism,
|
||||
comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an
|
||||
infringement of copyright. In determining whether the use made of a work in any particular case is a fair use the
|
||||
factors to be considered shall include—
|
||||
|
||||
(1) the purpose and character of the use, including whether such use is of a commercial nature or is for nonprofit
|
||||
educational purposes;
|
||||
(2) the nature of the copyrighted work;
|
||||
(3) the amount and substantiality of the portion used in relation to the copyrighted work as a whole; and
|
||||
(4) the effect of the use upon the potential market for or value of the copyrighted work.
|
||||
|
||||
The fact that a work is unpublished shall not itself bar a finding of fair use if such finding is made upon
|
||||
consideration of all the above factors.
|
||||
```
|
||||
|
||||
If you have any questions or concerns, please seek legal council. If you believe someone else has violated the terms
|
||||
of this license, please contact me at [mslockbo@gmail.com](mailto:mslockbo@gmail.com).
|
||||
|
||||
I am aware of Universities with Game Development programs such as DigiPen Institute of Technology and Full-Sail
|
||||
University which license student work to protect them and the faculty.
|
||||
|
||||
Champlain College does not license student projects and constitutes fair use.
|
||||
|
||||
I have not worked with Full-Sail University before, so I am not familiar with any of their staff members, and I will
|
||||
require legal council to consult with them which may dissuade permission to use my engine.
|
||||
|
||||
I hold a Bachelor's Degree in Computer Science and Real-Time Interactive Simulation from DigiPen Institute of Technology,
|
||||
so I am familiar with their copyright policy. Ask your professor about usage of my engine, and they or someone with
|
||||
appropriate standing will reach out to me. Eventually, with interest, I may reach out on my own terms to negotiate usage
|
||||
of fennec for educative purposes at DigiPen while retaining their license on student work.
|
||||
|
||||
If your University is not listed here, reach out to your professor for permission. Ask them to reach out to me, I am
|
||||
willing to work with educational institutes to protect both fennec and student work in accordance to university policy.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@@ -205,5 +383,5 @@ information displayed is correct.
|
||||
|
||||
There are some principles to keep in mind when contributing to fennec.
|
||||
|
||||
1. You must follow the style guide provided by the [GNU Coding Standard](https://www.gnu.org/prep/standards/html_node/Writing-C.html).
|
||||
2. Any changes must allow all projects to be forward compatible with newer engine verisons.
|
||||
1. You must follow the [standards provided above](#coding-standards).
|
||||
2. Any changes must allow all projects to be forward compatible with newer engine versions.
|
||||
|
||||
10
build.sh
10
build.sh
@@ -1,6 +1,6 @@
|
||||
## =====================================================================================================================
|
||||
## fennec, a free and open source game engine
|
||||
## Copyright © 2025 Medusa Slockbower
|
||||
## Copyright © 2025 - 2026 Medusa Slockbower
|
||||
##
|
||||
## This program is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
@@ -38,7 +38,7 @@ Help()
|
||||
Debug()
|
||||
{
|
||||
mkdir -p build/debug
|
||||
cd ./build/debug
|
||||
cd ./build/debug || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -47,7 +47,7 @@ Debug()
|
||||
Release()
|
||||
{
|
||||
mkdir -p build/release
|
||||
cd ./build/release
|
||||
cd ./build/release || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -56,7 +56,7 @@ Release()
|
||||
RelWithDebInfo()
|
||||
{
|
||||
mkdir -p build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -65,7 +65,7 @@ RelWithDebInfo()
|
||||
MinSizeRel()
|
||||
{
|
||||
mkdir -p build/minsizerel
|
||||
cd ./build/minsizerel
|
||||
cd ./build/minsizerel || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
|
||||
28
cmake/build.cmake
Normal file
28
cmake/build.cmake
Normal file
@@ -0,0 +1,28 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script handles functionality related to the build process and its info
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} FENNEC_BUILD_NAME)
|
||||
message(STATUS "Build: ${FENNEC_BUILD_NAME}")
|
||||
|
||||
if(${FENNEC_BUILD_NAME} MATCHES "debug")
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_RELEASE=false)
|
||||
else()
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_RELEASE=true)
|
||||
endif()
|
||||
30
cmake/compiler.cmake
Normal file
30
cmake/compiler.cmake
Normal file
@@ -0,0 +1,30 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script finds the compiler being used
|
||||
|
||||
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_LONG_COMPILER_NAME="${CMAKE_CXX_COMPILER_ID} ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR}"
|
||||
)
|
||||
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
set(FENNEC_COMPILER "GCC")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/gcc.cmake")
|
||||
endif()
|
||||
35
cmake/default_user.cmake
Normal file
35
cmake/default_user.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# sets up cmake variables for client vs server
|
||||
if(FENNEC_USER_CLIENT)
|
||||
set(FENNEC_USER_CLIENT 1)
|
||||
set(FENNEC_USER_SERVER 0)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_USER_CLIENT=1)
|
||||
elseif(FENNEC_USER_SERVER)
|
||||
set(FENNEC_USER_CLIENT 0)
|
||||
set(FENNEC_USER_SERVER 1)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_USER_SERVER=1)
|
||||
else()
|
||||
set(FENNEC_USER_CLIENT 1)
|
||||
set(FENNEC_USER_SERVER 0)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_USER_CLIENT=1)
|
||||
endif()
|
||||
32
cmake/gcc.cmake
Normal file
32
cmake/gcc.cmake
Normal file
@@ -0,0 +1,32 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script sets flags and variables for gnu and gnu-like compilers
|
||||
|
||||
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror" "-fms-extensions")
|
||||
|
||||
fennec_add_compile_options("-ffile-prefix-map=${FENNEC_SOURCE_DIR}=.")
|
||||
|
||||
fennec_add_link_options("-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates" "-pthread")
|
||||
|
||||
fennec_add_definitions(
|
||||
_GLIBCXX_INCLUDE_NEXT_C_HEADERS=1
|
||||
FENNEC_COMPILER_GCC=1
|
||||
FENNEC_NO_INLINE=[[gnu::noinline]]
|
||||
FENNEC_FUNCTION_NAME=__PRETTY_FUNCTION__
|
||||
)
|
||||
43
cmake/linux.cmake
Normal file
43
cmake/linux.cmake
Normal file
@@ -0,0 +1,43 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script finds and loads libraries related to the Linux operating system. It also sets platform specific variables.
|
||||
|
||||
macro(fennec_check_platform)
|
||||
# unix
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/unix.cmake")
|
||||
|
||||
# compile definitions
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_NAME="Linux"
|
||||
FENNEC_PLATFORM_LINUX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/linux/platform.h source/platform/linux/platform.cpp
|
||||
)
|
||||
|
||||
if(FENNEC_USER_CLIENT)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/wayland.cmake")
|
||||
|
||||
fennec_check_wayland()
|
||||
fennec_init_graphics()
|
||||
endif()
|
||||
|
||||
endmacro()
|
||||
43
cmake/opengl.cmake
Normal file
43
cmake/opengl.cmake
Normal file
@@ -0,0 +1,43 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
find_package(OpenGL)
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_EGL)
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/opengl/egl/fwd.h
|
||||
include/fennec/platform/opengl/egl/error.h
|
||||
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
|
||||
include/fennec/platform/opengl/egl/surface.h source/platform/opengl/egl/surface.cpp
|
||||
|
||||
include/fennec/platform/opengl/glad/egl.h source/platform/opengl/glad/egl.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TARGET OpenGL::GL)
|
||||
fennec_add_link_libraries(OpenGL::GL)
|
||||
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/opengl/glad/gl.h source/platform/opengl/glad/gl.c
|
||||
|
||||
include/fennec/renderers/opengl/glcontext.h source/renderers/opengl/glcontext.cpp
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
|
||||
endif()
|
||||
35
cmake/platform.cmake
Normal file
35
cmake/platform.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script finds the operating system of the build environment
|
||||
|
||||
message(STATUS "OS: ${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/default_user.cmake")
|
||||
|
||||
# Check for Linux
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(FENNEC_PLATFORM "Linux")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake")
|
||||
endif ()
|
||||
|
||||
# Graphics APIs
|
||||
macro(fennec_init_graphics)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/vulkan.cmake")
|
||||
endmacro()
|
||||
29
cmake/unix.cmake
Normal file
29
cmake/unix.cmake
Normal file
@@ -0,0 +1,29 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# generic unix functionality
|
||||
|
||||
# compile definitions
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_UNIX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
|
||||
)
|
||||
33
cmake/version.cmake
Normal file
33
cmake/version.cmake
Normal file
@@ -0,0 +1,33 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script contains the main version
|
||||
|
||||
set(FENNEC_VERSION_MAJOR 0)
|
||||
set(FENNEC_VERSION_MINOR 1)
|
||||
set(FENNEC_VERSION_PATCH 0)
|
||||
set(FENNEC_VERSION_STRING "${FENNEC_VERSION_MAJOR}.${FENNEC_VERSION_MINOR}.${FENNEC_VERSION_PATCH}")
|
||||
math(EXPR FENNEC_VERSION_NUM "(${FENNEC_VERSION_MAJOR} << 16) | (${FENNEC_VERSION_MINOR} << 8) | ${FENNEC_VERSION_PATCH}")
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
FENNEC_VERSION_MAJOR=${FENNEC_VERSION_MAJOR}
|
||||
FENNEC_VERSION_MINOR=${FENNEC_VERSION_MINOR}
|
||||
FENNEC_VERSION_PATCH=${FENNEC_VERSION_PATCH}
|
||||
FENNEC_VERSION_STRING=${FENNEC_VERSION_STRING}
|
||||
FENNEC_VERSION_NUM=${FENNEC_VERSION_NUM}
|
||||
)
|
||||
40
cmake/vulkan.cmake
Normal file
40
cmake/vulkan.cmake
Normal file
@@ -0,0 +1,40 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
find_package(Vulkan COMPONENTS glslang volk)
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_MOLTENVK)
|
||||
find_package(Vulkan COMPONENTS MoltenVK)
|
||||
endif()
|
||||
|
||||
if( TARGET Vulkan::Headers AND TARGET Vulkan::volk # Base Headers and Meta-Loader
|
||||
AND TARGET Vulkan::glslang # GLSL Compilation
|
||||
AND (NOT FENNEC_GRAPHICS_WANT_MOLTENVK OR TARGET Vulkan::MoltenVK)
|
||||
)
|
||||
fennec_add_link_libraries(Vulkan::volk Vulkan::glslang)
|
||||
fennec_add_definitions(FENNEC_GRAPHICS_VULKAN=1)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/renderers/vulkan/lib/app_info.h
|
||||
include/fennec/renderers/vulkan/lib/instance.h
|
||||
|
||||
include/fennec/renderers/vulkan/vkcontext.h include/fennec/renderers/vulkan/vkcontext.cpp
|
||||
)
|
||||
else()
|
||||
message(WARNING "No Suitable Vulkan implementation found.")
|
||||
endif()
|
||||
169
cmake/wayland.cmake
Normal file
169
cmake/wayland.cmake
Normal file
@@ -0,0 +1,169 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# https://gist.github.com/mariobadr/acc3c8adf4b4e722705be38c3deac59a
|
||||
# this script finds libwayland and dependencies
|
||||
|
||||
# some of this code is based on SDL3's use of wayland-scanner
|
||||
|
||||
function(fennec_wayland_get_protocol)
|
||||
set( _OPTIONS_ARGS )
|
||||
set( _ONE_VALUE_ARGS )
|
||||
set( _MULTI_VALUE_ARGS NAMES PATHS )
|
||||
|
||||
cmake_parse_arguments(_FINDPROTOCOLS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )
|
||||
|
||||
find_file(_FINDPROTOCOLS_TEMP NAMES ${_FINDPROTOCOLS_NAMES} PATHS ${_FINDPROTOCOLS_PATHS})
|
||||
file(COPY ${_FINDPROTOCOLS_TEMP} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
|
||||
endfunction()
|
||||
|
||||
macro(fennec_wayland_get_header _SCANNER _XML _FILE)
|
||||
set(_WAYLAND_PROT_H_CODE "${WAYLAND_HEADERS_DIR}/${_FILE}-client-protocols.h")
|
||||
set(_WAYLAND_PROT_C_CODE "${WAYLAND_SOURCES_DIR}/${_FILE}-client.c")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_SCANNER} client-header "${_XML}" "${_WAYLAND_PROT_H_CODE}"
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_SCANNER} private-code "${_XML}" "${_WAYLAND_PROT_C_CODE}"
|
||||
)
|
||||
|
||||
fennec_add_sources(${_WAYLAND_PROT_C_CODE} ${_WAYLAND_PROT_H_CODE})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_check_wayland)
|
||||
set(WAYLAND_CLIENT_FOUND 0)
|
||||
|
||||
find_path(
|
||||
WAYLAND_CLIENT_INCLUDE_DIR
|
||||
NAMES wayland-client.h
|
||||
)
|
||||
find_library(
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
NAMES wayland-client libwayland-client
|
||||
)
|
||||
find_program(WAYLAND_SCANNER NAMES wayland-scanner)
|
||||
|
||||
# EGL is required
|
||||
find_path(
|
||||
WAYLAND_EGL_INCLUDE_DIR
|
||||
NAMES wayland-egl.h
|
||||
)
|
||||
find_library(
|
||||
WAYLAND_EGL_LIBRARY
|
||||
NAMES wayland-egl libwayland-egl
|
||||
)
|
||||
|
||||
|
||||
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY AND WAYLAND_SCANNER)
|
||||
AND (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARY))
|
||||
message(STATUS "Found Wayland: ${WAYLAND_CLIENT_LIBRARY}")
|
||||
|
||||
set(WAYLAND_PROTOCOLS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/protocols)
|
||||
set(WAYLAND_HEADERS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/headers)
|
||||
set(WAYLAND_SOURCES_DIR ${FENNEC_SOURCE_DIR}/source/platform/linux/wayland/lib/sources)
|
||||
|
||||
# Search for base protocol xml
|
||||
fennec_wayland_get_protocol(NAMES "wayland.xml" PATHS "/usr/share/wayland" "/usr/share/wayland-protocols")
|
||||
fennec_wayland_get_protocol(NAMES "xdg-shell.xml" PATHS "/usr/share/wayland/stable/xdg-shell" "/usr/share/wayland-protocols/stable/xdg-shell")
|
||||
|
||||
# include sub-dependencies
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake")
|
||||
fennec_check_xkb()
|
||||
|
||||
# generate protocols, based on SDL3
|
||||
file(GLOB WAYLAND_PROTOCOLS_XML RELATIVE "${WAYLAND_PROTOCOLS_DIR}" "${WAYLAND_PROTOCOLS_DIR}/*.xml")
|
||||
foreach(_XML IN LISTS WAYLAND_PROTOCOLS_XML)
|
||||
get_filename_component(_FILE ${_XML} NAME_WLE)
|
||||
fennec_wayland_get_header("${WAYLAND_SCANNER}" "${WAYLAND_PROTOCOLS_DIR}/${_XML}" "${_FILE}")
|
||||
endforeach()
|
||||
|
||||
# Add sources and libraries
|
||||
get_filename_component(
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
${WAYLAND_CLIENT_LIBRARY}
|
||||
NAME
|
||||
)
|
||||
get_filename_component(
|
||||
WAYLAND_EGL_LIBRARY
|
||||
${WAYLAND_EGL_LIBRARY}
|
||||
NAME
|
||||
)
|
||||
|
||||
set(WAYLAND_CLIENT_FOUND 1)
|
||||
set(WAYLAND_EGL_FOUND 1)
|
||||
set(FENNEC_GRAPHICS_WANT_EGL 1)
|
||||
|
||||
fennec_add_sources(
|
||||
# Dynamic Library Files
|
||||
include/fennec/platform/linux/wayland/lib/sym.h
|
||||
include/fennec/platform/linux/wayland/lib/wayland.h
|
||||
include/fennec/platform/linux/wayland/lib/loader.h source/platform/linux/wayland/lib/loader.cpp
|
||||
|
||||
# Fennec Files
|
||||
include/fennec/platform/linux/wayland/fwd.h
|
||||
include/fennec/platform/linux/wayland/server.h source/platform/linux/wayland/server.cpp
|
||||
include/fennec/platform/linux/wayland/window.h source/platform/linux/wayland/window.cpp
|
||||
|
||||
# EGL
|
||||
include/fennec/platform/linux/wayland/egl/context.h source/platform/linux/wayland/egl/context.cpp
|
||||
include/fennec/platform/linux/wayland/egl/surface.h source/platform/linux/wayland/egl/surface.cpp
|
||||
)
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_WAYLAND=1
|
||||
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
|
||||
FENNEC_LIB_WAYLAND_EGL="${WAYLAND_EGL_LIBRARY}"
|
||||
VK_USE_PLATFORM_WAYLAND_KHR=1
|
||||
)
|
||||
|
||||
|
||||
# find libdecor
|
||||
find_path(
|
||||
LIBDECOR_INCLUDE_DIR
|
||||
PATH_SUFFIXES libdecor libdecor-0
|
||||
NAMES libdecor.h libdecor.h
|
||||
)
|
||||
find_library(
|
||||
LIBDECOR_LIBRARY
|
||||
PATH_SUFFIXES libdecor libdecor-0
|
||||
NAMES libdecor.so libdecor-0.so
|
||||
)
|
||||
|
||||
if(LIBDECOR_INCLUDE_DIR AND LIBDECOR_LIBRARY)
|
||||
message(STATUS "Found libdecor: ${LIBDECOR_LIBRARY}")
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_LIBDECOR=1
|
||||
FENNEC_LIB_LIBDECOR="${LIBDECOR_LIBRARY}"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${LIBDECOR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/linux/wayland/libdecor/sym.h
|
||||
include/fennec/platform/linux/wayland/libdecor/libdecor.h
|
||||
include/fennec/platform/linux/wayland/libdecor/loader.h source/platform/linux/wayland/libdecor/loader.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endmacro()
|
||||
58
cmake/xkb.cmake
Normal file
58
cmake/xkb.cmake
Normal file
@@ -0,0 +1,58 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# this script finds libxkbcommon and dependencies
|
||||
|
||||
macro(fennec_check_xkb)
|
||||
set(XKB_FOUND 0)
|
||||
|
||||
find_path(
|
||||
XKB_INCLUDE_DIR
|
||||
NAMES xkbcommon/xkbcommon.h
|
||||
)
|
||||
find_library(
|
||||
XKB_LIBRARY
|
||||
NAMES xkbcommon libxkbcommon
|
||||
)
|
||||
|
||||
if(XKB_INCLUDE_DIR AND XKB_LIBRARY)
|
||||
message(STATUS "Found XKB: ${XKB_LIBRARY}")
|
||||
|
||||
get_filename_component(
|
||||
XKB_LIBRARY
|
||||
${XKB_LIBRARY}
|
||||
NAME
|
||||
)
|
||||
|
||||
set(XKB_FOUND 1)
|
||||
|
||||
fennec_add_sources(
|
||||
# Dynamic Library Files
|
||||
include/fennec/platform/linux/xkb/lib/sym.h
|
||||
include/fennec/platform/linux/xkb/lib/xkb.h
|
||||
include/fennec/platform/linux/xkb/lib/loader.h source/platform/linux/xkb/lib/loader.cpp
|
||||
|
||||
# Fennec files
|
||||
)
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_XKB=1
|
||||
FENNEC_LIB_XKB="${XKB_LIBRARY}"
|
||||
)
|
||||
endif()
|
||||
endmacro()
|
||||
417774
conv/GLSLangSpec.4.60.pdf
Normal file
417774
conv/GLSLangSpec.4.60.pdf
Normal file
File diff suppressed because it is too large
Load Diff
BIN
conv/GNUCodingStandards.pdf
Normal file
BIN
conv/GNUCodingStandards.pdf
Normal file
Binary file not shown.
@@ -5,7 +5,6 @@
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type=""
|
||||
<tab type="topics" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="" intro="">
|
||||
<tab type="modulelist" visible="yes" title="" intro=""/>
|
||||
@@ -50,6 +49,7 @@
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
@@ -84,7 +84,6 @@
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -106,6 +105,7 @@
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
@@ -122,7 +122,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -151,6 +150,7 @@
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -168,7 +168,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
@@ -186,6 +185,7 @@
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
@@ -211,7 +211,6 @@
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
@@ -238,6 +237,7 @@
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -247,7 +247,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
@@ -257,10 +256,10 @@
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
|
||||
2869
doxy/Doxyfile
2869
doxy/Doxyfile
File diff suppressed because it is too large
Load Diff
@@ -260,7 +260,7 @@ INHERIT_DOCS = YES
|
||||
# of the file/class/namespace that contains it.
|
||||
# The default value is: NO.
|
||||
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
SEPARATE_MEMBER_PAGES = YES
|
||||
|
||||
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
|
||||
# uses this value to replace tabs by spaces in code fragments.
|
||||
@@ -582,7 +582,7 @@ RESOLVE_UNNAMED_PARAMS = YES
|
||||
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
|
||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||
@@ -591,7 +591,7 @@ HIDE_UNDOC_MEMBERS = YES
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
@@ -701,7 +701,7 @@ SORT_BRIEF_DOCS = NO
|
||||
# detailed member documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_MEMBERS_CTORS_1ST = YES
|
||||
|
||||
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
|
||||
# of group names into alphabetical order. If set to NO the group names will
|
||||
@@ -944,7 +944,6 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = "@PROJECT_SOURCE_DIR@/include/" \
|
||||
"@PROJECT_SOURCE_DIR@/source/" \
|
||||
"@PROJECT_SOURCE_DIR@/README.md"
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
@@ -1049,7 +1048,10 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE = "@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/wayland/lib" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/wayland/libdecor" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/xkb/lib" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/opengl/glad"
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -1079,7 +1081,7 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@"
|
||||
EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/examples"
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
@@ -1099,7 +1101,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = "@PROJECT_SOURCE_DIR@/doxy/static"
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -1981,7 +1983,7 @@ GENERATE_LATEX = NO
|
||||
# The default directory is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_OUTPUT = ./latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
@@ -2402,7 +2404,7 @@ MACRO_EXPANSION = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
|
||||
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
|
||||
# INCLUDE_PATH will be searched if a #include is found.
|
||||
@@ -2417,7 +2419,7 @@ SEARCH_INCLUDES = YES
|
||||
# RECURSIVE has no effect here.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = "@PROJECT_SOURCE_DIR@/include"
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -2435,8 +2437,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = "FENNEC_SCALAR_TEMPLATE=" \
|
||||
"FENNEC_VECTOR_TEMPLATE="
|
||||
PREDEFINED = "FENNEC_DOXYGEN="
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -2511,7 +2512,7 @@ EXTERNAL_PAGES = YES
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HIDE_UNDOC_RELATIONS = NO
|
||||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
@@ -2664,7 +2665,7 @@ TEMPLATE_RELATIONS = NO
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDE_GRAPH = NO
|
||||
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
@@ -2676,7 +2677,7 @@ INCLUDE_GRAPH = YES
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
|
||||
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
|
||||
# dependency graph for every global function or class method.
|
||||
|
||||
@@ -286,4 +286,9 @@ html.dark-mode {
|
||||
|
||||
td.odd_c {
|
||||
background-color: var(--odd-color)
|
||||
}
|
||||
}
|
||||
|
||||
a + h2.groupheader {
|
||||
display:none;
|
||||
}
|
||||
|
||||
|
||||
71
doxy/retrieve-emojis.py
Normal file
71
doxy/retrieve-emojis.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# script to download the emoticons from GitHub and to produce a table for
|
||||
# inclusion in Doxygen. Works with python 2.7+ and python 3.x
|
||||
import json
|
||||
import os
|
||||
import argparse
|
||||
import re
|
||||
try:
|
||||
import urllib.request as urlrequest
|
||||
except ImportError:
|
||||
import urllib as urlrequest
|
||||
|
||||
unicode_re = re.compile(r'.*?/unicode/(.*?).png\?.*')
|
||||
|
||||
def get_emojis():
|
||||
response = urlrequest.urlopen('https://api.github.com/emojis')
|
||||
raw_data = response.read()
|
||||
return json.loads(raw_data)
|
||||
|
||||
def download_images(dir_name, silent):
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
json_data = get_emojis()
|
||||
num_items = len(json_data)
|
||||
cur_item=0
|
||||
for image,url in sorted(json_data.items()):
|
||||
image_name = image+'.png'
|
||||
cur_item=cur_item+1
|
||||
if url.find('/unicode/')==-1 or not os.path.isfile(dir_name+'/'+image_name):
|
||||
success = True
|
||||
with open(dir_name+'/'+image_name,'wb') as file:
|
||||
if not silent:
|
||||
print('%s/%s: fetching %s' % (cur_item,num_items,image_name))
|
||||
try:
|
||||
file.write(urlrequest.urlopen(url).read())
|
||||
except:
|
||||
print('Unable to fetch %s' % (image_name))
|
||||
success = False
|
||||
if not success:
|
||||
os.remove(dir_name+'/'+image_name)
|
||||
else:
|
||||
if not silent:
|
||||
print('%s/%s: skipping %s' % (cur_item,num_items,image_name))
|
||||
|
||||
def produce_table():
|
||||
json_data = get_emojis()
|
||||
lines = []
|
||||
for image,url in sorted(json_data.items()):
|
||||
match = unicode_re.match(url)
|
||||
if match:
|
||||
unicodes = match.group(1).split('-')
|
||||
unicodes_html = ''.join(["&#x"+x+";" for x in unicodes])
|
||||
image_str = "\":"+image+":\","
|
||||
unicode_str = "\""+unicodes_html+"\""
|
||||
lines.append(' { %-42s %-38s }' % (image_str,unicode_str))
|
||||
out_str = ',\n'.join(lines)
|
||||
print("{")
|
||||
print(out_str)
|
||||
print("};")
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-d','--dir',help='directory to place images in')
|
||||
parser.add_argument('-t','--table',help='generate code fragment',action='store_true')
|
||||
parser.add_argument('-s','--silent',help='silent mode',action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.table:
|
||||
produce_table()
|
||||
if args.dir:
|
||||
download_images(args.dir, args.silent)
|
||||
4
doxy/static/graphs/containers/rdtree.svg
Normal file
4
doxy/static/graphs/containers/rdtree.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 60 KiB |
67
examples/assert.cpp
Normal file
67
examples/assert.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// =====================================================================================================================
|
||||
// I release this example code into the public domain
|
||||
// =====================================================================================================================
|
||||
|
||||
// This file contains code that tests the efficiency of the assert macro scheme in
|
||||
// fennec engine. This is purely looking at the branching aspect and not the private
|
||||
// assert definition. The code only uses passing values to see any performance overhead
|
||||
// from the conditional
|
||||
|
||||
// This code is based on the example code that cppreference provides at
|
||||
// https://en.cppreference.com/w/cpp/language/attributes/likely
|
||||
|
||||
// To my surprise, the difference between them is negligible.
|
||||
// Even when n is a crazy number like one billion, there isn't a conclusive difference.
|
||||
// I checked that it isn't the lambdas, they are optimized out.
|
||||
|
||||
// In debug mode, the results are to be expected; release < experimental < control
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#define assert_c(expression) \
|
||||
if(not(expression)) { \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
#define assert_e(expression) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
#define assert_r(expression) (static_cast<void> (0))
|
||||
|
||||
volatile int sink{}; // ensures a side effect
|
||||
|
||||
int main() {
|
||||
auto benchmark = [](auto fun, auto rem) {
|
||||
srand(0);
|
||||
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
for (auto size{1ULL}; size != 10'000'000ULL; ++size)
|
||||
sink = fun(rand());
|
||||
const std::chrono::duration<double> diff =
|
||||
std::chrono::high_resolution_clock::now() - start;
|
||||
|
||||
std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
|
||||
<< " sec " << rem << std::endl;
|
||||
};
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_c(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "control");
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_r(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "release");
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_e(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "experimental");
|
||||
}
|
||||
1
external/cpptrace
vendored
Submodule
1
external/cpptrace
vendored
Submodule
Submodule external/cpptrace added at 787d8af6f6
1
external/sdl
vendored
1
external/sdl
vendored
Submodule external/sdl deleted from d7939abf42
37
gdb/fennec/__init__.py
Normal file
37
gdb/fennec/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
|
||||
# GDB CODE =============================================================================================================
|
||||
|
||||
import gdb
|
||||
import gdb.printing
|
||||
|
||||
from . import containers
|
||||
from . import strings
|
||||
from . import memory
|
||||
from . import utility
|
||||
from . import filesystem
|
||||
from . import math
|
||||
|
||||
def register_printers(obj):
|
||||
gdb.printing.register_pretty_printer(obj, containers.printer)
|
||||
gdb.printing.register_pretty_printer(obj, strings.printer)
|
||||
gdb.printing.register_pretty_printer(obj, memory.printer)
|
||||
gdb.printing.register_pretty_printer(obj, filesystem.printer)
|
||||
gdb.printing.register_pretty_printer(obj, math.printer)
|
||||
678
gdb/fennec/containers.py
Normal file
678
gdb/fennec/containers.py
Normal file
@@ -0,0 +1,678 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
import gdb
|
||||
from collections import deque
|
||||
|
||||
# OPTIONAL =============================================================================================================
|
||||
class OptionalPrinter:
|
||||
"""Print a fennec::optional"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
is_initialized = self.val['_set']
|
||||
if is_initialized:
|
||||
return "{{ value = {} }}".format(self.val['_val'])
|
||||
else:
|
||||
return "{ empty }"
|
||||
|
||||
# PAIR =================================================================================================================
|
||||
class PairPrinter:
|
||||
"""Print a fennec::optional"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ first = " + str(self.val['first']) + ", second = " + str(self.val['second']) + " }"
|
||||
|
||||
def children(self):
|
||||
return ("first", self.val['first']), ("second", self.val['second'])
|
||||
|
||||
|
||||
# TUPLE ================================================================================================================
|
||||
|
||||
class TuplePrinter:
|
||||
"""Print a fennec::tuple"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.fields = val.type.fields()[0].type.fields()
|
||||
self.elems = val
|
||||
self.count = len(self.fields)
|
||||
|
||||
def to_string(self):
|
||||
return " { size = " + str(len(self.fields)) + " }"
|
||||
|
||||
def children(self):
|
||||
return (('[{}]'.format(i), self.elems.cast(self.fields[i].type)['value']) for i in range(self.count))
|
||||
|
||||
|
||||
|
||||
# ARRAY ================================================================================================================
|
||||
class ArrayPrinter:
|
||||
"""Print a fennec::array"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ length = " + str(self.val['elements'].type.range()[1] + 1) + " }"
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
def children(self):
|
||||
start = self.val['elements']
|
||||
return (('[{}]'.format(i), start[i]) for i in range(0, self.val['elements'].type.range()[1] + 1))
|
||||
|
||||
|
||||
# DYNARRAY =============================================================================================================
|
||||
class DynArrayPrinter:
|
||||
"""Print a fennec::dynarray"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_alloc']['_capacity']) + " }"
|
||||
|
||||
def children(self):
|
||||
size = int(self.val['_size'])
|
||||
start = self.val['_alloc']['_data']
|
||||
return (('[{}]'.format(i), start[i]) for i in range(size))
|
||||
|
||||
|
||||
# LIST =================================================================================================================
|
||||
class ListPrinter:
|
||||
"""Print a fennec::list"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, val):
|
||||
self.list = val
|
||||
self.node = self.list['_root']
|
||||
self.index = 0
|
||||
self.table = self.list['_table']['_data']
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.node == 18446744073709551615:
|
||||
raise StopIteration
|
||||
|
||||
i = self.index
|
||||
self.index = self.index + 1
|
||||
value = self.table[self.node]['value']['_val']
|
||||
self.node = self.table[self.node]['next']
|
||||
return '[{}]'.format(i), value
|
||||
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_capacity']) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.val)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
# DEQUE ================================================================================================================
|
||||
|
||||
class DequePrinter:
|
||||
"""Print a fennec::deque"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, start):
|
||||
self.node = start
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.node is None:
|
||||
raise StopIteration
|
||||
|
||||
i = self.index
|
||||
value = self.node.dereference()['value']
|
||||
|
||||
self.node = self.node.dereference()['next']
|
||||
self.index = self.index + 1
|
||||
|
||||
return '[{}]'.format(i), value
|
||||
|
||||
def __init__(self, val):
|
||||
self.first = val['_first']
|
||||
self.last = val['_last']
|
||||
self.size = val['_size']
|
||||
|
||||
def to_string(self):
|
||||
if self.first is None:
|
||||
return "{ empty }"
|
||||
return "{ length " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.first)
|
||||
|
||||
|
||||
# SET ==================================================================================================================
|
||||
|
||||
class SetPrinter:
|
||||
"""Print a fennec::set"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, table, capacity):
|
||||
self.table = table
|
||||
self.capacity = capacity
|
||||
self.node = 0
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
while self.node < self.capacity:
|
||||
if self.table[self.node]['value']['_set']:
|
||||
break
|
||||
self.node = self.node + 1
|
||||
|
||||
if self.node >= self.capacity:
|
||||
raise StopIteration
|
||||
|
||||
i = self.index
|
||||
value = self.table[self.node]['value']['_val']
|
||||
self.node = self.node + 1
|
||||
self.index = self.index + 1
|
||||
return '[{}]'.format(i), value
|
||||
|
||||
def __init__(self, val):
|
||||
self.table = val['_alloc']['_data']
|
||||
self.capacity = val['_alloc']['_capacity']
|
||||
self.size = val['_size']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.table, self.capacity)
|
||||
|
||||
|
||||
# MAP ==================================================================================================================
|
||||
|
||||
class MapPrinter:
|
||||
"""Print a fennec::map"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, table, capacity):
|
||||
self.table = table
|
||||
self.capacity = capacity
|
||||
self.node = 0
|
||||
self.index = 0
|
||||
self.move = False
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
while self.node < self.capacity:
|
||||
if self.table[self.node]['value']['_set']:
|
||||
break
|
||||
self.node = self.node + 1
|
||||
|
||||
if self.node >= self.capacity:
|
||||
raise StopIteration
|
||||
|
||||
i = self.index
|
||||
pair = self.table[self.node]['value']['_val']
|
||||
key = pair['first']
|
||||
val = pair['second']
|
||||
|
||||
if not self.move:
|
||||
self.move = True
|
||||
return 'key' + str(i), key
|
||||
else:
|
||||
self.move = False
|
||||
self.index = self.index + 1
|
||||
self.node = self.node + 1
|
||||
return 'value' + str(i), val
|
||||
|
||||
def __init__(self, val):
|
||||
self.table = val['_set']['_alloc']['_data']
|
||||
self.capacity = val['_set']['_alloc']['_capacity']
|
||||
self.size = val['_set']['_size']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.table, self.capacity)
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
|
||||
# OBJECT_POOL ==========================================================================================================
|
||||
class ObjectPoolPrinter:
|
||||
"""Print a fennec::object_pool"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, val):
|
||||
self.list = val
|
||||
self.index = 0
|
||||
self.capacity = self.list['_table']['_alloc']['_capacity']
|
||||
self.table = self.list['_table']['_alloc']['_data']
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
|
||||
i = self.index
|
||||
while True:
|
||||
i = self.index
|
||||
self.index = self.index + 1
|
||||
|
||||
if self.index >= self.capacity:
|
||||
raise StopIteration
|
||||
|
||||
if bool(self.table[i]['_set']):
|
||||
value = self.table[i]['_val']
|
||||
break
|
||||
|
||||
return '[{}]'.format(i), value
|
||||
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_alloc']['_capacity']) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.val)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
# RDTREE ===============================================================================================================
|
||||
|
||||
class RDTreePrinter:
|
||||
"""Print a fennec::rdtree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
self.visit.popleft()
|
||||
|
||||
value = self.tree[node]['value']
|
||||
|
||||
nnext = self.tree[node]['next']
|
||||
nprev = self.tree[node]['prev']
|
||||
nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
|
||||
child = self.tree[node]['child']
|
||||
n_chld = self.tree[node]['num_children']
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
|
||||
if nnext < self.capacity:
|
||||
self.visit.appendleft((nnext, i + 1, depth))
|
||||
|
||||
if child < self.capacity:
|
||||
self.visit.appendleft((child, 0, depth + 1))
|
||||
|
||||
# ┌ ─ ├ └
|
||||
|
||||
if nnext != 18446744073709551615:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_data']
|
||||
self.size = val['_size']
|
||||
self.capacity = val['_table']['_capacity']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, 0, self.capacity)
|
||||
|
||||
|
||||
# PRIORITY QUEUE =======================================================================================================
|
||||
|
||||
class PriorityQueuePrinter:
|
||||
"""Print a fennec::rdtree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
self.skip = True
|
||||
|
||||
self.visit.append((node, 0, 0, node))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
start = self.visit[0][3]
|
||||
self.visit.popleft()
|
||||
|
||||
if node == start and not self.skip:
|
||||
return self.__next__()
|
||||
|
||||
self.skip = False
|
||||
|
||||
value = self.tree[node]['_val']['key']
|
||||
|
||||
nnext = self.tree[node]['_val']['next']
|
||||
child = self.tree[node]['_val']['child']
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
|
||||
if nnext < self.capacity:
|
||||
self.visit.appendleft((nnext, i + 1, depth, start))
|
||||
|
||||
if child < self.capacity:
|
||||
self.visit.appendleft((child, 0, depth + 1, child))
|
||||
self.skip = True
|
||||
|
||||
# ┌ ─ ├ └
|
||||
|
||||
if nnext != 18446744073709551615:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_table']['_alloc']['_data']
|
||||
self.size = val['_table']['_size']
|
||||
self.capacity = val['_table']['_table']['_alloc']['_capacity']
|
||||
self.min = val['_min']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, self.min, self.capacity)
|
||||
|
||||
|
||||
# BINTREE ==============================================================================================================
|
||||
|
||||
class BinTreePrinter:
|
||||
"""Print a fennec::bintree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
|
||||
if capacity > 0:
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
parent = self.tree[node]['parent']
|
||||
self.visit.popleft()
|
||||
|
||||
value = self.tree[node]['value']
|
||||
left = self.tree[node]['child'][0]
|
||||
right = self.tree[node]['child'][1]
|
||||
|
||||
if right < self.capacity:
|
||||
self.visit.appendleft((right, 1, depth + 1))
|
||||
if left < self.capacity:
|
||||
self.visit.appendleft((left, 0, depth + 1))
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
if i == 0 and parent != 18446744073709551615 and self.tree[parent]['right'] != 18446744073709551615:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_data']
|
||||
self.size = val['_size']
|
||||
self.root = val['_root']
|
||||
self.capacity = val['_table']['_capacity']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, self.root, self.capacity)
|
||||
|
||||
|
||||
# SEQUENCE =============================================================================================================
|
||||
|
||||
class SequencePrinter:
|
||||
"""Print a fennec::sequence"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, node):
|
||||
self.visit = deque()
|
||||
|
||||
if node is not None:
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
self.visit.popleft()
|
||||
|
||||
value = node['key']
|
||||
left = node['child'][0]
|
||||
right = node['child'][1]
|
||||
print("it: ", node, " ", left, " ", right);
|
||||
|
||||
if right != 0:
|
||||
self.visit.appendleft((right, 1, depth + 1))
|
||||
if left != 0:
|
||||
self.visit.appendleft((left, 0, depth + 1))
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
if i == 0:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.size = val['_size']
|
||||
self.root = val['_root']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
print("root: ", self.root)
|
||||
return self.Iterator(self.root)
|
||||
|
||||
|
||||
# Graph ================================================================================================================
|
||||
|
||||
class GraphPrinter:
|
||||
"""Print a fennec::graph"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, val):
|
||||
self.node_pool = val['_vertex_pool']['_table']['_alloc']['_data']
|
||||
self.max_nodes = val['_vertex_pool']['_table']['_alloc']['_capacity']
|
||||
self.conn_map = val['_edge_map']['_alloc']['_data']
|
||||
self.max_conn = val['_edge_map']['_size']
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.index >= self.max_nodes:
|
||||
raise StopIteration
|
||||
|
||||
i = self.index
|
||||
self.index = self.index + 1
|
||||
|
||||
while not bool(self.node_pool[i]['_set']):
|
||||
i = self.index
|
||||
self.index = self.index + 1
|
||||
|
||||
conns = self.get_conns(i)
|
||||
value = self.node_pool[i]['_val']
|
||||
|
||||
return '[{} -> {{{}}}]'.format(i, str(conns)), value
|
||||
|
||||
|
||||
|
||||
def get_conns(self, index):
|
||||
indices = []
|
||||
|
||||
if index >= self.max_conn:
|
||||
return indices
|
||||
|
||||
map = self.conn_map[index]['_set']
|
||||
max_conns = map['_alloc']['_capacity']
|
||||
conns = map['_alloc']['_data']
|
||||
|
||||
print(max_conns)
|
||||
|
||||
if max_conns == 0:
|
||||
return indices
|
||||
|
||||
for i in range(0, max_conns):
|
||||
if bool(conns[i]['value']['_set']):
|
||||
conn = conns[i]['value']['_val']
|
||||
indices.append(str(int(conn['first'])))
|
||||
|
||||
return indices
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.size = val['_vertex_pool']['_size']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.val)
|
||||
|
||||
|
||||
|
||||
# GDB Code =============================================================================================================
|
||||
|
||||
def register_printers():
|
||||
print("registering containers")
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
|
||||
pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
|
||||
pp.add_printer('fennec::deque', '^fennec::deque<.*>$', DequePrinter)
|
||||
pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
|
||||
pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
|
||||
pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
|
||||
pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
|
||||
pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
|
||||
pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
|
||||
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
|
||||
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
|
||||
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
||||
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
|
||||
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', SequencePrinter)
|
||||
pp.add_printer('fennec::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter)
|
||||
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
||||
return pp
|
||||
|
||||
printer = register_printers()
|
||||
60
gdb/fennec/filesystem.py
Normal file
60
gdb/fennec/filesystem.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
# PATH =================================================================================================================
|
||||
class PathPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val['_str']['_str']
|
||||
|
||||
def to_string(self):
|
||||
value = "\"" + self.val['_data'].string('', 'replace', self.val['_capacity'] - 1) + "\""
|
||||
return value
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
|
||||
# PATH =================================================================================================================
|
||||
class FilePrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.path = val['_path']['_str']['_str']
|
||||
|
||||
def to_string(self):
|
||||
if self.val['_handle']:
|
||||
value = "{ path = \"" + self.path['_data'].string('', 'replace', self.path['_capacity'] - 1) + "\" }"
|
||||
return value
|
||||
return "{ closed }"
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
|
||||
# GDB Code =============================================================================================================
|
||||
|
||||
def register_printers():
|
||||
print("registering filesystem")
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::filesystem")
|
||||
pp.add_printer('fennec::path', '^fennec::path$', PathPrinter)
|
||||
pp.add_printer('fennec::file', '^fennec::file$', FilePrinter)
|
||||
return pp
|
||||
|
||||
printer = register_printers()
|
||||
99
gdb/fennec/math.py
Normal file
99
gdb/fennec/math.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
import os
|
||||
|
||||
import gdb
|
||||
from . import utility
|
||||
|
||||
# VECTOR =================================================================================================================
|
||||
class VectorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.base = val['data']['data']
|
||||
self.len = self.base.type.range()[1] + 1
|
||||
|
||||
def to_string(self):
|
||||
res = "< "
|
||||
if self.len > 0:
|
||||
res += "x = " + str(self.val['x'])
|
||||
if self.len > 1:
|
||||
res += ", y = " + str(self.val['y'])
|
||||
if self.len > 2:
|
||||
res += ", z = " + str(self.val['z'])
|
||||
if self.len > 3:
|
||||
res += ", w = " + str(self.val['w'])
|
||||
res += " >"
|
||||
return res
|
||||
|
||||
def children(self):
|
||||
return (('[{}]'.format(i), self.base[i]) for i in range(self.len))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
# PATH =================================================================================================================
|
||||
class QuaternionPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.base = val['data']['data']
|
||||
|
||||
def to_string(self):
|
||||
res = ("< "
|
||||
+ str(self.val['x']) + " i + "
|
||||
+ str(self.val['y']) + " j + "
|
||||
+ str(self.val['z']) + " k + "
|
||||
+ str(self.val['w']))
|
||||
res += " >"
|
||||
return res
|
||||
|
||||
def children(self):
|
||||
return (('[{}]'.format(i), self.base[i]) for i in range(4))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
# VECTOR =================================================================================================================
|
||||
class MatrixPrinter:
|
||||
def __init__(self, val):
|
||||
self.columns = val['data']['data']
|
||||
self.num_columns = self.columns.type.range()[1] + 1
|
||||
self.num_rows = val.type.template_argument(1)
|
||||
|
||||
def to_string(self):
|
||||
return "{ rows = " + str(self.num_rows) + ", columns = " + str(self.num_columns) + " }"
|
||||
|
||||
def children(self):
|
||||
return (('[{}]'.format(i), self.columns[i]) for i in range(self.num_columns))
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
# GDB Code =============================================================================================================
|
||||
|
||||
def register_printers():
|
||||
print("registering filesystem")
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::math")
|
||||
pp.add_printer('fennec::vector', '^fennec::vector<.*>$', VectorPrinter)
|
||||
pp.add_printer('fennec::quaternion', '^fennec::quaternion<.*>$', QuaternionPrinter)
|
||||
pp.add_printer('fennec::matrix', '^fennec::matrix<.*>$', MatrixPrinter)
|
||||
return pp
|
||||
|
||||
printer = register_printers()
|
||||
43
gdb/fennec/memory.py
Normal file
43
gdb/fennec/memory.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
import gdb
|
||||
|
||||
# ALLOCATION ===========================================================================================================
|
||||
class AllocationPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "{ capacity = " + str(self.val['_capacity']) + " }"
|
||||
|
||||
def children(self):
|
||||
size = int(self.val['_capacity'])
|
||||
start = self.val['_data']
|
||||
return (('[{}]'.format(i), start[i]) for i in range(size))
|
||||
|
||||
|
||||
# GDB Code =============================================================================================================
|
||||
|
||||
def register_printers():
|
||||
print("registering memory")
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::memory")
|
||||
pp.add_printer('fennec::allocation', '^fennec::allocation<.*>$', AllocationPrinter)
|
||||
return pp
|
||||
|
||||
printer = register_printers()
|
||||
61
gdb/fennec/strings.py
Normal file
61
gdb/fennec/strings.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
import gdb
|
||||
|
||||
# CSTRING ==============================================================================================================
|
||||
class CStringPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
def to_string(self):
|
||||
value = "\"" + self.val['_str'].string('', 'replace', self.val['_size']) + "\""
|
||||
return value
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
|
||||
# STRING ===============================================================================================================
|
||||
class StringPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val['_str']
|
||||
|
||||
def to_string(self):
|
||||
value = "\"" + self.val['_data'].string('', 'replace', self.val['_capacity'] - 1) + "\""
|
||||
return value
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
|
||||
# GDB Code =============================================================================================================
|
||||
|
||||
def register_printers():
|
||||
print("registering strings")
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::strings")
|
||||
pp.add_printer('fennec::cstring', '^fennec::cstring$', CStringPrinter)
|
||||
pp.add_printer('fennec::wcstring', '^fennec::wcstring$', CStringPrinter)
|
||||
pp.add_printer('fennec::string', '^fennec::_string<.*>$', StringPrinter)
|
||||
pp.add_printer('fennec::wstring', '^fennec::_wstring<.*>$', StringPrinter)
|
||||
return pp
|
||||
|
||||
printer = register_printers()
|
||||
20
gdb/fennec/utility.py
Normal file
20
gdb/fennec/utility.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
def printMembers(obj):
|
||||
print(str(dir(obj)))
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file array.h
|
||||
/// \brief fennec::array definition & implementation
|
||||
/// \file fennec/containers/array.h
|
||||
/// \brief A header containing the definition for a static/stack allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -34,101 +34,253 @@
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/assert.h>
|
||||
#include <fennec/lang/metasequences.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for fixed size arrays
|
||||
/// \brief Data Structure that defines a compile-time allocated array
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam ValueT value type
|
||||
/// \tparam ElemV number of elements
|
||||
///
|
||||
///
|
||||
template<typename ValueT, size_t ElemV>
|
||||
struct array
|
||||
{
|
||||
/// \tparam N number of elements
|
||||
template<typename ValueT, size_t N>
|
||||
struct array {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = ValueT; //!< Alias for \f$ValueT\f$
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
value_t data[N]; //!< Backing c-style array handle
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief backing c-style array handle
|
||||
ValueT elements[ElemV];
|
||||
/// \brief Returns the number of elements in the array.
|
||||
/// \returns \f$N\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
[[nodiscard]] constexpr size_t size() const { return N; }
|
||||
|
||||
///
|
||||
/// \brief Returns \f$true\f$ when the array is empty
|
||||
/// \returns \f$ElemV == 0\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
[[nodiscard]] constexpr bool_t is_empty() const { return N == 0; }
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Element Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \copydetails array::at(size_t) const
|
||||
constexpr ValueT& at(size_t i) { static_assert(i < ElemV); assert(i < ElemV); return elements[i]; }
|
||||
|
||||
///
|
||||
/// \brief access specified element, **with bounds checking**
|
||||
/// \details Returns a reference to the element at \c i
|
||||
/// \param i index of the element to return
|
||||
/// \return reference to the requested element
|
||||
///
|
||||
/// \par Time-Complexity
|
||||
/// Constant
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \par Space-Complexity
|
||||
/// Constant
|
||||
constexpr const ValueT& at(size_t i) const { static_assert(i < ElemV); assert(i < ElemV); return elements[i]; }
|
||||
|
||||
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
assertd(i < N, "Array Out of Bounds");
|
||||
return data[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \copydetails array::operator[](size_t) const
|
||||
constexpr ValueT& operator[](size_t i) { return elements[i]; }
|
||||
|
||||
///
|
||||
/// \brief access specified element
|
||||
/// \brief Indexed access
|
||||
/// \details Returns a reference to the element at \c i
|
||||
/// \param i index of the element to return
|
||||
/// \return reference to the requested element
|
||||
///
|
||||
/// \par Time-Complexity
|
||||
/// Constant
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \par Space-Complexity
|
||||
/// Constant
|
||||
constexpr const ValueT& operator[](size_t i) const { return elements[i]; }
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
assertd(i < N, "Array Out of Bounds");
|
||||
return data[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to \f$data[0]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& front() {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access the first element
|
||||
/// \returns A const-qualified reference to \f$data[0]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to \f$data[N - 1]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& back() {
|
||||
return data[N - 1];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access the last element
|
||||
/// \returns A const-qualified reference to \f$data[N - 1]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& back() const {
|
||||
return data[N - 1];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
/// \name Capacity
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements in the array
|
||||
/// \returns the number of elements in the array
|
||||
[[nodiscard]] constexpr size_t size() const { return ElemV; }
|
||||
|
||||
///
|
||||
/// \brief returns **true** when the array is empty
|
||||
/// \return \f$ElemV == 0\f$
|
||||
[[nodiscard]] constexpr bool_t empty() const { return ElemV == 0; }
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief
|
||||
friend constexpr bool_t operator==(const array& lhs, const array& rhs)
|
||||
{ return [lhs, rhs]<size_t...i>(index_sequence<i...>) -> bool_t // Lambda Declaration, Creates Indices at Compile Time
|
||||
{ return ((lhs[i] == rhs[i]) && ...); }(make_index_sequence<ElemV>{}); } // Lambda Implementation,
|
||||
/// \brief Checks if all elements in the arrays are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
friend constexpr bool_t operator==(const array& lhs, const array& rhs) {
|
||||
return array::_compare(lhs, rhs, make_index_metasequence<N>{});
|
||||
}
|
||||
|
||||
friend constexpr bool_t operator!=(const array& lhs, const array& rhs)
|
||||
{ return [lhs, rhs]<size_t...i>(index_sequence<i...>) -> bool_t
|
||||
{ return ((lhs[i] != rhs[i]) || ...); }(make_index_sequence<ElemV>{}); }
|
||||
///
|
||||
/// \brief Checks if any element in the arrays is not equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) {
|
||||
return not array::_compare(lhs, rhs, make_index_metasequence<N>{});
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the first element of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* begin() {
|
||||
return data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const-qualified pointer to the first element of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* begin() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to one after the end of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* end() {
|
||||
return data + N;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns A const-qualified pointer to one after the end of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* end() const {
|
||||
return data + N;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<size_t...i>
|
||||
static bool _compare(const array& lhs, const array& rhs, index_metasequence<i...>) {
|
||||
return ((lhs[i] == rhs[i]) and ...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
1268
include/fennec/containers/bintree.h
Normal file
1268
include/fennec/containers/bintree.h
Normal file
File diff suppressed because it is too large
Load Diff
323
include/fennec/containers/bitfield.h
Normal file
323
include/fennec/containers/bitfield.h
Normal file
@@ -0,0 +1,323 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/bitfield.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_BITFIELD_H
|
||||
#define FENNEC_CONTAINERS_BITFIELD_H
|
||||
|
||||
#include <fennec/containers/array.h>
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Bitfield Container with basic Bit Ops
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam N The number of bits in the bitfield
|
||||
template<size_t N>
|
||||
struct bitfield {
|
||||
|
||||
// Constants ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t bits = N; //!< The number of bits in the bitfield
|
||||
static constexpr size_t bytes = (N + 7) / 8; //!< The number of bytes that hold the bitfield
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default constructor.
|
||||
/// \details Initializes all bits with \f$0\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr bitfield()
|
||||
: _bytes() {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Boolean array constructor.
|
||||
/// \param arr An array of boolean values resembling each bit.
|
||||
/// \details Initializes each bit with the respective boolean value in \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
explicit constexpr bitfield(const bool (&arr)[N])
|
||||
: _bytes() {
|
||||
for (size_t i = 0; i < arr; ++i) {
|
||||
this->store(i, arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Index array constructor.
|
||||
/// \param arr An array of indices.
|
||||
/// \details Sets the bits of each index provided in \f$arr\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t I>
|
||||
explicit constexpr bitfield(const size_t (&arr)[I])
|
||||
: _bytes() {
|
||||
for (size_t i : arr) {
|
||||
this->set(i);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \param args A set of indices.
|
||||
/// \details This substitution assumes \f$ArgsT\ldots\f$ can be taken as an array of indices. <br>
|
||||
/// Sets the bits of each index provided in \f$args\ldots\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr bitfield(ArgsT&&...args)
|
||||
: _bytes() {
|
||||
(this->store(fennec::forward<ArgsT>(args), true), ...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Variadic array constructor
|
||||
/// \param args A set of boolean values.
|
||||
/// \details This substitution assumes \f$ArgsT\ldots\f$ can be taken as an array of booleans. <br>
|
||||
/// Initializes each bit with the respective boolean in \f$args\ldots\f$. <br>
|
||||
/// Does not necessitate the number of arguments be equal to the number of bits.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT> requires((is_bool_v<ArgsT> or is_convertible_v<ArgsT, bool>) and ...)
|
||||
constexpr bitfield(ArgsT&&...args)
|
||||
: _bytes() {
|
||||
size_t i = 0;
|
||||
(this->store(i++, fennec::forward<ArgsT>(args)), ...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param bf bitfield to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield(const bitfield& bf)
|
||||
: _bytes(bf._bytes) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param bf bitfield to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bitfield(bitfield&& bf) noexcept
|
||||
: _bytes(bf._bytes) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~bitfield() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param bf bitfield to copy
|
||||
/// \returns a reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield& operator=(const bitfield& bf) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param bf bitfield to move
|
||||
/// \returns a reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bitfield& operator=(bitfield&& bf) noexcept = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access & Modifiers ==================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access & Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief test a bit
|
||||
/// \param i the index of the bit
|
||||
/// \returns the value stored in the bit as a boolean
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bool test(size_t i) const {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
return _bytes[b] & (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief set a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void set(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] |= (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief clear a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void clear(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] &= ~(1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief toggle a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void toggle(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] ^= (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief store \f$v\f$ in bit \f$i\f$
|
||||
/// \param i the index of the bit
|
||||
/// \param v the value to store
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void store(size_t i, bool v) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
(_bytes[b] &= ~((1 << o))) |= ((v << o));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief not operator
|
||||
/// \returns a bitfield containing the bit-wise inverse
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield operator~() const {
|
||||
bitfield res = *this;
|
||||
res._inv_helper(make_index_metasequence_t<bytes>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
array<uint8_t, bytes> _bytes;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<size_t...I>
|
||||
void _inv_helper(index_metasequence<I...>) {
|
||||
((_bytes[I] = ~_bytes[I]), ...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_BITFIELD_H
|
||||
112
include/fennec/containers/containers.h
Normal file
112
include/fennec/containers/containers.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/containers.h
|
||||
/// \brief fennec containers library main header
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_CONTAINERS_H
|
||||
#define FENNEC_CONTAINERS_CONTAINERS_H
|
||||
|
||||
///
|
||||
/// \page fennec_containers Containers Library
|
||||
///
|
||||
/// \brief The fennec Containers Library.
|
||||
/// Includes various data structures, those specified in the C++ Standard Library, and custom data structures
|
||||
/// that fennec uses.
|
||||
///
|
||||
/// \code #include <fennec/containers/containers.h> \endcode
|
||||
///
|
||||
/// \section fennec_containers_container_section_properties Container Properties
|
||||
///
|
||||
/// | Property | Meaning |
|
||||
/// |:----------------|:-----------------------------------------------------------------------------------------------|
|
||||
/// | **stable** | Any pointer reference to an element remains constant for the lifetime of the container. |
|
||||
/// | **dynamic** | Memory for this container is allocated on the heap. |
|
||||
/// | **homogeneous** | The types of all elements are either identical, or inherit the same base type. |
|
||||
/// | **distinct** | Elements are guaranteed to be unique in their value. |
|
||||
/// | **ordered** | Elements are guaranteed to be in order, such that for any index \f$i\f$, \f$E_i < E_{i + 1}\f$ |
|
||||
/// | **space** | The amount of memory allocated with respect to the number of elements, in big-O notation. |
|
||||
/// | **linear** | Each element is sequential in terms of access. |
|
||||
/// | **access** | The runtime of the access operators and functions, in big-O notation. |
|
||||
/// | **find** | The runtime of finding an element in the container, in big-O notation. |
|
||||
/// | **insertion** | The runtime of inserting an element in the container, in big-O notation. |
|
||||
/// | **deletion** | The runtime of erasing an element in the container, in big-O notation. |
|
||||
/// | **space** | The space complexity of the container. |
|
||||
///
|
||||
///
|
||||
/// \section fennec_containers_section_cppstdlib C++ Standard Template Library
|
||||
///
|
||||
/// | Symbol | Implemented | Passed |
|
||||
/// |:----------------------------------------------------------------------------|:-----------:|:------:|
|
||||
/// | \ref fennec::generic "fennec::generic" `std::any` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::array "fennec::array" | ✅ | ✅ |
|
||||
/// | \ref fennec::bitfield "fennec::bitfield" `std::bitset` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::deque "fennec::deque" | 🚧 | 🚧 |
|
||||
/// | \ref fennec::dynarray "fennec::dynarray" `std::vector` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::list "fennec::list" | ✅ | ✅ |
|
||||
/// | \ref fennec::map "fennec::map" `std::unordered_map` | ✅ | ✅ |
|
||||
/// | \ref fennec::map_sequence "fennec::map_sequence" `std::map` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multiset "fennec::multiset" `std::unordered_multiset` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multisequence "fennec::multisequence" `std::multiset` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multimap "fennec::multimap" `std::unordered_multimap` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multimap_sequence "fennec::multimap_sequence" `std::multimap` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::optional "fennec::optional" | ✅ | ✅ |
|
||||
/// | \ref fennec::pair "fennec::pair" | ✅ | ✅ |
|
||||
/// | \ref fennec::sequence "fennec::sequence" `std::set` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::set "fennec::set" `std::unordered_set` | ✅ | ✅ |
|
||||
/// | \ref fennec::tuple "fennec::tuple" | 🚧 | 🚧 |
|
||||
/// | \ref fennec::variant "fennec::variant" | 🚧 | 🚧 |
|
||||
///
|
||||
///
|
||||
/// \section fennec_containers_section_fennec fennec
|
||||
///
|
||||
/// | Symbol | Implemented | Passed |
|
||||
/// |:-------------------------|:-----------:|:------:|
|
||||
/// | \ref fennec::graph | 🚧 | 🚧 |
|
||||
/// | \ref fennec::object_pool | 🚧 | 🚧 |
|
||||
/// | \ref fennec::rdtree | ✅ | ✅ |
|
||||
///
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#include <fennec/containers/traversal.h>
|
||||
|
||||
#include <fennec/containers/array.h>
|
||||
#include <fennec/containers/deque.h>
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/graph.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/map.h>
|
||||
#include <fennec/containers/object_pool.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/rdtree.h>
|
||||
#include <fennec/containers/set.h>
|
||||
#include <fennec/containers/tuple.h>
|
||||
|
||||
#endif // FENNEC_CONTAINERS_CONTAINERS_H
|
||||
442
include/fennec/containers/deque.h
Normal file
442
include/fennec/containers/deque.h
Normal file
@@ -0,0 +1,442 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/deque.h
|
||||
/// \brief A header containing the definition for a double-ended queue
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DEQUE_H
|
||||
#define FENNEC_CONTAINERS_DEQUE_H
|
||||
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
// TODO: Document
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief Data structure defining a double-ended queue.
|
||||
///
|
||||
/// \details
|
||||
/// This behaves the similar to fennec::list, however it does not allow arbitrary access, insertion, or deletion.
|
||||
/// It is one of the few data structures in this library that is stable, i.e. pointers to elements do not change.
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct deque {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The underlying allocator type
|
||||
using elem_t = node*; //!< The underlying element type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
class iterator;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque()
|
||||
: _alloc()
|
||||
, _first(nullptr)
|
||||
, _last(nullptr)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Constructor, initializes an empty deque with the specified allocator
|
||||
/// \param alloc the allocator to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque(const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _first(nullptr)
|
||||
, _last(nullptr)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param deque the deque to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
deque(const deque& deque)
|
||||
: _alloc(deque._alloc)
|
||||
, _first(nullptr)
|
||||
, _last(nullptr)
|
||||
, _size(0) {
|
||||
const elem_t node = deque._first;
|
||||
while (node) {
|
||||
this->push_back(node->value);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Deque Move Constructor
|
||||
/// \param deque the deque to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque(deque&& deque) noexcept
|
||||
: _alloc(deque._alloc)
|
||||
, _first(deque._first)
|
||||
, _last(deque._last)
|
||||
, _size(deque._size) {
|
||||
deque._first = nullptr;
|
||||
deque._last = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, calls deque::clear
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
~deque() {
|
||||
clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when the deque is empty, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns the number of elements in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns a reference to the first element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
value_t& front() {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _first->value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a const-qualified reference to the first element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const value_t& front() const {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _first->value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a reference to the last element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
value_t& back() {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _last->value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a const-qualified reference to the last element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const value_t& back() const {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _last->value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Push Front Move, moves a value to the front of the deque
|
||||
/// \param elem the value to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_front(value_t&& elem) {
|
||||
this->_push_front(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Copy, copies a value to the front of the deque
|
||||
/// \param elem the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_front(const value_t& elem) {
|
||||
this->_push_front(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Front, constructs a new value at the front of the deque
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments used to construct the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
void emplace_front(ArgsT&&...args) {
|
||||
this->_push_front(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Push Back Move, moves a value to the back of the deque
|
||||
/// \param elem the value to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_back(value_t&& elem) {
|
||||
this->_push_back(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Copy, copies a value to the back of the deque
|
||||
/// \param elem the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_back(const value_t& elem) {
|
||||
this->_push_back(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back, constructs a new value at the back of the deque
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments used to construct the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
void emplace_back(ArgsT&&...args) {
|
||||
this->_push_back(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the contents of the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
void clear() {
|
||||
elem_t it = _first;
|
||||
while (it) {
|
||||
elem_t next = it->next;
|
||||
fennec::destruct(it);
|
||||
_alloc.deallocate(it);
|
||||
it = next;
|
||||
}
|
||||
_first = nullptr;
|
||||
_last = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the First Element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void pop_front() {
|
||||
if (_first == nullptr) {
|
||||
return;
|
||||
}
|
||||
elem_t next = _first->next;
|
||||
fennec::destruct(_first);
|
||||
_alloc.deallocate(_first);
|
||||
_first = next;
|
||||
_last = next ? _last : nullptr;
|
||||
--_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the Last Element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void pop_back() {
|
||||
if (_last == nullptr) {
|
||||
return;
|
||||
}
|
||||
elem_t prev = _last->prev;
|
||||
fennec::destruct(_last);
|
||||
_alloc.deallocate(_last);
|
||||
_last = prev;
|
||||
_first = prev ? _first : nullptr;
|
||||
--_size;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
|
||||
/*
|
||||
* TODO: Decide whether you should be able to iterate over a deque
|
||||
*/
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
alloc_t _alloc;
|
||||
node *_first, *_last;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename...ArgsT>
|
||||
void _push_front(ArgsT&&...args) {
|
||||
elem_t next = _first;
|
||||
_first = _alloc.allocate(1);
|
||||
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
|
||||
if (next) {
|
||||
next->prev = _first;
|
||||
} else {
|
||||
_last = _first;
|
||||
}
|
||||
++_size;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void _push_back(ArgsT&&...args) {
|
||||
elem_t prev = _last;
|
||||
_last = _alloc.allocate(1);
|
||||
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
|
||||
if (prev) {
|
||||
prev->next = _last;
|
||||
} else {
|
||||
_first = _last;
|
||||
}
|
||||
++_size;
|
||||
}
|
||||
|
||||
|
||||
// Private Definitions =================================================================================================
|
||||
private:
|
||||
struct node {
|
||||
value_t value;
|
||||
node *prev, *next;
|
||||
|
||||
template<typename...ArgsT>
|
||||
node(node* prev, node* next, ArgsT&&...args)
|
||||
: value(fennec::forward<ArgsT>(args)...)
|
||||
, prev(prev), next(next) {
|
||||
}
|
||||
|
||||
~node() = default;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_DEQUE_H
|
||||
61
include/fennec/containers/detail/_tuple.h
Normal file
61
include/fennec/containers/detail/_tuple.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#define FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#include <fennec/lang/metasequences.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
|
||||
template <size_t I, typename T>
|
||||
struct _tuple_leaf
|
||||
{
|
||||
template <typename ArgT>
|
||||
constexpr _tuple_leaf(ArgT&& arg) : value(fennec::forward<ArgT>(arg)) {}
|
||||
|
||||
constexpr ~_tuple_leaf() = default;
|
||||
|
||||
constexpr _tuple_leaf& operator=(const _tuple_leaf&) = default;
|
||||
constexpr _tuple_leaf& operator=(_tuple_leaf&&) noexcept = default;
|
||||
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename, typename...>
|
||||
struct _tuple;
|
||||
|
||||
template <size_t...IndicesV, typename...TypesT>
|
||||
struct _tuple<index_metasequence<IndicesV...>, TypesT...> : _tuple_leaf<IndicesV, TypesT>...
|
||||
{
|
||||
template <typename...ArgsT>
|
||||
constexpr _tuple(ArgsT&&... args)
|
||||
: _tuple_leaf<IndicesV, TypesT>(fennec::forward<ArgsT>(args))... {
|
||||
}
|
||||
|
||||
constexpr _tuple& operator=(const _tuple&) = default;
|
||||
constexpr _tuple& operator=(_tuple&&) noexcept = default;
|
||||
|
||||
constexpr ~_tuple() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,20 +17,21 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file dynarray.h
|
||||
/// \brief fennec::array definition & implementation
|
||||
/// \file fennec/containers/dynarray.h
|
||||
/// \brief A header containing the definition for a dynamically allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DYNARRAY_H
|
||||
#define FENNEC_CONTAINERS_DYNARRAY_H
|
||||
|
||||
#include <fennec/containers/initializer_list.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/memory/new.h>
|
||||
@@ -38,127 +39,748 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
template<class TypeT, class Alloc>
|
||||
class dynarray
|
||||
{
|
||||
///
|
||||
///
|
||||
/// \brief Wrapper for dynamically sized and allocated arrays
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// This structure prefers shallow moves and deep copies.
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
struct dynarray {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using element_t = TypeT;
|
||||
using alloc_t = Alloc;
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
using alloc_t = Alloc; //!< Alias for the allocator type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty allocation.
|
||||
dynarray() : _alloc(8), _size(0) {}
|
||||
|
||||
///
|
||||
/// \breif Alloc Constructor, initalize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
||||
dynarray(const alloc_t& alloc) : _alloc(8, alloc), _size(0) {}
|
||||
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \brief Create an allocation with a size of `n` elements. All elements are initialized with the default constructor.
|
||||
dynarray(size_t n) : _alloc(n), _size(n)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) { fennec::construct(addr); }
|
||||
}
|
||||
|
||||
dynarray(size_t n, const alloc_t& alloc) : _alloc(n, alloc), _size(n)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) { fennec::construct(addr); }
|
||||
constexpr dynarray()
|
||||
: _alloc(8)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Create an allocation of size `n`, with each element constructed using the copy constructor
|
||||
/// \brief n the number of elements
|
||||
dynarray(size_t n, const TypeT& val)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) { fennec::construct(addr, val); }
|
||||
/// \brief Alloc Constructor, initialize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
|
||||
/// data.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
explicit constexpr dynarray(const alloc_t& alloc)
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation, initializes a dynarray with \f$n\f$ elements using the default constructor.
|
||||
/// \param n The number of elements.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
explicit constexpr dynarray(size_t n)
|
||||
: _alloc(n)
|
||||
, _size(n)
|
||||
{
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
|
||||
/// using the default constructor.
|
||||
/// \param n The number of elements
|
||||
/// \param alloc The allocator object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(size_t n, const alloc_t& alloc)
|
||||
: _alloc(n, alloc)
|
||||
, _size(n) {
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation Copy Constructor, Create an allocation of size \f$n\f$ elements, with each element
|
||||
/// constructed using the copy constructor
|
||||
/// \param n the number of elements
|
||||
/// \param val the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(size_t n, const TypeT& val)
|
||||
: _alloc(n)
|
||||
, _size(n) {
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam ArgsT A sequence of argument types
|
||||
/// \param n The number of objects to create
|
||||
/// \param args The arguments to create each object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
dynarray(size_t n, ArgsT...args)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) { fennec::construct(addr, args...); }
|
||||
constexpr explicit dynarray(size_t n, ArgsT&&...args)
|
||||
: _alloc(n)
|
||||
, _size(n) {
|
||||
for(; n > 0; --n) {
|
||||
fennec::construct(&_alloc[n], fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
~dynarray()
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) { fennec::destruct(addr); }
|
||||
///
|
||||
/// \brief Array Copy Constructor
|
||||
/// \tparam N The length of the array, automatically deduced
|
||||
/// \param arr The array to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray(const TypeT (&arr)[N])
|
||||
: _alloc(N)
|
||||
, _size(N) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
_alloc[i] = arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const { return _size; }
|
||||
///
|
||||
/// \brief Array Move Constructor
|
||||
/// \tparam N The length of the array, automatically deduced
|
||||
/// \param arr The array to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray(TypeT (&&arr)[N])
|
||||
: _alloc(N)
|
||||
, _size(N) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
_alloc[i] = fennec::move(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t capacity() const { return _alloc.capacity(); }
|
||||
///
|
||||
/// \brief Conversion Constructor, copies elements of conv as this \f$value_t\f$
|
||||
/// \tparam OTypeT The other value type
|
||||
/// \tparam OAlloc The other allocator type
|
||||
/// \param conv The dynarray to convert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OTypeT, class OAlloc>
|
||||
constexpr dynarray(const dynarray<OTypeT, OAlloc>& conv)
|
||||
: _alloc(conv.size())
|
||||
, _size(conv.size()) {
|
||||
size_t i = 0;
|
||||
for (const auto& it : conv) {
|
||||
fennec::construct(&_alloc[i++], it);
|
||||
}
|
||||
}
|
||||
|
||||
TypeT& operator[](size_t i) { return _alloc.data()[i]; }
|
||||
const TypeT& operator[](size_t i) const { return _alloc.data()[i]; }
|
||||
///
|
||||
/// \brief Initializer List Constructor
|
||||
/// \param l List of elements to initialize with
|
||||
/// \param alloc An allocator object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(initializer_list<value_t> l, const alloc_t& alloc = alloc_t())
|
||||
: _alloc(l.size(), alloc)
|
||||
, _size(l.size()) {
|
||||
size_t i = 0;
|
||||
for (auto& it : l) {
|
||||
fennec::construct(&_alloc[i++], fennec::move(it));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, uses the copy constructor to copy each element
|
||||
/// \param arr the dynarray to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(const dynarray& arr)
|
||||
: _alloc(arr._size)
|
||||
, _size(arr._size) {
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of the allocation
|
||||
/// \param arr the dynarray to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr dynarray(dynarray&& arr) noexcept
|
||||
: _alloc(fennec::move(arr._alloc))
|
||||
, _size(arr._size) {
|
||||
arr._size = 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~dynarray() {
|
||||
value_t* addr = _alloc.data();
|
||||
if (addr == nullptr) return;
|
||||
for(int n = _size; n > 0; --n, ++addr) {
|
||||
fennec::destruct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
private:
|
||||
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
||||
// behaviour when moving to multiple elements
|
||||
constexpr dynarray(size_t n, TypeT&& val) = delete;
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having copied each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray& operator=(const dynarray& arr) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = arr._size);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param arr the array to move
|
||||
/// \returns A dynarray after having taken ownership of the contents of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr dynarray& operator=(dynarray&& arr) noexcept {
|
||||
this->clear();
|
||||
_alloc = fennec::move(arr._alloc);
|
||||
_size = arr._size;
|
||||
arr._size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Copy Assignment Operator
|
||||
/// \tparam N the length of the array
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having copied each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray& operator=(const TypeT (&arr)[N]) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = N);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Copy Assignment Operator
|
||||
/// \tparam N the length of the array
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having moved each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray& operator=(TypeT (&&arr)[N]) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = N);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::move(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the dynarray in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The current capacity, in elements, of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns True when there are no elements active, otherwise false
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Element Access ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i The index to access
|
||||
/// \returns A reference to the element at index \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& operator[](size_t i) {
|
||||
assertd(i < _size, "Array Out of Bounds");
|
||||
return _alloc[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator (const)
|
||||
/// \param i The index to access
|
||||
/// \returns A const qualified reference to the element at index \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& operator[](size_t i) const {
|
||||
assertd(i < _size, "Array Out of Bounds");
|
||||
return _alloc[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Reference to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& front() {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified reference to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& front() const {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& back() {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified reference to the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& back() const {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* data() {
|
||||
return _alloc.data();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT* data() const {
|
||||
return _alloc.data();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void insert(size_t i, TypeT&& val) {
|
||||
|
||||
void insert(size_t i, const TypeT& val)
|
||||
{
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) _grow();
|
||||
if(_size == capacity()) {
|
||||
_grow();
|
||||
}
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = fennec::min(i, _size) < _size) {
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i
|
||||
, _alloc.data() + i + 1
|
||||
(void*)(_alloc.data() + i + 1)
|
||||
, (void*)(_alloc.data() + i)
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, fennec::forward<TypeT>(val));
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void insert(size_t i, const TypeT& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) {
|
||||
_grow();
|
||||
}
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
(void*)(_alloc.data() + i),
|
||||
(void*)(_alloc.data() + i + 1),
|
||||
(_size - i) * sizeof(TypeT)
|
||||
);
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, val);
|
||||
++_size;
|
||||
}
|
||||
|
||||
void insert(size_t i, TypeT&& val)
|
||||
{
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) _grow();
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(size_t i, ArgsT&&...args) {
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = fennec::min(i, _size) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i
|
||||
, _alloc.data() + i + 1
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) {
|
||||
_grow();
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, fennec::forward(val));
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void emplace(size_t i, ArgsT...args)
|
||||
{
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) _grow();
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = fennec::min(i, _size) < _size) {
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i
|
||||
, _alloc.data() + i + 1
|
||||
(void*)(_alloc.data() + i)
|
||||
, (void*)(_alloc.data() + i + 1)
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, fennec::forward<ArgsT>(args)...);
|
||||
++_size;
|
||||
}
|
||||
|
||||
void push_back(const TypeT& val) { insert(_size, val); }
|
||||
void push_back(TypeT&& val) { insert(_size, fennec::forward(val)); }
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param val Value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void push_back(const TypeT& val) {
|
||||
dynarray::insert(_size, val);
|
||||
}
|
||||
|
||||
template<typename...ArgsT> void emplace_back(ArgsT...args) { emplace(_size, fennec::forward<ArgsT>(args)...); }
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param val Value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void push_back(TypeT&& val) {
|
||||
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace_back(ArgsT...args) {
|
||||
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase last element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_back() {
|
||||
fennec::destruct(&_alloc[--_size]);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Resize the dynarray, invoking the default constructor for all new elements
|
||||
/// \param n The new size in elements
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void resize(size_t n) {
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
|
||||
for (size_t i = _size; i < n; ++i) {
|
||||
fennec::construct(&_alloc[i]);
|
||||
}
|
||||
|
||||
_size = n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Resize the dynarray, invoking the copy constructor for all new elements
|
||||
/// \param n The new size in elements
|
||||
/// \param val The value to fill with
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void resize(size_t n, const TypeT& val) {
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
|
||||
for (size_t i = _size; i < n; ++i) {
|
||||
fennec::construct(&_alloc[i], val);
|
||||
}
|
||||
|
||||
_size = n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Reserve the array, allocating new space without initialization
|
||||
/// \param n The new capacity in elements
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void reserve(size_t n) {
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_reduce(0);
|
||||
_alloc.deallocate();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* begin() { return _alloc; }
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const qualified pointer to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT* begin() const { return _alloc; }
|
||||
|
||||
|
||||
///
|
||||
/// \return A pointer to the address after the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* end() { return begin() + _size; }
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \return A const qualified pointer to the address after the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT* end() const { return begin() + _size; }
|
||||
|
||||
/// @}
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
private:
|
||||
void _grow() const { _alloc.reallocate(_alloc.capacity() * 2); }
|
||||
|
||||
allocation<element_t, alloc_t> _alloc;
|
||||
allocation<value_t, alloc_t> _alloc;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
// helper to double the capacity of the allocation
|
||||
constexpr void _grow() {
|
||||
_alloc.reallocate(_alloc.capacity() * 2);
|
||||
}
|
||||
|
||||
// helper to destruct elements past n
|
||||
constexpr void _reduce(size_t n) {
|
||||
while (_size > n) {
|
||||
fennec::destruct(&_alloc[--_size]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
363
include/fennec/containers/generic.h
Normal file
363
include/fennec/containers/generic.h
Normal file
@@ -0,0 +1,363 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/generic.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_GENERIC_H
|
||||
#define FENNEC_CONTAINERS_GENERIC_H
|
||||
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/rtti/type.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A struct capable of holding a single object of any type
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
struct generic {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
|
||||
// based on GCC
|
||||
enum op_ : uint8_t {
|
||||
op_clone,
|
||||
op_destroy,
|
||||
op_type,
|
||||
};
|
||||
|
||||
using manager_t = void* (*)(uint8_t, void*);
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic()
|
||||
: _handle(nullptr)
|
||||
, _manage(nullptr) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param gen The generic object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic(const generic& gen)
|
||||
: _handle(nullptr)
|
||||
, _manage(gen._manage) {
|
||||
if (_manage) {
|
||||
_handle = _manage(op_clone, gen._handle);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param gen The generic object to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic(generic&& gen)
|
||||
: _handle(gen._handle)
|
||||
, _manage(gen._manage) {
|
||||
gen._handle = nullptr;
|
||||
gen._manage = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Value Constructor
|
||||
/// \tparam T The type of the value
|
||||
/// \param x The value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
generic(T&& x)
|
||||
: _handle(new T(fennec::forward<T>(x)))
|
||||
, _manage(_manage_impl<T>) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam T The type to construct
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
generic(type_identity<T>, ArgsT&&...args)
|
||||
: _handle(new T(fennec::forward<ArgsT>(args)...))
|
||||
, _manage(_manage_impl<T>) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
~generic() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief runtime type acquisition
|
||||
/// \returns a runtime type struct referencing the held type
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
type type() const {
|
||||
return *static_cast<fennec::type*>(_manage(op_type, nullptr));
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if there is a held value, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bool has_value() const {
|
||||
return _handle != nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param gen the generic to copy
|
||||
/// \returns a reference to self after copying the contents of \f$gen\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic& operator=(const generic& gen) {
|
||||
if (this == &gen) { // self-assignment case
|
||||
return *this;
|
||||
}
|
||||
|
||||
reset();
|
||||
_manage = gen._manage;
|
||||
_handle = _manage(op_clone, gen._handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param gen the generic to move
|
||||
/// \returns a reference to self after swapping contents with \f$gen\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic& operator=(generic&& gen) noexcept {
|
||||
swap(gen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief value assignment
|
||||
/// \tparam T the type of the value
|
||||
/// \param x the value to assign
|
||||
/// \returns a reference to self after having assigned \f$x\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
generic& operator=(T&& x) {
|
||||
reset();
|
||||
_handle = new T(fennec::forward<T>(x));
|
||||
_manage = _manage_impl<T>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief emplace value
|
||||
///
|
||||
/// \details constructs a new value of type \f$T\f$ using \f$args\ldots\f$
|
||||
/// \tparam T the type to construct
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
void emplace(ArgsT&&...args) {
|
||||
reset();
|
||||
_handle = new T(fennec::forward<ArgsT>(args)...);
|
||||
_manage = _manage_impl<T>;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief reset value
|
||||
/// \details clears the held value using the appropriate destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void reset() {
|
||||
if (_manage) {
|
||||
_handle = _manage(op_destroy, _handle);
|
||||
_manage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ 11 Swap Specification
|
||||
/// \param gen the generic to swap with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void swap(generic& gen) noexcept {
|
||||
fennec::swap(_handle, gen._handle);
|
||||
fennec::swap(_manage, gen._manage);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Casting =============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Casting
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief cast value
|
||||
///
|
||||
/// \details equivalent to \f$reinterpret_cast\f$
|
||||
/// \tparam T The type to cast to
|
||||
/// \returns The contents of generic after having cast to \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename U = remove_cvref_t<T>>
|
||||
T cast() {
|
||||
return static_cast<T>(*static_cast<U*>(_handle));
|
||||
}
|
||||
|
||||
///
|
||||
/// \details equivalent to \f$reinterpret_cast\f$
|
||||
/// \tparam T The type to cast to
|
||||
/// \returns The contents of generic after having cast to \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename U = remove_cvref_t<T>>
|
||||
T cast() const {
|
||||
return static_cast<T>(*static_cast<U*>(_handle));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
void* _handle;
|
||||
manager_t _manage;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename T>
|
||||
static void* _manage_impl(uint8_t op, void* hnd) {
|
||||
static fennec::type t = type::get<T>();
|
||||
T* ptr = hnd;
|
||||
|
||||
switch (op) {
|
||||
case op_clone:
|
||||
return new T(*ptr);
|
||||
case op_destroy:
|
||||
delete ptr;
|
||||
return nullptr;
|
||||
case op_type:
|
||||
return &t;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_GENERIC_H
|
||||
650
include/fennec/containers/graph.h
Normal file
650
include/fennec/containers/graph.h
Normal file
@@ -0,0 +1,650 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/graph.h
|
||||
/// \brief A header containing the definition for a graph of vertices connected by edges
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_GRAPH_H
|
||||
#define FENNEC_CONTAINERS_GRAPH_H
|
||||
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/map.h>
|
||||
#include <fennec/containers/object_pool.h>
|
||||
#include <fennec/containers/set.h>
|
||||
|
||||
/*
|
||||
* With the directed tree we were able to cheat a little, the structure has more rules to it which allows
|
||||
* tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
|
||||
* properties or rules to edges which can simply be an extension to this graph.
|
||||
*
|
||||
* The most effective way to do this is to have a dynarray of lists, however this results in double the
|
||||
* memory being used. This can also result in two edge objects being created.
|
||||
*
|
||||
* There is no nice way to avoid the problem of mapping vertices to edges
|
||||
*/
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Graph Data Structure, describes sets of arbitrarily connected vertices
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:--------------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(M)\f$ |
|
||||
/// | space | \f$O(N + M)\f$ |
|
||||
///
|
||||
/// Graphs contain vertices and edges. Graphs are either directed
|
||||
/// or undirected. This structure allows the creation of both directed and undirected edges. As
|
||||
/// far as what that means; a directed graph means that edges have direction, where there are edges
|
||||
/// that are "to" and "from," rather than "between" which is used in undirected graphs.
|
||||
///
|
||||
/// An undirected graph is connected if there is a path between every pair of vertices in the graph.
|
||||
///
|
||||
/// A directed graph is weakly connected if replacing all of its directed edges with undirected edges would
|
||||
/// produce a connected graph. We will call this "disjointed"
|
||||
///
|
||||
/// A directed graph is semi-connected if there is a directed path p for \f$u\f$ → \f$v\f$ *or* \f$v\f$ → \f$u\f$ for every
|
||||
/// pair of vertices \f$[u, v]\f$. We will call this "unilateral"
|
||||
///
|
||||
/// A directed graph is strongly-connected if there is a directed path p for \f$u\f$ → \f$v\f$ *and* \f$v\f$ → \f$u\f$ for every pair
|
||||
/// of vertices \f$[u, v]\f$. We will call this "connected"
|
||||
///
|
||||
/// \tparam VertexT The type associated with each vertex
|
||||
/// \tparam EdgeT The type associated with each edge
|
||||
template<typename VertexT, typename EdgeT = empty_t>
|
||||
struct graph {
|
||||
public:
|
||||
// Definitions =========================================================================================================
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using edge_t = EdgeT; //!< Alias for the edge type
|
||||
using vertex_t = VertexT; //!< Alias for the vertex type
|
||||
using vertex_pool_t = object_pool<vertex_t>; //!< Alias for a pool of vertices
|
||||
using edge_map_t = dynarray<map<size_t, size_t>>; //!< Alias for edge mapping
|
||||
using edge_pool_t = object_pool<edge_t>; //!< Alias for a pool of edges
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t npos = -1; //!< Constant for a non-existent vertex
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr graph() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
constexpr ~graph() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param g The graph to copy
|
||||
/// \returns A reference to this after assigning g
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
constexpr graph& operator=(const graph& g) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param g The graph to copy
|
||||
/// \returns A reference to this after assigning g
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr graph& operator=(graph&& g) = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of vertices in the graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_vertices() const {
|
||||
return _vertex_pool.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The number of edges in the graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_edges() const {
|
||||
return _edge_pool.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the vertex pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _vertex_pool.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no vertices in the graph, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return num_vertices() == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e\f$ that starts from \f$a\f$ and ends at \f$b\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if the edge exists, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool exists(size_t a, size_t b) const {
|
||||
return _edge_map[a][b] != nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e0\f$ that starts from \f$a\f$ and ends at \f$b\f$ and \f$e1\f$ that starts from \f$b\f$
|
||||
/// and ends at \f$a\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_symmetric(size_t a, size_t b) const {
|
||||
return exists(a, b) and exists(b, a);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e\f$ between \f$a\f$ and \f$b\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_undirected(size_t a, size_t b) const {
|
||||
const auto* e0 = _edge_map[a][b];
|
||||
const auto* e1 = _edge_map[b][a];
|
||||
if (not (e0 != nullptr && e1 != nullptr)) {
|
||||
return false;
|
||||
}
|
||||
return *e0 == *e1;
|
||||
}
|
||||
|
||||
// TODO: connected, disjoint, unilateral, get_component
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief vertex Access Operator
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A reference to the value stored in the vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr vertex_t& operator[](size_t vertex) {
|
||||
return _vertex_pool[vertex];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief vertex Const Access Operator
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A reference to the value stored in the vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const vertex_t& operator[](size_t vertex) const {
|
||||
return _vertex_pool[vertex];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief edge Access Operator
|
||||
/// \param a The id of the first vertex
|
||||
/// \param b The id of the second vertex
|
||||
/// \returns A pointer to the value stored in the edge, \f$nullptr\f$ if not found
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr edge_t* operator[](size_t a, size_t b) {
|
||||
if (is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
edge_t* it = _edge_map[a][b];
|
||||
if (it) {
|
||||
return _edge_pool[*it];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief edge Const Access Operator
|
||||
/// \param a The id of the first vertex
|
||||
/// \param b The id of the second vertex
|
||||
/// \returns A const-qualified pointer to the value stored in the edge, \f$nullptr\f$ if not found
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const edge_t* operator[](size_t a, size_t b) const {
|
||||
if (is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const edge_t* it = _edge_map[a][b];
|
||||
if (it) {
|
||||
return _edge_pool[*it];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ with edges from \f$vertex\f$ to \f$x\ldots\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> outgoing(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge from \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ with edges from \f$x\ldots\f$ to \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> incoming(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (size_t n = 0; n < _edge_map.size(); ++n) {
|
||||
if (_edge_map[n][vertex]) {
|
||||
res.push_back(n);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> symmetric(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
if (_edge_map[it.first][vertex]) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from \f$x\ldots\f$ and share the same value
|
||||
/// \details
|
||||
/// "Joined" edges may also be referred to as "undirected." A joined, or undirected, edge may be
|
||||
/// turned into a directed edge by changing the weight object associated with the edge, or by
|
||||
/// removing one of the sub-edges.
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> undirected(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
const auto* at = _edge_map[it.first][vertex];
|
||||
if (at != nullptr && *at == it.second) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for the internal storage of mapped edges from this vertex.
|
||||
/// Use this when you want to iterate over edges that start from this vertex.
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A pointer to a map containing edges mapped from this vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
const auto* edges(size_t vertex) {
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_edge_map[vertex];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move a new vertex into the graph
|
||||
/// \param vertex The vertex to move into the graph
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(vertex_t&& vertex) {
|
||||
return this->_insert(fennec::forward<vertex_t>(vertex));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy a new vertex into the graph
|
||||
/// \param vertex The vertex to copy into the graph
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(const vertex_t& vertex) {
|
||||
return this->_insert(vertex);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Construct a new vertex in the graph
|
||||
/// \tparam ArgsT The types of the arguments
|
||||
/// \param args The arguments to construct the vertex with
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(ArgsT&&...args) {
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a vertex from the graph
|
||||
/// \param vertex The id of the vertex to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
constexpr void erase(size_t vertex) {
|
||||
cut(vertex);
|
||||
_vertex_pool.erase(vertex);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Form an edge from vertex \f$a\f$ to vertex \f$b\f$
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
/// \param args The arguments to construct the edge with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
|
||||
if (a == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_edge_map.size() < _vertex_pool.capacity()) {
|
||||
_edge_map.resize(_vertex_pool.capacity());
|
||||
}
|
||||
|
||||
auto it = _edge_map[a][b];
|
||||
size_t conn;
|
||||
if (it != nullptr) {
|
||||
conn = *it;
|
||||
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
_edge_map[a].emplace(b, conn);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Form an undirected edge between vertex \f$a\f$ and vertex \f$b\f$
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
/// \param args The arguments to construct the edge with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void make_edge2(size_t a, size_t b, ArgsT&&...args) {
|
||||
if (a == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_edge_map.size() < _vertex_pool.capacity()) {
|
||||
_edge_map.resize(_vertex_pool.capacity());
|
||||
}
|
||||
|
||||
auto it = _edge_map[a][b];
|
||||
size_t conn;
|
||||
if (it != nullptr) {
|
||||
conn = *it;
|
||||
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
_edge_map[a].emplace(b, conn);
|
||||
_edge_map[b].emplace(a, conn);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Disconnect an edge from vertex \f$a\f$ to vertex \f$b\f$
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void cut_edge(size_t a, size_t b) {
|
||||
|
||||
// Find the edge object
|
||||
const auto* it = _edge_map[a][b];
|
||||
if (not it) {
|
||||
return;
|
||||
}
|
||||
size_t c = *it;
|
||||
|
||||
// Check if undirected
|
||||
const auto* at = _edge_map[b][a];
|
||||
if (not at || *at != c) {
|
||||
_edge_pool.erase(c);
|
||||
}
|
||||
|
||||
// Erase the edge mapping
|
||||
_edge_map[a].erase(b);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Disconnect both directed edges between vertices \f$a\f$ and \f$b\f$
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void cut_edge2(size_t a, size_t b) {
|
||||
const auto* ita = _edge_map[a][b];
|
||||
const auto* itb = _edge_map[a][b];
|
||||
if (not (ita || itb)) {
|
||||
return;
|
||||
}
|
||||
if (ita) _edge_pool.erase(*ita);
|
||||
if (itb) _edge_pool.erase(*itb);
|
||||
_edge_map[a].erase(b);
|
||||
_edge_map[b].erase(a);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Break *all* edges connected to \f$n\f$
|
||||
/// \param n The vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
void cut(size_t n) {
|
||||
for (const auto it : outgoing(n)) {
|
||||
cut_edge(n, it);
|
||||
}
|
||||
for (const auto it : incoming(n)) {
|
||||
cut_edge(it, n);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clear the graph, destructing all vertices and edges.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
void clear() {
|
||||
_vertex_pool.clear();
|
||||
_edge_pool.clear();
|
||||
_edge_map.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// edges =========================================================================================================
|
||||
|
||||
|
||||
private:
|
||||
vertex_pool_t _vertex_pool;
|
||||
edge_pool_t _edge_pool;
|
||||
edge_map_t _edge_map;
|
||||
|
||||
template<typename...ArgsT>
|
||||
size_t _insert(ArgsT&&...args) {
|
||||
return _vertex_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_GRAPH_H
|
||||
63
include/fennec/containers/initializer_list.h
Normal file
63
include/fennec/containers/initializer_list.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/initializer_list.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
#define FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
|
||||
// Since initializer lists are completely intertwined with the compiler, and this is part of the c++ standard
|
||||
// (specifically standard, and not the standard template library) we need to use std::initializer_list.
|
||||
// We can at least alias it for proper naming conventions.
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
using std::initializer_list;
|
||||
|
||||
///
|
||||
/// \param inls the initializer list
|
||||
/// \returns A const qualified pointer to the first element in \f$inls\f$
|
||||
template<typename T>
|
||||
constexpr const T* begin(initializer_list<T> inls) noexcept {
|
||||
return inls.begin();
|
||||
}
|
||||
|
||||
///
|
||||
/// \param inls the initializer list
|
||||
/// \returns A const qualified pointer to one past the last element in \f$inls\f$
|
||||
template<typename T>
|
||||
constexpr const T* end(initializer_list<T> inls) noexcept {
|
||||
return inls.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
867
include/fennec/containers/list.h
Normal file
867
include/fennec/containers/list.h
Normal file
@@ -0,0 +1,867 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/list.h
|
||||
/// \brief A header containing the definition for a linked list of values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_LIST_H
|
||||
#define FENNEC_CONTAINERS_LIST_H
|
||||
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
#include <fennec/math/common.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief Data Structure defining lists of elements
|
||||
/// \details
|
||||
/// This data-structure behaves like a linked list, but does not use pointers. Instead, it is in-array. This creates the
|
||||
/// following properties:
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(N)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \note Access, Insertion, and Deletion are \f$O(N)\f$ using the index pattern,
|
||||
/// using the iterator pattern yields \f$O(1)\f$ runtime.
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
struct list {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
/// \brief Alias for the allocator type, rebound to list nodes
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<node>;
|
||||
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
|
||||
static constexpr size_t npos = -1; //!< Constant representing a non-existant position
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
private:
|
||||
using table_t = dynarray<node, alloc_t>;
|
||||
using freed_t = dynarray<size_t>;
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty list.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list()
|
||||
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies all elements in \f$l\f$ with optimized layout
|
||||
/// \param l The list to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr list(const list& l)
|
||||
: list() {
|
||||
for (const value_t& it : l) {
|
||||
this->push_back(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of the list
|
||||
/// \param l The list to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list(list&& l) noexcept
|
||||
: _table(fennec::move(l._table))
|
||||
, _freed(fennec::move(l._freed))
|
||||
, _root(l._root)
|
||||
, _last(l._last)
|
||||
, _size(l._size) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, destructs all elements then releases the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~list() {
|
||||
clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param l the list to copy
|
||||
/// \returns \f$this\f$ after having copied all elements of \f$l\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr list& operator=(const list& l) {
|
||||
this->clear();
|
||||
for (const value_t& it : l) {
|
||||
this->push_back(it);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param l the list to copy
|
||||
/// \returns \f$this\f$ after having taken ownership over the contents of \f$l\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list& operator=(list&& l) noexcept {
|
||||
this->clear();
|
||||
_table = fennec::move(l._table);
|
||||
_freed = fennec::move(l._freed);
|
||||
_root = l._root; _last = l._last;
|
||||
_size = l._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the list in elements.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the list in elements.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when the list is empty, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _root == npos;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A reference to the element at \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr value_t& operator[](int i) {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
assertd(n != npos, "Index out of Bounds");
|
||||
return *_table[n].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A const-qualified reference to the element at \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](int i) const {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
assertd(n != npos, "Index out of Bounds");
|
||||
return *_table[n].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access Front Element
|
||||
/// \returns A reference to the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& front() {
|
||||
return *_table[_root].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Access Front Element
|
||||
/// \returns A const-qualified reference to the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return *_table[_root].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access Back Element
|
||||
/// \returns A reference to the last element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& back() {
|
||||
return *_table[_last].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Access Back Element
|
||||
/// \returns A const-qualified reference to the last element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& back() const {
|
||||
return *_table[_last].value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const iterator& it, const value_t& x) {
|
||||
return this->_insert(it._n, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const iterator& it, value_t&& x) {
|
||||
return this->_insert(it._n, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr iterator insert(size_t i, const value_t& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr iterator insert(size_t i, value_t&& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param i Index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace(size_t i, ArgsT&&...args) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param it Location to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace(const iterator& it, ArgsT&&...args) {
|
||||
return this->_insert(it._n, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Copy
|
||||
/// \param x Value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_front(const value_t& x) {
|
||||
return this->_insert(_root, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Move
|
||||
/// \param x Value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_front(value_t&& x) {
|
||||
return this->_insert(_root, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Front
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace_front(ArgsT&&...args) {
|
||||
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param x Value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_back(const value_t& x) {
|
||||
return this->_insert(npos, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param x Value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_back(value_t&& x) {
|
||||
return this->_insert(npos, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace_back(ArgsT&&...args) {
|
||||
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param i Index to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void erase(size_t i) {
|
||||
assert(i < size(), "Index out of Bounds!");
|
||||
size_t n = _walk(i);
|
||||
_erase(n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param it Location to Erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const iterator& it) {
|
||||
this->_erase(it._n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Front, erases first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_front() {
|
||||
_erase(_root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Back, erases first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_back() {
|
||||
_erase(_last);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the list, destructing all elements in order
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
size_t i = _root;
|
||||
while (i != npos) {
|
||||
_table[i].value = nullopt;
|
||||
i = this->_next(i);
|
||||
}
|
||||
_table.clear();
|
||||
_root = npos;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns An iterator for the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const iterator for the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator begin() const {
|
||||
return const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator for the end of the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const C++ Iterator Specification \f$end()\f$
|
||||
/// \returns A const iterator for the end of the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
~iterator() {
|
||||
_list = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param rhs the iterator to increment
|
||||
/// \returns \f$rhs\f$ after having moved to the next element in the list
|
||||
constexpr friend iterator& operator++(iterator& rhs) {
|
||||
rhs._n = rhs._list->_next(rhs._n);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param lhs the iterator to increment
|
||||
/// \returns \f$lhs\f$ before having moved to the next element in the list
|
||||
constexpr friend iterator operator++(iterator& lhs, int) {
|
||||
iterator prev = lhs;
|
||||
++lhs;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr value_t& operator*() {
|
||||
return *(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr value_t* operator->() {
|
||||
return &*(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& it) {
|
||||
return _list == it._list and _n == it._n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& it) {
|
||||
return _list != it._list or _n != it._n;
|
||||
}
|
||||
|
||||
private:
|
||||
list* _list;
|
||||
size_t _n;
|
||||
friend list;
|
||||
|
||||
iterator(list* ls, size_t n)
|
||||
: _list(ls)
|
||||
, _n(n) {
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Iterator Class for Const Access
|
||||
class const_iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
~const_iterator() {
|
||||
_list = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param rhs the iterator to increment
|
||||
/// \returns \f$rhs\f$ after having moved to the next element in the list
|
||||
constexpr friend const_iterator& operator++(const_iterator& rhs) {
|
||||
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
||||
return rhs;
|
||||
}
|
||||
rhs._n = npos;
|
||||
return rhs;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param lhs the iterator to increment
|
||||
/// \returns \f$lhs\f$ before having moved to the next element in the list
|
||||
constexpr friend const_iterator operator++(const_iterator& lhs, int) {
|
||||
const_iterator prev = lhs;
|
||||
++lhs;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr const value_t& operator*() {
|
||||
return *(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr const value_t* operator->() {
|
||||
return &*(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const const_iterator& it) {
|
||||
return _list == it._list and _n == it._n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const const_iterator& it) {
|
||||
return _list != it._list or _n != it._n;
|
||||
}
|
||||
|
||||
private:
|
||||
const list* _list;
|
||||
size_t _n;
|
||||
friend list;
|
||||
|
||||
const_iterator(const list* ls, size_t n)
|
||||
: _list(ls)
|
||||
, _n(n) {
|
||||
}
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _root, _last, _size;
|
||||
|
||||
friend class iterator;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
_table.resize(fennec::max(_table.size(), size_t(4)) * 2);
|
||||
}
|
||||
|
||||
struct node {
|
||||
optional<value_t> value;
|
||||
size_t prev, next;
|
||||
|
||||
constexpr node()
|
||||
: value()
|
||||
, prev(npos)
|
||||
, next(npos) {
|
||||
}
|
||||
|
||||
constexpr ~node() = default;
|
||||
|
||||
constexpr void clear() {
|
||||
value = nullopt;
|
||||
prev = npos;
|
||||
next = npos;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr size_t _next(size_t n) const {
|
||||
return _table[n].next;
|
||||
}
|
||||
|
||||
constexpr size_t _prev(size_t n) const {
|
||||
return _table[n].prev;
|
||||
}
|
||||
|
||||
constexpr size_t _walk(size_t i) const {
|
||||
size_t n = _root;
|
||||
if (n == npos) return n;
|
||||
while (i > 0 && n != npos) {
|
||||
n = _next(n); --i;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
constexpr size_t _next_free() {
|
||||
if (not _freed.is_empty()) {
|
||||
size_t n = _freed.back();
|
||||
_freed.pop_back();
|
||||
return n;
|
||||
}
|
||||
return _size;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator _insert(size_t n, ArgsT&&...args) {
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
++_size;
|
||||
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
|
||||
|
||||
if (_root == npos) {
|
||||
_table[i].prev = npos;
|
||||
_table[i].next = npos;
|
||||
_root = _last = i;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
if (n == npos) {
|
||||
_table[_last].next = i;
|
||||
_table[i].prev = _last;
|
||||
_table[i].next = npos;
|
||||
_last = i;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
_table[i].prev = _prev(n);
|
||||
_table[i].next = n;
|
||||
_table[n].prev = i;
|
||||
_root = n == _root ? i : _root;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
constexpr void _erase(size_t n) {
|
||||
if (n == npos) return;
|
||||
|
||||
_table[n].value = nullopt;
|
||||
_freed.push_back(n);
|
||||
--_size;
|
||||
|
||||
size_t prev = _prev(n);
|
||||
size_t next = _next(n);
|
||||
|
||||
if (prev != npos) {
|
||||
_table[prev].next = next;
|
||||
}
|
||||
|
||||
if (next != npos) {
|
||||
_table[next].prev = prev;
|
||||
}
|
||||
|
||||
_root = (n == _root) ? next : _root;
|
||||
_last = (n == _last) ? prev : _last;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_LIST_H
|
||||
396
include/fennec/containers/map.h
Normal file
396
include/fennec/containers/map.h
Normal file
@@ -0,0 +1,396 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/map.h
|
||||
/// \brief A header containing the definition for a mapping of keys to values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_MAP_H
|
||||
#define FENNEC_CONTAINERS_MAP_H
|
||||
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/set.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
/* Ramblings
|
||||
*
|
||||
* Definitions:
|
||||
* user = Programmer using this data structure
|
||||
*
|
||||
* The STL maps are very contrived. Some of its functionality encourages younger programmers to use
|
||||
* the exception model. Ideally, I would like this structure to never throw an error with typical use.
|
||||
*
|
||||
* The array access operator is, in my opinion, poorly implemented. I do not think that this operator should handle
|
||||
* insertions and should handle access only. This is the only data structure in STL that has this behavior, no other
|
||||
* data structure modifies contents by inherently calling operator[].
|
||||
*
|
||||
* Currently, I am considering implementing this as the following:
|
||||
* Access will be handled only via operator[]. Return value will be a pointer which forces user validation.
|
||||
* Insertions will be handled only via an insert/emplace function.
|
||||
* Deletions will be handled only via an erase function.
|
||||
*/
|
||||
|
||||
///
|
||||
/// \brief Data Structure defining a mapping of \f$key\f$ \f$KeyT\f$ to \f$value\f$ \f$ValueT\f$
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \note These runtimes are amortized, in theory the worst case is \f$O(N)\f$, but that is highly improbable.
|
||||
///
|
||||
/// \tparam KeyT The Key Type
|
||||
/// \tparam ValueT The Value Type
|
||||
/// \tparam Hash The Hash to Use
|
||||
/// \tparam Alloc The Allocator to Use
|
||||
template<typename KeyT, typename ValueT, typename Hash = hash<KeyT>, typename Alloc = allocator<pair<KeyT, ValueT>>>
|
||||
struct map {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
struct key_hash; //!< Hash for node keys
|
||||
struct key_equals; //!< Comparison for node keys
|
||||
using key_t = KeyT; //!< The key type
|
||||
using value_t = ValueT; //!< The value type
|
||||
using elem_t = pair<KeyT, ValueT>; //!< then node type
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>; //!< Rebinds the allocator type to nodes
|
||||
using hash_t = Hash; //!< The hash type
|
||||
using set_t = set<elem_t, key_hash, key_equals, alloc_t>; //!< The underlying set
|
||||
using iterator = set_t::iterator; //!< Iterator type
|
||||
|
||||
///
|
||||
/// \brief key hash helper
|
||||
struct key_hash : hash_t {
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification \f$operator()\f$
|
||||
/// \param p the pair to hash
|
||||
/// \returns the hash of the key
|
||||
constexpr size_t operator()(const elem_t& p) const {
|
||||
return hash_t::operator()(p.first);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief key comparison helper
|
||||
struct key_equals : equality<KeyT> {
|
||||
///
|
||||
/// \brief C++ 11 Compare Specification \f$operator()\f$
|
||||
/// \param a the first pair
|
||||
/// \param b the second pair
|
||||
/// \returns \f$true\f$ if the keys are equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const elem_t& a, const elem_t& b) const {
|
||||
return equality<KeyT>::operator()(a.first, b.first);
|
||||
}
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty map
|
||||
constexpr map() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor, Destructs all elements and releases the allocation
|
||||
constexpr ~map() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the set
|
||||
constexpr size_t size() const {
|
||||
return _set.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no elements in the set, \f$false\f$ otherwise
|
||||
constexpr size_t is_empty() const {
|
||||
return _set.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
constexpr size_t capacity() const {
|
||||
return _set.capacity();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Key Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* operator[](const KeyT& key) {
|
||||
auto it = _set.at(this->_find(key));
|
||||
return it ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key Const Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A const-qualified pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* operator[](const KeyT& key) const {
|
||||
auto it = _set.at(this->_find(key));
|
||||
return it ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Argument Key Access Operator
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct the key with
|
||||
/// \returns A pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr value_t* operator[](ArgsT&&...args) {
|
||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
return it ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Argument Key Const Access Operator
|
||||
/// \tparam ArgsT Argument Type
|
||||
/// \param args Argument to construct the key with
|
||||
/// \returns A const-qualified pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr const value_t* operator[](ArgsT&&...args) const {
|
||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
return it ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param pair a pair containing the key and its value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void insert(elem_t&& pair) {
|
||||
this->_insert(fennec::forward<elem_t>(pair));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param key key to insert
|
||||
/// \param args Arguments for constructing the key-value pair
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
|
||||
this->_insert(key, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param args Arguments for constructing the key-value pair
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(KeyT&& key) {
|
||||
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const KeyT& key) {
|
||||
_set.erase(this->_find(key));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Argument Erase
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct a key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void erase(ArgsT&&...args) {
|
||||
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the map destructing all elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void clear() {
|
||||
_set.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the start of the map
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return _set.begin();
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator at the end of the map
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return _set.end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
set_t _set;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename...ArgsT>
|
||||
set_t::iterator _find(ArgsT&&...args) const {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
pair<KeyT, ValueT> val;
|
||||
|
||||
~U() {
|
||||
fennec::destruct(&root);
|
||||
}
|
||||
} trick = {
|
||||
.root = { KeyT(fennec::forward<ArgsT>(args)...), 0 }
|
||||
};
|
||||
return _set.find(trick.val);
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void _insert(ArgsT&&...args) {
|
||||
elem_t elem(fennec::forward<ArgsT>(args)...);
|
||||
auto it = this->_find(elem.first);
|
||||
if (it != _set.end()) {
|
||||
_set.at(it)->second = fennec::move(elem.second);
|
||||
} else {
|
||||
_set.insert(fennec::move(elem));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_MAP_H
|
||||
542
include/fennec/containers/object_pool.h
Normal file
542
include/fennec/containers/object_pool.h
Normal file
@@ -0,0 +1,542 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/object_pool.h
|
||||
/// \brief A header containing the definition for a pool of objects associated by ids
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||
#define FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Struct which holds a pool of objects associated with ids
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The value type
|
||||
/// \tparam AllocT The allocator type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct object_pool {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< The held object type
|
||||
using elem_t = optional<value_t>; //!< The element type
|
||||
using table_t = dynarray<elem_t, AllocT>; //!< The underlying table type
|
||||
using freed_t = list<size_t, AllocT>; //!< The underlying free queue
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr object_pool()
|
||||
: _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs objects then releases the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~object_pool() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of active objects in the pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no objects in the pool, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Retrieve the next id \f$i\f$ that would be assigned to an object \f$o\f$ were it added to the object pool
|
||||
///
|
||||
/// \details This can be useful if there are constant members that need to be assigned at construction.
|
||||
/// \returns The id of the next inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next_id() const {
|
||||
size_t next = _size;
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i id of the object
|
||||
/// \returns a reference to the object with id \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
assert(_table[i], "Attempted to access null object.")
|
||||
return *_table[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Const Access Operator
|
||||
/// \param i id of the object
|
||||
/// \returns a const-qualified reference to the object with id \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
assert(_table[i], "Attempted to access null object.")
|
||||
return *_table[i];
|
||||
}
|
||||
|
||||
constexpr bool operator()(size_t i) const {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
return _table[i];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, inserts \f$x\f$ into the pool
|
||||
/// \param x the object to move
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(value_t&& x) {
|
||||
return this->_insert(fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, inserts a copy of \f$x\f$ into the pool
|
||||
/// \param x the object to copy
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(const value_t& x) {
|
||||
return this->_insert(x);
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Emplacement, constructs a new object using \f$args\ldots\f$
|
||||
/// \param args The arguments to construct the new object with
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(ArgsT&&...args) {
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase an object from the pool
|
||||
/// \param i The id of the object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(size_t i) {
|
||||
_table[i] = nullopt;
|
||||
_freed.push_back(i);
|
||||
--_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clear the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_table.clear();
|
||||
_freed.clear();
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
|
||||
///
|
||||
/// \returns an iterator at the start of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
iterator begin() {
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the start of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const_iterator begin() const {
|
||||
return const_iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns an iterator at the start of the end of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
iterator end() {
|
||||
return iterator(this, _size);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator at the start of the end of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const_iterator end() const {
|
||||
return const_iterator(this, _size);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
iterator(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
iterator(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief public destructor
|
||||
~iterator() = default;
|
||||
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param it the iterator to copy
|
||||
/// \returns a reference to self after having copied \f$it\f$
|
||||
iterator& operator=(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param it the iterator to move
|
||||
/// \returns a reference to self after having moved \f$it\f$
|
||||
iterator& operator=(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$it\f$ before having been incremented
|
||||
friend iterator operator++(iterator& it, int) {
|
||||
iterator ret = it;
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$it\f$ after having moved to the next element
|
||||
friend iterator& operator++(iterator& it) {
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
value_t& operator*() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
value_t* operator->() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
bool operator==(const iterator& it) {
|
||||
return pool == it.pool and curr == it.curr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
bool operator!=(const iterator& it) {
|
||||
return pool != it.pool or curr != it.curr;
|
||||
}
|
||||
|
||||
private:
|
||||
object_pool* pool;
|
||||
size_t curr;
|
||||
|
||||
void _fix() {
|
||||
while (curr < pool->_size and not pool->_table[curr]) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
|
||||
iterator(object_pool* pool, size_t start)
|
||||
: pool(pool), curr(start) {
|
||||
_fix();
|
||||
}
|
||||
|
||||
friend struct object_pool;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$const_iterator\f$
|
||||
class const_iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
const_iterator(const const_iterator&it ) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
const_iterator(const_iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief public destructor
|
||||
~const_iterator() = default;
|
||||
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param it the iterator to copy
|
||||
/// \returns a reference to self after having copied \f$it\f$
|
||||
const_iterator& operator=(const const_iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param it the iterator to move
|
||||
/// \returns a reference to self after having moved \f$it\f$
|
||||
const_iterator& operator=(const_iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$it\f$ before having been incremented
|
||||
friend const_iterator operator++(const_iterator& it, int) {
|
||||
const_iterator ret = it;
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$it\f$ after having moved to the next element
|
||||
friend const_iterator& operator++(const_iterator& it) {
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
bool operator==(const const_iterator& it) {
|
||||
return pool == it.pool and curr == it.curr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
bool operator!=(const const_iterator& it) {
|
||||
return pool != it.pool or curr != it.curr;
|
||||
}
|
||||
|
||||
private:
|
||||
const object_pool* pool;
|
||||
size_t curr;
|
||||
|
||||
void _fix() {
|
||||
while (curr < pool->_size and not pool->_table[curr]) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator(const object_pool* pool, size_t start)
|
||||
: pool(pool), curr(start) {
|
||||
_fix();
|
||||
}
|
||||
|
||||
friend struct object_pool;
|
||||
};
|
||||
|
||||
private:
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _size;
|
||||
|
||||
size_t _next_free() {
|
||||
size_t next = _size;
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
_freed.pop_front();
|
||||
}
|
||||
++_size;
|
||||
return next;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
size_t _insert(ArgsT&&...args) {
|
||||
size_t i = _next_free();
|
||||
if (i >= _table.size()) {
|
||||
_table.resize(fennec::max(_table.size() * 2, size_t(8)));
|
||||
}
|
||||
_table[i].emplace(fennec::forward<ArgsT>(args)...);
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||
500
include/fennec/containers/optional.h
Normal file
500
include/fennec/containers/optional.h
Normal file
@@ -0,0 +1,500 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/optional.h
|
||||
/// \brief A header containing the definition for a container with an optionally present variable
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_OPTIONAL_H
|
||||
#define FENNEC_CONTAINERS_OPTIONAL_H
|
||||
|
||||
#include <fennec/lang/utility.h>
|
||||
#include <fennec/memory/new.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
///
|
||||
/// \brief struct to represent a \f$null\f$ `fennec::optional`
|
||||
struct nullopt_t {};
|
||||
|
||||
///
|
||||
/// \brief value representing a \f$null\f$ `fennec::optional`
|
||||
constexpr nullopt_t nullopt_v = {};
|
||||
|
||||
///
|
||||
/// \brief alias for representing a \f$null\f$ `fennec::optional`
|
||||
#define nullopt nullopt_v
|
||||
|
||||
///
|
||||
/// \brief Structure to hold an optional value.
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam T
|
||||
template<typename T>
|
||||
struct optional {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using reference_t = T&; //!< reference type
|
||||
using pointer_t = T*; //!< pointer type
|
||||
using const_reference_t = T&; //!< const reference type
|
||||
using const_pointer_t = const T*; //!< const pointer type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional()
|
||||
: _root(0)
|
||||
, _set(false) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(nullopt_t)
|
||||
: _root(0)
|
||||
, _set(false) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Copy Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Move Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(T&& val)
|
||||
: _val(fennec::forward<T>(val))
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param opt the optional to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(const optional& opt) requires is_copy_assignable_v<T>
|
||||
: optional() {
|
||||
_set = opt._set;
|
||||
if (_set) {
|
||||
_val = opt._val;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param opt the optional to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
|
||||
: optional() {
|
||||
_set = opt._set;
|
||||
if (_set) {
|
||||
_val = fennec::move(opt._val);
|
||||
}
|
||||
opt = nullopt;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr optional(ArgsT&&...args)
|
||||
: _val(fennec::forward<ArgsT>(args)...)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr ~optional() {
|
||||
if constexpr(is_fundamental_v<T>) {
|
||||
return;
|
||||
}
|
||||
if (_set) {
|
||||
fennec::destruct(&_val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Implicit Boolean Check
|
||||
/// \returns \f$true\f$ when there is a value contained
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there is no held value, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return not _set;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment Operators ================================================================================================
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Null Assignment
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(nullopt_t) {
|
||||
if constexpr(not is_fundamental_v<T>) {
|
||||
if (_set) {
|
||||
fennec::destruct(&_val);
|
||||
}
|
||||
}
|
||||
_root = '\0';
|
||||
_set = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Copy Assignment
|
||||
/// \param val The value to set with
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(const T& val) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = val;
|
||||
} else {
|
||||
fennec::construct(&_val, val);
|
||||
_set = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Move Assignment
|
||||
/// \param val The value to set with
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(T&& val) requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = fennec::forward<T>(val);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::forward<T>(val));
|
||||
_set = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment
|
||||
/// \param opt The optional to copy
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(const optional& opt) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
if (_set) { // Construct
|
||||
fennec::construct(&_val, opt._val);
|
||||
} else { // Destruct
|
||||
fennec::destruct(&_val);
|
||||
_root = 0;
|
||||
}
|
||||
} else if (_set) { // Copy Assignment
|
||||
_val = opt._val;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment
|
||||
/// \param opt The optional to move
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(optional&& opt) noexcept requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
if (_set) { // Construct
|
||||
fennec::construct(&_val, fennec::move(opt._val));
|
||||
} else { // Destruct
|
||||
fennec::destruct(&_val);
|
||||
_root = 0;
|
||||
}
|
||||
} else if (_set) { // Copy Assignment
|
||||
_val = fennec::move(opt._val);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the value, \f$nullptr\f$ if there is no value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pointer_t operator->() noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified pointer to the value, \f$nullptr\f$ if there is no value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_pointer_t operator->() const noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& operator*() & noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const T& operator*() const& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T&& operator*() && noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const T&& operator*() const&& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment, Move overload
|
||||
/// \param val The object to take ownership of
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& emplace(T&& val) {
|
||||
if (_set) {
|
||||
_val = fennec::forward<T>(val);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::forward<T>(val));
|
||||
_set = true;
|
||||
}
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment, Copy overload
|
||||
/// \param val The object to copy
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& emplace(const T& val) {
|
||||
if (_set) {
|
||||
_val = val;
|
||||
} else {
|
||||
fennec::construct(&_val, val);
|
||||
_set = true;
|
||||
}
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment
|
||||
/// \param args The arguments to construct with
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr T& emplace(ArgsT&&...args) {
|
||||
if (_set) {
|
||||
_val = T(fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::forward<ArgsT>(args)...);
|
||||
_set = true;
|
||||
}
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Reset the Optional
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void reset() {
|
||||
this->operator=(nullopt);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
union {
|
||||
char _root;
|
||||
T _val;
|
||||
};
|
||||
bool _set;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_OPTIONAL_H
|
||||
300
include/fennec/containers/pair.h
Normal file
300
include/fennec/containers/pair.h
Normal file
@@ -0,0 +1,300 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/pair.h
|
||||
/// \brief A header containing the definition for a container holding a pair of values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_PAIR_H
|
||||
#define FENNEC_CONTAINERS_PAIR_H
|
||||
|
||||
#include <fennec/containers/tuple.h>
|
||||
#include <fennec/lang/hashing.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
///
|
||||
/// \brief Struct for holding a pair of values
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypeT0 The type of the first value
|
||||
/// \tparam TypeT1 The type of the second value
|
||||
template<typename TypeT0, typename TypeT1>
|
||||
struct pair {
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
TypeT0 first; //!< The first value in the pair
|
||||
TypeT1 second; //!< The second value in the pair
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, invokes default constructor for both elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor, invokes destructor for both elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr ~pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Pair Copy Constructor
|
||||
/// \param x Value to copy for the first element
|
||||
/// \param y Value to copy for the first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(const TypeT0& x, const TypeT1& y)
|
||||
: first(x)
|
||||
, second(y) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pair Move Constructor
|
||||
/// \param x Value to move for the first element
|
||||
/// \param y Value to move for the first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(TypeT0&& x, TypeT1&& y) noexcept
|
||||
: first(fennec::forward<TypeT0>(x))
|
||||
, second(fennec::forward<TypeT1>(y)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pair Implicit Constructor
|
||||
/// \param arg1 Value to initialize the first element
|
||||
/// \param arg2 Value to initialize the first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename Arg1T, typename Arg2T>
|
||||
constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
|
||||
: first(fennec::forward<Arg1T>(arg1))
|
||||
, second(fennec::forward<Arg2T>(arg2)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies both elements
|
||||
/// \param pair The pair to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(const pair& pair)
|
||||
: first(fennec::copy(pair.first))
|
||||
, second(fennec::copy(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, moves both elements
|
||||
/// \param pair The pair to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(pair&& pair) noexcept
|
||||
: first(fennec::move(pair.first))
|
||||
, second(fennec::move(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment, copies both elements
|
||||
/// \param pair The pair to copy
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair& operator=(const pair& pair) {
|
||||
first = fennec::copy(pair.first);
|
||||
second = fennec::copy(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment, moves both elements
|
||||
/// \param pair The pair to move
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair& operator=(pair&& pair) {
|
||||
first = fennec::move(pair.first);
|
||||
second = fennec::move(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Equality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns \f$true\f$ when both elements of each pair are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator==(const pair& p) const {
|
||||
return first == p.first and second == p.second;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Inequality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns \f$true\f$ when either element of each pair are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator!=(const pair& p) const {
|
||||
return first != p.first or second != p.second;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Less Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is less, or they are
|
||||
/// equal and the second element is less
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator<(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second < p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Less Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is less, or they are
|
||||
/// equal and the second element is less or equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator<=(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second <= p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Greater Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is greater, or they are
|
||||
/// equal and the second element is greater
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator>(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second > p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Greater Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is greater, or they are
|
||||
/// equal and the second element is greater or equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator>=(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second >= p.second);
|
||||
}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification for `fennec::pair<TypeT0, TypeT1>`
|
||||
/// \tparam TypeT0 The first type of the pair
|
||||
/// \tparam TypeT1 The second type of the pair
|
||||
template<typename TypeT0, typename TypeT1>
|
||||
struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification \f$operator()\f$
|
||||
/// \param p The pair to hash
|
||||
/// \returns a pairing of the hashes of both elements of \f$p\f$ using `fennec::pair_hash`
|
||||
constexpr size_t operator()(const pair<TypeT0, TypeT1>& p) const {
|
||||
return fennec::pair_hash( // pair the hashes of both elements
|
||||
hash<TypeT0>::operator()(p.first),
|
||||
hash<TypeT1>::operator()(p.second)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_PAIR_H
|
||||
289
include/fennec/containers/priority_queue.h
Normal file
289
include/fennec/containers/priority_queue.h
Normal file
@@ -0,0 +1,289 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/priority_queue.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
#define FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
|
||||
#include <fennec/containers/object_pool.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
// Binary heaps are just kinda busted.
|
||||
// In-array binary heaps are one of the most efficient data structures for computers
|
||||
// -> Cache Locality
|
||||
// -> log(n) runtime
|
||||
// -> No auxiliary structures or constant runtimes
|
||||
//
|
||||
// I tried just about every heap under the sun
|
||||
// -> strict fibonacci heap, got blown out of the water by std::priority_queue
|
||||
// -> fibonacci heap, got blown out of the water by std::priority_queue
|
||||
// -> binomial heap, on-par with std::set, blown out of the water by std::priority_queue
|
||||
//
|
||||
// Then I relented and fell back to ye old binary heap
|
||||
// This implementation roughly matches gcc's std::priority_queue
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief a priority queue data structure implemented using a binary heap
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ✅ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam ValueT The value type
|
||||
/// \tparam CompareT The compare type, defaults to `fennec::less`
|
||||
/// \tparam AllocT The allocator type, defaults to `fennec::allocator`
|
||||
template<typename ValueT, class CompareT = less<ValueT>, class AllocT = allocator<ValueT>>
|
||||
struct priority_queue {
|
||||
|
||||
// Definitions & Constants =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = ValueT; //!< Alias for the value type
|
||||
using compare_t = CompareT; //!< Alias for the compare type
|
||||
using alloc_t = allocation<value_t, AllocT>; //!< The underlying allocation type
|
||||
|
||||
/// @}
|
||||
|
||||
static constexpr size_t npos = -1; //!< value representing a null node
|
||||
|
||||
private:
|
||||
constexpr size_t left(size_t n) const {
|
||||
n = n * 2 + 1;
|
||||
return n >= _size ? npos : n;
|
||||
}
|
||||
|
||||
constexpr size_t right(size_t n) const {
|
||||
n = n * 2 + 2;
|
||||
return n >= _size ? npos : n;
|
||||
}
|
||||
|
||||
constexpr size_t parent(size_t n) const {
|
||||
return n == 0 ? npos : (n - 1) / 2;
|
||||
}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief default constructor
|
||||
/// \details initializes an empty queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr priority_queue()
|
||||
: _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~priority_queue() {
|
||||
while (_size > 0) {
|
||||
--_size;
|
||||
fennec::destruct(&_table[_size]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \returns the size of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns the capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the queue holds no elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \returns the value at the front of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return _table[0];
|
||||
}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief push a new key into the queue
|
||||
/// \param key the key to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void push(const value_t& key) {
|
||||
this->_insert(key);
|
||||
}
|
||||
|
||||
///
|
||||
/// \param key the key to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void push(value_t&& key) {
|
||||
this->_insert(fennec::forward<value_t>(key));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief emplace a new key into the queue
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pop the element at the front of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void pop() {
|
||||
fennec::swap(_table[0], _table[--_size]);
|
||||
fennec::destruct(&_table[_size]);
|
||||
_fix_erase(0);
|
||||
}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
compare_t _compare;
|
||||
alloc_t _table;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void _insert(ArgsT&&...args) {
|
||||
if (_size == _table.capacity()) {
|
||||
_expand();
|
||||
}
|
||||
fennec::construct(&_table[_size], fennec::forward<ArgsT>(args)...);
|
||||
_fix_insert(_size++);
|
||||
}
|
||||
|
||||
constexpr void _expand() {
|
||||
_table.reallocate((_table.capacity() + 1) * 2 - 1);
|
||||
}
|
||||
|
||||
constexpr size_t _min(size_t a, size_t b) {
|
||||
if (a == npos) { return b; }
|
||||
if (b == npos) { return a; }
|
||||
return _compare(_table[a], _table[b]) ? a : b;
|
||||
}
|
||||
|
||||
void _fix_insert(size_t n) {
|
||||
size_t p = parent(n);
|
||||
while (p != npos && _compare(_table[n], _table[p])) {
|
||||
fennec::swap(_table[n], _table[p]);
|
||||
n = p;
|
||||
p = parent(n);
|
||||
}
|
||||
}
|
||||
|
||||
void _fix_erase(size_t n) {
|
||||
size_t c = _min(left(n), right(n));
|
||||
while (n != npos && c != npos && _compare(_table[c], _table[n])) {
|
||||
fennec::swap(_table[c], _table[n]);
|
||||
n = c;
|
||||
c = _min(left(n), right(n));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
929
include/fennec/containers/rdtree.h
Normal file
929
include/fennec/containers/rdtree.h
Normal file
@@ -0,0 +1,929 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/rdtree.h
|
||||
/// \brief A header containing the definition for a tree with a root and directed edges
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_RDTREE_H
|
||||
#define FENNEC_CONTAINERS_RDTREE_H
|
||||
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/traversal.h>
|
||||
#include <fennec/containers/pair.h>
|
||||
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Rooted-Directed Tree
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT Data type
|
||||
/// \tparam AllocT Allocator Type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct rdtree {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< the held value type
|
||||
using alloc_t = typename allocator_traits<AllocT>::template rebind<node>; //!< the underlying allocator type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t root = 0; //!< the id of the root node
|
||||
static constexpr size_t npos = -1; //!< the id of a null node
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
struct node {
|
||||
value_t value;
|
||||
size_t parent, child, prev, next;
|
||||
size_t depth, num_children;
|
||||
|
||||
constexpr node()
|
||||
: value()
|
||||
, parent(npos), child(npos)
|
||||
, prev(npos), next(npos)
|
||||
, depth(0), num_children(0) {
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
|
||||
: value(fennec::forward<ArgsT>(args)...)
|
||||
, parent(p), child(c), prev(v), next(n)
|
||||
, depth(d), num_children(0) {
|
||||
}
|
||||
|
||||
constexpr ~node() {
|
||||
parent = npos;
|
||||
child = npos;
|
||||
prev = npos;
|
||||
next = npos;
|
||||
depth = 0;
|
||||
num_children = 0;
|
||||
}
|
||||
};
|
||||
|
||||
using table_t = allocation<node, alloc_t>;
|
||||
using freed_t = list<size_t>;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Root Constructor, constructs the root node of the tree
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the root with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
explicit constexpr rdtree(ArgsT&&...args)
|
||||
: _table(), _freed(), _size(1) {
|
||||
_table.reallocate(8);
|
||||
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies the contents of \f$tree\f$
|
||||
/// \param tree the rdtree to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr rdtree(const rdtree& tree)
|
||||
: _table(tree._table), _freed(tree._freed), _size(tree._size) {
|
||||
// TODO: properly invoke copy constructor for all elements
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership over the contents of \f$tree\f$
|
||||
/// \param tree the rdtree to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr rdtree(rdtree&& tree) noexcept
|
||||
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param rhs the rdtree to copy
|
||||
/// \returns \f$this\f$ after copying the contents of \f$rhs\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr rdtree& operator=(const rdtree& rhs) {
|
||||
// TODO: properly invoke copy constructor for all elements
|
||||
for (value_t* it : this->_table) {
|
||||
fennec::destruct(it);
|
||||
}
|
||||
_table = rhs._table;
|
||||
_freed = rhs._freed;
|
||||
_size = rhs._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param rhs the rdtree to move
|
||||
/// \returns \f$this\f$ after taking ownership over the contents of \f$rhs\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr rdtree& operator=(rdtree&& rhs) noexcept {
|
||||
for (value_t* it : _table) {
|
||||
fennec::destruct(it);
|
||||
}
|
||||
_table = fennec::move(rhs._table);
|
||||
_freed = fennec::move(rhs._freed);
|
||||
_size = rhs._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of nodes in the tree
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no nodes in the tree, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the parent node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t parent(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
return i == npos ? npos : _table[i].parent;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the child node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t child(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity() && n != npos) return npos;
|
||||
size_t c = i == npos ? npos : _table[i].child;
|
||||
if (n != 0)
|
||||
return next(c, n == npos ? npos : n - 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the next node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity() && n != npos) return npos;
|
||||
if (i == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t org = i;
|
||||
size_t nxt = _table[i].next;
|
||||
while (nxt != npos) {
|
||||
i = nxt;
|
||||
nxt = _table[i].next;
|
||||
if (n != npos) {
|
||||
if (n-- == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i == org && n != npos ? npos : i;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the previous node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t prev(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if (i == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t org = i;
|
||||
size_t prv = _table[i].prev;
|
||||
while (prv != npos) {
|
||||
i = prv;
|
||||
prv = _table[i].prev;
|
||||
if (n != npos) {
|
||||
if (n-- == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i == org && n != npos ? npos : i;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i the node to start at
|
||||
/// \returns the left-most child of node \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr size_t left_most(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
size_t n = i;
|
||||
if ((n = child(n)) == npos) {
|
||||
return i;
|
||||
}
|
||||
while (true) {
|
||||
size_t p = n;
|
||||
if ((n = child(n)) == npos) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i the node to start at
|
||||
/// \returns the right-most child of node \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr size_t right_most(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if ((i = child(i)) == npos) {
|
||||
return npos;
|
||||
}
|
||||
while (true) {
|
||||
size_t n;
|
||||
while ((n = next(i)) != npos) {
|
||||
i = n;
|
||||
}
|
||||
n = i;
|
||||
if ((i = child(i)) == npos) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The depth of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t depth(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
return i == npos ? npos : _table[i].depth;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The number of children the node has
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_children(size_t i) const {
|
||||
if (i >= _table.capacity()) return 0;
|
||||
return i == npos ? 0 : _table[i].num_children;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The next node id were \f$insert\f$ or \f$emplace\f$ to be called
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next_id() const {
|
||||
size_t i = _size;
|
||||
if (not _freed.is_empty()) {
|
||||
i = _freed.front();
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A reference to the value of the node wrapped in an optional
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
return _table[i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A const-qualified reference to the value of the node wrapped in an optional
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
return _table[i].value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
|
||||
return this->_insert(parent, next, val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
|
||||
return this->_insert(parent, next, fennec::forward<value_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief tree insertion, copies the contents of tree into self with the root at the specified node location
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent
|
||||
/// \param tree the tree to insert
|
||||
/// \returns the index of the inserted root
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, const rdtree& tree) {
|
||||
list<pair<size_t, size_t>> visit;
|
||||
visit.push_front({ root, parent });
|
||||
size_t res = npos;
|
||||
|
||||
while (not visit.is_empty()) {
|
||||
auto node = visit.front();
|
||||
visit.pop_front();
|
||||
|
||||
size_t p = this->_insert(node.second, node.second == parent ? next : npos, tree[node.first]);
|
||||
|
||||
res = (res == npos) ? p : res;
|
||||
|
||||
size_t c = tree.child(node.first, npos);
|
||||
while (c != npos) {
|
||||
visit.push_front({ c, p });
|
||||
c = tree._table[c].prev;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
|
||||
/// \param args the args to construct the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
|
||||
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Swap two nodes
|
||||
/// \param i0 The id of the first node
|
||||
/// \param i1 The id of the second node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void swap(size_t i0, size_t i1) {
|
||||
assertf(i0 != root and i1 != root, "Cannot Swap With Root");
|
||||
|
||||
size_t p0 = parent(i0);
|
||||
size_t p1 = parent(i1);
|
||||
|
||||
fennec::swap(_table[i0].parent, _table[i1].parent);
|
||||
fennec::swap(_table[i0].child, _table[i1].child);
|
||||
fennec::swap(_table[i0].next, _table[i1].next);
|
||||
fennec::swap(_table[i0].prev, _table[i1].prev);
|
||||
fennec::swap(_table[i0].depth, _table[i1].depth);
|
||||
fennec::swap(_table[i0].num_children, _table[i1].num_children);
|
||||
|
||||
if (child(p0) == i0) _table[p0].child = i1;
|
||||
if (child(p1) == i1) _table[p1].child = i0;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Erase a node in the tree and all of it's children
|
||||
/// \param i the index of the node
|
||||
constexpr void erase(size_t i) {
|
||||
_erase(i);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Traversal ===========================================================================================================
|
||||
|
||||
/// \name Traversal
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Traverse the tree using a specified order and visiting functor
|
||||
///
|
||||
/// \details
|
||||
/// The visitor should accept a reference to a value of type \f$TypeT\f$ and a \f$size_t\f$ which contains the node's id.
|
||||
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
|
||||
///
|
||||
/// \tparam OrderT The order with which to traverse the tree.
|
||||
/// \tparam VisitorT The visitor, should fulfill the signature \f$uint8_t visit(TypeT&, size_t)\f$
|
||||
/// \param visit The visiting object
|
||||
/// \param i The node to start at
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OrderT, typename VisitorT>
|
||||
constexpr void traverse(VisitorT&& visit, size_t i = root) {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
uint8_t mode = traversal_control_continue;
|
||||
if (_table[i].value) {
|
||||
mode = visit(_table[i].value, i);
|
||||
}
|
||||
if (mode == traversal_control_break) {
|
||||
break;
|
||||
}
|
||||
i = order[*this, i, mode];
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverse the tree using a specified order and visiting functor
|
||||
///
|
||||
/// \details
|
||||
/// The visitor should accept a reference to a value of type \f$TypeT\f$ and a \f$size_t\f$ which contains the node's id.
|
||||
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
|
||||
///
|
||||
/// \tparam OrderT The order with which to traverse the tree.
|
||||
/// \tparam VisitorT The visitor, should fulfill the signature \f$uint8_t visit(TypeT&, size_t)\f$
|
||||
/// \param visit The visiting object
|
||||
/// \param i The node to start at
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OrderT, typename VisitorT>
|
||||
constexpr void traverse(VisitorT&& visit, size_t i = root) const {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
uint8_t mode = traversal_control_continue;
|
||||
if (_table[i].value) {
|
||||
mode = visit(_table[i].value, i);
|
||||
}
|
||||
if (mode == traversal_control_break) {
|
||||
break;
|
||||
}
|
||||
i = order[*this, i, mode];
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for breadth-first traversal
|
||||
struct breadth_first {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \param mode The mode specifying branch conditions
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t nxt = tree.next(node);
|
||||
size_t chd = tree.next(node);
|
||||
|
||||
if (nxt != npos && node != head) {
|
||||
visit.push_front(nxt);
|
||||
}
|
||||
|
||||
if (chd != npos && mode != traversal_control_jump_over) {
|
||||
visit.push_back(chd);
|
||||
}
|
||||
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
node = npos;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for pre-order traversal
|
||||
struct pre_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \param mode The mode specifying branch conditions
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t nxt = tree.next(node);
|
||||
size_t chd = tree.child(node);
|
||||
|
||||
if (nxt != npos && node != head) {
|
||||
visit.push_front(nxt);
|
||||
}
|
||||
|
||||
if (chd != npos && mode != traversal_control_jump_over) {
|
||||
visit.push_front(chd);
|
||||
}
|
||||
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
node = npos;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for in-order traversal
|
||||
struct in_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t prnt = tree.parent(node);
|
||||
size_t next = tree.next(node);
|
||||
if (node != head) {
|
||||
if (tree.child(prnt) == node) {
|
||||
visit.push_back(prnt);
|
||||
if (next != npos) {
|
||||
visit.push_back(tree.left_most(next));
|
||||
}
|
||||
} else if (next != npos) {
|
||||
visit.push_front(tree.left_most(next));
|
||||
}
|
||||
}
|
||||
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
node = npos;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for post-order traversal
|
||||
struct post_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t prnt = tree.parent(node);
|
||||
size_t next = tree.next(node);
|
||||
|
||||
if (node != head) {
|
||||
if (next != npos) {
|
||||
visit.push_front(tree.left_most(next));
|
||||
} else {
|
||||
visit.push_front(prnt);
|
||||
}
|
||||
}
|
||||
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
node = npos;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
void _expand() {
|
||||
_table.reallocate(_table.capacity() * 2);
|
||||
}
|
||||
|
||||
size_t _next_free() {
|
||||
size_t next = _size;
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
_freed.pop_front();
|
||||
}
|
||||
if (_size >= capacity()) {
|
||||
_expand();
|
||||
}
|
||||
++_size;
|
||||
return next;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) {
|
||||
if (_size == 0) {
|
||||
fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
|
||||
_size = 1;
|
||||
return root;
|
||||
}
|
||||
|
||||
if (p == npos) {
|
||||
_table[root].value = value_t(fennec::forward<ArgsT>(args)...);
|
||||
_size = _size == 0 ? 1 : _size;
|
||||
return root;
|
||||
}
|
||||
|
||||
size_t idx = _next_free();
|
||||
size_t nxt = child(p, n);
|
||||
size_t prv = n == npos ? npos : prev(n);
|
||||
|
||||
++_table[p].num_children;
|
||||
if ((nxt == child(p) && n != npos) || nxt == npos) {
|
||||
_table[p].child = idx;
|
||||
}
|
||||
|
||||
if (n == npos) {
|
||||
if (nxt != npos) {
|
||||
_table[nxt].next = idx;
|
||||
}
|
||||
fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
if (nxt != npos) {
|
||||
_table[nxt].prev = idx;
|
||||
}
|
||||
if (prv != npos) {
|
||||
_table[prv].next = idx;
|
||||
}
|
||||
fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
constexpr void _erase(size_t i) {
|
||||
list<size_t> queue;
|
||||
queue.push_back(child(i));
|
||||
while (not queue.is_empty()) {
|
||||
size_t n = queue.front(); queue.pop_front();
|
||||
if (n == npos) continue;
|
||||
queue.push_back(next(n));
|
||||
queue.push_back(child(n));
|
||||
fennec::destruct(&_table[n]);
|
||||
_freed.push_back(n);
|
||||
--_size;
|
||||
}
|
||||
|
||||
fennec::destruct(&_table[i]);
|
||||
if (i != root) _freed.push_back(i);
|
||||
--_size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_RDTREE_H
|
||||
927
include/fennec/containers/sequence.h
Normal file
927
include/fennec/containers/sequence.h
Normal file
@@ -0,0 +1,927 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/sequence.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_SEQUENCE_H
|
||||
#define FENNEC_CONTAINERS_SEQUENCE_H
|
||||
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||||
// https://www.geeksforgeeks.org/dsa/insertion-in-red-black-tree/
|
||||
// https://github.com/anandarao/Red-Black-Tree/blob/master/RBTree.cpp
|
||||
|
||||
// After rewriting the _fix_insert and _fix_erase functions the performance decreased significantly in the lower end
|
||||
// but now in the higher end it remains consistent. Something I was doing was disturbing both the rb-tree and bst tree
|
||||
// properties, now that is fixed. I'll see about optimizing more in the future.
|
||||
|
||||
// I realized that the way bintree is setup makes some insert calls O(n + log n) = O(n), so I switched to a pointer based model.
|
||||
// This increased performance overall maintaining O(log n).
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for ordered sets of elements, called sequences in mathematics
|
||||
/// \details
|
||||
/// This data-structure behaves like an ordered-set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:---------------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ✅ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(\log N)\f$ |
|
||||
/// | find | \f$O(\log N)\f$ |
|
||||
/// | insertion | \f$O(\log N)\f$ |
|
||||
/// | deletion | \f$O(\log N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
/// \tparam CompareT Function for comparing two values
|
||||
/// \tparam AllocT An allocator class
|
||||
template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
|
||||
struct sequence {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct _node;
|
||||
|
||||
enum color_ : bool {
|
||||
black = false,
|
||||
red = true,
|
||||
};
|
||||
|
||||
enum dir_ : bool {
|
||||
dir_left = false,
|
||||
dir_right = true,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< the value type
|
||||
using node_t = pair<TypeT, bool>; //!< the node type
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<_node>; //!< underlying alloc type
|
||||
using compare_t = CompareT; //!< comparison type
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
private:
|
||||
using node = _node*;
|
||||
|
||||
struct _node {
|
||||
node parent;
|
||||
node child[2];
|
||||
value_t key;
|
||||
bool color;
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr _node(ArgsT&&...args)
|
||||
: parent(nullptr)
|
||||
, child { nullptr, nullptr }
|
||||
, key(fennec::forward<ArgsT>(args)...)
|
||||
, color(red) {
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr _node(node p, node l, node r, ArgsT&&...args)
|
||||
: parent(p)
|
||||
, child { l, r }
|
||||
, key(fennec::forward<ArgsT>(args)...)
|
||||
, color(red) {
|
||||
}
|
||||
|
||||
constexpr ~_node() {
|
||||
parent = nullptr;
|
||||
child[0] = nullptr;
|
||||
child[1] = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Member Access Helpers
|
||||
|
||||
constexpr value_t& _key(node n) {
|
||||
return n->key;
|
||||
}
|
||||
|
||||
constexpr bool& _color(node n) {
|
||||
return n->color;
|
||||
}
|
||||
|
||||
constexpr node& _parent(node n) {
|
||||
return n->parent;
|
||||
}
|
||||
|
||||
constexpr node& _child(node n, bool dir) {
|
||||
return n->child[dir];
|
||||
}
|
||||
|
||||
constexpr node& _left(node n) {
|
||||
return n->child[dir_left];
|
||||
}
|
||||
|
||||
constexpr node& _right(node n) {
|
||||
return n->child[dir_right];
|
||||
}
|
||||
|
||||
|
||||
// Safe Member Access Helpers
|
||||
|
||||
|
||||
constexpr const value_t& key(node n) const {
|
||||
return n->key;
|
||||
}
|
||||
|
||||
constexpr bool color(node n) {
|
||||
return n ? n->color : (bool)black;
|
||||
}
|
||||
|
||||
constexpr node parent(node n) {
|
||||
return n ? n->parent : nullptr;
|
||||
}
|
||||
|
||||
constexpr node child(node n, bool dir) {
|
||||
return n ? n->child[dir] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node left(node n) {
|
||||
return n ? n->child[dir_left] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node right(node n) {
|
||||
return n ? n->child[dir_right] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node leftmost(node n) {
|
||||
if (n == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (this->_left(n)) {
|
||||
n = this->_left(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
constexpr node rightmost(node n) {
|
||||
if (n == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (this->_right(n)) {
|
||||
n = this->_right(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Constructors & Destructors ==========================================================================================
|
||||
public:
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr sequence()
|
||||
: _root(nullptr), _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of a sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr sequence(sequence&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies a sequence
|
||||
constexpr sequence(const sequence&) = default;
|
||||
// TODO: properly implement
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs elements *in-order*
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~sequence() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Search ==============================================================================================================
|
||||
public:
|
||||
/// \name Search
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Value Find Function, finds the iterator position for \f$val\f$, otherwise returns \f$end()\f$
|
||||
/// \param val The value to find
|
||||
/// \returns An iterator at the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr iterator find(const value_t& val) {
|
||||
node node = _root;
|
||||
while (node) {
|
||||
if (_compare(val, _key(node))) {
|
||||
node = _left(node);
|
||||
} else if (_compare(_key(node), val)) {
|
||||
node = _right(node);
|
||||
} else {
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
}
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Value Contains Function, checks if the sequence contains a value
|
||||
/// \param val The value to find
|
||||
/// \returns \f$true\f$ if \f$val\f$ is in the sequence, \f$false\f$ otherwise
|
||||
bool contains(const value_t& val) {
|
||||
return find(val) != end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of elements in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no elements in the sequence, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, moves \f$val\f$ into the sequence
|
||||
/// \param val The value to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void insert(value_t&& val) {
|
||||
node i = _insert_bst(fennec::forward<value_t>(val));
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion, inserts a copy of \f$val\f$ into the sequence
|
||||
/// \param val The value to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void insert(const value_t& val) {
|
||||
node i = _insert_bst(val);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplacement, constructs and adds a value into the sequence
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
node i = _insert_bst(fennec::forward<ArgsT>(args)...);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the specified value from the sequence
|
||||
/// \param val the value to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void erase(const value_t& val) {
|
||||
_erase(find(val)._node);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructs all elements, *in-order*, contained in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
list<node> visit;
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
visit.push_back(it._node);
|
||||
}
|
||||
for (node n : visit) {
|
||||
this->_free_node(n);
|
||||
}
|
||||
_root = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
|
||||
///
|
||||
/// \returns An iterator at the smallest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return sequence::iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns An iterator at the smallest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr const_iterator begin() const {
|
||||
return sequence::const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator after the largest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return sequence::iterator(this, _root, nullptr);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const C++ Iterator Specification \f$end()\f$
|
||||
/// \returns An iterator after the largest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator end() const {
|
||||
return sequence::const_iterator(this, _root, nullptr);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
friend iterator& operator++(iterator& it) {
|
||||
if (it._node == nullptr) {
|
||||
return it;
|
||||
}
|
||||
|
||||
node parent = it._seq->_parent(it._node);
|
||||
node pright = it._seq->right(parent);
|
||||
node next = it._seq->leftmost(it._seq->right(it._node));
|
||||
|
||||
if (it._node != pright && parent != nullptr) {
|
||||
it._visit.push_front(parent);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
it._visit.push_front(next);
|
||||
}
|
||||
|
||||
if (not it._visit.is_empty()) {
|
||||
it._node = it._visit.front();
|
||||
it._visit.pop_front();
|
||||
} else {
|
||||
it._node = nullptr;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
friend iterator operator++(iterator& it, int) {
|
||||
iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return _node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return &_node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr friend bool operator==(const iterator& lhs, const iterator& rhs) {
|
||||
return lhs._seq == rhs._seq and lhs._node == rhs._node;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr friend bool operator!=(const iterator& lhs, const iterator& rhs) {
|
||||
return lhs._seq != rhs._seq or lhs._node != rhs._node;
|
||||
}
|
||||
|
||||
private:
|
||||
sequence* _seq;
|
||||
node _head;
|
||||
node _node;
|
||||
list<node> _visit;
|
||||
|
||||
constexpr iterator(sequence* seq, node start)
|
||||
: _seq(seq)
|
||||
, _head(start)
|
||||
, _node(seq->leftmost(start)) {
|
||||
}
|
||||
|
||||
constexpr iterator(sequence* seq, node root, node start)
|
||||
: _seq(seq)
|
||||
, _head(root)
|
||||
, _node(start) {
|
||||
}
|
||||
|
||||
friend struct sequence;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class const_iterator {
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
friend const_iterator& operator++(const_iterator& it) {
|
||||
if (it._node == nullptr) {
|
||||
return it;
|
||||
}
|
||||
|
||||
node parent = it._seq->_parent(it._node);
|
||||
node pright = it._seq->right(parent);
|
||||
node next = it._seq->leftmost(it._seq->right(it._node));
|
||||
|
||||
if (it._node != pright && parent != nullptr) {
|
||||
it._visit.push_front(parent);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
it._visit.push_front(next);
|
||||
}
|
||||
|
||||
if (not it._visit.is_empty()) {
|
||||
it._node = it._visit.front();
|
||||
it._visit.pop_front();
|
||||
} else {
|
||||
it._node = nullptr;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
friend const_iterator operator++(const_iterator& it, int) {
|
||||
const_iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return _node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return &_node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr friend bool operator==(const const_iterator& lhs, const const_iterator& rhs) {
|
||||
return lhs._seq == rhs._seq and lhs._node == rhs._node;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr friend bool operator!=(const const_iterator& lhs, const const_iterator& rhs) {
|
||||
return lhs._seq != rhs._seq or lhs._node != rhs._node;
|
||||
}
|
||||
|
||||
private:
|
||||
const sequence* _seq;
|
||||
node _head;
|
||||
node _node;
|
||||
list<node> _visit;
|
||||
|
||||
constexpr const_iterator(const sequence* seq, node start)
|
||||
: _seq(seq)
|
||||
, _head(start)
|
||||
, _node(seq->leftmost(start)) {
|
||||
}
|
||||
|
||||
constexpr const_iterator(const sequence* seq, node root, node start)
|
||||
: _seq(seq)
|
||||
, _head(root)
|
||||
, _node(start) {
|
||||
}
|
||||
|
||||
friend struct sequence;
|
||||
};
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
alloc_t _alloc;
|
||||
node _root;
|
||||
compare_t _compare;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr node _make_node(ArgsT&&...args) {
|
||||
node res = _alloc.allocate(1);
|
||||
fennec::construct(res, fennec::forward<ArgsT>(args)...);
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr void _free_node(node n) {
|
||||
fennec::destruct(n);
|
||||
_alloc.deallocate(n);
|
||||
}
|
||||
|
||||
constexpr node _rotate(node sub, bool dir) {
|
||||
if (sub == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
node sub_parent = _parent(sub);
|
||||
node new_root = _child(sub, not dir);
|
||||
node new_child = _child(new_root, dir);
|
||||
|
||||
_child(sub, not dir) = new_child;
|
||||
if (new_child != nullptr) {
|
||||
_parent(new_child) = sub;
|
||||
}
|
||||
_child(new_root, dir) = sub;
|
||||
|
||||
_parent(new_root) = sub_parent;
|
||||
_parent(sub) = new_root;
|
||||
if (sub_parent != nullptr) {
|
||||
_child(sub_parent, sub == _right(sub_parent)) = new_root;
|
||||
} else {
|
||||
_root = new_root;
|
||||
}
|
||||
return new_root;
|
||||
}
|
||||
|
||||
constexpr void _recolor(node n) {
|
||||
bool c = color(n) == black;
|
||||
if (n == _root) { // Only recolor if not the root node
|
||||
_color(n) = c;
|
||||
}
|
||||
_color(_left(n)) = !_color(_left(n)) ;
|
||||
_color(_right(n)) = !_color(_right(n));
|
||||
}
|
||||
|
||||
// run-of-the-mill bst insert
|
||||
template<typename...ArgsT>
|
||||
constexpr node _insert_bst(ArgsT&&...args) {
|
||||
node res = _make_node(fennec::forward<ArgsT>(args)...);
|
||||
|
||||
if (_root == nullptr) {
|
||||
++_size;
|
||||
_color(res) = black;
|
||||
return _root = res;
|
||||
}
|
||||
|
||||
node i = _root;
|
||||
node p = nullptr;
|
||||
bool d = dir_left;
|
||||
while (i != nullptr) {
|
||||
p = i;
|
||||
|
||||
if (_compare(_key(res), _key(i))) {
|
||||
i = _left(i);
|
||||
d = dir_left;
|
||||
} else if (_compare(_key(i), _key(res))) {
|
||||
i = _right(i);
|
||||
d = dir_right;
|
||||
} else {
|
||||
_free_node(res);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
++_size;
|
||||
_child(p, d) = res;
|
||||
_parent(res) = p;
|
||||
return res;
|
||||
}
|
||||
|
||||
// This makes some cheats given that the structure is modified only by internal functions
|
||||
// If such is the case, ONLY LL, LR, RL, and RR will show up
|
||||
// Then we just need to handle splitting a 4-node
|
||||
constexpr void _fix_insert(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node p = _parent(n);
|
||||
while (n != _root && color(n) == red && color(p) == red) {
|
||||
node g = _parent(p);
|
||||
bool d = n == _right(p);
|
||||
bool r = p == _right(g);
|
||||
node u = _child(g, !r);
|
||||
|
||||
if (color(u) == red) {
|
||||
_recolor(g);
|
||||
n = g;
|
||||
p = _parent(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d != r) {
|
||||
_rotate(p, r);
|
||||
n = p;
|
||||
p = _parent(n);
|
||||
}
|
||||
|
||||
_rotate(g, not r);
|
||||
fennec::swap(_color(p), _color(g));
|
||||
n = p;
|
||||
p = _parent(n);
|
||||
}
|
||||
_color(_root) = black;
|
||||
}
|
||||
|
||||
constexpr void _swap_val(node a, node b) {
|
||||
fennec::swap(_key(a), _key(b));
|
||||
}
|
||||
|
||||
constexpr node _red_child(node x) {
|
||||
node l = _left(x);
|
||||
node r = _right(x);
|
||||
|
||||
if (color(l) == red) {
|
||||
return l;
|
||||
}
|
||||
|
||||
if (color(r) == red) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr void _fix_erase(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == _root) {
|
||||
_root = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
node o = n;
|
||||
node p = _parent(n);
|
||||
if (p == nullptr) {
|
||||
_root = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
bool d = n == _right(p);
|
||||
node c = _red_child(n);
|
||||
node s = nullptr;
|
||||
if (_color(n) == red || c != nullptr) {
|
||||
_child(p, d) = c;
|
||||
if (c != nullptr) {
|
||||
_parent(c) = p;
|
||||
}
|
||||
_color(c) = black;
|
||||
return;
|
||||
}
|
||||
|
||||
while (n != _root) {
|
||||
p = _parent(n);
|
||||
d = n == _right(p);
|
||||
s = _child(p, !d);
|
||||
|
||||
if (s == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (_color(s) == red) {
|
||||
_color(s) = black;
|
||||
_color(p) = red;
|
||||
_rotate(p, d);
|
||||
continue;
|
||||
}
|
||||
|
||||
node nc = _child(s, d);
|
||||
node nf = _child(s, !d);
|
||||
|
||||
if (color(nc) == black && color(nf) == black) {
|
||||
_color(s) = red;
|
||||
if (_color(p) == red) {
|
||||
_color(p) = black;
|
||||
break;
|
||||
}
|
||||
n = p;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (color(nf) == black) {
|
||||
_color(nc) = black;
|
||||
_color(s) = red;
|
||||
_rotate(s, !d);
|
||||
s = nc;
|
||||
nf = s;
|
||||
}
|
||||
_color(s) = _color(p);
|
||||
_color(p) = black;
|
||||
_color(nf) = black;
|
||||
_rotate(p, d);
|
||||
break;
|
||||
}
|
||||
|
||||
p = parent(o);
|
||||
if (p != nullptr) {
|
||||
if (o == _left(p)) {
|
||||
_left(p) = nullptr;
|
||||
} else {
|
||||
_right(p) = nullptr;
|
||||
}
|
||||
_color(_root) = black;
|
||||
} else {
|
||||
_root = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void _erase(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node l = _left(n);
|
||||
node r = _right(n);
|
||||
|
||||
// 2 children
|
||||
if (l != nullptr && r != nullptr) {
|
||||
node s = leftmost(r);
|
||||
_swap_val(n, s);
|
||||
n = s;
|
||||
l = _left(n);
|
||||
r = _right(n);
|
||||
}
|
||||
|
||||
node p = _parent(n);
|
||||
bool d = n == right(p);
|
||||
node c = l != nullptr ? l : r;
|
||||
|
||||
// Single child
|
||||
if (c != nullptr) {
|
||||
_parent(c) = p;
|
||||
}
|
||||
|
||||
// Handles root cases
|
||||
if (p == nullptr) {
|
||||
_root = c;
|
||||
if (c == nullptr) {
|
||||
_free_node(n);
|
||||
--_size;
|
||||
return;
|
||||
} else {
|
||||
_color(c) = black;
|
||||
}
|
||||
}
|
||||
|
||||
// Single Child, Red, and Root cases
|
||||
if (p == nullptr || c != nullptr || _color(n) == red) {
|
||||
if (p != nullptr) {
|
||||
_child(p, d) = c;
|
||||
}
|
||||
_free_node(n);
|
||||
--_size;
|
||||
return;
|
||||
}
|
||||
|
||||
_fix_erase(n);
|
||||
_free_node(n);
|
||||
--_size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_SEQUENCE_H
|
||||
637
include/fennec/containers/set.h
Normal file
637
include/fennec/containers/set.h
Normal file
@@ -0,0 +1,637 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/set.h
|
||||
/// \brief A header containing the definition for a set of unique values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_SET_H
|
||||
#define FENNEC_CONTAINERS_SET_H
|
||||
|
||||
// https://programming.guide/robin-hood-hashing.html
|
||||
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/set.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/math/ext/primes.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/lang/hashing.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for sets of elements
|
||||
/// \details
|
||||
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
||||
struct set {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<node>; //!< the allocator type
|
||||
|
||||
using hash_t = Hash; //!< the hash type
|
||||
using equal_t = Equals; //!< the equality type
|
||||
using elem_t = TypeT; //!< the element type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr double default_load = 0.8; //!< the default load factor for reallocation
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
class iterator;
|
||||
|
||||
private:
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
using table_t = dynarray<node, alloc_t>;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty set
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set()
|
||||
: _table()
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
||||
/// \param hash the hash object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const hash_t& hash)
|
||||
: _table()
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
||||
/// \param alloc the allocator object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr set(const alloc_t& alloc)
|
||||
: _table(alloc)
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
/// \param hash the hash object
|
||||
/// \param alloc the allocator object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const hash_t& hash, const alloc_t& alloc)
|
||||
: _table(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Copy Constructor
|
||||
/// \param set Set to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const set& set)
|
||||
: _table(set._table)
|
||||
, _hash(set._hash)
|
||||
, _size(set._size)
|
||||
, _sumpsl(set._sumpsl)
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Move Constructor
|
||||
/// \param set Set to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(set&& set) noexcept
|
||||
: _table(fennec::move(set._table))
|
||||
, _hash(fennec::move(set._hash))
|
||||
, _size(fennec::move(set._size))
|
||||
, _sumpsl(set._sumpsl)
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, destructs all elements and releases the allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~set() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_table[i].value = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns Size of the set in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when the set is empty, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Capacity of the set in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.size();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Find an Element
|
||||
/// \param val Value to find
|
||||
/// \returns An iterator at the location of the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator find(const elem_t& val) const {
|
||||
if (capacity() == 0) {
|
||||
return end();
|
||||
}
|
||||
size_t s = _hash(val) % capacity(); // Initial search index
|
||||
int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
|
||||
size_t i = (s + psl) % capacity(); // Median search
|
||||
size_t n = 0;
|
||||
|
||||
// Check the first element;
|
||||
if (_table[i].psl >= psl && _table[i].value) {
|
||||
if (_equal(*_table[i].value, val)) {
|
||||
return iterator(this, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop while there is a value and its psl is greater than our probe
|
||||
while (true) {
|
||||
++n;
|
||||
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
|
||||
size_t i1 = (i + n) % capacity();
|
||||
int p0 = psl - n, p1 = psl + n;
|
||||
bool c0 = p0 >= 0 && _table[i0].psl >= p0, c1 = _table[i1].psl >= p1; // Check that we are in range
|
||||
|
||||
if (c0 && _table[i0].value) {
|
||||
if (_equal(*_table[i0].value, val)) {
|
||||
return iterator(this, i0);
|
||||
}
|
||||
}
|
||||
|
||||
if (c1 && _table[i1].value) {
|
||||
if (_equal(*_table[i1].value, val)) {
|
||||
return iterator(this, i1);
|
||||
}
|
||||
}
|
||||
|
||||
if (not(c0 or c1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Check if a set contains a value
|
||||
/// \param val Value to check
|
||||
/// \returns \f$true\f$ if \f$val\f$ can be found, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool contains(const elem_t& val) const {
|
||||
return this->find(val) != end();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Access
|
||||
/// \param it Location to access
|
||||
/// \returns A pointer to the element, \f$nullptr\f$ if not found.
|
||||
/// The value should not be changed in a manner that will change the hash of the element.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
if (it == end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (not _table[it._i].value) {
|
||||
return nullptr;
|
||||
}
|
||||
return &*_table[it._i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Const Access
|
||||
/// \param it Location to access
|
||||
/// \returns A const-qualified pointer to the element, \f$nullptr\f$ if not found.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
if (not _table[it._i].value) return nullptr;
|
||||
return &*_table[it._i].value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param val Value to insert
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(elem_t&& val) {
|
||||
return this->_insert(fennec::forward<elem_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param val Value to insert
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const elem_t& val) {
|
||||
return this->_insert(val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace(ArgsT&&...args) {
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param it Location to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(iterator it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) {
|
||||
return;
|
||||
} // These are separated due to compilers being inconsistent
|
||||
if (not _table[i].value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_table[i].value = nullopt;
|
||||
_sumpsl -= _table[i].psl;
|
||||
--_size;
|
||||
size_t p = i;
|
||||
while (_table[i = (i + 1) % capacity()].value) {
|
||||
if (_table[i].psl == 0) break;
|
||||
|
||||
fennec::swap(_table[p].value, _table[i].value);
|
||||
--_table[p].psl, --_sumpsl;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param val Value to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const elem_t& val) {
|
||||
this->erase(this->find(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clear all elements from the set, destructing them
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_table.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns An iterator for all elements of the set in no particular order
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator begin() const {
|
||||
iterator it(this, 0);
|
||||
while (not _table[it._i].value) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns An iterator representing the end of the set
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() const {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~iterator() {
|
||||
_set = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
constexpr friend iterator& operator++(iterator& it) {
|
||||
while (++it._i < it._set->capacity()) {
|
||||
if (it._set->_table[it._i].value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
it._i = npos;
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
constexpr friend iterator operator++(iterator& it, int) {
|
||||
iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr const elem_t& operator*() const {
|
||||
return *_set->_table[_i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_table[_i].value) return nullptr;
|
||||
return &*_set->_table[_i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& it) const {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& it) const {
|
||||
return _set != it._set or _i != it._i;
|
||||
}
|
||||
|
||||
private:
|
||||
const set* _set;
|
||||
size_t _i;
|
||||
|
||||
constexpr iterator(const set* set, size_t i)
|
||||
: _set(set)
|
||||
, _i(i) {
|
||||
}
|
||||
|
||||
friend set;
|
||||
};
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
table_t _table;
|
||||
hash_t _hash;
|
||||
equal_t _equal;
|
||||
size_t _size;
|
||||
size_t _sumpsl;
|
||||
float _load;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
set cpy; // Create a new set
|
||||
cpy._table.resize(
|
||||
fennec::next_prime2(_table.capacity())
|
||||
);
|
||||
|
||||
// rehash
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
if (_table[i].value) {
|
||||
cpy.insert(fennec::move(*_table[i].value));
|
||||
}
|
||||
}
|
||||
|
||||
// Swap buffers
|
||||
fennec::swap(_table, cpy._table);
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator _insert(ArgsT&&...args) {
|
||||
if (_size == 0 or static_cast<float>(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
|
||||
elem_t value(fennec::forward<ArgsT>(args)...);
|
||||
size_t i = _hash(value) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
while (_table[i].value) { // Search for empty cell
|
||||
if (_equal(*_table[i].value, value)) { // Check to see if this element is already inserted
|
||||
return iterator(this, i);
|
||||
}
|
||||
if (psl > _table[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _table[i].psl;
|
||||
fennec::swap(_table[i].psl, psl);
|
||||
fennec::swap(*_table[i].value, value);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_table[i].value = fennec::move(value);
|
||||
_sumpsl += (_table[i].psl = psl);
|
||||
++_size;
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
|
||||
// Private Definitions =================================================================================================
|
||||
private:
|
||||
struct node {
|
||||
optional<elem_t> value;
|
||||
int psl;
|
||||
|
||||
constexpr node() = default;
|
||||
constexpr ~node() = default;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_SET_H
|
||||
47
include/fennec/containers/traversal.h
Normal file
47
include/fennec/containers/traversal.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/traversal.h
|
||||
/// \brief a header containing constants and utilities related to traversal
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_TRAVERSAL_H
|
||||
#define FENNEC_CONTAINERS_TRAVERSAL_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A set of constants used in the traverser-visitor pattern
|
||||
enum traversal_control_ {
|
||||
traversal_control_continue = 0, //!< Continue to the next element
|
||||
traversal_control_break = 1, //!< Break the traversal loop
|
||||
traversal_control_jump_over = 2, //!< Jump over the next element
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_TRAVERSAL_H
|
||||
156
include/fennec/containers/tuple.h
Normal file
156
include/fennec/containers/tuple.h
Normal file
@@ -0,0 +1,156 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/tuple.h
|
||||
/// \brief A header containing the definition for a container with multiple values of differing types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_TUPLE_H
|
||||
#define FENNEC_CONTAINERS_TUPLE_H
|
||||
|
||||
#include <fennec/containers/detail/_tuple.h>
|
||||
#include <fennec/lang/type_sequences.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
///
|
||||
/// \brief Tuple, holds a collection of values of different types
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypesT The types to store
|
||||
template<typename...TypesT> struct tuple;
|
||||
|
||||
///
|
||||
/// \brief tuple get
|
||||
/// \tparam i the index
|
||||
/// \tparam TypesT the types held in the tuple
|
||||
/// \param x the tuple
|
||||
/// \returns the \f$i\f$th element of the tuple
|
||||
template<size_t i, typename...TypesT>
|
||||
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
||||
auto& it = *static_cast<detail::_tuple_leaf<i, elem_t>*>(&x);
|
||||
return it.value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam i the index
|
||||
/// \tparam TypesT the types held in the tuple
|
||||
/// \param x the tuple
|
||||
/// \returns the \f$i\f$th element of the tuple
|
||||
template<size_t i, typename...TypesT>
|
||||
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<TypesT...>& x) {
|
||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
||||
const auto& it = *static_cast<const detail::_tuple_leaf<i, elem_t>*>(&x);
|
||||
return it.value;
|
||||
}
|
||||
|
||||
|
||||
template<typename ...TypesT>
|
||||
struct tuple : public detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...> {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using base_t = detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...>; //!< the base type
|
||||
|
||||
template<size_t i>
|
||||
using elem_t = typename nth_element<i, TypesT...>::type; //!< helper for getting the \f$i\f$th element
|
||||
|
||||
static constexpr size_t size = sizeof...(TypesT); //!< the number of elements held by the tuple
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief tuple constructor
|
||||
/// \tparam ArgsT The element types
|
||||
/// \param args The arguments to initialize the tuple with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
tuple(ArgsT&&...args)
|
||||
: base_t(fennec::forward<ArgsT>(args)...) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param cpy the tuple to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
tuple(const tuple& cpy)
|
||||
: base_t(cpy) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param mov the tuple to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
tuple(tuple&& mov)
|
||||
: base_t(fennec::forward<tuple>(mov)) {
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Helper for deducing the tuple constructor
|
||||
/// \tparam TypesT the types of the tuple
|
||||
/// \returns A new tuple containing the passed values
|
||||
template<typename...TypesT>
|
||||
tuple(TypesT...) -> tuple<TypesT...>;
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_TUPLE_H
|
||||
385
include/fennec/containers/variant.h
Normal file
385
include/fennec/containers/variant.h
Normal file
@@ -0,0 +1,385 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/variant.h
|
||||
/// \brief Contains the definition for a structure that holds a single value from multiple types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_VARIANT_H
|
||||
#define FENNEC_CONTAINERS_VARIANT_H
|
||||
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/lang/type_sequences.h>
|
||||
#include <fennec/math/ext/common.h>
|
||||
#include <fennec/rtti/type.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A structure that represents a union between \f$TypesT\ldots\f$
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypesT The types to hold in the variant
|
||||
template<typename...TypesT>
|
||||
struct variant {
|
||||
// Assertions ==========================================================================================================
|
||||
public:
|
||||
|
||||
static_assert(
|
||||
is_unique_v<TypesT...> and // No two types in TypesT... may be equivalent
|
||||
not (is_reference_v<TypesT> or ...) and // No type in TypesT... may be a reference
|
||||
not (is_array_v<TypesT> or ...) and // No type in TypesT... may be an array
|
||||
not (is_void_v<TypesT> or ...) // No type in TypesT... may be void
|
||||
);
|
||||
|
||||
|
||||
// Definitions & Constants =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t size = max_element_size_v<TypesT...>; //!< size of the variant in bytes
|
||||
static constexpr size_t nulltype = sizeof...(TypesT); //!< id for a null type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, constructs the first type in \f$TypesT\ldots\f$ that is default constructible
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant()
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
using construct_t = search_element_t<is_default_constructible, TypesT...>;
|
||||
this->_construct<construct_t>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Conversion Constructor, constructs the type in \f$TypesT\ldots\f$ that is identical to \f$T\f$
|
||||
/// or the first that is constructible with \f$T\f$
|
||||
/// \tparam T The type of the value
|
||||
/// \param t The value to forward
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
variant(T&& t)
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type() {
|
||||
using same_t = search_element_args<is_same, type_sequence<T>, TypesT...>::type;
|
||||
using convert_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>::type;
|
||||
using construct_t = conditional_t<is_void_v<same_t>, convert_t, same_t>;
|
||||
this->_construct<construct_t>(fennec::forward<T>(t));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor, constructs a type \f$T\f$ that is in \f$TypesT\ldots\f$ that is constructible with \f$ArgsT\ldots\f$
|
||||
/// \tparam ArgsT The arguments of the constructor
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
variant(type_identity<T>, ArgsT&&...args)
|
||||
: _bytes{}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
static_assert(contains_element_v<T, TypesT...>, "T must be in TypesT...");
|
||||
this->_construct<T>(fennec::forward<ArgsT>(args)...);
|
||||
_type = find_element_v<T>;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param v The variant to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant(const variant& v)
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
|
||||
if (v._type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((v._type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_construct<TypesT>(v.get<TypesT>()) :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = v._type;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param v The variant to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant(variant&& v) noexcept
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type() {
|
||||
|
||||
if (v._type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((v._type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_construct<TypesT>(fennec::move(v.get<TypesT>())) :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = v._type;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, if a type is held, destruct it.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
~variant() {
|
||||
_clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment
|
||||
/// @{
|
||||
|
||||
|
||||
///
|
||||
/// \brief value assignment operator
|
||||
/// \tparam T The type to assign
|
||||
/// \param t the value to assign
|
||||
/// \returns a reference to \f$self\f$ after assigning \f$t\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
variant& operator=(T&& t) {
|
||||
|
||||
// First, check if T is in TypesT...
|
||||
if constexpr((contains_element_v<T, TypesT> or ...)) {
|
||||
using type_t = remove_reference_t<T>;
|
||||
if (_type == find_element_v<type_t, TypesT...>) {
|
||||
*_get<type_t>() = fennec::forward<T>(t);
|
||||
} else {
|
||||
_clear();
|
||||
this->_construct<type_t>(fennec::forward<T>(t));
|
||||
_type = find_element_v<type_t, TypesT...>;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Next, try to assign using the currently held type
|
||||
bool assigned = false;
|
||||
if (_type != nulltype) {
|
||||
((_type == find_element_v<TypesT, TypesT...> ?
|
||||
(*_get<TypesT>() = fennec::forward<T>(t), assigned = true) :
|
||||
((void)0)
|
||||
), ...);
|
||||
}
|
||||
|
||||
if (assigned) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Otherwise, destruct, then construct
|
||||
_clear();
|
||||
using construct_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
|
||||
this->_construct<construct_t>(fennec::forward<T>(t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief emplace function
|
||||
/// \tparam T The type to construct
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT> requires(contains_element_v<T, TypesT...>)
|
||||
void emplace(ArgsT&&...args) {
|
||||
_clear();
|
||||
this->_construct<T>(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief deduced emplace function
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename...ArgsT>
|
||||
void emplace(ArgsT&&...args) {
|
||||
using type_t = nth_element_t<I, TypesT...>;
|
||||
_clear();
|
||||
this->_construct<type_t>(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief get the value of the variant interpreted as \f$T\f$
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T> requires(contains_element_v<T, TypesT...>)
|
||||
T& get() {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T> requires(contains_element_v<T, TypesT...>)
|
||||
const T& get() const {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
|
||||
T& get() {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
|
||||
const T& get() const {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
byte_t _bytes[size];
|
||||
void* _handle;
|
||||
size_t _type;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
T* _get() const {
|
||||
return static_cast<T*>(_handle);
|
||||
}
|
||||
|
||||
void _clear() {
|
||||
if (_type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((_type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_destruct<TypesT>() :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = nulltype;
|
||||
}
|
||||
|
||||
template<typename ConstructT, typename...ArgsT>
|
||||
void _construct(ArgsT&&...args) {
|
||||
fennec::construct<ConstructT>(_get<ConstructT>(), fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
template<typename DestructT>
|
||||
void _destruct() {
|
||||
fennec::destruct(_get<DestructT>());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_VARIANT_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,44 +17,67 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file engine.h
|
||||
/// \file fennec/core/engine.h
|
||||
/// \brief fennec::engine definition
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
///
|
||||
/// \page documentation Documentation
|
||||
/// \page contents Contents
|
||||
///
|
||||
/// 1. \ref introduction "Introduction"
|
||||
/// 1. \ref coding-standards "Coding Standards"
|
||||
/// 2. \ref building-from-source "Building from Source"
|
||||
/// 1. \ref building-from-terminal "Building from Terminal"
|
||||
/// 1. \ref debian "Debian"
|
||||
/// 2. \ref arch "Arch"
|
||||
/// 3. \ref fedora "Fedora"
|
||||
/// 2. \ref building-on-windows "Building on Windows"
|
||||
/// 3. \ref running-the-test-suite "Running the Test Suite"
|
||||
/// 4. \ref usage "Usage"
|
||||
/// 1. \ref licensing "Licensing"
|
||||
/// 5. \ref contribution "Contribution"
|
||||
/// 6. \subpage libraries
|
||||
/// 1. \ref fennec_lang "C++ Language Library"
|
||||
/// 2. \ref fennec_math "Math Library"
|
||||
/// 2. \ref fennec_memory "Memory Management Library"
|
||||
/// 2. \ref fennec_containers "Containers Library"
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
///
|
||||
/// \page libraries Libraries
|
||||
///
|
||||
/// | Library | Brief |
|
||||
/// | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
/// | \subpage fennec_lang | Implementation for functions and classes related to the C++ Language, including base types, common utility functions, and metaprogramming templates |
|
||||
/// | \subpage fennec_math | Implementation of math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). Additional extensions are provided for other common math functions. |
|
||||
/// | Library | Brief |
|
||||
/// |:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// | \subpage fennec_lang | Implementation for functions and classes related to the C++ Language, including base types, common utility functions, and metaprogramming templates |
|
||||
/// | \subpage fennec_math | Implementation of math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). Additional extensions are provided for other common math functions. |
|
||||
/// | \subpage fennec_memory | Implementation of functions related to memory management. |
|
||||
/// | \subpage fennec_containers | Implementation of common data structures, those that are specified in the C++ STD Library, and custom data structures that fennec uses. |
|
||||
///
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CORE_ENGINE_H
|
||||
#define FENNEC_CORE_ENGINE_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class engine {
|
||||
public:
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_ENGINE_H
|
||||
|
||||
169
include/fennec/core/event.h
Normal file
169
include/fennec/core/event.h
Normal file
@@ -0,0 +1,169 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_CORE_EVENT_H
|
||||
#define FENNEC_CORE_EVENT_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/rtti/enable.h>
|
||||
|
||||
#include <fennec/rtti/typeid.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
struct event;
|
||||
|
||||
///
|
||||
/// \brief Class outlining the interface for an object that listens for events
|
||||
class event_listener {
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Virtual Destructor
|
||||
virtual ~event_listener();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Event Handling ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Event Handling
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief event handler callback
|
||||
/// \param event the event to handle
|
||||
virtual void handle_event(event* event) = 0;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
FENNEC_RTTI_CLASS_ENABLE() {
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Main event interface, includes static methods for registering listeners and dispatching events
|
||||
struct event {
|
||||
|
||||
// Constructor & Destructor ============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
event() = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
event(const event&) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
event(event&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Virtual Destructor
|
||||
virtual ~event() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Static Event System Interface =======================================================================================
|
||||
public:
|
||||
|
||||
/// \name Event System Interface
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Handles the event loop to distribute events.
|
||||
///
|
||||
/// \details Blocking
|
||||
static void handle_events();
|
||||
|
||||
///
|
||||
/// \brief Registers a listener for the event type
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \tparam EventT the event type
|
||||
/// \param listener the listener to register
|
||||
template<typename EventT>
|
||||
static void add_listener(event_listener* listener) {
|
||||
event::_add_listener(listener, type::get<EventT>().id());
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Removes a listener from the event system
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \param listener the listener to remove
|
||||
static void remove_listener(event_listener* listener);
|
||||
|
||||
///
|
||||
/// \brief Dispatch an event at the beginning of the next tick.
|
||||
///
|
||||
/// \details Non-Blocking, Lock-Free, Wait-Free
|
||||
/// \tparam EventT The event type
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the event with
|
||||
template<typename EventT, typename...ArgsT>
|
||||
static void dispatch(ArgsT&&...args) {
|
||||
event::_dispatch(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dispatch an event immediately, on the current thread.
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \tparam EventT The event type
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the event with
|
||||
template<typename EventT, typename...ArgsT>
|
||||
static void dispatch_immediate(ArgsT&&...args) {
|
||||
event::_dispatch_immediate(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
static void _add_listener(event_listener* listener, uint64_t type);
|
||||
static void _handle_event(unique_ptr<event>& event);
|
||||
static void _dispatch(unique_ptr<event>&& event);
|
||||
static void _dispatch_immediate(unique_ptr<event>&& event);
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
FENNEC_RTTI_CLASS_ENABLE() {
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_EVENT_H
|
||||
115
include/fennec/core/logger.h
Normal file
115
include/fennec/core/logger.h
Normal file
@@ -0,0 +1,115 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/core/logger.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_CORE_LOGGER_H
|
||||
#define FENNEC_CORE_LOGGER_H
|
||||
|
||||
#include <fennec/filesystem/file.h>
|
||||
#include <fennec/rtti/singleton.h>
|
||||
#include <fennec/containers/tuple.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief logger class
|
||||
class logger : public singleton<logger> {
|
||||
|
||||
// Logger System Interface =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name System Interface
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Log a string to the log file and cout
|
||||
/// \param str the string to log
|
||||
/// \param _line the line of the log call
|
||||
/// \param _file the file log was called in
|
||||
static void log(const cstring& str,
|
||||
uint32_t _line = FENNEC_BUILTIN_LINE(),
|
||||
const char* _file = FENNEC_BUILTIN_FILE()
|
||||
) {
|
||||
logger& inst = instance();
|
||||
|
||||
if (inst._logfile.is_open()) {
|
||||
inst._logfile.print(cstring(_file, strlen(_file)));
|
||||
inst._logfile.printf("({}): ", _line);
|
||||
inst._logfile.println(str);
|
||||
}
|
||||
|
||||
inst._cout->print(cstring(_file, strlen(_file)));
|
||||
inst._cout->printf("({}): ", _line);
|
||||
inst._cout->println(str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Log a string to the log file and cout
|
||||
/// \param str the string to log
|
||||
/// \param _line the line of the log call
|
||||
/// \param _file the file log was called in
|
||||
static void log(const string& str,
|
||||
uint32_t _line = FENNEC_BUILTIN_LINE(),
|
||||
const char* _file = FENNEC_BUILTIN_FILE()
|
||||
) {
|
||||
logger& inst = instance();
|
||||
|
||||
if (inst._logfile.is_open()) {
|
||||
inst._logfile.print(cstring(_file, strlen(_file)));
|
||||
inst._logfile.printf("({}): ", _line);
|
||||
inst._logfile.println(str);
|
||||
}
|
||||
|
||||
inst._cout->print(cstring(_file, strlen(_file)));
|
||||
inst._cout->printf("({}): ", _line);
|
||||
inst._cout->println(str);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
file _logfile;
|
||||
file* _cout;
|
||||
|
||||
|
||||
// Private Constructors & Destructor ===================================================================================
|
||||
private:
|
||||
logger();
|
||||
~logger();
|
||||
|
||||
friend struct singleton;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_LOGGER_H
|
||||
47
include/fennec/core/system.h
Normal file
47
include/fennec/core/system.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_CORE_SYSTEM_H
|
||||
#define FENNEC_CORE_SYSTEM_H
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class system {
|
||||
public:
|
||||
using tick_f = void (*)(system*, double);
|
||||
using frame_f = void (*)(system*, size_t);
|
||||
|
||||
const string name;
|
||||
const tick_f tick;
|
||||
const frame_f frame;
|
||||
|
||||
system(const cstring& name, tick_f tick, frame_f frame)
|
||||
: name(name), tick(tick), frame(frame) {
|
||||
}
|
||||
|
||||
virtual ~system() = default;
|
||||
|
||||
virtual void init() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_SYSTEM_H
|
||||
123
include/fennec/core/version.h
Normal file
123
include/fennec/core/version.h
Normal file
@@ -0,0 +1,123 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/core/version.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_CORE_VERSION_H
|
||||
#define FENNEC_CORE_VERSION_H
|
||||
|
||||
#if FENNEC_COMPILER_GCC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201)
|
||||
#endif
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief simple version struct for drivers and systems
|
||||
struct version {
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
union {
|
||||
uint64_t num; //!< long version number
|
||||
struct {
|
||||
uint16_t major = { 1 }; //!< the major version
|
||||
uint16_t minor = { 0 }; //!< the minor version
|
||||
uint16_t patch = { 0 }; //!< the patch version
|
||||
uint16_t meta = { 0 }; //!< the meta version, e.g. "rc.1"
|
||||
};
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
// Comparison Operators ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Equality Operator
|
||||
/// \param lhs The e
|
||||
friend bool operator==(const version& lhs, const version& rhs) {
|
||||
return lhs.num == rhs.num;
|
||||
}
|
||||
|
||||
friend bool operator!=(const version& lhs, const version& rhs) {
|
||||
return lhs.num != rhs.num;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \param lhs
|
||||
/// \param rhs
|
||||
/// \return
|
||||
friend bool operator<(const version& lhs, const version& rhs) {
|
||||
return lhs.num < rhs.num;
|
||||
|
||||
// This generates branching instructions, even in -O4
|
||||
//return lhs.major < rhs.major or(
|
||||
// (lhs.major == rhs.major and lhs.minor < rhs.minor) or (
|
||||
// lhs.minor == rhs.minor and lhs.patch < rhs.patch
|
||||
// )
|
||||
//);
|
||||
}
|
||||
|
||||
friend bool operator>(const version& lhs, const version& rhs) {
|
||||
return lhs.num > rhs.num;
|
||||
}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#ifdef FENNEC_COMPILER_GCC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_VERSION_H
|
||||
24
include/fennec/filesystem/detail/_stdio.h
Normal file
24
include/fennec/filesystem/detail/_stdio.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
#define FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#endif // FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
589
include/fennec/filesystem/file.h
Normal file
589
include/fennec/filesystem/file.h
Normal file
@@ -0,0 +1,589 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FILESYSTEM_FILE_H
|
||||
#define FENNEC_FILESYSTEM_FILE_H
|
||||
|
||||
#include <fennec/filesystem/path.h>
|
||||
#include <fennec/format/format.h>
|
||||
|
||||
#include <fennec/string/cstring.h>
|
||||
#include <fennec/string/string.h>
|
||||
#include <fennec/string/wstring.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Mode flags for opening a file
|
||||
///
|
||||
/// fmode_binary and fmode_wide are independent of the other modes
|
||||
///
|
||||
/// \details Valid Flag Combinations
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_LANGPROC_io_fmode">
|
||||
/// <tr><th style="vertical-align: top">Flags
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-only, reading from start
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$write\f$
|
||||
/// <td style="vertical-align: top">Opens file as write-only, writing to end
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read | write\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-write, reading from start
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$write | trunc\f$
|
||||
/// <td style="vertical-align: top">Opens file as write-only, destroying contents
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read | write | trunc\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-write, destroying contents
|
||||
/// </table>
|
||||
enum fmode_ : uint8_t
|
||||
{
|
||||
fmode_read = 0b00000001 //!< Opens file for reading
|
||||
, fmode_write = 0b00000010 //!< Opens file for writing
|
||||
, fmode_trunc = 0b00000100 //!< Contents of the file will be destroyed, only compatible with write enabled modes
|
||||
, fmode_exclusive = 0b00001000 //!< Generates an error if the opened file is not empty
|
||||
, fmode_binary = 0b00010000 //!< Open in binary mode
|
||||
, fmode_wide = 0b00100000 //!< Opens a file in wide mode
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Structure for handling streams of data
|
||||
///
|
||||
/// \details operations, when errored, will return a corresponding error.
|
||||
/// Use file::get_error() to check if an error is present and return a corresponding string.
|
||||
/// Use file::clear_error() to clear the errored state.
|
||||
/// Some operations, specifically file::rename() and file::copy().
|
||||
/// <br>
|
||||
/// This file paradigm is to subvert time-of-check time-of-use (TOCTOU) attacks. This involves a threat actor
|
||||
/// reading that our application checks if a file exists, and replacing the file with another malicious file
|
||||
/// or symlink between our call to open the file.
|
||||
class file
|
||||
{
|
||||
// Constants ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
/// \brief value of an invalid position
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Validation Functions ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Validation Functions
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Check if the provided mode bitflags are a valid combination
|
||||
/// \param mode the bitfield
|
||||
/// \returns true if the combination of flags is valid, false otherwise
|
||||
static constexpr bool is_valid(uint8_t mode) {
|
||||
const bool t = mode & fmode_trunc;
|
||||
const bool x = mode & fmode_exclusive;
|
||||
const bool w = mode & fmode_write;
|
||||
|
||||
// when x is true, t must be true
|
||||
// when t is true, w must be true
|
||||
return (t && x && w)
|
||||
|| (t && w)
|
||||
|| !(t || x);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// STD Text Streams ====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name STD Text Streams
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the c stdout stream
|
||||
static file& cout();
|
||||
|
||||
///
|
||||
/// \returns the c stdin stream
|
||||
static file& cin();
|
||||
|
||||
///
|
||||
/// \returns the c stderr stream
|
||||
static file& cerr();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default constructor
|
||||
/// \details Initializes an empty stream
|
||||
file();
|
||||
|
||||
///
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const cstring& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const string& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path constructor
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const path& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move constructor
|
||||
/// \param file the stream to take ownership of
|
||||
file(file&& file) noexcept;
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
/// \details Flushes and closes an open stream
|
||||
~file();
|
||||
|
||||
///
|
||||
/// \brief Move assignment
|
||||
/// \param file the stream to take ownership of
|
||||
file& operator=(file&& file) noexcept;
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
// don't allow copying streams
|
||||
file(const file&) = delete;
|
||||
file& operator=(const file&) = delete;
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The path the stream
|
||||
const path& get_path() const {
|
||||
return _path;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The mode of the stream
|
||||
uint8_t mode() const {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if there is a valid, open stream.
|
||||
bool is_open() const {
|
||||
return _handle != nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Access =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const cstring& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const string& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \brief Open a file
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const path& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \brief Close a stream
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool close();
|
||||
|
||||
///
|
||||
/// \brief Commit the streams buffer to the file
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool commit();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Operations =====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief closes the stream and erases the file
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool erase();
|
||||
|
||||
///
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const cstring& path);
|
||||
|
||||
///
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const string& path);
|
||||
|
||||
///
|
||||
/// \brief Rebind the stream.
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const path& path);
|
||||
|
||||
///
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const cstring& path);
|
||||
|
||||
///
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const string& path);
|
||||
|
||||
///
|
||||
/// \brief Copy contents to a new file.
|
||||
/// \details Copies the contents of the current stream into a new stream bound to the file at the provided path.
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const path& path);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Positioning ====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Positioning
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the position index in the stream
|
||||
size_t get_pos() const;
|
||||
|
||||
///
|
||||
/// \param i the new index to move to
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool set_pos(size_t i);
|
||||
|
||||
///
|
||||
/// \brief return to the start of the stream
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool rewind();
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the stream has reached the end of the file, \f$false\f$ otherwise
|
||||
bool eof() const;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Binary Read Operations ==============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Binary Read Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief binary read
|
||||
/// \param data the buffer to write to
|
||||
/// \param size the size of each object in bytes
|
||||
/// \param n the number of objects to read
|
||||
/// \returns the number of objects successfully read
|
||||
size_t read(void* data, size_t size, size_t n);
|
||||
|
||||
///
|
||||
/// \brief type read
|
||||
/// \tparam T the type to read
|
||||
/// \param data the buffer to write to
|
||||
/// \param n the number of objects to read
|
||||
/// \returns the number of objects successfully read
|
||||
template<typename T>
|
||||
size_t read(T* data, size_t n) {
|
||||
return read(static_cast<void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief type read
|
||||
/// \tparam T the type to read
|
||||
/// \tparam n the number of objects to read
|
||||
/// \param data the buffer to write to
|
||||
/// \returns the number of objects successfully read
|
||||
template<typename T, size_t n>
|
||||
size_t read(T (&data)[n]) {
|
||||
return read(static_cast<void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Binary Write Operations =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Binary Write Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief put a character at the current position in the stream
|
||||
/// \param c the character to put
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool putc(char c);
|
||||
|
||||
///
|
||||
/// \brief put a wide character at the current position in the stream
|
||||
/// \param c the character to put
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool putwc(wchar_t c);
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \param data the buffer to read from
|
||||
/// \param size the size of each object in bytes
|
||||
/// \param n the number of objects to write
|
||||
/// \returns the number of objects successfully written
|
||||
size_t write(const void* data, size_t size, size_t n);
|
||||
|
||||
///
|
||||
/// \brief write a character buffer to at the current position in the stream
|
||||
/// \tparam n the number of characters to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<size_t n>
|
||||
size_t write(const char (&data)[n]) {
|
||||
return write(data, sizeof(char), n - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a wide character buffer to at the current position in the stream
|
||||
/// \tparam n the number of characters to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<size_t n>
|
||||
size_t write(const wchar_t (&data)[n]) {
|
||||
return write(data, sizeof(wchar_t), n - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \tparam T the object type to write
|
||||
/// \param data the buffer to read from
|
||||
/// \param n the number of objects to write
|
||||
/// \returns the number of objects successfully written
|
||||
template<typename T>
|
||||
size_t write(const T* data, size_t n) {
|
||||
return write(static_cast<const void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \tparam T the object type to write
|
||||
/// \tparam n the number of objects to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<typename T, size_t n>
|
||||
size_t write(const T (&data)[n]) {
|
||||
return write(static_cast<const void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Read Operations =====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Read Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the character read at the current position in the stream
|
||||
/// \details Advances the position by one character
|
||||
char getc();
|
||||
|
||||
///
|
||||
/// \returns the wide character read at the current position in the stream
|
||||
/// \details Advances the position by one wide character
|
||||
wchar_t getwc();
|
||||
|
||||
///
|
||||
/// \returns A string containing the characters from the current position to the next newline character
|
||||
/// \details Advances the position to the character following the next newline character
|
||||
string getline();
|
||||
|
||||
///
|
||||
/// \returns A wide string containing the characters from the current position to the next newline character
|
||||
/// \details Advances the position to the character following the next newline character
|
||||
wstring getwline();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Printing Operations =================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Print Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param str the string to print
|
||||
void print(const cstring& str);
|
||||
|
||||
///
|
||||
/// \brief print a string to the stream
|
||||
/// \param str the string to print
|
||||
void print(const string& str);
|
||||
|
||||
///
|
||||
/// \param str the string to print
|
||||
void println(const cstring& str);
|
||||
|
||||
///
|
||||
/// \brief print a string to the stream followed by a newline character
|
||||
/// \param str the string to print
|
||||
void println(const string& str);
|
||||
|
||||
///
|
||||
/// \brief print a formatted string to the stream
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param str the format string
|
||||
/// \param args the argument values
|
||||
template<typename...ArgsT>
|
||||
void printf(const cstring& str, ArgsT&&...args) {
|
||||
string fmt = fennec::format(str, fennec::forward<ArgsT>(args)...);
|
||||
this->print(cstring(fmt.cstr(), fmt.length()));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
// Error Handling ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Error Handling
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Returns the current error state.
|
||||
/// \returns A string containing the current error.
|
||||
cstring get_error() const { return { _error, ::strlen(_error) }; }
|
||||
|
||||
///
|
||||
/// \brief clears the errored state
|
||||
void clear_error() { _error = nullptr; }
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
FILE* _handle;
|
||||
path _path;
|
||||
uint8_t _mode;
|
||||
char* _error;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FILESYSTEM_FILE_H
|
||||
438
include/fennec/filesystem/path.h
Normal file
438
include/fennec/filesystem/path.h
Normal file
@@ -0,0 +1,438 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FILESYSTEM_PATH_H
|
||||
#define FENNEC_FILESYSTEM_PATH_H
|
||||
|
||||
#include <fennec/filesystem/path.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief struct for handling file paths
|
||||
///
|
||||
/// \details This structure makes no guarantees about the validity of a path.
|
||||
/// Operations do not examine the system's file structure.
|
||||
struct path {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
class iterator;
|
||||
friend class iterator;
|
||||
|
||||
|
||||
// Current Working Director ============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Current Working Directory
|
||||
/// @{
|
||||
|
||||
/// \brief Get the current working directory
|
||||
/// \returns a path containing the absolute path to the working directory
|
||||
static path get_current();
|
||||
|
||||
/// \brief Set the current working directory
|
||||
/// \param path the path to the new working directory
|
||||
/// \returns a path containing the absolute path to the working directory
|
||||
static path set_current(const path& path);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, returns the root of the current working directory
|
||||
path() : _str("/") { }
|
||||
|
||||
///
|
||||
/// \brief C-String Conversion Constructor
|
||||
/// \param str the cstring to convert
|
||||
path(const cstring& str)
|
||||
: _str(str) {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief String Conversion Constructor
|
||||
/// \param str the string to convert
|
||||
path(const string& str)
|
||||
: _str(str) {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Copy Constructor
|
||||
/// \param p the path to copy
|
||||
path(const path& p)
|
||||
: _str(p._str) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Move Constructor
|
||||
/// \param p the path to take ownership of
|
||||
path(path&& p) noexcept : _str(move(p._str)) { }
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
~path() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment Operators ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C-String Assignment Operator
|
||||
/// \param str the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
template<size_t n>
|
||||
path& operator=(const char (&str)[n]) {
|
||||
_str = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C-String Assignment Operator
|
||||
/// \param p the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
path& operator=(const cstring& p) {
|
||||
_str = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief String Assignment Operator
|
||||
/// \param p the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
path& operator=(const string& p) {
|
||||
_str = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Copy Assignment Operator
|
||||
/// \param p the path to copy
|
||||
/// \returns a reference to \f$this\f$ after copying \f$p\f$
|
||||
path& operator=(const path& p) {
|
||||
_str = p._str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Move Assignment Operator
|
||||
/// \param p the path to take ownership of
|
||||
/// \returns a reference to \f$this\f$ after taking ownership of \f$p\f$
|
||||
path& operator=(path&& p) noexcept {
|
||||
_str = move(p._str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief path equality operator
|
||||
/// \param p the path to compare against
|
||||
/// \returns \f$true\f$ if the paths are identical, \f$false\f$ otherwise. relative paths are not resolved
|
||||
bool operator==(const path& p) const {
|
||||
return _str == p._str;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param str the filename to append
|
||||
/// \returns a path containing the current path followed by \f$str\f$
|
||||
path operator/(const cstring& str) const {
|
||||
return path(_str + '/' + str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param str the filename to append
|
||||
/// \returns a path containing the current path followed by \f$str\f$
|
||||
path operator/(const string& str) const {
|
||||
return path(_str + '/' + str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param p the path to append
|
||||
/// \returns a path containing the current path followed by \f$p\f$
|
||||
path operator/(const path& p) const {
|
||||
return path(_str + '/' + p._str);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
///
|
||||
/// \brief the filename of the current path
|
||||
/// \returns a string containing a copy of the filename
|
||||
string filename() const {
|
||||
size_t i = _str.rfind('/');
|
||||
return _str.substring(i + 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns the underlying string representation
|
||||
const string& str() const { return _str; }
|
||||
|
||||
///
|
||||
/// \returns the underlying C-Style string representation
|
||||
const char* cstr() const { return _str.cstr(); }
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the path is empty or points to root
|
||||
bool is_empty() {
|
||||
size_t size = _str.size();
|
||||
if (size == 0) return true;
|
||||
#if FENNEC_PLATFORM_WINDOWS
|
||||
return (_str[1] == ':' && size == 3);
|
||||
#else
|
||||
return (_str[0] == '/' && size == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a copy of the path with the filename removed
|
||||
path parent() const {
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
size_t start = _str.size() - 1;
|
||||
start = _str[start] == '/' || _str[start] == '\\' ? start - 1 : start;
|
||||
|
||||
size_t r = _str.rfind('/', start);
|
||||
size_t l = _str.rfind('\\', start);
|
||||
if (r == _str.size()) {
|
||||
start = l;
|
||||
}
|
||||
else if (l == _str.size()) {
|
||||
start = r;
|
||||
}
|
||||
else {
|
||||
start = max(r, l);
|
||||
}
|
||||
return _str.substring(0, start);
|
||||
#else
|
||||
size_t start = _str.size();
|
||||
start = _str[start] == '/' ? start - 1 : start;
|
||||
return path(_str.substring(0, _str.rfind('/', start)));
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief absolute path
|
||||
/// \returns a copy of the path with relative paths resolved
|
||||
path absolute() const {
|
||||
path parse = *this;
|
||||
path working; working._str.resize(0);
|
||||
|
||||
// Check if this is a rooted path;
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
if (_str[1] != ':') {
|
||||
#else
|
||||
if (_str[0] != '/') {
|
||||
#endif
|
||||
working = get_current();
|
||||
}
|
||||
|
||||
while (not parse.is_empty()) {
|
||||
// Handle dots
|
||||
while (not parse.is_empty() && parse._str[0] == '.') {
|
||||
// Check for ".."
|
||||
if (parse._str[1] == '.') {
|
||||
// ".."
|
||||
if (parse._str.size() == 2) {
|
||||
parse = path();
|
||||
working = working.parent();
|
||||
}
|
||||
// "../"
|
||||
else if (parse._str[2] == '/') {
|
||||
working = working.parent();
|
||||
parse._str = parse._str.substring(3);
|
||||
}
|
||||
}
|
||||
// "./"
|
||||
else if (parse._str[1] == '/') {
|
||||
parse._str = parse._str.substring(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (parse.is_empty()) break;
|
||||
|
||||
// Push the path
|
||||
const size_t loc = parse._str.find('/');
|
||||
working._str += '/';
|
||||
working._str += parse._str.substring(0, loc);
|
||||
parse._str = parse._str.substring(loc + 1);
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the first filename in the path
|
||||
iterator begin() const {
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator to the end of the path
|
||||
iterator end() const {
|
||||
return iterator(this, _str.size());
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
constexpr iterator(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
constexpr iterator(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns copy of the current file in the path
|
||||
constexpr string operator*() const {
|
||||
if ((*_str)[_pos] == '/') {
|
||||
return string("");
|
||||
}
|
||||
|
||||
size_t e = _str->find('/', _pos);
|
||||
return _str->substring(_pos, e - _pos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$self\f$ after having moved to the next element in the list
|
||||
constexpr iterator& operator++() {
|
||||
_pos = min(_str->find('/', _pos) + 1, _str->size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$self\f$ before having moved to the next element in the list
|
||||
constexpr iterator operator++(int) {
|
||||
iterator it = *this;
|
||||
this->operator++();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& rhs) const {
|
||||
return _str == rhs._str and _pos == rhs._pos;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& rhs) const {
|
||||
return _str != rhs._str or _pos != rhs._pos;
|
||||
}
|
||||
|
||||
private:
|
||||
const string* _str;
|
||||
size_t _pos;
|
||||
|
||||
constexpr iterator(const path* path, size_t p)
|
||||
: _str(&path->_str)
|
||||
, _pos(p) {
|
||||
|
||||
// Handle end()
|
||||
if (p == _str->size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle rooted paths
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
if ((*_str)[1] == ':') {
|
||||
_pos = max(_pos, size_t(3));
|
||||
}
|
||||
#else
|
||||
if ((*_str)[0] == '/') {
|
||||
_pos = max(_pos, size_t(1));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure we are at the start of a directory/file name
|
||||
if (_pos != 0 && (*_str)[_pos - 1] != '/') {
|
||||
_pos = _str->find('/', _pos) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
friend struct path;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
string _str;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FILESYSTEM_PATH_H
|
||||
201
include/fennec/format/charconv.h
Normal file
201
include/fennec/format/charconv.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/charconv.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_CHARCONV_H
|
||||
#define FENNEC_FORMAT_CHARCONV_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed short x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned short x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed int x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned int x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long long x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long long x, int base);
|
||||
|
||||
///
|
||||
/// \brief integer to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x, char fmt);
|
||||
|
||||
///
|
||||
/// \brief float to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \param precision the number of decimal places to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x, char fmt, int precision);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x, char fmt);
|
||||
|
||||
///
|
||||
/// \brief float to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \param precision the number of decimal places to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x, char fmt, int precision);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_CHARCONV_H
|
||||
152
include/fennec/format/detail/_format.h
Normal file
152
include/fennec/format/detail/_format.h
Normal file
@@ -0,0 +1,152 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
#define FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
|
||||
#include <fennec/memory/pointers.h>
|
||||
#include <fennec/format/formatter.h>
|
||||
#include <fennec/format/format_arg.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
// Impl interface for templated polymorphism fuckery
|
||||
struct _format_argimpl {
|
||||
_format_argimpl() {};
|
||||
virtual ~_format_argimpl() {};
|
||||
virtual string format(const format_arg& fmt) = 0;
|
||||
virtual bool is_integer() = 0;
|
||||
virtual int64_t int_value() = 0;
|
||||
};
|
||||
|
||||
// Polymorphic template specialization
|
||||
template<typename T>
|
||||
struct _format_arg : _format_argimpl {
|
||||
formatter<T> fmtr;
|
||||
const T& val;
|
||||
_format_arg(const T& arg) : val(arg) {
|
||||
}
|
||||
virtual ~_format_arg() = default;
|
||||
string format(const format_arg& fmt) override {
|
||||
return fennec::forward<string>(fmtr(fmt, val));
|
||||
}
|
||||
|
||||
bool is_integer() override {
|
||||
return is_integral_v<T> or is_convertible_v<T, int64_t>;
|
||||
}
|
||||
virtual int64_t int_value() override {
|
||||
if constexpr(is_integral_v<T>) {
|
||||
return val;
|
||||
} else if constexpr(is_convertible_v<T, int64_t>) {
|
||||
return val;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<T&> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<const T> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<const T&> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Containing array for format args
|
||||
template<size_t N>
|
||||
struct _format_argarray {
|
||||
array<unique_ptr<_format_argimpl>, N> args;
|
||||
template<typename...ArgsT>
|
||||
_format_argarray(ArgsT&&...args)
|
||||
: args { unique_ptr<_format_argimpl>(new _format_arg<ArgsT>(fennec::forward<ArgsT>(args)))... } {
|
||||
}
|
||||
|
||||
string format(size_t i, const format_arg& fmt) {
|
||||
return args[i]->format(fmt);
|
||||
}
|
||||
|
||||
bool is_integer(size_t i) {
|
||||
return args[i]->is_integer();
|
||||
}
|
||||
|
||||
int64_t int_value(size_t i) {
|
||||
return args[i]->int_value();
|
||||
}
|
||||
};
|
||||
|
||||
// checks if character is a valid format type
|
||||
constexpr bool _isfmt_t(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 's': case '?': // strings
|
||||
case 'c': // char
|
||||
case 'd': // decimal
|
||||
case 'b': case 'B': // binary
|
||||
case 'o': // octal
|
||||
case 'x': case 'X': // hex
|
||||
case 'a': case 'A': // float hex
|
||||
case 'e': case 'E': // scientific notation
|
||||
case 'f': case 'F': // fixed precision
|
||||
case 'g': case 'G': // general precision
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if character is a valid format int type
|
||||
constexpr bool _isfmt_i(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 'd': // decimal
|
||||
case 'b': case 'B': // binary
|
||||
case 'o': // octal
|
||||
case 'x': case 'X': // hex
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if character is a valid format float type
|
||||
constexpr bool _isfmt_f(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 'a': case 'A': // float hex
|
||||
case 'e': case 'E': // scientific notation
|
||||
case 'f': case 'F': // fixed precision
|
||||
case 'g': case 'G': // general precision
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
384
include/fennec/format/format.h
Normal file
384
include/fennec/format/format.h
Normal file
@@ -0,0 +1,384 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/format.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMAT_H
|
||||
#define FENNEC_FORMAT_FORMAT_H
|
||||
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
#include <fennec/format/detail/_format.h>
|
||||
#include <fennec/format/formatter.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief C++ 20 format specification
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param str The format string
|
||||
/// \param args The argument values
|
||||
/// \returns A formatted string using the C++20 format specification
|
||||
template<typename...ArgsT>
|
||||
string format(const cstring& str, ArgsT&&...args) {
|
||||
static constexpr size_t argc = sizeof...(ArgsT);
|
||||
static constexpr format_arg default_fmt = {
|
||||
.fill = ' ',
|
||||
.align = '\0', // default to locale
|
||||
.sign = '\0', // default to sign only for negative numbers, gets handled later in code
|
||||
.alt = false, // default no prefix
|
||||
.upper = false,
|
||||
.width = 0,
|
||||
.precision = 6, // default to 6 sigfigs
|
||||
.base = 10,
|
||||
.type = '\0',
|
||||
};
|
||||
|
||||
// empty case
|
||||
if constexpr(argc == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
detail::_format_argarray<argc> argarray = { fennec::forward<ArgsT>(args)... };
|
||||
string res;
|
||||
size_t i = 0;
|
||||
size_t arg_c = -1;
|
||||
|
||||
while (i <= str.length()) {
|
||||
size_t brace = str.find('{', i);
|
||||
size_t end = str.find('}', i);
|
||||
format_arg fmt = default_fmt;
|
||||
|
||||
// check for '}}'
|
||||
if (end < brace) {
|
||||
if (str[end + 1] == '}') {
|
||||
res += string(str.data() + i, end - i);
|
||||
i = end + 2;
|
||||
continue;
|
||||
}
|
||||
assertf(false, "fennec::format syntax error, encountered unexpected '{'")
|
||||
}
|
||||
|
||||
// append string
|
||||
if (brace >= str.length()) { // handle end case
|
||||
res += string(str.data() + i, str.length() - i);
|
||||
break;
|
||||
}
|
||||
res += string(str.data() + i, brace - i);
|
||||
|
||||
// next brace, validate escape
|
||||
size_t next_brace = str.find('{', brace + 1);
|
||||
if (brace + 1 == next_brace) {
|
||||
res += '{';
|
||||
i = next_brace + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// find contained colon
|
||||
size_t colon = str.find(':', brace);
|
||||
|
||||
// validate colon and brace location
|
||||
assertf(colon < next_brace or end < next_brace, "fennec::format syntax error, mismatched '{}'");
|
||||
|
||||
// parse index if present
|
||||
size_t id = min(colon, end) - 1;
|
||||
if (id > brace) {
|
||||
arg_c = 0;
|
||||
} else {
|
||||
++arg_c;
|
||||
}
|
||||
for (size_t j = id, k = 1; j > brace; --j, k *= 10) {
|
||||
size_t u = (str[j] - '0');
|
||||
assertf(u < 10, "fennec::format syntax error, invalid argument index");
|
||||
arg_c += k * u;
|
||||
}
|
||||
|
||||
// store argument to allow nested replacement fields
|
||||
size_t arg = arg_c;
|
||||
|
||||
// validate index
|
||||
assertf(arg < argc, "fennec::format syntax error, invalid argument index");
|
||||
|
||||
// early return case for no colon
|
||||
if (colon > end) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse format specifiers
|
||||
|
||||
// we're going to parse right-to-left since the valid combinations
|
||||
// of specifiers change based on the type of the argument
|
||||
|
||||
// to compensate for this, the nested replacement fields need to be computed in this loop
|
||||
// (nested replacement deduced)
|
||||
size_t nrfd = 0;
|
||||
size_t nnrf = 0;
|
||||
|
||||
// first find the matching '}' brace, e is not necessarily the matching brace
|
||||
// since some specifiers allow nested replacement fields
|
||||
size_t parse = colon;
|
||||
while (str[parse + 1] != '}') {
|
||||
if (next_brace < end) { // if the next brace is before the next closing brace
|
||||
++nnrf;
|
||||
nrfd += str[end - 1] == '{';
|
||||
parse = end + 1;
|
||||
end = str.find('}', parse);
|
||||
next_brace = str.find('{', parse);
|
||||
} else {
|
||||
parse = end - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertf(nrfd <= 2 and parse < str.length() - 1 and str[parse + 1] == '}',
|
||||
"fennec::format syntax error, mismatched '{}'");
|
||||
|
||||
// check type
|
||||
switch (str[parse]) {
|
||||
default: break;
|
||||
|
||||
case 's': case '?': // strings
|
||||
case 'c': // char
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
|
||||
case 'd': // decimal
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'B': // binary
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'b':
|
||||
fmt.base = 2;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'o': // octal
|
||||
fmt.base = 8;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'X': // hex
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'x':
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
|
||||
case 'A':
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'a': // float hex
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'E': // scientific notation
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'e':
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'F': // fixed precision
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'f':
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'G': // general precision
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'g':
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
}
|
||||
|
||||
// early return
|
||||
if (parse == colon) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// search for width and precision
|
||||
size_t x = 0, j = 1;
|
||||
bool found_decimal = false;
|
||||
size_t num_decimals = 0;
|
||||
bool is_float_t = detail::_isfmt_f(fmt.type);
|
||||
bool is_str_t = fmt.type == 's';
|
||||
bool is_integer_t = detail::_isfmt_i(fmt.type);
|
||||
bool ded_width_f = false;
|
||||
bool ded_width = false;
|
||||
size_t ded_temp_i = 0;
|
||||
|
||||
// default "precision" for strings should be 0 for no limit
|
||||
if (is_str_t) {
|
||||
fmt.precision = 0;
|
||||
}
|
||||
|
||||
// parse width and precision
|
||||
while (isdigit(str[parse]) or (found_decimal = (str[parse] == '.')) or str[parse] == '{' or str[parse] == '}') {
|
||||
// handle decimal point for precision
|
||||
if (found_decimal) {
|
||||
assertf(is_float_t or is_str_t, "fennec::format syntax error, encountered precision argument on non-floating point format");
|
||||
assertf(num_decimals == 0, "fennec::format syntax error, multiple decimals detected in floating point format");
|
||||
++num_decimals;
|
||||
found_decimal = false;
|
||||
|
||||
fmt.precision = x;
|
||||
x = 0, j = 1;
|
||||
--parse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for nested replacement field
|
||||
if (str[parse] == '{') {
|
||||
assertf(str[parse - 1] == '0' or str[parse - 1] == '.' or not isdigit(str[parse - 1]),
|
||||
"fennec::format syntax error, unexpected digit preceding nested replacement field");
|
||||
|
||||
bool prec = str[parse - 1] == '.';
|
||||
bool ded = str[parse + 1] == '}';
|
||||
|
||||
size_t sub;
|
||||
if (nrfd == 2) { // if both are deduced, parse normally. Hack with prefix and postfix.
|
||||
sub = prec ? ++arg_c + 1 : arg_c++;
|
||||
} else if (nrfd == 1 and nnrf == 2 and prec and ded) { // if only precision is nrf, deduce width first
|
||||
ded_width_f = true;
|
||||
ded_temp_i = parse;
|
||||
continue;
|
||||
} else { // otherwise deduce normally
|
||||
sub = ded ? ++arg_c : x;
|
||||
}
|
||||
|
||||
assertf(sub < argc, "fennec::format syntax error, argument index out of range in nested replacement field");
|
||||
assertf(argarray.is_integer(sub), "fennec::format argument error, nested replacement field argument is not convertible to integral type");
|
||||
|
||||
(prec ? fmt.precision : fmt.width) = argarray.int_value(sub);
|
||||
x = 0;
|
||||
|
||||
if (ded_width_f) {
|
||||
ded_width_f = false;
|
||||
ded_width = true;
|
||||
swap(ded_temp_i, parse);
|
||||
arg_c = sub;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ded_width) {
|
||||
parse = ded_temp_i;
|
||||
ded_width = false;
|
||||
}
|
||||
|
||||
parse -= 1 + prec;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore closing brace for nested replacement fields
|
||||
if (str[parse] == '}') {
|
||||
--parse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// crude way to only handle 0 case if 0 is the last digit
|
||||
fmt.fill = str[parse] == '0' ? '0' : ' ';
|
||||
|
||||
// parse the number
|
||||
x += j * (str[parse] - '0');
|
||||
j *= 10;
|
||||
--parse;
|
||||
}
|
||||
if (x != 0) {
|
||||
fmt.width = x;
|
||||
}
|
||||
|
||||
// early return
|
||||
if (parse == colon) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for alt form
|
||||
if (str[parse] == '#') {
|
||||
assertf(is_float_t or is_integer_t, "fennec::format syntax error, encountered alt spec ('#') with non-decimal type");
|
||||
fmt.alt = true;
|
||||
--parse;
|
||||
}
|
||||
|
||||
// check for sign
|
||||
if (str[parse] == '-' or str[parse] == '+' or str[parse] == ' ') {
|
||||
fmt.sign = str[parse];
|
||||
if (str[parse] == ' ') { // handle fill if only space, gets overwritten if encounters fill character
|
||||
fmt.fill = ' ';
|
||||
}
|
||||
--parse;
|
||||
}
|
||||
|
||||
// check for alignment
|
||||
if (str[parse] == '<' or str[parse] == '>' or str[parse] == '^') {
|
||||
fmt.align = str[parse];
|
||||
--parse;
|
||||
}
|
||||
|
||||
// fill character
|
||||
if (str[parse] != ':') {
|
||||
fmt.fill = str[parse];
|
||||
if (str[parse] == ' ') {
|
||||
fmt.sign = fmt.sign == '\0' ? ' ' : fmt.sign;
|
||||
}
|
||||
--parse;
|
||||
}
|
||||
|
||||
// default sign
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
|
||||
// validate that we handled the entire format arg
|
||||
assertf(parse == colon, "fennec::format syntax error, malformed format string detected, possible double colon");
|
||||
|
||||
|
||||
// add formatted argument
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMAT_H
|
||||
55
include/fennec/format/format_arg.h
Normal file
55
include/fennec/format/format_arg.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/format_arg.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMAT_ARG_H
|
||||
#define FENNEC_FORMAT_FORMAT_ARG_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief helper struct for `fennec::format`
|
||||
struct format_arg {
|
||||
char fill; //!< the fill character of the argument
|
||||
char align; //!< the alignment of the argument
|
||||
char sign; //!< the sign to use
|
||||
bool alt; //!< alternate form
|
||||
bool upper; //!< print as uppercase
|
||||
size_t width; //!< the width of the fill
|
||||
size_t precision; //!< the number of decimal points
|
||||
size_t base; //!< the base to use
|
||||
char type; //!< the argument type
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMAT_ARG_H
|
||||
301
include/fennec/format/formatter.h
Normal file
301
include/fennec/format/formatter.h
Normal file
@@ -0,0 +1,301 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/formatter.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMATTER_H
|
||||
#define FENNEC_FORMAT_FORMATTER_H
|
||||
|
||||
#include <fennec/format/charconv.h>
|
||||
#include <fennec/string/string.h>
|
||||
#include <fennec/format/format_arg.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// base template =======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Formatter struct, used to turn values into formatted strings
|
||||
/// \tparam T The type to format
|
||||
template<typename T>
|
||||
struct formatter {
|
||||
///
|
||||
/// \brief default format function
|
||||
///
|
||||
/// \details throws a static assertion
|
||||
/// \returns empty string
|
||||
string operator()(const format_arg&, const T&) {
|
||||
static_assert(false, "Formatter not implemented for the provided type.");
|
||||
return string("");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// strings =============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief formatter of a character array
|
||||
/// \tparam N the number of characters
|
||||
template<size_t N>
|
||||
struct formatter<char[N]> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const char (&str)[N]) {
|
||||
return string(str);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a character array
|
||||
/// \tparam N the number of characters
|
||||
template<size_t N>
|
||||
struct formatter<const char[N]> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const char (&str)[N]) {
|
||||
return string(str);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a C-Style string
|
||||
template<>
|
||||
struct formatter<cstring> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const cstring& str) {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a string
|
||||
template<>
|
||||
struct formatter<string> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const string& str) {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// decimal types =======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief formatter of integral types
|
||||
template<typename IntT> requires(is_integral_v<IntT> and not is_bool_v<IntT>)
|
||||
struct formatter<IntT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the integral argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, IntT x) {
|
||||
char digits[128] = {};
|
||||
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.base);
|
||||
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
||||
size_t len = chk - digits;
|
||||
|
||||
// handle uppercase
|
||||
if (fmt.upper) {
|
||||
for (auto& digit : digits) {
|
||||
if (digit == 0) {
|
||||
break;
|
||||
}
|
||||
digit = toupper(digit);
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_sign = (x < 0 or fmt.sign != '-');
|
||||
const bool zero = fmt.fill == '0';
|
||||
const size_t prefix = fmt.alt ? (fmt.type == 'd' ? 0 : 2 - (fmt.type == 'o')) : 0;
|
||||
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
||||
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
||||
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
||||
size_t sign = 0;
|
||||
|
||||
string res = string(explen);
|
||||
|
||||
if (fill > 0) {
|
||||
switch (fmt.align) {
|
||||
case '<':
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
||||
break;
|
||||
case '>': case '\0':
|
||||
memcpy(res.data() + explen - len, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, explen - len);
|
||||
break;
|
||||
case '^':
|
||||
size_t bef = fill / 2 + has_sign + prefix;
|
||||
size_t aft = explen - bef;
|
||||
memcpy(res.data() + bef, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, bef);
|
||||
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
}
|
||||
|
||||
if (has_sign) {
|
||||
res[sign] = (x < 0) ? '-' : fmt.sign;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
res[sign + has_sign] = '0';
|
||||
if (fmt.type != 'o') {
|
||||
res[sign + has_sign + 1] = fmt.type;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of boolean types
|
||||
template<typename BoolT> requires(is_bool_v<BoolT>)
|
||||
struct formatter<BoolT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the boolean argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, BoolT x) {
|
||||
if (fmt.type == 's' or fmt.type == '\0') {
|
||||
return x ? string("true") : string("false");
|
||||
}
|
||||
|
||||
return formatter<uint8_t>{}(fmt, static_cast<uint8_t>(x));
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of floating point types
|
||||
template<typename FloatT> requires(is_floating_point_v<FloatT>)
|
||||
struct formatter<FloatT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the float argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, FloatT x) {
|
||||
|
||||
// nan & inf cases
|
||||
if (fennec::isnan(x)) {
|
||||
return string("nan");
|
||||
}
|
||||
if (fennec::isinf(x)) {
|
||||
return string("inf");
|
||||
}
|
||||
|
||||
|
||||
char digits[128] = {};
|
||||
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.type, fmt.precision);
|
||||
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
||||
size_t len = chk - digits;
|
||||
|
||||
// handle uppercase
|
||||
if (fmt.upper) {
|
||||
for (auto& digit : digits) {
|
||||
if (digit == 0) {
|
||||
break;
|
||||
}
|
||||
digit = toupper(digit);
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_sign = (x < 0 or fmt.sign != '-');
|
||||
const bool zero = fmt.fill == '0';
|
||||
const size_t prefix = fmt.alt ? 2 : 0;
|
||||
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
||||
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
||||
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
||||
size_t sign = 0;
|
||||
|
||||
string res = string(explen);
|
||||
|
||||
if (fill > 0) {
|
||||
switch (fmt.align) {
|
||||
case '<':
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
||||
break;
|
||||
case '>': case '\0':
|
||||
memcpy(res.data() + explen - len, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, explen - len);
|
||||
break;
|
||||
case '^':
|
||||
size_t bef = fill / 2 + has_sign + prefix;
|
||||
size_t aft = explen - bef;
|
||||
memcpy(res.data() + bef, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, bef);
|
||||
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
}
|
||||
|
||||
if (has_sign) {
|
||||
res[sign] = (x < 0) ? '-' : fmt.sign;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
res[sign + has_sign] = '0';
|
||||
res[sign + has_sign + 1] = fmt.type + ('x' - 'a');
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMATTER_H
|
||||
113
include/fennec/interpret/tokenizer.h
Normal file
113
include/fennec/interpret/tokenizer.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/interpret/tokenizer.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_INTERPRET_TOKENIZER_H
|
||||
#define FENNEC_INTERPRET_TOKENIZER_H
|
||||
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/map.h>
|
||||
#include <fennec/containers/priority_queue.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
//
|
||||
// escape sequences are tricky, sometimes they must be separated by white space,
|
||||
// other times they don't. Requiring a list of all possible escape sequences is unrealistic.
|
||||
// We need to allow the user of this struct to specify rules for escape sequences. Here are some basic rules:
|
||||
//
|
||||
// An escape sequence is marked by an escape character, e.g. %, \, {{
|
||||
// Multiple escape characters may be used in a single tokenizer and will have different rules
|
||||
// Escape characters may also be operators, brackets, or quotes
|
||||
// Escape sequences may contain operators, brackets, or quotes
|
||||
//
|
||||
// Here are a few examples of escape sequences from various formats and languages
|
||||
// C: \\, \n, \0, \u200b
|
||||
// PrintF: %s, %2.2f
|
||||
// Python FMT: {{, }}
|
||||
// SPSS: ''
|
||||
//
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
///
|
||||
/// \brief Escape Sequence Specification
|
||||
struct escape_sequence {
|
||||
virtual size_t operator[](const string& str, size_t i) = 0;
|
||||
};
|
||||
|
||||
struct tokenizer {
|
||||
using escseq = escape_sequence*;
|
||||
using escmap = map<char, escape_sequence*>;
|
||||
|
||||
string delimiter; //!< markers that separate tokens
|
||||
string operators; //!< operators are treated as individual tokens
|
||||
string brackets; //!< characters that mark brackets
|
||||
string quotes; //!< characters that mark a string sequence, entire string sequence is treated as one token
|
||||
escmap escapes; //!< characters that mark the start of an escape sequence and validate them
|
||||
bool numbers; //!< Anything that resembles a number
|
||||
|
||||
enum token_ : uint8_t {
|
||||
token_text = 0,
|
||||
token_integer,
|
||||
token_string,
|
||||
token_newline,
|
||||
token_escaped,
|
||||
token_operator,
|
||||
token_bracket,
|
||||
token_quoted,
|
||||
|
||||
num_token_types
|
||||
};
|
||||
|
||||
using token = pair<string, uint8_t>;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t token_delimiter = num_token_types;
|
||||
|
||||
constexpr list<token> operator()(const string& line) {
|
||||
list<token> res;
|
||||
priority_queue<pair<size_t, uint8_t>> idx;
|
||||
|
||||
for (char c : delimiter) {
|
||||
size_t i = 0;
|
||||
while (i != line.size()) {
|
||||
size_t n = line.find(c, i);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_INTERPRET_TOKENIZER_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,12 +16,102 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/assert.h
|
||||
/// \brief \ref fennec_lang_assert
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_ASSERT_H
|
||||
#define FENNEC_LANG_ASSERT_H
|
||||
|
||||
using assert_handler = void (*)(const char *, const char *, int , const char *);
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
extern void __assert_impl(const char* expression, const char* file, int line, const char* function);
|
||||
#define assert(expression) if(not(expression)) { __assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__); }
|
||||
///
|
||||
/// \page fennec_lang_assert Assertions
|
||||
///
|
||||
/// \code #include <fennec/lang/assert.h> \endcode
|
||||
///
|
||||
/// This header contains macros for making assertions about code behaviour.
|
||||
///
|
||||
/// fennec defines the following assert implementations:
|
||||
///
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_bits">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assert(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion with expression \f$expr\f$ and provide a description \f$desc\f$. Only halts in debug mode.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assertf(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion with expression \f$expr\f$ and provide a description \f$desc\f$. Always halts.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assertd(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion, ***only in debug mode***, with expression \f$expr\f$ and provide a description \f$desc\f$.
|
||||
/// This should be used when the branching caused by \f$assert\f$ would hinder performance in release mode.
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
///
|
||||
///
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
void _assert_impl(const char* expr, size_t expr_l,
|
||||
const char* file, size_t file_l, int line,
|
||||
const char* func, size_t func_l,
|
||||
const char* desc, bool halt);
|
||||
|
||||
template<size_t ExprL, size_t FileL, size_t FuncL>
|
||||
void _assert(const char (&expr)[ExprL],
|
||||
const char (&file)[FileL], int line,
|
||||
const char (&func)[FuncL],
|
||||
const char* desc,
|
||||
bool halt) {
|
||||
::_assert_impl(expr, ExprL, file, FileL, line, func, FuncL, desc, halt);
|
||||
}
|
||||
#endif
|
||||
|
||||
///
|
||||
/// \brief base assert function, halts only in debug mode
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#define assert(expression, description) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, not FENNEC_RELEASE); \
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief fail assert function, always halts
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#define assertf(expression, description) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief debug assert function, only defined in debug mode
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#if FENNEC_RELEASE
|
||||
#define assertd(expression, description)
|
||||
#else
|
||||
#define assertd(expression, description) assert(expression, description)
|
||||
#endif
|
||||
|
||||
#endif // FENNEC_LANG_ASSERT_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright (C) 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file bits.h
|
||||
/// \file fennec/lang/bits.h
|
||||
/// \brief \ref fennec_lang_bit_manipulation
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_bits">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::bit_cast "ToT bit_cast(const FromT& x)"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
@@ -81,8 +80,8 @@
|
||||
///
|
||||
|
||||
#include <fennec/lang/intrinsics.h>
|
||||
#include <fennec/memory/memory.h>
|
||||
#include <fennec/lang/detail/__bits.h>
|
||||
#include <fennec/memory/common.h>
|
||||
#include <fennec/lang/detail/_bits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -96,12 +95,10 @@ namespace fennec
|
||||
/// \param from Value to bit cast
|
||||
/// \returns A value containing a bitwise copy of the input
|
||||
template<typename ToT, typename FromT> requires(sizeof(ToT) == sizeof(FromT))
|
||||
constexpr ToT bit_cast(const FromT& from)
|
||||
{
|
||||
if constexpr(FENNEC_HAS_BUILTIN_BIT_CAST)
|
||||
constexpr ToT bit_cast(const FromT& from) {
|
||||
if constexpr(FENNEC_HAS_BUILTIN_BIT_CAST) {
|
||||
return FENNEC_BUILTIN_BIT_CAST(ToT, from);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ToT to;
|
||||
fennec::memcpy(&to, &from, sizeof(ToT));
|
||||
return to;
|
||||
@@ -120,15 +117,16 @@ constexpr ToT bit_cast(const FromT& from)
|
||||
/// \param mask the mask to and against arr
|
||||
/// \param n the number of bytes
|
||||
/// \returns the pointer \f$arr\f$
|
||||
constexpr void* bit_and(void* arr, const void* mask, size_t n)
|
||||
{
|
||||
if (arr == mask) return arr;
|
||||
constexpr void* bit_and(void* arr, const void* mask, size_t n) {
|
||||
if (arr == mask) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
uint8_t* d = static_cast<uint8_t*>(arr);
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
size_t step = detail::__bit_and(d, s, n);
|
||||
while (n > 0) {
|
||||
const size_t step = detail::_bit_and(d, s, n);
|
||||
d += step; s += step; n -= step;
|
||||
}
|
||||
|
||||
@@ -139,11 +137,14 @@ constexpr void* bit_and(void* arr, const void* mask, size_t n)
|
||||
/// \brief Safe version of fennec::bit_and
|
||||
///
|
||||
/// \details Safe version of fennec::bit_and
|
||||
/// \copydetails fennec::bit_and
|
||||
/// \param arr the array of bytes to modify
|
||||
/// \param n0 the size of arr in bytes
|
||||
/// \param mask the mask to and against arr
|
||||
/// \param n1 the size of mask in bytes
|
||||
constexpr void* bit_and_s(void* arr, size_t n0, const void* mask, size_t n1)
|
||||
{ return bit_and(arr, mask, n0 < n1 ? n0 : n1); }
|
||||
/// \returns the pointer arr
|
||||
constexpr void* bit_and_s(void* arr, size_t n0, const void* mask, size_t n1) {
|
||||
return bit_and(arr, mask, n0 < n1 ? n0 : n1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -157,15 +158,16 @@ constexpr void* bit_and_s(void* arr, size_t n0, const void* mask, size_t n1)
|
||||
/// \param mask the mask to or against arr
|
||||
/// \param n the number of bytes
|
||||
/// \returns the pointer arr
|
||||
constexpr void* bit_or(void* arr, const void* mask, size_t n)
|
||||
{
|
||||
if (arr == mask) return arr;
|
||||
constexpr void* bit_or(void* arr, const void* mask, size_t n) {
|
||||
if (arr == mask) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
uint8_t* d = static_cast<uint8_t*>(arr);
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
size_t step = detail::__bit_or(d, s, n);
|
||||
const size_t step = detail::_bit_or(d, s, n);
|
||||
d += step; s += step; n -= step;
|
||||
}
|
||||
|
||||
@@ -176,11 +178,14 @@ constexpr void* bit_or(void* arr, const void* mask, size_t n)
|
||||
/// \brief Safe version of fennec::bit_or
|
||||
///
|
||||
/// \details Safe version of fennec::bit_or
|
||||
/// \copydetails fennec::bit_or
|
||||
/// \param arr the array of bytes to modify
|
||||
/// \param n0 the size of arr in bytes
|
||||
/// \param mask the mask to or against arr
|
||||
/// \param n1 the size of mask in bytes
|
||||
constexpr void* bit_or_s(void* arr, size_t n0, const void* mask, size_t n1)
|
||||
{ return bit_or(arr, mask, n0 < n1 ? n0 : n1); }
|
||||
/// \returns the pointer arr
|
||||
constexpr void* bit_or_s(void* arr, size_t n0, const void* mask, size_t n1) {
|
||||
return bit_or(arr, mask, n0 < n1 ? n0 : n1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -194,15 +199,15 @@ constexpr void* bit_or_s(void* arr, size_t n0, const void* mask, size_t n1)
|
||||
/// \param mask the mask to or against arr
|
||||
/// \param n the number of bytes
|
||||
/// \returns the pointer arr
|
||||
constexpr void* bit_xor(void* arr, const void* mask, size_t n)
|
||||
{
|
||||
if (arr == mask) return arr;
|
||||
constexpr void* bit_xor(void* arr, const void* mask, size_t n) {
|
||||
if (arr == mask) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
uint8_t* d = static_cast<uint8_t*>(arr);
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
size_t step = detail::__bit_xor(d, s, n);
|
||||
while (n > 0) {
|
||||
const size_t step = detail::_bit_xor(d, s, n);
|
||||
d += step; s += step; n -= step;
|
||||
}
|
||||
|
||||
@@ -213,11 +218,14 @@ constexpr void* bit_xor(void* arr, const void* mask, size_t n)
|
||||
/// \brief Safe version of fennec::bit_xor
|
||||
///
|
||||
/// \details Safe version of fennec::bit_xor
|
||||
/// \copydetails fennec::bit_xor
|
||||
/// \param arr the array of bytes to modify
|
||||
/// \param n0 the size of arr in bytes
|
||||
/// \param mask the mask to or against arr
|
||||
/// \param n1 the size of mask in bytes
|
||||
constexpr void* bit_xor_s(void* arr, size_t n0, const void* mask, size_t n1)
|
||||
{ return bit_xor(arr, mask, n0 < n1 ? n0 : n1); }
|
||||
/// \returns the pointer arr
|
||||
constexpr void* bit_xor_s(void* arr, size_t n0, const void* mask, size_t n1) {
|
||||
return bit_xor(arr, mask, n0 < n1 ? n0 : n1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
238
include/fennec/lang/compare.h
Normal file
238
include/fennec/lang/compare.h
Normal file
@@ -0,0 +1,238 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_COMPARE_H
|
||||
#define FENNEC_LANG_COMPARE_H
|
||||
|
||||
#include <fennec/lang/type_operators.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// equality ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test equality of two values
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> struct equality;
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common equality operator \f$==\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_equals_v<T0, T1>
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x == y;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common less operator \f$<\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires(not has_equals_v<T0, T1>
|
||||
and has_less_v<T0, T1> and has_less_v<T1, T0>)
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not(x < y) and not(y < x);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common greater operator \f$>\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires(not(has_equals_v<T0, T1>)
|
||||
and(not has_less_v<T0, T1> or not has_less_v<T1, T0>)
|
||||
and(has_greater_v<T0, T1> and has_greater_v<T1, T0>))
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not(x > y) and not(y > x);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// inequality ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test inequality of two values
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> struct inequality;
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common inequality operator \f$\neq\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_nequals_v<T0, T1>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x != y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common equality operator \f$==\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_equals_v<T0, T1>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not (x == y);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common less operator \f$<\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_less_v<T0, T1> and has_less_v<T1, T0>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return (x < y) or (y < x);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common greater operator \f$>\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_greater_v<T0, T1> and has_greater_v<T1, T0>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return (x > y) or (y > x);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// less ================================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is less than a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_less_v<T0, T1>
|
||||
struct less {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if less, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x < y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// less_equal ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is less than or equal to a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_less_equals_v<T0, T1>
|
||||
struct less_equals {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if less than or equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x <= y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// greater =============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is greater than a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_greater_v<T0, T1>
|
||||
struct greater {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if greater, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x > y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// less_equal ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is greater than or equal to a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_greater_equals_v<T0, T1>
|
||||
struct greater_equals {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if greater than or equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x >= y;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_COMPARE_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file conditional_types.h
|
||||
/// \file fennec/lang/conditional_types.h
|
||||
/// \brief \ref fennec_lang_conditional_types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#ifndef FENNEC_LANG_CONDITIONAL_TYPES_H
|
||||
#define FENNEC_LANG_CONDITIONAL_TYPES_H
|
||||
|
||||
#include <fennec/lang/type_identity.h>
|
||||
|
||||
///
|
||||
/// \page fennec_lang_conditional_types Conditional Types
|
||||
///
|
||||
@@ -43,10 +45,9 @@
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_conditional_types">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::conditional "typename conditional<bool_t B, TrueT, FalseT>::type"<br>
|
||||
/// \ref fennec::conditional_t "conditional_t<bool_t B, TrueT, FalseT>"
|
||||
/// \ref fennec::conditional "typename conditional<bool B, TrueT, FalseT>::type"<br>
|
||||
/// \ref fennec::conditional_t "conditional_t<bool B, TrueT, FalseT>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::conditional
|
||||
///
|
||||
@@ -57,17 +58,14 @@
|
||||
/// \copydetails fennec::detect
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::enable_if "typename enable_if<bool_t B, TypeT>::type"<br>
|
||||
/// \ref fennec::enable_if_t "enable_if_t<bool_t B, TypeT>"
|
||||
/// \ref fennec::enable_if "typename enable_if<bool B, TypeT>::type"<br>
|
||||
/// \ref fennec::enable_if_t "enable_if_t<bool B, TypeT>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::enable_if
|
||||
/// </table>
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
@@ -77,57 +75,55 @@ namespace fennec
|
||||
/// \brief select between two types based on a condition
|
||||
///
|
||||
/// \details Selects between \p TrueT and \p FalseT based on the boolean value \p b.
|
||||
/// The chosen type is stored in `conditional::type`.
|
||||
/// The chosen type is stored in `fennec::conditional::type`.
|
||||
/// \tparam B the value of the condition
|
||||
/// \tparam TrueT type to use when \f$B == true\f$
|
||||
/// \tparam FalseT type to use when \f$B == false\f$
|
||||
template<bool_t B, typename TrueT, typename FalseT>
|
||||
template<bool B, typename TrueT, typename FalseT>
|
||||
struct conditional;
|
||||
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename conditional<ConditionV, TrueT, FalseT>::type```
|
||||
template<bool_t B, typename TrueT, typename FalseT>
|
||||
/// \brief Shorthand for `typename fennec::conditional<ConditionV, TrueT, FalseT>::type`
|
||||
template<bool B, typename TrueT, typename FalseT>
|
||||
using conditional_t
|
||||
= typename conditional<B, TrueT, FalseT>::type;
|
||||
|
||||
|
||||
// specialization of fennec::conditional for `true` case
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// specialization of fennec::conditional for \f$true\f$ case
|
||||
template<typename T, typename F>
|
||||
struct conditional<true, T, F>
|
||||
: type_transform<T>{};
|
||||
struct conditional<true, T, F> : type_identity<T>{};
|
||||
|
||||
|
||||
// specialization of fennec::conditional for `false` case
|
||||
// specialization of fennec::conditional for \f$false\f$ case
|
||||
template<typename T, typename F>
|
||||
struct conditional<false, T, F>
|
||||
: type_transform<F>{};
|
||||
|
||||
struct conditional<false, T, F> : type_identity<F>{};
|
||||
#endif
|
||||
|
||||
// fennec::detect ======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Detect whether `DetectT<ArgsT...>` is a valid type
|
||||
/// \brief Detect whether \f$DetectT<ArgsT...>\f$ is a valid type
|
||||
///
|
||||
/// \details Selects `DetectT<ArgsT...>` if it exists, otherwise selects `DefaultT` The chosen type is stored in `detect::type` and
|
||||
/// a boolean value is stored in `detect::is_detected` representing whether `DetectT<ArgsT...>` is found.
|
||||
/// \details Selects \f$DetectT<ArgsT...>\f$ if it exists, otherwise selects \f$DefaultT\f$ The chosen type is stored in `fennec::detect::type` and
|
||||
/// a boolean value is stored in `fennec::detect::is_detected` representing whether \f$DetectT<ArgsT...>\f$ is found.
|
||||
/// \tparam DefaultT Default type
|
||||
/// \tparam DetectT Type to detect
|
||||
/// \tparam ArgsT Any template arguments for `DetectT<ArgsT>`
|
||||
/// \tparam ArgsT Any template arguments for \f$DetectT<ArgsT>\f$
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
struct detect
|
||||
{
|
||||
using type = DefaultT;
|
||||
static constexpr bool is_detected = false;
|
||||
using type = DefaultT; //!< the detected type
|
||||
static constexpr bool is_detected = false; //!< whether it was detected
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename detect<DefaultT, DetectT, ArgsT...>::type```
|
||||
/// \brief Shorthand for `typename fennec::detect<DefaultT, DetectT, ArgsT...>::type`
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
using detect_t
|
||||
= typename detect<DefaultT, DetectT, ArgsT...>::type;
|
||||
using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;
|
||||
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// true case
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
requires requires { typename DetectT<ArgsT...>; }
|
||||
@@ -135,8 +131,8 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
||||
{
|
||||
using type = DetectT<ArgsT...>;
|
||||
static constexpr bool is_detected = true;
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
// fennec::enable_if ===================================================================================================
|
||||
@@ -144,7 +140,7 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
||||
///
|
||||
/// \brief Leverage SFINAE to conditionally enable a function or class at compile-time
|
||||
///
|
||||
/// \details If `B` is `true`, define a public member type `type`. Otherwise, there is no member. <br>
|
||||
/// \details If \f$B\f$ is \f$true\f$, define a public member type \f$type\f$. Otherwise, there is no member. <br>
|
||||
/// **Example Usage**
|
||||
/// \code{.cpp}
|
||||
/// template<typename TypeT,
|
||||
@@ -154,17 +150,19 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
||||
///
|
||||
/// \tparam B A boolean value
|
||||
/// \tparam T The type to conditionally define
|
||||
template<bool_t B, typename T = void>
|
||||
template<bool B, typename T = void>
|
||||
struct enable_if {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename enable_if<B, T>::type```
|
||||
template<bool_t B, typename T = void>
|
||||
/// \brief Shorthand for `typename fennec::enable_if<B, T>::type`
|
||||
template<bool B, typename T = void>
|
||||
using enable_if_t = typename enable_if<B, T>::type;
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// true case
|
||||
template<typename T>
|
||||
struct enable_if<true, T> { using type = T; };
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,20 +17,22 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file constants.h
|
||||
/// \file fennec/lang/constants.h
|
||||
/// \brief \ref fennec_lang_constants
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_CONSTANTS_H
|
||||
#define FENNEC_LANG_CONSTANTS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
///
|
||||
///
|
||||
/// \page fennec_lang_constants Constants
|
||||
@@ -43,27 +45,24 @@
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_constants">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::integral_constant "integral_constant<IntT, IntT ValueV>::type"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::integral_constant
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::bool_constant "bool_constant<bool_t ValueV>::type"<br>
|
||||
/// \ref fennec::bool_constant "bool_constant<bool ValueV>::type"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::bool_constant
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::bool_constant "bool_constant<bool_t ValueV>::type"<br>
|
||||
/// \ref fennec::bool_constant "bool_constant<bool ValueV>::type"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::bool_constant
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
@@ -91,7 +90,7 @@ template<typename IntT, IntT ValueV> struct integral_constant
|
||||
///
|
||||
/// \details
|
||||
/// \tparam ValueV value of the constant
|
||||
template<bool_t ValueV>
|
||||
template<bool ValueV>
|
||||
struct bool_constant
|
||||
: integral_constant<bool_t, ValueV> {};
|
||||
|
||||
|
||||
52
include/fennec/lang/declval.h
Normal file
52
include/fennec/lang/declval.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/declval.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_DECLVAL_H
|
||||
#define FENNEC_LANG_DECLVAL_H
|
||||
|
||||
#include <fennec/lang/detail/_declval.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// fennec::declval =====================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Metaprogramming helper for testing values of type T
|
||||
/// \tparam T the type
|
||||
/// \returns a metavalue of type \f$T\f$
|
||||
template<typename T> auto declval() noexcept -> decltype(detail::_declval<T>(0)) {
|
||||
static_assert(detail::_declval_protector<T>{}, "declval must not be used");
|
||||
return detail::_declval<T>(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DECLVAL_H
|
||||
@@ -1,138 +0,0 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_BITS_H
|
||||
#define FENNEC_LANG_DETAIL_BITS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// helper for bitwise and for 1 byte
|
||||
constexpr size_t __bit_and_8(void* dst, const void* src)
|
||||
{ *static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) & *static_cast<const uint8_t*>(src); return 1; }
|
||||
|
||||
// helper for bitwise and 2 bytes at once
|
||||
constexpr size_t __bit_and_16(void* dst, const void* src)
|
||||
{ *static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) & *static_cast<const uint16_t*>(src); return 2; }
|
||||
|
||||
// helper for bitwise and 4 bytes at once
|
||||
constexpr size_t __bit_and_32(void* dst, const void* src)
|
||||
{ *static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) & *static_cast<const uint32_t*>(src); return 4; }
|
||||
|
||||
// helper for bitwise and 8 bytes at once
|
||||
constexpr size_t __bit_and_64(void* dst, const void* src)
|
||||
{ *static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) & *static_cast<const uint64_t*>(src); return 8; }
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t __bit_and(void* dst, const void* src, size_t n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return __bit_and_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_and_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_and_32(dst, src);
|
||||
default:
|
||||
return __bit_and_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise or for 1 byte
|
||||
constexpr size_t __bit_or_8(void* dst, const void* src)
|
||||
{ *static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) | *static_cast<const uint8_t*>(src); return 1; }
|
||||
|
||||
// helper for bitwise or 2 bytes at once
|
||||
constexpr size_t __bit_or_16(void* dst, const void* src)
|
||||
{ *static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) | *static_cast<const uint16_t*>(src); return 2; }
|
||||
|
||||
// helper for bitwise or 4 bytes at once
|
||||
constexpr size_t __bit_or_32(void* dst, const void* src)
|
||||
{ *static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) | *static_cast<const uint32_t*>(src); return 4; }
|
||||
|
||||
// helper for bitwise or 8 bytes at once
|
||||
constexpr size_t __bit_or_64(void* dst, const void* src)
|
||||
{ *static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) | *static_cast<const uint64_t*>(src); return 8; }
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t __bit_or(void* dst, const void* src, size_t n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return __bit_or_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_or_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_or_32(dst, src);
|
||||
default:
|
||||
return __bit_or_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise and 1 byte
|
||||
constexpr size_t __bit_xor_8(void* dst, const void* src)
|
||||
{ *static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) ^ *static_cast<const uint8_t*>(src); return 1; }
|
||||
|
||||
// helper for bitwise xor 2 bytes at once
|
||||
constexpr size_t __bit_xor_16(void* dst, const void* src)
|
||||
{ *static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) ^ *static_cast<const uint16_t*>(src); return 2; }
|
||||
|
||||
// helper for bitwise xor 4 bytes at once
|
||||
constexpr size_t __bit_xor_32(void* dst, const void* src)
|
||||
{ *static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) ^ *static_cast<const uint32_t*>(src); return 4; }
|
||||
|
||||
// helper for bitwise xor 8 bytes at once
|
||||
constexpr size_t __bit_xor_64(void* dst, const void* src)
|
||||
{ *static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) ^ *static_cast<const uint64_t*>(src); return 8; }
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t __bit_xor(void* dst, const void* src, size_t n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return __bit_xor_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_xor_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_xor_32(dst, src);
|
||||
default:
|
||||
return __bit_xor_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_BITS_H
|
||||
@@ -1,64 +0,0 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
#define FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename> struct __make_unsigned : undefined_t {};
|
||||
|
||||
template<> struct __make_unsigned<char_t> : type_transform<uchar_t> {};
|
||||
template<> struct __make_unsigned<uchar_t> : type_transform<uchar_t> {};
|
||||
template<> struct __make_unsigned<schar_t> : type_transform<uchar_t> {};
|
||||
template<> struct __make_unsigned<short_t> : type_transform<ushort_t> {};
|
||||
template<> struct __make_unsigned<ushort_t> : type_transform<ushort_t> {};
|
||||
template<> struct __make_unsigned<uint_t> : type_transform<uint_t> {};
|
||||
template<> struct __make_unsigned<int_t> : type_transform<uint_t> {};
|
||||
template<> struct __make_unsigned<long_t> : type_transform<ulong_t> {};
|
||||
template<> struct __make_unsigned<ulong_t> : type_transform<ulong_t> {};
|
||||
template<> struct __make_unsigned<llong_t> : type_transform<ullong_t> {};
|
||||
template<> struct __make_unsigned<ullong_t> : type_transform<ullong_t> {};
|
||||
|
||||
|
||||
template<typename> struct __make_signed : undefined_t {};
|
||||
|
||||
template<> struct __make_signed<char_t> : type_transform<schar_t> {};
|
||||
template<> struct __make_signed<uchar_t> : type_transform<schar_t> {};
|
||||
template<> struct __make_signed<schar_t> : type_transform<schar_t> {};
|
||||
template<> struct __make_signed<short_t> : type_transform<short_t> {};
|
||||
template<> struct __make_signed<ushort_t> : type_transform<short_t> {};
|
||||
template<> struct __make_signed<uint_t> : type_transform<int_t> {};
|
||||
template<> struct __make_signed<int_t> : type_transform<int_t> {};
|
||||
template<> struct __make_signed<long_t> : type_transform<long_t> {};
|
||||
template<> struct __make_signed<ulong_t> : type_transform<long_t> {};
|
||||
template<> struct __make_signed<llong_t> : type_transform<llong_t> {};
|
||||
template<> struct __make_signed<ullong_t> : type_transform<llong_t> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
@@ -1,69 +0,0 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
|
||||
#include <fennec/lang/constants.h>
|
||||
#include <fennec/lang/float.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// Nothing interesting to note here
|
||||
template<typename> struct __is_void : false_type {};
|
||||
template<> struct __is_void<void> : true_type {};
|
||||
|
||||
template<typename> struct __is_bool : false_type {};
|
||||
template<> struct __is_bool<bool_t> : true_type {};
|
||||
|
||||
// Provides definitions for all builtin int types
|
||||
template<typename> struct __is_integral : false_type {};
|
||||
template<> struct __is_integral<bool_t> : true_type {};
|
||||
template<> struct __is_integral<char_t> : true_type {};
|
||||
template<> struct __is_integral<char8_t> : true_type {};
|
||||
template<> struct __is_integral<char16_t> : true_type {};
|
||||
template<> struct __is_integral<char32_t> : true_type {};
|
||||
template<> struct __is_integral<schar_t> : true_type {};
|
||||
template<> struct __is_integral<uchar_t> : true_type {};
|
||||
template<> struct __is_integral<wchar_t> : true_type {};
|
||||
template<> struct __is_integral<short_t> : true_type {};
|
||||
template<> struct __is_integral<ushort_t> : true_type {};
|
||||
template<> struct __is_integral<int_t> : true_type {};
|
||||
template<> struct __is_integral<uint_t> : true_type {};
|
||||
template<> struct __is_integral<long_t> : true_type {};
|
||||
template<> struct __is_integral<ulong_t> : true_type {};
|
||||
template<> struct __is_integral<llong_t> : true_type {};
|
||||
template<> struct __is_integral<ullong_t> : true_type {};
|
||||
|
||||
// Most unsigned types will underflow `-1` to the types maximum value
|
||||
template<typename TypeT> struct __is_signed : bool_constant<TypeT(-1) < TypeT(0)> {};
|
||||
template<typename TypeT> struct __is_unsigned : bool_constant<TypeT(-1) >= TypeT(0)> {};
|
||||
|
||||
template<typename> struct __is_floating_point : false_type {};
|
||||
template<> struct __is_floating_point<float_t> : true_type {};
|
||||
template<> struct __is_floating_point<double_t> : true_type {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
139
include/fennec/lang/detail/_bits.h
Normal file
139
include/fennec/lang/detail/_bits.h
Normal file
@@ -0,0 +1,139 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_BITS_H
|
||||
#define FENNEC_LANG_DETAIL_BITS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
// helper for bitwise and for 1 byte
|
||||
constexpr size_t _bit_and_8(void* dst, const void* src) {
|
||||
*static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) & *static_cast<const uint8_t*>(src); return 1;
|
||||
}
|
||||
|
||||
// helper for bitwise and 2 bytes at once
|
||||
constexpr size_t _bit_and_16(void* dst, const void* src) {
|
||||
*static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) & *static_cast<const uint16_t*>(src); return 2;
|
||||
}
|
||||
|
||||
// helper for bitwise and 4 bytes at once
|
||||
constexpr size_t _bit_and_32(void* dst, const void* src) {
|
||||
*static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) & *static_cast<const uint32_t*>(src); return 4;
|
||||
}
|
||||
|
||||
// helper for bitwise and 8 bytes at once
|
||||
constexpr size_t _bit_and_64(void* dst, const void* src) {
|
||||
*static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) & *static_cast<const uint64_t*>(src); return 8;
|
||||
}
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t _bit_and(void* dst, const void* src, size_t n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return _bit_and_8(dst, src);
|
||||
case 2: case 3:
|
||||
return _bit_and_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return _bit_and_32(dst, src);
|
||||
default:
|
||||
return _bit_and_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise or for 1 byte
|
||||
constexpr size_t _bit_or_8(void* dst, const void* src) {
|
||||
*static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) | *static_cast<const uint8_t*>(src); return 1;
|
||||
}
|
||||
|
||||
// helper for bitwise or 2 bytes at once
|
||||
constexpr size_t _bit_or_16(void* dst, const void* src) {
|
||||
*static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) | *static_cast<const uint16_t*>(src); return 2;
|
||||
}
|
||||
|
||||
// helper for bitwise or 4 bytes at once
|
||||
constexpr size_t _bit_or_32(void* dst, const void* src) {
|
||||
*static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) | *static_cast<const uint32_t*>(src); return 4;
|
||||
}
|
||||
|
||||
// helper for bitwise or 8 bytes at once
|
||||
constexpr size_t _bit_or_64(void* dst, const void* src) {
|
||||
*static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) | *static_cast<const uint64_t*>(src); return 8;
|
||||
}
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t _bit_or(void* dst, const void* src, size_t n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return _bit_or_8(dst, src);
|
||||
case 2: case 3:
|
||||
return _bit_or_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return _bit_or_32(dst, src);
|
||||
default:
|
||||
return _bit_or_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise and 1 byte
|
||||
constexpr size_t _bit_xor_8(void* dst, const void* src) {
|
||||
*static_cast<uint8_t*>(dst) = *static_cast<uint8_t*>(dst) ^ *static_cast<const uint8_t*>(src); return 1;
|
||||
}
|
||||
|
||||
// helper for bitwise xor 2 bytes at once
|
||||
constexpr size_t _bit_xor_16(void* dst, const void* src) {
|
||||
*static_cast<uint16_t*>(dst) = *static_cast<uint16_t*>(dst) ^ *static_cast<const uint16_t*>(src); return 2;
|
||||
}
|
||||
|
||||
// helper for bitwise xor 4 bytes at once
|
||||
constexpr size_t _bit_xor_32(void* dst, const void* src) {
|
||||
*static_cast<uint32_t*>(dst) = *static_cast<uint32_t*>(dst) ^ *static_cast<const uint32_t*>(src); return 4;
|
||||
}
|
||||
|
||||
// helper for bitwise xor 8 bytes at once
|
||||
constexpr size_t _bit_xor_64(void* dst, const void* src) {
|
||||
*static_cast<uint64_t*>(dst) = *static_cast<uint64_t*>(dst) ^ *static_cast<const uint64_t*>(src); return 8;
|
||||
}
|
||||
|
||||
// helper for selecting size
|
||||
constexpr size_t _bit_xor(void* dst, const void* src, size_t n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return _bit_xor_8(dst, src);
|
||||
case 2: case 3:
|
||||
return _bit_xor_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return _bit_xor_32(dst, src);
|
||||
default:
|
||||
return _bit_xor_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_BITS_H
|
||||
34
include/fennec/lang/detail/_declval.h
Normal file
34
include/fennec/lang/detail/_declval.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
#define FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
|
||||
#include <fennec/lang/constants.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename T, typename U = T&&> U _declval(int);
|
||||
template<typename T> T _declval(long);
|
||||
|
||||
template<typename T> struct _declval_protector : bool_constant<false> {};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
33
include/fennec/lang/detail/_function.h
Normal file
33
include/fennec/lang/detail/_function.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
#define FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
34
include/fennec/lang/detail/_int.h
Normal file
34
include/fennec/lang/detail/_int.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_INT_H
|
||||
#define FENNEC_LANG_DETAIL_INT_H
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4117)
|
||||
|
||||
#define __PTRDIFF_TYPE__ ptrdiff_t
|
||||
#endif
|
||||
|
||||
// Include math since stdint will define its own versions of isinf and isnan
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_INT_H
|
||||
59
include/fennec/lang/detail/_numeric_transforms.h
Normal file
59
include/fennec/lang/detail/_numeric_transforms.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
#define FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename> struct _make_unsigned : type_identity<undefined_t> {};
|
||||
|
||||
template<> struct _make_unsigned<char_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<uchar_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<schar_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<short_t> : type_identity<ushort_t> {};
|
||||
template<> struct _make_unsigned<ushort_t> : type_identity<ushort_t> {};
|
||||
template<> struct _make_unsigned<uint_t> : type_identity<uint_t> {};
|
||||
template<> struct _make_unsigned<int_t> : type_identity<uint_t> {};
|
||||
template<> struct _make_unsigned<long_t> : type_identity<ulong_t> {};
|
||||
template<> struct _make_unsigned<ulong_t> : type_identity<ulong_t> {};
|
||||
template<> struct _make_unsigned<llong_t> : type_identity<ullong_t> {};
|
||||
template<> struct _make_unsigned<ullong_t> : type_identity<ullong_t> {};
|
||||
|
||||
|
||||
template<typename> struct _make_signed : type_identity<undefined_t> {};
|
||||
|
||||
template<> struct _make_signed<char_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<uchar_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<schar_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<short_t> : type_identity<short_t> {};
|
||||
template<> struct _make_signed<ushort_t> : type_identity<short_t> {};
|
||||
template<> struct _make_signed<uint_t> : type_identity<int_t> {};
|
||||
template<> struct _make_signed<int_t> : type_identity<int_t> {};
|
||||
template<> struct _make_signed<long_t> : type_identity<long_t> {};
|
||||
template<> struct _make_signed<ulong_t> : type_identity<long_t> {};
|
||||
template<> struct _make_signed<llong_t> : type_identity<llong_t> {};
|
||||
template<> struct _make_signed<ullong_t> : type_identity<llong_t> {};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
24
include/fennec/lang/detail/_stdlib.h
Normal file
24
include/fennec/lang/detail/_stdlib.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_STDLIB_H
|
||||
#define FENNEC_LANG_DETAIL_STDLIB_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_STDLIB_H
|
||||
96
include/fennec/lang/detail/_type_sequences.h
Normal file
96
include/fennec/lang/detail/_type_sequences.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
|
||||
#include <fennec/math/common.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
template<typename...TypesT> struct _type_sequence {};
|
||||
|
||||
template<typename FirstT, typename... RestT> struct _first_element : type_identity<FirstT> {};
|
||||
|
||||
|
||||
|
||||
// fennec::nth_element =================================================================================================
|
||||
|
||||
template<size_t n, size_t i, typename...TypesT> struct _nth_element;
|
||||
|
||||
template<size_t n, size_t i> struct _nth_element<n, i> : type_identity<void> {};
|
||||
|
||||
template<size_t n, size_t i, typename HeadT, typename...RestT>
|
||||
struct _nth_element<n, i, HeadT, RestT...> : conditional<
|
||||
n == i, HeadT,
|
||||
typename _nth_element<n, i + 1, RestT...>::type
|
||||
> {};
|
||||
|
||||
|
||||
|
||||
// fennec::max_element_size ============================================================================================
|
||||
|
||||
template<size_t, typename...> struct _max_element_size;
|
||||
|
||||
template<size_t M, typename HeadT>
|
||||
struct _max_element_size<M, HeadT>
|
||||
: integral_constant<size_t, fennec::max(M, sizeof(HeadT))> {
|
||||
};
|
||||
|
||||
template<size_t M, typename HeadT, typename...RestT>
|
||||
struct _max_element_size<M, HeadT, RestT...>
|
||||
: _max_element_size<fennec::max(M, sizeof(HeadT)), RestT...> {
|
||||
};
|
||||
|
||||
|
||||
|
||||
// fennec::find_element ================================================================================================
|
||||
|
||||
template<size_t N, typename, typename...> struct _find_element;
|
||||
|
||||
template<size_t N, typename FindT, typename HeadT>
|
||||
struct _find_element<N, FindT, HeadT> : integral_constant<size_t, is_same_v<FindT, HeadT> ? N : N + 1> {};
|
||||
|
||||
template<size_t N, typename FindT, typename HeadT, typename...RestT>
|
||||
struct _find_element<N, FindT, HeadT, RestT...>
|
||||
: conditional_t<is_same_v<FindT, HeadT>, integral_constant<size_t, N>, _find_element<N + 1, FindT, RestT...>> {};
|
||||
|
||||
|
||||
// fennec::search_element ==============================================================================================
|
||||
|
||||
template<template<typename> typename, typename...> struct _search_element;
|
||||
|
||||
template<template<typename> typename SearchT> struct _search_element<SearchT> : type_identity<void> {};
|
||||
|
||||
template<template<typename> typename SearchT, typename HeadT, typename...RestT> requires(SearchT<HeadT>{})
|
||||
struct _search_element<SearchT, HeadT, RestT...>
|
||||
: conditional_t<SearchT<HeadT>{}, type_identity<HeadT>, _search_element<SearchT, RestT...>> {
|
||||
};
|
||||
|
||||
template<template<typename, typename...> typename, typename, typename...> struct _search_element_args;
|
||||
|
||||
template<template<typename, typename...> typename SearchT, typename...ArgsT>
|
||||
struct _search_element_args<SearchT, _type_sequence<ArgsT...>> : type_identity<void> {};
|
||||
|
||||
template<template<typename, typename...> typename SearchT, typename HeadT, typename...RestT, typename...ArgsT>
|
||||
struct _search_element_args<SearchT, _type_sequence<ArgsT...>, HeadT, RestT...>
|
||||
: conditional_t<SearchT<HeadT, ArgsT...>{}, type_identity<HeadT>, _search_element_args<SearchT, _type_sequence<ArgsT...>, RestT...>> {};
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
151
include/fennec/lang/detail/_type_traits.h
Normal file
151
include/fennec/lang/detail/_type_traits.h
Normal file
@@ -0,0 +1,151 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
|
||||
#include <fennec/lang/ranges.h>
|
||||
#include <fennec/lang/constants.h>
|
||||
#include <fennec/lang/declval.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
// Nothing interesting to note here
|
||||
template<typename> struct _is_void : false_type {};
|
||||
template<> struct _is_void<void> : true_type {};
|
||||
|
||||
template<typename> struct _is_bool : false_type {};
|
||||
template<> struct _is_bool<bool_t> : true_type {};
|
||||
|
||||
template<typename> struct _is_null_pointer : false_type {};
|
||||
template<> struct _is_null_pointer<nullptr_t> : true_type {};
|
||||
|
||||
// Provides definitions for all builtin int types
|
||||
template<typename> struct _is_integral : false_type {};
|
||||
template<> struct _is_integral<bool_t> : true_type {};
|
||||
template<> struct _is_integral<char_t> : true_type {};
|
||||
template<> struct _is_integral<char8_t> : true_type {};
|
||||
template<> struct _is_integral<char16_t> : true_type {};
|
||||
template<> struct _is_integral<char32_t> : true_type {};
|
||||
template<> struct _is_integral<schar_t> : true_type {};
|
||||
template<> struct _is_integral<uchar_t> : true_type {};
|
||||
template<> struct _is_integral<wchar_t> : true_type {};
|
||||
template<> struct _is_integral<short_t> : true_type {};
|
||||
template<> struct _is_integral<ushort_t> : true_type {};
|
||||
template<> struct _is_integral<int_t> : true_type {};
|
||||
template<> struct _is_integral<uint_t> : true_type {};
|
||||
template<> struct _is_integral<long_t> : true_type {};
|
||||
template<> struct _is_integral<ulong_t> : true_type {};
|
||||
template<> struct _is_integral<llong_t> : true_type {};
|
||||
template<> struct _is_integral<ullong_t> : true_type {};
|
||||
|
||||
template<typename> struct _is_const : false_type {};
|
||||
template<typename T> struct _is_const<const T> : true_type {};
|
||||
|
||||
template<typename> struct _is_volatile : false_type {};
|
||||
template<typename T> struct _is_volatile<volatile T> : true_type {};
|
||||
|
||||
// Most unsigned types will underflow `-1` to the types maximum value
|
||||
template<typename TypeT> struct _is_signed : bool_constant<TypeT(-1) < TypeT(0)> {};
|
||||
template<typename TypeT> struct _is_unsigned : bool_constant<TypeT(-1) >= TypeT(0)> {};
|
||||
|
||||
template<typename> struct _is_floating_point : false_type {};
|
||||
template<> struct _is_floating_point<float_t> : true_type {};
|
||||
template<> struct _is_floating_point<double_t> : true_type {};
|
||||
|
||||
template<typename> struct _is_pointer : false_type {};
|
||||
template<typename T> struct _is_pointer<T*> : true_type {};
|
||||
|
||||
template<typename> struct _is_reference : false_type {};
|
||||
template<typename T> struct _is_reference<T&> : true_type {};
|
||||
template<typename T> struct _is_reference<T&&> : true_type {};
|
||||
|
||||
template<typename> struct _is_lvalue_reference : false_type {};
|
||||
template<typename T> struct _is_lvalue_reference<T&> : true_type {};
|
||||
|
||||
template<typename> struct _is_rvalue_reference : false_type {};
|
||||
template<typename T> struct _is_rvalue_reference<T&&> : true_type {};
|
||||
|
||||
template<typename T> struct _is_complete {
|
||||
template<typename U>
|
||||
static auto test(U*) -> bool_constant<sizeof(U) == sizeof(U)>;
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(_is_complete::test(static_cast<T*>(nullptr)));
|
||||
};
|
||||
|
||||
template<typename T> struct _is_destructible {
|
||||
template<typename U, typename = decltype(declval<T&>().~T())>
|
||||
static auto test(int) -> true_type;
|
||||
template<typename>
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(test<T>(0));
|
||||
};
|
||||
|
||||
template<typename T> struct _is_nothrow_destructible {
|
||||
template<typename U, typename = decltype(noexcept(declval<T&>().~T()))>
|
||||
static auto test(int) -> true_type;
|
||||
template<typename>
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(test<T>(0));
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/13830158/how-to-write-a-trait-which-checks-whether-a-type-is-iterable
|
||||
template<typename T>
|
||||
auto _is_iterable(int) -> decltype(
|
||||
fennec::begin(declval<T&>()) != fennec::end(declval<T&>()),
|
||||
void(),
|
||||
++declval<decltype(fennec::begin(declval<T&>()))&>(),
|
||||
void(*fennec::begin(declval<T&>())),
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_iterable(...) -> false_type;
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
auto _is_indexable(int) -> decltype(
|
||||
declval<T&>()[0],
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_indexable(...) -> false_type;
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
auto _is_mappable(int) -> decltype(
|
||||
declval<T&>()[declval<typename T::key_t&>()],
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_mappable(...) -> false_type;
|
||||
|
||||
|
||||
template<typename B> auto _ptr_conv(const volatile B*) -> true_type;
|
||||
template<typename> auto _ptr_conv(const volatile void*) -> false_type;
|
||||
|
||||
template<typename B, typename D> auto _is_base_of(int) -> decltype(detail::_ptr_conv<B>(static_cast<D*>(nullptr)));
|
||||
template<typename, typename> auto _is_base_of(...) -> false_type;
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
93
include/fennec/lang/detail/_type_transforms.h
Normal file
93
include/fennec/lang/detail/_type_transforms.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/detail/_type_traits.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
template<typename TypeT> struct _add_pointer : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _add_pointer<TypeT&> : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _remove_pointer : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_pointer<TypeT*> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT> struct _add_const : type_identity<const TypeT> {};
|
||||
template<typename TypeT> struct _remove_const : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_const<const TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT> struct _add_volatile : type_identity<volatile TypeT> {};
|
||||
template<typename TypeT> struct _remove_volatile : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_volatile<volatile TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT> struct _add_cv : _add_const<typename _add_volatile<TypeT>::type> {};
|
||||
template<typename TypeT> struct _remove_cv : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<const TypeT> : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<volatile TypeT> : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<const volatile TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay_selector : conditional_t<_is_const<const TypeT>{}, _remove_cv<TypeT>, _add_pointer<TypeT>> {};
|
||||
|
||||
template<typename TypeT> requires requires { typename TypeT::decay_t; }
|
||||
struct _decay_selector<TypeT> : type_identity<typename TypeT::decay_t> {};
|
||||
|
||||
template<typename TypeT, size_t N>
|
||||
struct _decay_selector<TypeT[N]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay_selector<TypeT[]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename TypeT> struct _decay {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_lvalue_reference {
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _add_lvalue_reference<TypeT, void_t<TypeT&>> {
|
||||
using type = TypeT&;
|
||||
};
|
||||
|
||||
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_rvalue_reference {
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _add_rvalue_reference<TypeT, void_t<TypeT&&>> {
|
||||
using type = TypeT&&;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,13 +17,15 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file float.h
|
||||
/// \file fennec/lang/float.h
|
||||
/// \brief metaprogramming floating point type info
|
||||
///
|
||||
///
|
||||
/// \details this file is automatically generated for the current build environment
|
||||
/// \details This file is automatically generated for the current build environment.
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// Environment for this build: GNU Linux x86_64
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -32,56 +34,158 @@
|
||||
|
||||
#include <fennec/lang/bits.h>
|
||||
|
||||
#undef FLT_HAS_INFINITY
|
||||
#undef FLT_HAS_QUIET_NAN
|
||||
#undef FLT_HAS_SIGNALING_NAN
|
||||
#undef FLT_HAS_DENORM
|
||||
#undef FLT_HAS_DENORM_LOSS
|
||||
#undef FLT_ROUNDS
|
||||
#undef FLT_IS_IEC559
|
||||
#undef FLT_MANT_DIG
|
||||
#undef FLT_DIG
|
||||
#undef FLT_DECIMAL_DIG
|
||||
#undef FLT_RADIX
|
||||
#undef FLT_MIN_EXP
|
||||
#undef FLT_MAX_EXP
|
||||
#undef FLT_MIN_10_EXP
|
||||
#undef FLT_MAX_10_EXP
|
||||
#undef FLT_TRAPS
|
||||
#undef FLT_TINYNESS_BEFORE
|
||||
#undef FLT_MIN
|
||||
#undef FLT_MAX
|
||||
#undef FLT_EPSILON
|
||||
#undef FLT_INF
|
||||
#undef FLT_QUIET_NAN
|
||||
#undef FLT_SIGNALING_NAN
|
||||
#undef FLT_DENORM_MIN
|
||||
#undef FLT_ROUND_ERR
|
||||
|
||||
/// \brief Does \f$float\f$ have an infinity?
|
||||
#define FLT_HAS_INFINITY 1
|
||||
/// \brief Does \f$float\f$ have a quiet NaN?
|
||||
#define FLT_HAS_QUIET_NAN 1
|
||||
/// \brief Does \f$float\f$ have a signaling NaN?
|
||||
#define FLT_HAS_SIGNALING_NAN 1
|
||||
/// \brief Does \f$float\f$ use denormalization?
|
||||
#define FLT_HAS_DENORM 1
|
||||
/// \brief Does \f$float\f$ have loss with denormalization?
|
||||
#define FLT_HAS_DENORM_LOSS 0
|
||||
/// \brief What rounding style does \f$float\f$ use?
|
||||
#define FLT_ROUNDS 1
|
||||
/// \brief Does \f$float\f$ use the IEEE floating point specification?
|
||||
#define FLT_IS_IEC559 1
|
||||
/// \brief The number of mantissa bits in \f$float\f$.
|
||||
#define FLT_MANT_DIG 24
|
||||
/// \brief The number of decimal digits guaranteed to be preserved in a \f$float\f$ → \f$text\f$ → \f$float\f$.
|
||||
#define FLT_DIG 6
|
||||
/// \brief The decimal precision required to serialize and deserialize a \f$float\f$.
|
||||
#define FLT_DECIMAL_DIG 9
|
||||
/// \brief The radix, or integer base, used to represent a \f$float\f$.
|
||||
#define FLT_RADIX 2
|
||||
/// \brief The minimum negative integer such that \f${FLT_RADIX}^{FLT_MIN_EXP}\f$ results in a normalized \f$float\f$.
|
||||
#define FLT_MIN_EXP -125
|
||||
/// \brief The maximum positive integer such that \f${FLT_RADIX}^{FLT_MAX_EXP}\f$ results in a non-infinite \f$float\f$.
|
||||
#define FLT_MAX_EXP 128
|
||||
/// \brief The minimum negative integer such that \f${10}^{FLT_MIN_EXP}\f$ results in a normalized \f$float\f$.
|
||||
#define FLT_MIN_10_EXP -37
|
||||
/// \brief The maximum positive integer such that \f${10}^{FLT_MAX_EXP}\f$ results in a non-infinite \f$float\f$.
|
||||
#define FLT_MAX_10_EXP 38
|
||||
/// \brief Do arithmetics operations with \f$float\f$ trap?
|
||||
#define FLT_TRAPS 0
|
||||
/// \brief Do arithmetics operations with \f$float\f$ check for underflow?
|
||||
#define FLT_TINYNESS_BEFORE 0
|
||||
/// \brief Smallest positive, finite, normal value of \f$float\f$.
|
||||
#define FLT_MIN fennec::bit_cast<float>(0x800000)
|
||||
/// \brief Largest positive, finite value of \f$float\f$.
|
||||
#define FLT_MAX fennec::bit_cast<float>(0x7f7fffff)
|
||||
/// \brief The difference between \f$1.0\f$ and the next representable value of \f$float\f$.
|
||||
#define FLT_EPSILON fennec::bit_cast<float>(0x34000000)
|
||||
/// \brief A value representing \f$\inf\f$ of type \f$float\f$.
|
||||
#define FLT_INF fennec::bit_cast<float>(0x7f800000)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$float\f$ that does not trap.
|
||||
#define FLT_QUIET_NAN fennec::bit_cast<float>(0x7fc00000)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$float\f$ that traps.
|
||||
#define FLT_SIGNALING_NAN fennec::bit_cast<float>(0x7fa00000)
|
||||
/// \brief Smallest positive, finite, subnormal value of \f$float\f$.
|
||||
#define FLT_DENORM_MIN fennec::bit_cast<float>(0x1)
|
||||
/// \brief Maximum rounding error of type \f$float\f$.
|
||||
#define FLT_ROUND_ERR fennec::bit_cast<float>(0x3f000000)
|
||||
|
||||
#undef DBL_HAS_INFINITY
|
||||
#undef DBL_HAS_QUIET_NAN
|
||||
#undef DBL_HAS_SIGNALING_NAN
|
||||
#undef DBL_HAS_DENORM
|
||||
#undef DBL_HAS_DENORM_LOSS
|
||||
#undef DBL_ROUNDS
|
||||
#undef DBL_IS_IEC559
|
||||
#undef DBL_MANT_DIG
|
||||
#undef DBL_DIG
|
||||
#undef DBL_DECIMAL_DIG
|
||||
#undef DBL_RADIX
|
||||
#undef DBL_MIN_EXP
|
||||
#undef DBL_MAX_EXP
|
||||
#undef DBL_MIN_10_EXP
|
||||
#undef DBL_MAX_10_EXP
|
||||
#undef DBL_TRAPS
|
||||
#undef DBL_TINYNESS_BEFORE
|
||||
#undef DBL_MIN
|
||||
#undef DBL_MAX
|
||||
#undef DBL_EPSILON
|
||||
#undef DBL_INF
|
||||
#undef DBL_QUIET_NAN
|
||||
#undef DBL_SIGNALING_NAN
|
||||
#undef DBL_DENORM_MIN
|
||||
#undef DBL_ROUND_ERR
|
||||
|
||||
/// \brief Does \f$double\f$ have an infinity?
|
||||
#define DBL_HAS_INFINITY 1
|
||||
/// \brief Does \f$double\f$ have a quiet NaN?
|
||||
#define DBL_HAS_QUIET_NAN 1
|
||||
/// \brief Does \f$double\f$ have a signaling NaN?
|
||||
#define DBL_HAS_SIGNALING_NAN 1
|
||||
/// \brief Does \f$double\f$ use denormalization?
|
||||
#define DBL_HAS_DENORM 1
|
||||
/// \brief Does \f$double\f$ have loss with denormalization?
|
||||
#define DBL_HAS_DENORM_LOSS 0
|
||||
/// \brief What rounding style does \f$double\f$ use?
|
||||
#define DBL_ROUNDS 1
|
||||
/// \brief Does \f$double\f$ use the IEEE doubleing point specification?
|
||||
#define DBL_IS_IEC559 1
|
||||
/// \brief The number of mantissa bits in \f$double\f$.
|
||||
#define DBL_MANT_DIG 53
|
||||
/// \brief The number of decimal digits guaranteed to be preserved in a \f$double\f$ → \f$text\f$ → \f$double\f$.
|
||||
#define DBL_DIG 15
|
||||
/// \brief The decimal precision required to serialize and deserialize a \f$double\f$.
|
||||
#define DBL_DECIMAL_DIG 17
|
||||
/// \brief The radix, or integer base, used to represent a \f$double\f$.
|
||||
#define DBL_RADIX 2
|
||||
/// \brief The minimum negative integer such that \f${DBL_RADIX}^{DBL_MIN_EXP}\f$ results in a normalized \f$double\f$.
|
||||
#define DBL_MIN_EXP -1021
|
||||
/// \brief The maximum positive integer such that \f${DBL_RADIX}^{DBL_MAX_EXP}\f$ results in a non-infinite \f$double\f$.
|
||||
#define DBL_MAX_EXP 1024
|
||||
/// \brief The minimum negative integer such that \f${10}^{DBL_MIN_EXP}\f$ results in a normalized \f$double\f$.
|
||||
#define DBL_MIN_10_EXP -307
|
||||
/// \brief The maximum positive integer such that \f${10}^{DBL_MAX_EXP}\f$ results in a non-infinite \f$double\f$.
|
||||
#define DBL_MAX_10_EXP 308
|
||||
/// \brief Do arithmetics operations with \f$double\f$ trap?
|
||||
#define DBL_TRAPS 0
|
||||
/// \brief Do arithmetics operations with \f$double\f$ check for underflow?
|
||||
#define DBL_TINYNESS_BEFORE 0
|
||||
/// \brief Smallest positive, finite, normal value of \f$double\f$.
|
||||
#define DBL_MIN fennec::bit_cast<double>(0x10000000000000ll)
|
||||
/// \brief Largest positive, finite value of \f$double\f$.
|
||||
#define DBL_MAX fennec::bit_cast<double>(0x7fefffffffffffffll)
|
||||
/// \brief The difference between \f$1.0\f$ and the next representable value of \f$double\f$.
|
||||
#define DBL_EPSILON fennec::bit_cast<double>(0x3cb0000000000000ll)
|
||||
/// \brief A value representing \f$\inf\f$ of type \f$double\f$.
|
||||
#define DBL_INF fennec::bit_cast<double>(0x7ff0000000000000ll)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$double\f$ that does not trap.
|
||||
#define DBL_QUIET_NAN fennec::bit_cast<double>(0x7ff8000000000000ll)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$double\f$ that traps.
|
||||
#define DBL_SIGNALING_NAN fennec::bit_cast<double>(0x7ff4000000000000ll)
|
||||
/// \brief Smallest positive, finite, subnormal value of \f$double\f$.
|
||||
#define DBL_DENORM_MIN fennec::bit_cast<double>(0x1ll)
|
||||
/// \brief Maximum rounding error of type \f$double\f$.
|
||||
#define DBL_ROUND_ERR fennec::bit_cast<double>(0x3fe0000000000000ll)
|
||||
|
||||
#endif // FENNEC_LANG_FLOAT_H
|
||||
|
||||
136
include/fennec/lang/function.h
Normal file
136
include/fennec/lang/function.h
Normal file
@@ -0,0 +1,136 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/function.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_LANG_FUNCTION_H
|
||||
#define FENNEC_LANG_FUNCTION_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/assert.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
#include <fennec/lang/detail/_function.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
///
|
||||
/// \brief a class capable of holding a function or non-capturing lambda
|
||||
template<typename> class function;
|
||||
|
||||
///
|
||||
/// \brief a class capable of holding a function or non-capturing lambda
|
||||
/// \tparam ReturnT The return type of the function
|
||||
/// \tparam ArgsT The argument types of the function
|
||||
template<typename ReturnT, typename...ArgsT>
|
||||
class function<ReturnT(ArgsT...)> {
|
||||
public:
|
||||
///
|
||||
/// \brief default constructor
|
||||
constexpr function() noexcept
|
||||
: call(nullptr) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~function() = default;
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param func the function to copy
|
||||
constexpr function(const function& func) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param func the function to take ownership of
|
||||
constexpr function(function&& func) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief function constructor
|
||||
/// \param func the function to capture
|
||||
constexpr function(ReturnT (*func)(ArgsT...))
|
||||
: call(func) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief null constructor
|
||||
constexpr function(nullptr_t) noexcept : function() {}
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param func the function to copy
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(const function& func) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param func the function to capture
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(function&& func) = default;
|
||||
|
||||
///
|
||||
/// \brief null assignment
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(nullptr_t) {
|
||||
call = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief function assignment
|
||||
/// \param func the function to capture
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(ReturnT (*func)(ArgsT...)) {
|
||||
call = func;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief implicit bool check
|
||||
/// \returns \f$true\f$ if a function is captured, \f$false\f$ otherwise
|
||||
constexpr operator bool() const noexcept {
|
||||
return call != nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief function call operator
|
||||
/// \param args the arguments to call the function with
|
||||
/// \returns the result of the function call
|
||||
ReturnT operator()(ArgsT...args) const noexcept {
|
||||
assertf(call != nullptr, "Attempted to call a null function object!");
|
||||
return call(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
ReturnT (*call)(ArgsT&&...) { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_FUNCTION_H
|
||||
109
include/fennec/lang/hashing.h
Normal file
109
include/fennec/lang/hashing.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_HASHING_H
|
||||
#define FENNEC_LANG_HASHING_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_traits.h>
|
||||
#include <fennec/lang/bits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Struct for hashing types, there is no default hashing function
|
||||
/// \tparam Key The type to hash
|
||||
template<typename Key> struct hash;
|
||||
|
||||
/// \brief Murmur3 Hash for 64-bit ints
|
||||
template<>
|
||||
struct hash<uint64_t> {
|
||||
using type_t = uint64_t; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(uint64_t x) const {
|
||||
// Murmur3
|
||||
x ^= x >> 33U;
|
||||
x *= 0xff51afd7ed558ccd;
|
||||
x ^= x >> 33U;
|
||||
x *= 0xc4ceb9fe1a85ec53;
|
||||
x ^= x >> 33U;
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Hashing for integer types
|
||||
/// \tparam IntT the integer type
|
||||
template<typename IntT>
|
||||
requires is_integral_v<IntT>
|
||||
struct hash<IntT> : hash<uint64_t> {
|
||||
using type_t = IntT; //!< the type of the hash
|
||||
};
|
||||
|
||||
/// \brief Hashing for pointer types
|
||||
/// \tparam PtrT The base type
|
||||
template<typename PtrT>
|
||||
struct hash<PtrT*> : hash<uintptr_t> {
|
||||
using type_t = PtrT*; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param ptr the pointer to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(PtrT* ptr) const {
|
||||
return hash<uintptr_t>::operator()((uintptr_t)(const void*)ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// Float
|
||||
template<>
|
||||
struct hash<float> : hash<uint32_t> {
|
||||
using type_t = float; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(float x) const {
|
||||
return hash<uint32_t>::operator()(bit_cast<uint32_t>(x));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<double> : hash<uint64_t> {
|
||||
using type_t = double; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(double x) const {
|
||||
return hash<uint64_t>::operator()(bit_cast<uint64_t>(x));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief Pairs two hashes
|
||||
/// \param x first hash
|
||||
/// \param y second hash
|
||||
/// \returns a pairing of the two hashes
|
||||
constexpr size_t pair_hash(size_t x, size_t y) {
|
||||
// Szudzik Pairing
|
||||
return (x >= y ? (x * x) + x + y : (y * y) + x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_HASHING_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,13 +17,15 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file integer.h
|
||||
/// \file fennec/lang/integer.h
|
||||
/// \brief metaprogramming integer type info
|
||||
///
|
||||
///
|
||||
/// \details this file is automatically generated for the current build environment
|
||||
/// \details This file is automatically generated for the current build environment.
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// Environment for this build: GNU Linux x86_64
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -51,114 +53,212 @@
|
||||
#undef ULLONG_MIN
|
||||
#undef ULLONG_MAX
|
||||
|
||||
/// \brief Is \f$char\f$ signed?
|
||||
#define CHAR_IS_SIGNED true
|
||||
/// \brief Rounding style of type \f$char\f$.
|
||||
#define CHAR_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$char\f$.
|
||||
#define CHAR_RADIX_DIG 0x7
|
||||
/// \brief Number of decimal digits represented by \f$char\f$.
|
||||
#define CHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$char\f$.
|
||||
#define CHAR_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$char\f$.
|
||||
#define CHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$char\f$ trap?
|
||||
#define CHAR_TRAPS 0xtrue
|
||||
#define CHAR_MIN 0xffffff80
|
||||
/// \brief Smallest finite value of \f$char\f$.
|
||||
#define CHAR_MIN 0x80
|
||||
/// \brief Largest finite value of \f$char\f$.
|
||||
#define CHAR_MAX 0x7f
|
||||
|
||||
#define WCHAR_IS_SIGNED false
|
||||
/// \brief Is \f$wchar_t\f$ signed?
|
||||
#define WCHAR_IS_SIGNED true
|
||||
/// \brief Rounding style of type \f$wchar_t\f$.
|
||||
#define WCHAR_ROUNDS 0x0
|
||||
#define WCHAR_RADIX_DIG 0x10
|
||||
#define WCHAR_DIG 0x4
|
||||
/// \brief Number of radix digits represented by \f$wchar_t\f$.
|
||||
#define WCHAR_RADIX_DIG 0x1f
|
||||
/// \brief Number of decimal digits represented by \f$wchar_t\f$.
|
||||
#define WCHAR_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$wchar_t\f$.
|
||||
#define WCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$wchar_t\f$.
|
||||
#define WCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$wchar_t\f$ trap?
|
||||
#define WCHAR_TRAPS 0xtrue
|
||||
#define WCHAR_MIN 0x0
|
||||
#define WCHAR_MAX 0xffff
|
||||
/// \brief Smallest finite value of \f$wchar_t\f$.
|
||||
#define WCHAR_MIN 0x80000000
|
||||
/// \brief Largest finite value of \f$wchar_t\f$.
|
||||
#define WCHAR_MAX 0x7fffffff
|
||||
|
||||
/// \brief Is \f$signed char\f$ signed?
|
||||
#define SCHAR_ROUNDS 0x0
|
||||
/// \brief Rounding style of type \f$signed char\f$.
|
||||
#define SCHAR_RADIX_DIG 0x7
|
||||
/// \brief Number of radix digits represented by \f$signed char\f$.
|
||||
#define SCHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits represented by \f$signed char\f$.
|
||||
#define SCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$signed char\f$.
|
||||
#define SCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$signed char\f$ trap?
|
||||
#define SCHAR_TRAPS 0xtrue
|
||||
#define SCHAR_MIN 0xffffff80
|
||||
/// \brief Smallest finite value of \f$signed char\f$.
|
||||
#define SCHAR_MIN 0x80
|
||||
/// \brief Largest finite value of \f$signed char\f$.
|
||||
#define SCHAR_MAX 0x7f
|
||||
|
||||
/// \brief Is \f$unsigned char\f$ unsigned?
|
||||
#define UCHAR_ROUNDS 0x0
|
||||
/// \brief Rounding style of type \f$unsigned char\f$.
|
||||
#define UCHAR_RADIX_DIG 0x8
|
||||
/// \brief Number of radix digits represented by \f$unsigned char\f$.
|
||||
#define UCHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits represented by \f$unsigned char\f$.
|
||||
#define UCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned char\f$.
|
||||
#define UCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned char\f$ trap?
|
||||
#define UCHAR_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned char\f$.
|
||||
#define UCHAR_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned char\f$.
|
||||
#define UCHAR_MAX 0xff
|
||||
|
||||
/// \brief Rounding style of type \f$short\f$.
|
||||
#define SHORT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$short\f$.
|
||||
#define SHORT_RADIX_DIG 0xf
|
||||
/// \brief Number of decimal digits represented by \f$short\f$.
|
||||
#define SHORT_DIG 0x4
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$short\f$.
|
||||
#define SHORT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$short\f$.
|
||||
#define SHORT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$short\f$ trap?
|
||||
#define SHORT_TRAPS 0xtrue
|
||||
#define SHORT_MIN 0x8000
|
||||
/// \brief Smallest finite value of \f$short\f$.
|
||||
#define SHORT_MIN 0xffff8000
|
||||
/// \brief Largest finite value of \f$short\f$.
|
||||
#define SHORT_MAX 0x7fff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned short\f$.
|
||||
#define USHORT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned short\f$.
|
||||
#define USHORT_RADIX_DIG 0x10
|
||||
/// \brief Number of decimal digits represented by \f$unsigned short\f$.
|
||||
#define USHORT_DIG 0x4
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned short\f$.
|
||||
#define USHORT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$unsigned short\f$.
|
||||
#define USHORT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned short\f$ trap?
|
||||
#define USHORT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned short\f$.
|
||||
#define USHORT_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned short\f$.
|
||||
#define USHORT_MAX 0xffff
|
||||
|
||||
/// \brief Rounding style of type \f$int\f$.
|
||||
#define INT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$int\f$.
|
||||
#define INT_RADIX_DIG 0x1f
|
||||
/// \brief Number of decimal digits represented by \f$int\f$.
|
||||
#define INT_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$int\f$.
|
||||
#define INT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$int\f$.
|
||||
#define INT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$int\f$ trap?
|
||||
#define INT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$int\f$.
|
||||
#define INT_MIN 0x80000000
|
||||
/// \brief Largest finite value of \f$int\f$.
|
||||
#define INT_MAX 0x7fffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned int\f$.
|
||||
#define UINT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned int\f$.
|
||||
#define UINT_RADIX_DIG 0x20
|
||||
/// \brief Number of decimal digits represented by \f$unsigned int\f$.
|
||||
#define UINT_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned int\f$.
|
||||
#define UINT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned integer base, used to represent a \f$unsigned int\f$.
|
||||
#define UINT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned int\f$ trap?
|
||||
#define UINT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned int\f$.
|
||||
#define UINT_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned int\f$.
|
||||
#define UINT_MAX 0xffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$long int\f$.
|
||||
#define LONG_ROUNDS 0x0
|
||||
#define LONG_RADIX_DIG 0x1f
|
||||
#define LONG_DIG 0x9
|
||||
/// \brief Number of radix digits represented by \f$long int\f$.
|
||||
#define LONG_RADIX_DIG 0x3f
|
||||
/// \brief Number of decimal digits represented by \f$long int\f$.
|
||||
#define LONG_DIG 0x12
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$long int\f$.
|
||||
#define LONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or long integer base, used to represent a \f$long int\f$.
|
||||
#define LONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$long int\f$ trap?
|
||||
#define LONG_TRAPS 0xtrue
|
||||
#define LONG_MIN 0x80000000
|
||||
#define LONG_MAX 0x7fffffff
|
||||
/// \brief Smallest finite value of \f$long int\f$.
|
||||
#define LONG_MIN 0x8000000000000000
|
||||
/// \brief Largest finite value of \f$long int\f$.
|
||||
#define LONG_MAX 0x7fffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned long int\f$.
|
||||
#define ULONG_ROUNDS 0x0
|
||||
#define ULONG_RADIX_DIG 0x20
|
||||
#define ULONG_DIG 0x9
|
||||
/// \brief Number of radix digits represented by \f$unsigned long int\f$.
|
||||
#define ULONG_RADIX_DIG 0x40
|
||||
/// \brief Number of decimal digits represented by \f$unsigned long int\f$.
|
||||
#define ULONG_DIG 0x13
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned long int\f$.
|
||||
#define ULONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned long integer base, used to represent a \f$unsigned long int\f$.
|
||||
#define ULONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned long int\f$ trap?
|
||||
#define ULONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned long int\f$.
|
||||
#define ULONG_MIN 0x0
|
||||
#define ULONG_MAX 0xffffffff
|
||||
/// \brief Largest finite value of \f$unsigned long int\f$.
|
||||
#define ULONG_MAX 0xffffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$long long\f$.
|
||||
#define LLONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$long long\f$.
|
||||
#define LLONG_RADIX_DIG 0x3f
|
||||
/// \brief Number of decimal digits represented by \f$long long\f$.
|
||||
#define LLONG_DIG 0x12
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$long long\f$.
|
||||
#define LLONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or long longeger base, used to represent a \f$long long\f$.
|
||||
#define LLONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$long long\f$ trap?
|
||||
#define LLONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$long long\f$.
|
||||
#define LLONG_MIN 0x8000000000000000
|
||||
/// \brief Largest finite value of \f$long long\f$.
|
||||
#define LLONG_MAX 0x7fffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned long long\f$.
|
||||
#define ULLONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned long long\f$.
|
||||
#define ULLONG_RADIX_DIG 0x40
|
||||
/// \brief Number of decimal digits represented by \f$unsigned long long\f$.
|
||||
#define ULLONG_DIG 0x13
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned long long\f$.
|
||||
#define ULLONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned long longeger base, used to represent a \f$unsigned long long\f$.
|
||||
#define ULLONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned long long\f$ trap?
|
||||
#define ULLONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned long long\f$.
|
||||
#define ULLONG_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned long long\f$.
|
||||
#define ULLONG_MAX 0xffffffffffffffff
|
||||
|
||||
#endif // FENNEC_LANG_INTEGER_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright (C) 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,10 +17,10 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file intrinsics.h
|
||||
/// \file fennec/lang/intrinsics.h
|
||||
/// \brief \ref fennec_lang_intrinsics
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -39,78 +39,71 @@
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_intrinsics">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \f$FENNEC_HAS_BUILTIN_BIT_CAST\f$ <br>
|
||||
/// \f$Y FENNEC_BUILTIN_BIT_CAST(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// An intrinsic for doing a bitwise cast without using \f$reinterpret_cast\f$.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_BIT_CAST` <br>
|
||||
/// `Y FENNEC_BUILTIN_BIT_CAST(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_ADDRESSOF\f$ <br>
|
||||
/// \f$Y FENNEC_BUILTIN_ADDRESSOF(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// An intrinsic for doing a bitwise cast without using `reinterpret_cast`.
|
||||
/// Obtains the true address of an object in circumstances where \f$operator&\f$ is overloaded.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_ADDRESSOF` <br>
|
||||
/// `Y FENNEC_BUILTIN_ADDRESSOF(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_CONVERTIBLE\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Obtains the true address of an object in circumstances where `operator&` is overloaded.
|
||||
/// Checks if type \f$X\f$ can be converted to type \f$Y\f$.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_CONVERTIBLE` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_EMPTY\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_EMPTY(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` can be converted to type `Y`.
|
||||
/// Checks if type \f$X\f$ stores no data.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_EMPTY` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_EMPTY(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_POLYMORPHIC\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_POLYMORPHIC(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` stores no data.
|
||||
/// Checks if type \f$X\f$ is polymorphic, this is for classes only thus checks only for subtyping
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_POLYMORPHIC` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_POLYMORPHIC(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_FINAL\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_FINAL(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is polymorphic, this is for classes only thus checks only for subtyping
|
||||
/// Checks if type \f$X\f$ is final, meaning a function or class cannot be derived from.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_FINAL` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_FINAL(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_ABSTRACT\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_ABSTRACT(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is final, meaning a function or class cannot be derived from.
|
||||
/// Opposite of \f$FENNEC_BUILTIN_IS_FINAL\f$, checks if abstract, meaning \f$X\f$ has at least one pure virtual function.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_ABSTRACT` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_ABSTRACT(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Opposite of `FENNEC_BUILTIN_IS_FINAL`, checks if abstract, meaning `X` has at least one pure virtual function.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)`
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if `X` has a standard layout, here is [full criteria](https://www.cppreference.com/w/cpp/language/classes.html#Standard-layout_class)
|
||||
/// Checks if \f$X\f$ has a standard layout, here is [full criteria](https://www.cppreference.com/w/cpp/language/classes.html#Standard-layout_class)
|
||||
/// for this trait
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is constructible with args `...`, such that `X::X(...)` exists.
|
||||
/// Checks if type \f$X\f$ is constructible with args \f$\ldots\f$, such that \f$X::X(...)\f$ exists.
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
// Most major compilers support __has_builtin, notably GCC, MINGW, CLANG, and MSVC
|
||||
// Most major compilers support __has_builtin, notably GCC, MINGW, and CLANG
|
||||
#if defined(__has_builtin)
|
||||
|
||||
|
||||
// bitcast is slightly more efficient for build times than using memcpy
|
||||
#if __has_builtin(__builtin_bit_cast)
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 1
|
||||
# define FENNEC_BUILTIN_BIT_CAST(type, arg) __builtin_bit_cast(type, arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
|
||||
#endif
|
||||
// UTILITIES ===========================================================================================================
|
||||
|
||||
// addressof is very difficult to implement without intrinsics.
|
||||
#if __has_builtin(__builtin_addressof)
|
||||
@@ -120,9 +113,202 @@
|
||||
# define FENNEC_HAS_BUILTIN_ADDRESSOF 0
|
||||
#endif
|
||||
|
||||
// bitcast is slightly more efficient for build times than using memcpy
|
||||
#if __has_builtin(__builtin_bit_cast)
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 1
|
||||
# define FENNEC_BUILTIN_BIT_CAST(type, arg) __builtin_bit_cast(type, arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_LINE)
|
||||
# define FENNEC_HAS_BUILTIN_LINE 1
|
||||
# define FENNEC_BUILTIN_LINE() __builtin_LINE()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_LINE 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_COLUMN)
|
||||
# define FENNEC_HAS_BUILTIN_COLUMN 1
|
||||
# define FENNEC_BUILTIN_COLUMN() __builtin_COLUMN()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_COLUMN 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_FILE)
|
||||
# define FENNEC_HAS_BUILTIN_FILE 1
|
||||
# define FENNEC_BUILTIN_FILE() __builtin_FILE()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_FILE 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_FUNCTION)
|
||||
# define FENNEC_HAS_BUILTIN_FUNCTION 1
|
||||
# define FENNEC_BUILTIN_FUNCTION() __builtin_FUNCTION()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_FUNCTION 0
|
||||
#endif
|
||||
|
||||
|
||||
// PROPERTIES ==========================================================================================================
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_abstract)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
|
||||
# define FENNEC_BUILTIN_IS_ABSTRACT(arg) __is_abstract(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_array)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ARRAY 1
|
||||
# define FENNEC_BUILTIN_IS_ARRAY(arg) __is_array(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_ARRAY
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_bounded_array)
|
||||
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY 1
|
||||
# define FENNEC_BUILTIN_IS_BOUNDED_ARRAY(arg) __is_bounded_array(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_class)
|
||||
# define FENNEC_HAS_BUILTIN_IS_CLASS 1
|
||||
# define FENNEC_BUILTIN_IS_CLASS(arg) __is_class(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_CLASS
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_scoped_enum)
|
||||
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM 1
|
||||
# define FENNEC_BUILTIN_IS_SCOPED_ENUM(arg) __is_scoped_enum(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_POINTER(arg) __is_member_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_function_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_FUNCTION_POINTER(arg) __is_member_function_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_object_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_OBJECT_POINTER(arg) __is_member_object_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 0
|
||||
#endif
|
||||
|
||||
|
||||
// CONSTRUCTORS ========================================================================================================
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(type, ...) __is_constructible(type __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE(type, ...) __is_trivially_constructible(type __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_nothrow_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE(type, ...) __is_nothrow_constructible(type __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__has_trivial_destructor)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE(type) __has_trivial_destructor(type)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
|
||||
// ASSIGNMENTS =========================================================================================================
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_assignable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 1
|
||||
# define FENNEC_BUILTIN_IS_ASSIGNABLE(a, b) __is_assignable(a, b)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_assignable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_ASSIGNABLE(a, b) __is_trivially_assignable(a, b)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_nothrow_assignable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 1
|
||||
# define FENNEC_BUILTIN_IS_NOTHROW_ASSIGNABLE(a, b) __is_nothrow_assignable(a, b)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivial)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIAL(a) __is_trivial(a)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_copyable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_COPYABLE(a) __is_trivially_copyable(a)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__is_standard_layout)
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
|
||||
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__has_unique_object_representations)
|
||||
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
|
||||
# define FENNEC_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS(arg) __has_unique_object_representations(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 0
|
||||
#endif
|
||||
|
||||
|
||||
// Type Traits
|
||||
|
||||
// can_convert is also very difficult to implement without intrinsics
|
||||
#if __has_builtin(__is_convertible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONVERTIBLE 1
|
||||
@@ -137,14 +323,30 @@
|
||||
# define FENNEC_BUILTIN_IS_EMPTY(arg) __is_empty(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_EMPTY 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_polymorphic)
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
|
||||
# define FENNEC_BUILTIN_IS_POLYMORPHIC(arg) __is_polymorphic(arg)
|
||||
#if __has_builtin(__is_final)
|
||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
|
||||
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
|
||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics.
|
||||
#if __has_builtin(__is_enum)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ENUM 1
|
||||
# define FENNEC_BUILTIN_IS_ENUM(arg) __is_enum(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics.
|
||||
#if __has_builtin(__is_union)
|
||||
# define FENNEC_HAS_BUILTIN_IS_UNION 1
|
||||
# define FENNEC_BUILTIN_IS_UNION(arg) __is_union(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_UNION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
@@ -156,27 +358,43 @@
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_abstract)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
|
||||
# define FENNEC_BUILTIN_IS_ABSTRACT(arg) __is_abstract(arg)
|
||||
#if __has_builtin(__is_aggregate)
|
||||
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 1
|
||||
# define FENNEC_BUILTIN_IS_AGGREGATE(arg) __is_aggregate(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 0
|
||||
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__is_standard_layout)
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
|
||||
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__builtin_is_implicit_lifetime)
|
||||
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 1
|
||||
# define FENNEC_BUILTIN_IS_IMPLICIT_LIFETIME(arg) __builtin_is_implicit_lifetime(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 0
|
||||
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(type, ...) __is_constructible(type, __VA_ARGS__)
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_function)
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 1
|
||||
# define FENNEC_BUILTIN_IS_FUNCTION(arg) __is_function(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 0
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_object)
|
||||
# define FENNEC_HAS_BUILTIN_IS_OBJECT 1
|
||||
# define FENNEC_BUILTIN_IS_OBJECT(arg) __is_object(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_polymorphic)
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
|
||||
# define FENNEC_BUILTIN_IS_POLYMORPHIC(arg) __is_polymorphic(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
|
||||
#endif
|
||||
|
||||
|
||||
@@ -185,6 +403,34 @@
|
||||
|
||||
// TODO: More compiler support
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_ADDRESS_OF 1
|
||||
# define FENNEC_BUILTIN_ADDRESS_OF(arg) __builtin_addressof(arg)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 1
|
||||
# define FENNEC_BUILTIN_BIT_CAST(type, arg) __builtin_bit_cast(type, arg)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONVERTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_CONVERTIBLE(arg0, arg1) __is_convertible_to(arg0, arg1)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(...) __is_constructible(__VA_ARGS__)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_EMPTY 1
|
||||
# define FENNEC_BUILTIN_IS_EMPTY(arg) __is_empty(arg)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
|
||||
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
|
||||
# define FENNEC_BUILTIN_IS_POLYMORPHIC(arg) __is_polymorphic(arg)
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
|
||||
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FENNEC_LANG_INTRINSICS_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,23 +17,22 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file lang.h
|
||||
/// \file fennec/lang/lang.h
|
||||
/// \brief \ref fennec_lang
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_H
|
||||
#define FENNEC_LANG_H
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
#include <fennec/lang/bits.h>
|
||||
#include <fennec/lang/intrinsics.h>
|
||||
#include <fennec/lang/limits.h>
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
@@ -42,6 +41,7 @@
|
||||
///
|
||||
/// This library implements the parts of the C++ stdlib that relate to built-in types and metaprogramming.
|
||||
///
|
||||
/// - \subpage fennec_lang_assert
|
||||
/// - \subpage fennec_lang_bit_manipulation
|
||||
/// - \subpage fennec_lang_intrinsics
|
||||
/// - \subpage fennec_lang_limits
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright (C) 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file limits.h
|
||||
/// \file fennec/lang/limits.h
|
||||
/// \brief \ref fennec_lang_limits
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_limits">
|
||||
/// <tr><th style="vertical-align: top">Member
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><th colspan=2 style="text-align: center;">Traits
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// <tt>\ref fennec::numeric_limits::is_specialized "is_specialized"</tt>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
@@ -109,7 +107,6 @@
|
||||
/// \copybrief fennec::numeric_limits::traps
|
||||
///
|
||||
/// <tr><th colspan=2 style="text-align: center;">Binary
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// <tt>\ref fennec::numeric_limits::radix "radix"</tt>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
@@ -151,7 +148,6 @@
|
||||
/// \copybrief fennec::numeric_limits::max_exponent10
|
||||
///
|
||||
/// <tr><th colspan=2 style="text-align: center;">Limits
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// <tt>\ref fennec::numeric_limits::min "min()"</tt>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
@@ -201,8 +197,6 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_traits.h>
|
||||
|
||||
@@ -228,42 +222,42 @@ enum float_round_style
|
||||
/// \tparam TypeT Numeric types, may be overloaded for custom types
|
||||
template<typename TypeT> struct numeric_limits
|
||||
{
|
||||
static constexpr bool is_specialized = false; ///< Check if the template is specialized for TypeT
|
||||
static constexpr bool is_signed = false; ///< Check if TypeT is signed
|
||||
static constexpr bool is_integer = false; ///< Check if TypeT is of an integral type
|
||||
static constexpr bool is_exact = false; ///< Check if TypeT is exact in its precision
|
||||
static constexpr bool has_infinity = false; ///< Check if TypeT can hold a value representing infinity
|
||||
static constexpr bool has_quiet_nan = false; ///< Check if TypeT can hold a non-signaling nan
|
||||
static constexpr bool has_signaling_nan = false; ///< Check if TypeT can hold a signaling nan
|
||||
static constexpr bool has_denorm = false; ///< Check if TypeT denormalizes
|
||||
static constexpr bool has_denorm_loss = false; ///< Check if TypeT has precision loss when denormalized
|
||||
static constexpr bool is_iec559 = false; ///< Check if a TypeT representing a float is IEC 559 or IEEE 754
|
||||
static constexpr bool is_bounded = false; ///< Check if TypeT represents a finite set of values
|
||||
static constexpr bool is_modulo = false; ///< Check if TypeT can handle modulo arithmetic
|
||||
static constexpr bool tinyness_before = false; ///< Check if TypeT checks for tinyness before rounding
|
||||
static constexpr bool traps = false; ///< Check if TypeT can cause operations to trap
|
||||
static constexpr bool is_specialized = false; //!< Check if the template is specialized for TypeT
|
||||
static constexpr bool is_signed = false; //!< Check if TypeT is signed
|
||||
static constexpr bool is_integer = false; //!< Check if TypeT is of an integral type
|
||||
static constexpr bool is_exact = false; //!< Check if TypeT is exact in its precision
|
||||
static constexpr bool has_infinity = false; //!< Check if TypeT can hold a value representing infinity
|
||||
static constexpr bool has_quiet_nan = false; //!< Check if TypeT can hold a non-signaling nan
|
||||
static constexpr bool has_signaling_nan = false; //!< Check if TypeT can hold a signaling nan
|
||||
static constexpr bool has_denorm = false; //!< Check if TypeT denormalizes
|
||||
static constexpr bool has_denorm_loss = false; //!< Check if TypeT has precision loss when denormalized
|
||||
static constexpr bool is_iec559 = false; //!< Check if a TypeT representing a float is IEC 559 or IEEE 754
|
||||
static constexpr bool is_bounded = false; //!< Check if TypeT represents a finite set of values
|
||||
static constexpr bool is_modulo = false; //!< Check if TypeT can handle modulo arithmetic
|
||||
static constexpr bool tinyness_before = false; //!< Check if TypeT checks for tinyness before rounding
|
||||
static constexpr bool traps = false; //!< Check if TypeT can cause operations to trap
|
||||
|
||||
static constexpr int radix = 0; ///< Get the base representation of the type
|
||||
static constexpr int digits = 0; ///< Get the number of radix digits TypeT represents
|
||||
static constexpr int digits10 = 0; ///< Get the number of decimal digits TypeT represents
|
||||
static constexpr int max_digits10 = 0; ///< Get the maximum number of decimal digits TypeT represents
|
||||
static constexpr int min_exponent = 0; ///< Get the minimum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int min_exponent10 = 0; ///< Get the minimum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent = 0; ///< Get the maximum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent10 = 0; ///< Get the maximum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int radix = 0; //!< Get the base representation of the type
|
||||
static constexpr int digits = 0; //!< Get the number of radix digits TypeT represents
|
||||
static constexpr int digits10 = 0; //!< Get the number of decimal digits TypeT represents
|
||||
static constexpr int max_digits10 = 0; //!< Get the maximum number of decimal digits TypeT represents
|
||||
static constexpr int min_exponent = 0; //!< Get the minimum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int min_exponent10 = 0; //!< Get the minimum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent = 0; //!< Get the maximum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent10 = 0; //!< Get the maximum number of decimal digits that represent the exponent of TypeT
|
||||
|
||||
static constexpr float_round_style rounding_style = round_indeterminate; ///< The rounding style of TypeT
|
||||
static constexpr float_round_style rounding_style = round_indeterminate; //!< The rounding style of TypeT
|
||||
|
||||
// This is very poorly named and defined in the C++ Standard so these functions differ
|
||||
static constexpr TypeT min() { return TypeT(); } ///< Returns the minimum finite value of TypeT
|
||||
static constexpr TypeT max() { return TypeT(); } ///< Returns the maximum finite value of TypeT
|
||||
static constexpr TypeT lowest() { return TypeT(); } ///< Returns the smallest positive value of TypeT
|
||||
static constexpr TypeT epsilon() { return TypeT(); } ///< Returns the difference between 1.0 and the next representable value
|
||||
static constexpr TypeT round_error() { return TypeT(); } ///< Returns the max rounding error of TypeT
|
||||
static constexpr TypeT infinity() { return TypeT(); } ///< Returns a value of TypeT holding a positive infinity
|
||||
static constexpr TypeT quiet_NaN() { return TypeT(); } ///< Returns a value of TypeT holding a quiet NaN
|
||||
static constexpr TypeT signaling_NaN() { return TypeT(); } ///< Returns a value of TypeT holding a signaling NaN
|
||||
static constexpr TypeT denorm_min() { return TypeT(); } ///< Returns a value of TypeT holding the smallest positive subnormal
|
||||
static constexpr TypeT min() { return TypeT(); } //!< \returns the minimum finite value of TypeT
|
||||
static constexpr TypeT max() { return TypeT(); } //!< \returns the maximum finite value of TypeT
|
||||
static constexpr TypeT lowest() { return TypeT(); } //!< \returns the smallest positive value of TypeT
|
||||
static constexpr TypeT epsilon() { return TypeT(); } //!< \returns the difference between 1.0 and the next representable value
|
||||
static constexpr TypeT round_error() { return TypeT(); } //!< \returns the max rounding error of TypeT
|
||||
static constexpr TypeT infinity() { return TypeT(); } //!< \returns a value of TypeT holding a positive infinity
|
||||
static constexpr TypeT quiet_NaN() { return TypeT(); } //!< \returns a value of TypeT holding a quiet NaN
|
||||
static constexpr TypeT signaling_NaN() { return TypeT(); } //!< \returns a value of TypeT holding a signaling NaN
|
||||
static constexpr TypeT denorm_min() { return TypeT(); } //!< \returns a value of TypeT holding the smallest positive subnormal
|
||||
};
|
||||
|
||||
// Overload definitions for basic types
|
||||
@@ -295,15 +289,15 @@ template<> struct numeric_limits<float>
|
||||
static constexpr int max_exponent = FLT_MAX_EXP;
|
||||
static constexpr int max_exponent10 = FLT_MAX_10_EXP;
|
||||
|
||||
static constexpr double min() { return -FLT_MAX; }
|
||||
static constexpr double max() { return FLT_MAX; }
|
||||
static constexpr double lowest() { return FLT_MIN; }
|
||||
static constexpr double epsilon() { return FLT_EPSILON; }
|
||||
static constexpr double round_error() { return FLT_ROUND_ERR; }
|
||||
static constexpr double infinity() { return FLT_INF; }
|
||||
static constexpr double quiet_NaN() { return FLT_QUIET_NAN; }
|
||||
static constexpr double signaling_NaN() { return FLT_SIGNALING_NAN; }
|
||||
static constexpr double denorm_min() { return FLT_DENORM_MIN; }
|
||||
static constexpr float min() { return -FLT_MAX; }
|
||||
static constexpr float max() { return FLT_MAX; }
|
||||
static constexpr float lowest() { return FLT_MIN; }
|
||||
static constexpr float epsilon() { return FLT_EPSILON; }
|
||||
static constexpr float round_error() { return FLT_ROUND_ERR; }
|
||||
static constexpr float infinity() { return FLT_INF; }
|
||||
static constexpr float quiet_NaN() { return FLT_QUIET_NAN; }
|
||||
static constexpr float signaling_NaN() { return FLT_SIGNALING_NAN; }
|
||||
static constexpr float denorm_min() { return FLT_DENORM_MIN; }
|
||||
};
|
||||
|
||||
// Overload for the bultin double precision floating point type
|
||||
@@ -371,15 +365,15 @@ template<> struct numeric_limits<char>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return CHAR_MIN; }
|
||||
static constexpr double max() { return CHAR_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr char min() { return static_cast<char>(CHAR_MIN); }
|
||||
static constexpr char max() { return CHAR_MAX; }
|
||||
static constexpr char lowest() { return 1; }
|
||||
static constexpr char epsilon() { return 1; }
|
||||
static constexpr char round_error() { return 0; }
|
||||
static constexpr char infinity() { return 0; }
|
||||
static constexpr char quiet_NaN() { return 0; }
|
||||
static constexpr char signaling_NaN() { return 0; }
|
||||
static constexpr char denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -409,15 +403,15 @@ template<> struct numeric_limits<signed char>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return SCHAR_MIN; }
|
||||
static constexpr double max() { return SCHAR_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr signed char min() { return static_cast<signed char>(SCHAR_MIN); }
|
||||
static constexpr signed char max() { return SCHAR_MAX; }
|
||||
static constexpr signed char lowest() { return 1; }
|
||||
static constexpr signed char epsilon() { return 1; }
|
||||
static constexpr signed char round_error() { return 0; }
|
||||
static constexpr signed char infinity() { return 0; }
|
||||
static constexpr signed char quiet_NaN() { return 0; }
|
||||
static constexpr signed char signaling_NaN() { return 0; }
|
||||
static constexpr signed char denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -447,15 +441,15 @@ template<> struct numeric_limits<unsigned char>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return UCHAR_MIN; }
|
||||
static constexpr double max() { return UCHAR_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr unsigned char min() { return UCHAR_MIN; }
|
||||
static constexpr unsigned char max() { return UCHAR_MAX; }
|
||||
static constexpr unsigned char lowest() { return 1; }
|
||||
static constexpr unsigned char epsilon() { return 1; }
|
||||
static constexpr unsigned char round_error() { return 0; }
|
||||
static constexpr unsigned char infinity() { return 0; }
|
||||
static constexpr unsigned char quiet_NaN() { return 0; }
|
||||
static constexpr unsigned char signaling_NaN() { return 0; }
|
||||
static constexpr unsigned char denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -485,15 +479,15 @@ template<> struct numeric_limits<short>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return SHORT_MIN; }
|
||||
static constexpr double max() { return SHORT_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr short min() { return static_cast<short>(SHORT_MIN); }
|
||||
static constexpr short max() { return SHORT_MAX; }
|
||||
static constexpr short lowest() { return 1; }
|
||||
static constexpr short epsilon() { return 1; }
|
||||
static constexpr short round_error() { return 0; }
|
||||
static constexpr short infinity() { return 0; }
|
||||
static constexpr short quiet_NaN() { return 0; }
|
||||
static constexpr short signaling_NaN() { return 0; }
|
||||
static constexpr short denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -523,15 +517,15 @@ template<> struct numeric_limits<unsigned short>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return USHORT_MIN; }
|
||||
static constexpr double max() { return USHORT_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr unsigned short min() { return USHORT_MIN; }
|
||||
static constexpr unsigned short max() { return USHORT_MAX; }
|
||||
static constexpr unsigned short lowest() { return 1; }
|
||||
static constexpr unsigned short epsilon() { return 1; }
|
||||
static constexpr unsigned short round_error() { return 0; }
|
||||
static constexpr unsigned short infinity() { return 0; }
|
||||
static constexpr unsigned short quiet_NaN() { return 0; }
|
||||
static constexpr unsigned short signaling_NaN() { return 0; }
|
||||
static constexpr unsigned short denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -561,15 +555,15 @@ template<> struct numeric_limits<int>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return INT_MIN; }
|
||||
static constexpr double max() { return INT_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr int min() { return INT_MIN; }
|
||||
static constexpr int max() { return INT_MAX; }
|
||||
static constexpr int lowest() { return 1; }
|
||||
static constexpr int epsilon() { return 1; }
|
||||
static constexpr int round_error() { return 0; }
|
||||
static constexpr int infinity() { return 0; }
|
||||
static constexpr int quiet_NaN() { return 0; }
|
||||
static constexpr int signaling_NaN() { return 0; }
|
||||
static constexpr int denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -599,15 +593,15 @@ template<> struct numeric_limits<unsigned int>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return UINT_MIN; }
|
||||
static constexpr double max() { return UINT_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr unsigned int min() { return UINT_MIN; }
|
||||
static constexpr unsigned int max() { return UINT_MAX; }
|
||||
static constexpr unsigned int lowest() { return 1; }
|
||||
static constexpr unsigned int epsilon() { return 1; }
|
||||
static constexpr unsigned int round_error() { return 0; }
|
||||
static constexpr unsigned int infinity() { return 0; }
|
||||
static constexpr unsigned int quiet_NaN() { return 0; }
|
||||
static constexpr unsigned int signaling_NaN() { return 0; }
|
||||
static constexpr unsigned int denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -637,15 +631,15 @@ template<> struct numeric_limits<long int>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return LONG_MIN; }
|
||||
static constexpr double max() { return LONG_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr long int min() { return LONG_MIN; }
|
||||
static constexpr long int max() { return LONG_MAX; }
|
||||
static constexpr long int lowest() { return 1; }
|
||||
static constexpr long int epsilon() { return 1; }
|
||||
static constexpr long int round_error() { return 0; }
|
||||
static constexpr long int infinity() { return 0; }
|
||||
static constexpr long int quiet_NaN() { return 0; }
|
||||
static constexpr long int signaling_NaN() { return 0; }
|
||||
static constexpr long int denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -675,15 +669,15 @@ template<> struct numeric_limits<unsigned long int>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return ULONG_MIN; }
|
||||
static constexpr double max() { return ULONG_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr unsigned long min() { return ULONG_MIN; }
|
||||
static constexpr unsigned long max() { return ULONG_MAX; }
|
||||
static constexpr unsigned long lowest() { return 1; }
|
||||
static constexpr unsigned long epsilon() { return 1; }
|
||||
static constexpr unsigned long round_error() { return 0; }
|
||||
static constexpr unsigned long infinity() { return 0; }
|
||||
static constexpr unsigned long quiet_NaN() { return 0; }
|
||||
static constexpr unsigned long signaling_NaN() { return 0; }
|
||||
static constexpr unsigned long denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -713,15 +707,15 @@ template<> struct numeric_limits<long long>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return LLONG_MIN; }
|
||||
static constexpr double max() { return LLONG_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr long long min() { return LLONG_MIN; }
|
||||
static constexpr long long max() { return LLONG_MAX; }
|
||||
static constexpr long long lowest() { return 1; }
|
||||
static constexpr long long epsilon() { return 1; }
|
||||
static constexpr long long round_error() { return 0; }
|
||||
static constexpr long long infinity() { return 0; }
|
||||
static constexpr long long quiet_NaN() { return 0; }
|
||||
static constexpr long long signaling_NaN() { return 0; }
|
||||
static constexpr long long denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
// Overload for the builtin signed char type
|
||||
@@ -751,15 +745,15 @@ template<> struct numeric_limits<unsigned long long>
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
|
||||
static constexpr double min() { return ULLONG_MIN; }
|
||||
static constexpr double max() { return ULLONG_MAX; }
|
||||
static constexpr double lowest() { return 1; }
|
||||
static constexpr double epsilon() { return 1; }
|
||||
static constexpr double round_error() { return 0; }
|
||||
static constexpr double infinity() { return 0; }
|
||||
static constexpr double quiet_NaN() { return 0; }
|
||||
static constexpr double signaling_NaN() { return 0; }
|
||||
static constexpr double denorm_min() { return 0; }
|
||||
static constexpr unsigned long long min() { return ULLONG_MIN; }
|
||||
static constexpr unsigned long long max() { return ULLONG_MAX; }
|
||||
static constexpr unsigned long long lowest() { return 1; }
|
||||
static constexpr unsigned long long epsilon() { return 1; }
|
||||
static constexpr unsigned long long round_error() { return 0; }
|
||||
static constexpr unsigned long long infinity() { return 0; }
|
||||
static constexpr unsigned long long quiet_NaN() { return 0; }
|
||||
static constexpr unsigned long long signaling_NaN() { return 0; }
|
||||
static constexpr unsigned long long denorm_min() { return 0; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file metaprogramming.h
|
||||
/// \file fennec/lang/metaprogramming.h
|
||||
/// \brief \ref fennec_lang
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
/// - \subpage fennec_lang_constants
|
||||
/// - \subpage fennec_lang_conditional_types
|
||||
/// - \subpage fennec_lang_numeric_transforms
|
||||
/// - \subpage fennec_lang_sequences
|
||||
/// - \subpage fennec_lang_metasequences
|
||||
/// - \subpage fennec_lang_type_identity
|
||||
/// - \subpage fennec_lang_type_sequences
|
||||
/// - \subpage fennec_lang_type_traits
|
||||
/// - \subpage fennec_lang_type_transforms
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user