Compare commits

..

68 Commits

Author SHA1 Message Date
086c73f058 Merge branch 'main' of https://git.mslockbo.org/mslockbo/fennec
Messed up some files and git wants to merge
2025-08-22 12:03:26 -04:00
339f5c8cd8 - Added XDG Shell 2025-08-22 12:03:04 -04:00
18c0a7099d - Comment noting an error with stdint isinf and isnan 2025-08-22 03:03:54 -04:00
540c7fbce8 - Similar refactor on XKB 2025-08-22 02:53:09 -04:00
cbcd699ab0 - Decided to remove boost due to extensive dependencies
- Huge refactor on Wayland loading to support retrieval of Protocol headers
 - Setup EGL to create surfaces for Wayland windows
2025-08-22 02:15:57 -04:00
ff27caab4f - Fixed some variable naming with graph and it's PrettyPrinter
- Added boost-atomic and boost-thread as dependencies for concurrency support
2025-08-21 06:44:22 -04:00
fe4c49d092 - Fixed several memory errors 2025-08-20 20:57:15 -04:00
037c62bf12 - Added missing functionality from C++ spec 2025-08-20 14:00:52 -04:00
494d766741 - Missing functionality and documentation 2025-08-20 00:49:15 -04:00
83f0c01e29 - Fixes for Doxygen Layouts
- Changed dynarray indexing to use size_t
 - Added groups to optional documentation
2025-08-19 18:05:09 -04:00
4ff739d625 - Fixed Doxygen Structure once more, this bug with sections appearing under the first subpage is becoming frustrating. Currently got it so everything appears under "Contents" 2025-08-18 23:13:09 -04:00
7cd38604a7 - More Documentation 2025-08-18 19:41:08 -04:00
733fca41ef - Git ignore for generated README 2025-08-18 14:16:30 -04:00
55a8c54119 - Documentation of containers and adjusting page hierarchy 2025-08-18 14:13:35 -04:00
27754a56d7 - missed dnf for Doxygen under Fedora 2025-08-17 12:00:51 -04:00
fcf9c6adcb - Fixed naming issue from copying set as multiset.h 2025-08-17 11:55:53 -04:00
e6c0a60ea9 - Update Fedora in README.md 2025-08-17 11:51:46 -04:00
5252ba84c9 - Update TOC in README.md 2025-08-17 10:03:43 -04:00
73041e994d - Added Debian dependencies to README.md and fixed some issues that I ran into on a clean machine 2025-08-17 09:51:08 -04:00
3ddc2b3d97 - bugfix for deque with _size not being initialized
- wrote PrettyPrinter for deque
2025-08-16 13:20:51 -04:00
e91c2aa9f1 - Fixed logic error with making graph connections regarding connection objects 2025-08-16 13:05:45 -04:00
38b7221fa0 - deque, object_pool, and graph data structures + PrettyPrinters 2025-08-16 07:56:25 -04:00
8bfb59cd20 - Fixed rdtree traversers once more, a bug with initializing the queues was causing faulty results. Consider setting up more robust tests. 2025-08-14 21:28:41 -04:00
2535e1ac4b Reworked RD-Tree to behave more consistently. The construction of the tree did not allow specifying what index to insert a child at under a parent.
Traverser orders were also broken, which is now fixed.
2025-08-14 17:07:48 -04:00
f173c3e7cd - Fixed some semantics issues to make data structure names more akin to their mathematical equivalents
- multiset.h TODO: test
 - Fixed some double underscores that I missed
2025-08-14 02:57:46 -04:00
cc4d85c393 - Outlined more functions for component systems
- Tidied up map and set structures to invoke less constructors and assignments
2025-08-12 13:55:07 -04:00
d6e31a89b0 - Implemented PrettyPrinters for vector, quaternion, and matrix 2025-08-11 17:39:51 -04:00
74fb525453 - Implemented file.h and path.h PrettyPrinter 2025-08-11 12:17:57 -04:00
b9de039a10 - Debugged more PrettyPrinters, all implemented thus far work in testing
- Fixed implementation of tuple.h, TODO: Still need to complete
 - Wrote a PrettyPrinter for tuple.h
2025-08-10 23:46:36 -04:00
9f96155856 - Adjusted some tests while debugging PrettyPrinters
- Adjusted RDTreePrinter to print more "tree-like"
 - Added SetPrinter and MapPrinter
 - Fixed Issues with CStringPrinter and StringPrinter
2025-08-10 00:27:04 -04:00
d2be083a8f - Fixed up PrettyWriters 2025-08-09 19:43:26 -04:00
efe56b3699 - test doc 2025-08-08 17:02:04 -04:00
b7d8426e86 - PrettyPrinters working, added cstring/wcstring, string/wstring, optional, allocation, list 2025-08-08 01:54:39 -04:00
2cb41e1437 - Documented and Debugged containers
- Attempted to setup gdb prettywriters
2025-08-07 19:03:34 -04:00
0f721f57ea - Moved OpenGL library wrapper into platform
- Finished reorganizing PLANNING.md
2025-08-05 16:14:00 -04:00
4a3639ecb4 - Continued Texture Implementation
- Began reorganizing the planning document into /planning/
2025-08-04 21:11:22 -04:00
ff4d6efedc - Finished Buffer Object Implementation
- Implemented Vertex Array Object
 - Began Texture Implementation
2025-08-03 13:49:33 -04:00
9dc9ed4ed1 - More buffer functions 2025-08-03 02:10:27 -04:00
5e04eb0ca6 - Started implementing OpenGL wrappers 2025-08-02 20:59:56 -04:00
3d42dea9eb - Started interface for renderers
- Renamed fproc -> langproc (I'll probably never settle on a naming convention for this)
 - Refactored set to use median psl
2025-08-02 13:17:20 -04:00
3d4ea4398a - Setup Contexts to pull more info from the GPU
- Started outlining OpenGL implementation
2025-07-28 21:06:52 -04:00
7aafa4c9aa - Implemented EGL Context 2025-07-28 13:00:20 -04:00
8124ea2ae5 - Refactor on platform implementation. See comment in interface/platform.h for more info 2025-07-27 22:44:32 -04:00
d02a51fd8d - Removed Double Underscores for portability 2025-07-26 21:13:32 -04:00
7493b5252a - More implementations and dependencies for Linux Wayland support 2025-07-26 20:57:25 -04:00
7ea2710ee0 List Data Structure 2025-07-23 13:26:50 -04:00
f9de242b87 Adjusted Platform Structure 2025-07-23 12:12:29 -04:00
2117e4347c Merge branch 'main' of https://git.mslockbo.org/mslockbo/fennec 2025-07-23 12:05:30 -04:00
5ab2952e83 - Adjusted Formatting of tests
- Finished map implementation and unit tests

 TODO: Threading
2025-07-23 12:05:18 -04:00
65573f28e4 - Adjusted Formatting of tests
- Finished map implementation and unit tests

 TODO: Threading
2025-07-23 12:05:02 -04:00
73333b4c67 - Separated Platform and Compiler Dependent Behaviour into CMake scripts
- Implemented Basic Platform Interfaces
 - Implemented partial Linux platform and Wayland Display.
 - Implemented Dependencies for the above
   - map
     - set
       - optional
     - pair

TODO: threading
2025-07-22 00:59:41 -04:00
ab1c7d94be - Component-Wise Functions for Quaternions
- Fixed Allocation Bug with Strings
2025-07-17 23:16:01 -04:00
86286e84d7 - README.md Formatting 2025-07-16 23:35:32 -04:00
c72d1afe32 - Quaternions
- Started Tests for Quaternions
 - Fixed some style issues with constructors
2025-07-16 23:16:54 -04:00
f1552b89b1 - Functions to construct matrices from translations, scaling, and rotations 2025-07-16 03:45:54 -04:00
89f59c75f3 - Wrote and Debugged Unit Tests for fennec::file 2025-07-14 21:15:39 -04:00
6ae682aff6 - Removed a bug with attempt to include pure c headers
- Added some more information about the license
 - fennec::file implementation
2025-07-14 05:11:52 -04:00
5e0dc78210 - Fixed some more compilation issues
- Added some more information to the licensing section of README.md
2025-07-10 08:48:00 -04:00
4c0d36c933 - Fixed a bunch of compilation errors and warnings
- Added frameworks for retrieving specific filesystem information for a target platform
2025-07-10 01:10:13 -04:00
cc20af7504 - Removed fennec::path, see #Security Ramblings in PLANNING.md 2025-07-08 23:35:37 -04:00
649e39c70e - Switched from Allman (BSD) to 1TBS (K&R)
Namespaces, Types, and Requires/Concepts still use Allman
2025-07-08 12:08:59 -04:00
2573de0904 - Fixed some circular includes
- Documentation
 - File Declaration, TODO: Implementation
2025-07-07 21:13:07 -04:00
17d8218124 - Adjusted how asserts work and types of asserts 2025-07-07 01:09:54 -04:00
012052641d - Setup Basic Implementation for String Library 2025-07-06 19:29:28 -04:00
a33bf5206f - Stacktrace generation with failed asserts 2025-07-05 14:22:59 -04:00
0afaae72ac - Micro Optimization 2025-07-02 18:23:53 -04:00
0eeb7ae3cf - More performant roundEven 2025-07-02 17:57:57 -04:00
e2ea22f12d - Fixed some minor issues due to MSVC compat 2025-07-02 17:26:22 -04:00
234 changed files with 454541 additions and 3998 deletions

7
.gdbinit Normal file
View 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

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@
/docs/ /docs/
/bin/ /bin/
/lib/ /lib/
/doxy/README.md

6
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "external/sdl"] [submodule "external/cpptrace"]
path = external/sdl path = external/cpptrace
url = https://github.com/libsdl-org/SDL.git url = https://github.com/jeremy-rifkin/cpptrace.git

View File

@@ -1,42 +1,109 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
cmake_minimum_required(VERSION 3.30) cmake_minimum_required(VERSION 3.30)
project(fennec) project(fennec)
# External dependencies should be loaded here # External dependencies should be loaded here
add_subdirectory(external/cpptrace)
# SDL is a dependency of the project, added as a git submodule set(CMAKE_CXX_STANDARD 23)
set(SDL_STATIC 1) set(CMAKE_C_STANDARD 23)
add_subdirectory(external/sdl) set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_STANDARD 26) add_custom_target(fennec-dependencies
set(CMAKE_C_STANDARD 26) COMMAND ${CMAKE_COMMAND} -E echo "Running dependencies."
COMMENT "Running dependencies."
)
macro(fennec_add_sources)
list(APPEND FENNEC_EXTRA_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()
# 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 dependencies
find_package(Doxygen) find_package(Doxygen)
fennec_check_platform()
# any necessary include directories # 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. # Metaprogramming is a dependency for generating various type info before compilation of the engine.
add_subdirectory(metaprogramming) add_subdirectory(metaprogramming)
string(TOLOWER ${CMAKE_BUILD_TYPE} FENNEC_BUILD_NAME)
message(STATUS "OS: ${CMAKE_SYSTEM_NAME}") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
message(STATUS "Build: ${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})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME}) # add the test suite as a sub-project
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME}) add_subdirectory(test)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
add_library(fennec STATIC add_library(fennec STATIC
# CORE ================================================================================================================= # CORE =================================================================================================================
include/fennec/core/engine.h source/core/engine.cpp include/fennec/core/engine.h source/core/engine.cpp
include/fennec/core/event.h source/core/event.cpp
include/fennec/core/system.h
# SCENE ================================================================================================================
include/fennec/scene/scene.h
include/fennec/scene/component.h
# CONTAINERS =========================================================================================================== # CONTAINERS ===========================================================================================================
include/fennec/containers/containers.h
include/fennec/containers/array.h include/fennec/containers/array.h
include/fennec/containers/deque.h
include/fennec/containers/dynarray.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/traversal.h
include/fennec/containers/tuple.h
include/fennec/containers/variant.h
include/fennec/containers/detail/_tuple.h
# LANG ================================================================================================================= # LANG =================================================================================================================
@@ -46,34 +113,49 @@ add_library(fennec STATIC
include/fennec/lang/bits.h include/fennec/lang/bits.h
include/fennec/lang/constants.h include/fennec/lang/constants.h
include/fennec/lang/conditional_types.h include/fennec/lang/conditional_types.h
include/fennec/lang/hashing.h
include/fennec/lang/intrinsics.h include/fennec/lang/intrinsics.h
include/fennec/lang/limits.h include/fennec/lang/limits.h
include/fennec/lang/numeric_transforms.h include/fennec/lang/numeric_transforms.h
include/fennec/lang/sequences.h include/fennec/lang/const_sequences.h
include/fennec/lang/startup.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_traits.h
include/fennec/lang/type_transforms.h include/fennec/lang/type_transforms.h
include/fennec/lang/typeuuid.h
include/fennec/lang/types.h include/fennec/lang/types.h
include/fennec/lang/utility.h include/fennec/lang/utility.h
include/fennec/lang/type_sequences.h
include/fennec/lang/integer.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/assert.h source/lang/assert.cpp
include/fennec/lang/detail/_bits.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
include/fennec/lang/detail/_typeuuid.h
# MEMORY =============================================================================================================== # MEMORY ===============================================================================================================
include/fennec/memory/new.h source/memory/new.cpp include/fennec/memory/new.h source/memory/new.cpp
include/fennec/memory/allocator.h include/fennec/memory/allocator.h
include/fennec/memory/bytes.h
include/fennec/memory/common.h
include/fennec/memory/memory.h include/fennec/memory/memory.h
include/fennec/memory/pointers.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 include/fennec/memory/detail/_ptr_traits.h
# DEBUG ================================================================================================================
source/debug/assert_impl.cpp
# MATH ================================================================================================================= # MATH =================================================================================================================
@@ -97,41 +179,65 @@ add_library(fennec STATIC
include/fennec/math/trigonometric.h include/fennec/math/trigonometric.h
include/fennec/math/relational.h include/fennec/math/relational.h
include/fennec/math/detail/__fwd.h include/fennec/math/ext/common.h
include/fennec/math/detail/__types.h
include/fennec/math/detail/__vector_traits.h
source/debug/assert_impl.cpp
include/fennec/math/detail/__math.h
include/fennec/lang/detail/__int.h
include/fennec/lang/detail/__stdlib.h
include/fennec/math/detail/__matrix.h
include/fennec/math/ext/constants.h include/fennec/math/ext/constants.h
include/fennec/math/ext/primes.h
include/fennec/math/ext/quaternion.h
include/fennec/math/ext/transform.h
include/fennec/math/ext/trigonometric.h
include/fennec/math/detail/_fwd.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
# langproc ================================================================================================================
# Strings
include/fennec/langproc/strings/cstring.h
include/fennec/langproc/strings/locale.h
include/fennec/langproc/strings/string.h
include/fennec/langproc/strings/detail/_ctype.h
# Filesystem
include/fennec/langproc/filesystem/file.h source/langproc/filesystem/file.cpp
include/fennec/langproc/filesystem/path.h source/langproc/filesystem/path.cpp
# PLATFORM =============================================================================================================
include/fennec/platform/interface/fwd.h
include/fennec/platform/interface/platform.h source/platform/interface/platform.cpp
include/fennec/platform/interface/display.h source/platform/interface/display.cpp
include/fennec/platform/interface/gfxcontext.h
include/fennec/platform/interface/gfxsurface.h
# EXTRA SOURCES ========================================================================================================
${FENNEC_EXTRA_SOURCES}
) )
# add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled add_dependencies(fennec metaprogramming fennec-dependencies)
if(DOXYGEN_FOUND)
add_dependencies(fennec fennecdocs metaprogramming SDL3-static)
else()
add_dependencies(fennec metaprogramming SDL3-static)
endif()
# Compiler Warning Flags target_compile_definitions(fennec PUBLIC
if(MSVC) ${FENNEC_COMPILE_DEFINITIONS}
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 target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_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. # 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 # This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
target_link_libraries(fennec PRIVATE
${FENNEC_LINK_LIBRARIES}
# add the test suite as a sub-project cpptrace::cpptrace
add_subdirectory(test) )
@@ -141,10 +247,12 @@ add_subdirectory(test)
file(COPY logo DESTINATION docs/logo) file(COPY logo DESTINATION docs/logo)
if(DOXYGEN_FOUND) if(DOXYGEN_FOUND)
set(DOXY_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/docs") add_dependencies(fennec fennecdocs)
get_filename_component(DOXYGEN_PROJECT_NAME ${PROJECT_SOURCE_DIR} NAME) # Set Doxy Project name to the name of the root dir set(DOXY_OUTPUT_DIR "${FENNEC_SOURCE_DIR}/docs")
set(DOXYGEN_CONFIG_IN "${PROJECT_SOURCE_DIR}/doxy/Doxyfile.in") # Input config file with preprocessor arguments set(DOXY_EXAMPLES_DIR "${FENNEC_SOURCE_DIR}/examples")
set(DOXYGEN_CONFIG_OUT "${PROJECT_SOURCE_DIR}/doxy/Doxyfile") # Generated config file from input 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") # Generated config file from input
configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY) # Execute preprocessing step configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY) # Execute preprocessing step
message("Doxygen Build Started.") message("Doxygen Build Started.")
@@ -153,8 +261,8 @@ if(DOXYGEN_FOUND)
# Target for building docs # Target for building docs
add_custom_target(fennecdocs ALL add_custom_target(fennecdocs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT} COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${FENNEC_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/logo/raster.png COMMAND ${CMAKE_COMMAND} -E copy ${FENNEC_SOURCE_DIR}/logo/raster.png
${DOXY_OUTPUT_DIR}/logo/raster.png ${DOXY_OUTPUT_DIR}/logo/raster.png
COMMENT "Generating Doxygen Documentation" COMMENT "Generating Doxygen Documentation"
VERBATIM) VERBATIM)
@@ -163,7 +271,7 @@ if(DOXYGEN_FOUND)
# Target for cleaning docs # Target for cleaning docs
add_custom_target(fennecdocs-clean ALL add_custom_target(fennecdocs-clean ALL
COMMAND ${CMAKE_COMMAND} -E remove -f "${PROJECT_SOURCE_DIR}/docs/" COMMAND ${CMAKE_COMMAND} -E remove -f "${FENNEC_SOURCE_DIR}/docs"
COMMENT "Cleaning Doxygen Documentation" COMMENT "Cleaning Doxygen Documentation"
VERBATIM) VERBATIM)
else() else()

View File

@@ -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 &rarr; AI `graph`
- Necessary for 2D and 3D navigation.
- Rooted Directed Tree &rarr; 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` &rarr; the texture buffer
- `int16` &rarr; 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 &rarr; `D24`
- Visibility Info &rarr; `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 &rarr; `D24_S8`
* S &rarr; used to represent the lighting model.
* Diffuse &rarr; `RGBA8`
* A &rarr; Ambient Occlusion
* Emission &rarr; `RGB8`
* Normal &rarr; `RGB8`
* Specular &rarr; `RGB8`
* R &rarr; Roughness
* G &rarr; Specularity (sometimes called the Metallicness)
* B &rarr; 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 &rarr; `D24`
- Lighting Buffer &rarr; `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) &rarr; 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 &rarr; Finite Element Simulation
* Cloth &rarr; Position-Based Dynamics
* Water
* Oceans &rarr; iWave
* Reasoning: iWave provides interactive lightweight fluid dynamics suitable for flat planes of water.
<br><br>
* 3D Fluid Dynamics &rarr; 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 &rarr; 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
View 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 &ndash; Jason Gregory
- https://www.gameenginebook.com/
- OpenGL 4 Shading Language Cookbook, Ed. 3 &ndash; David A. Wolff
- https://www.packtpub.com/en-us/product/opengl-4-shading-language-cookbook-9781789340662
- Design Patterns: Elements of Reusable Object-Oriented Software &ndash; Erich Gamma, Richard Helm, Ralph Johnson, John Vilssides
- https://www.oreilly.com/library/view/design-patterns-elements/0201633612/
- Head First Design Patterns &ndash; Eric FReeman, Elisabeth Robson, Bert Bates, Kathy Sierra
- https://www.oreilly.com/library/view/head-first-design/0596007124/

200
README.md
View File

@@ -8,17 +8,23 @@
<br><br> <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)
* [Debian](#debian) &rarr; `apt`
* [Arch](#arch) &rarr; `pacman`
* [Fedora](#fedora) &rarr; `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>
<br> <br>
@@ -31,6 +37,8 @@
may be used through the provided editor application, or as a standalone library to may be used through the provided editor application, or as a standalone library to
link against your application. link against your application.
<br> <br>
<a id="coding-standards"></a> <a id="coding-standards"></a>
@@ -51,8 +59,8 @@ Some main areas where the engine strays from the GNU standard includes the follo
fennec Standards: fennec Standards:
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by `<APP_NAME>_`. * As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
Macros that wrap C-Style functions may use normal `snake_case`. `<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. - 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`. The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`.
@@ -63,24 +71,25 @@ fennec Standards:
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
Doxygen documentation. 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. * Most behaviours should be type independent. Specifically interactions with the core systems of the engine.
<br><br> <br><br>
<a id="f1"></a> <a id="f1"></a>
[1] Okay, I will elaborate. If we were to use the exception paradigm for all erroneous behaviour, we couldn't <sup>[1]</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
guarantee that the state will not be corrupted when an exception is thrown. The behaviour afterward is that the state will not be corrupted when an exception is thrown. The behaviour afterward is undefined
undefined because of this, and we also don't really know when, how, or where that exception will be handled. 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 The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
it is handled. In a debug build we can immediately halt the program, we don't care about the state afterward, only code and how it is handled. In a debug build we can immediately halt the program, we don't care about
beforehand. Now for a release build, this is first and foremost a game engine, so we want to crash as gracefully as the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
possible, prevent data loss, and get some debug information for it. fennec defines its own `assert` macro to so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
be used, defining a hook in the private version of the function. This hook is used to clean up any state information fennec defines its own `assert` macro to be used, defining a hook in the private version of the
within the engine and may be used to send immediate events to listeners so that outside functionality may decide function. This hook is used to clean up any state information within the engine and may be used to send
how to handle the impending crash. There is nothing that can be done to stop the crash, as soon as the branch immediate events to listeners so that outside functionality may decide how to handle the impending crash.
finishes, `abort()` will be called. 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> <br>
@@ -122,6 +131,7 @@ is also a viable IDE but involves some extra setup.
|-------------------|----------------------------------------------------------------------------------------------------------| |-------------------|----------------------------------------------------------------------------------------------------------|
| C/C++ Compiler | GCC/G++ is the compiler that fennec is designed around, however, Clang, MSVC, and MinGW may also be used | | 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 | | CMake | The build manager used by the engine |
| glew | OpenGL Extension Wrangler, necessary for modern OpenGL |
| A build system | Any build system will work, however, `build.sh` uses Ninja by default. | | 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. | | 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 | | Doxygen | Doxygen is required for building the documentation for fennec. This is an optional dependency |
@@ -138,6 +148,53 @@ for more info.
&ensp; By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the &ensp; 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). 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).
&ensp; 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="debian"></a>
#### Debian
On Debian-based distributions, you can install dependencies using the following command:
```shell
sudo apt install build-essential cmake ninja-build libglew-dev valgrind
git submodule update --force --init --recursive --remote
```
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 glew valgrind
git submodule update --force --init --recursive --remote
```
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 glew-devel valgrind
git submodule update --force --init --recursive --remote
```
for Doxygen run:
```shell
sudo dnf install doxygen graphviz
```
<br> <br>
<a id="building-on-windows"></a> <a id="building-on-windows"></a>
@@ -147,7 +204,7 @@ build scripts to use another build manager, see the [CMake documentation for ava
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require 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. 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. Otherwise, follow the sequence of commands provided in the bash script.
@@ -197,6 +254,95 @@ information displayed is correct.
<a id="usage"></a> <a id="usage"></a>
## Usage ## 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.
&ensp; 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.
&ensp; 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.
&ensp; 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.
&ensp; 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.
&ensp; 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>
<br> <br>
@@ -205,5 +351,5 @@ information displayed is correct.
There are some principles to keep in mind when contributing to fennec. 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). 1. You must follow the [standards provided above](#coding-standards).
2. Any changes must allow all projects to be forward compatible with newer engine verisons. 2. Any changes must allow all projects to be forward compatible with newer engine versions.

View File

@@ -38,7 +38,7 @@ Help()
Debug() Debug()
{ {
mkdir -p build/debug mkdir -p build/debug
cd ./build/debug cd ./build/debug || exit
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B . cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
cmake --build . --target fennec cmake --build . --target fennec
cd ../.. cd ../..
@@ -47,7 +47,7 @@ Debug()
Release() Release()
{ {
mkdir -p build/release mkdir -p build/release
cd ./build/release cd ./build/release || exit
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B . cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
cmake --build . --target fennec cmake --build . --target fennec
cd ../.. cd ../..
@@ -56,7 +56,7 @@ Release()
RelWithDebInfo() RelWithDebInfo()
{ {
mkdir -p build/relwithdebinfo mkdir -p build/relwithdebinfo
cd ./build/relwithdebinfo cd ./build/relwithdebinfo || exit
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B . cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
cmake --build . --target fennec cmake --build . --target fennec
cd ../.. cd ../..
@@ -65,7 +65,7 @@ RelWithDebInfo()
MinSizeRel() MinSizeRel()
{ {
mkdir -p build/minsizerel mkdir -p build/minsizerel
cd ./build/minsizerel cd ./build/minsizerel || exit
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B . cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
cmake --build . --target fennec cmake --build . --target fennec
cd ../.. cd ../..

28
cmake/build.cmake Normal file
View File

@@ -0,0 +1,28 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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()

24
cmake/compiler.cmake Normal file
View File

@@ -0,0 +1,24 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# this script finds the compiler being used
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
View File

@@ -0,0 +1,35 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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()

25
cmake/gcc.cmake Normal file
View File

@@ -0,0 +1,25 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# this script sets flags and variables for gnu and gnu-like compilers
add_compile_options("-mxsave" "-Wall" "-Wextra" "-pedantic" "-Werror")
set(FENNEC_PRIVATE_LINK_OPTIONS "-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates")
list(APPEND FENNEC_COMPILE_DEFINITIONS _GLIBCXX_INCLUDE_NEXT_C_HEADERS=1 FENNEC_COMPILER_GCC=1 FENNEC_NO_INLINE=[[gnu::noinline]])

43
cmake/linux.cmake Normal file
View File

@@ -0,0 +1,43 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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()

57
cmake/opengl.cmake Normal file
View File

@@ -0,0 +1,57 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
if(FENNEC_GRAPHICS_WANT_EGL)
find_package(OpenGL REQUIRED COMPONENTS EGL)
find_package(GLEW REQUIRED)
message(STATUS "EGL Requested")
else()
find_package(OpenGL)
find_package(GLEW REQUIRED)
endif()
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}")
fennec_add_link_libraries(OpenGL::GL GLEW::GLEW)
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
else()
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
endif()
if(FENNEC_GRAPHICS_WANT_EGL)
if(NOT TARGET OpenGL::EGL)
message(FATAL_ERROR "EGL Library not found.")
endif()
message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}")
fennec_add_link_libraries(OpenGL::EGL)
fennec_add_definitions(FENNEC_GRAPHICS_EGL=1)
fennec_add_sources(
include/fennec/platform/opengl/lib/fwd.h
include/fennec/platform/opengl/lib/enum.h
include/fennec/platform/opengl/lib/buffer.h
include/fennec/platform/opengl/lib/texture.h
include/fennec/platform/opengl/lib/vertex_array.h
include/fennec/platform/opengl/egl/fwd.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
)
endif()

34
cmake/platform.cmake Normal file
View File

@@ -0,0 +1,34 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# this script finds the operating system of the build environment
message(STATUS "OS: ${CMAKE_SYSTEM_NAME}")
include("${FENNEC_SOURCE_DIR}/cmake/default_user.cmake")
# Graphics APIs
macro(fennec_init_graphics)
include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake")
endmacro()
# Check for Linux
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(FENNEC_PLATFORM "Linux")
include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake")
endif ()

29
cmake/unix.cmake Normal file
View File

@@ -0,0 +1,29 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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
View File

@@ -0,0 +1,33 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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}
)

122
cmake/wayland.cmake Normal file
View File

@@ -0,0 +1,122 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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
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}/include/fennec/platform/linux/wayland/lib/sources)
# Search for base protocol xml
find_file(WAYLAND_PROTOCOL NAMES wayland.xml PATHS /usr/share/wayland /usr/share/wayland-protocols)
file(COPY ${WAYLAND_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
# search for xdg protocols
find_file(XDG_SHELL_PROTOCOL NAMES xdg-shell.xml PATHS /usr/share/wayland-protocols/stable/xdg-shell)
file(COPY ${XDG_SHELL_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
# 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/display.h source/platform/linux/wayland/display.cpp
include/fennec/platform/linux/wayland/window.h source/platform/linux/wayland/window.cpp
)
fennec_add_definitions(
FENNEC_HAS_WAYLAND=1
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
FENNEC_LIB_WAYLAND_EGL="${WAYLAND_EGL_LIBRARY}"
)
endif()
endmacro()

58
cmake/xkb.cmake Normal file
View File

@@ -0,0 +1,58 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# 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

File diff suppressed because it is too large Load Diff

BIN
conv/GNUCodingStandards.pdf Normal file

Binary file not shown.

View File

@@ -5,7 +5,6 @@
<navindex> <navindex>
<tab type="mainpage" visible="yes" title=""/> <tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/> <tab type="pages" visible="yes" title="" intro=""/>
<tab type=""
<tab type="topics" visible="yes" title="" intro=""/> <tab type="topics" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""> <tab type="modules" visible="yes" title="" intro="">
<tab type="modulelist" visible="yes" title="" intro=""/> <tab type="modulelist" visible="yes" title="" intro=""/>
@@ -50,6 +49,7 @@
<includes visible="$SHOW_HEADERFILE"/> <includes visible="$SHOW_HEADERFILE"/>
<inheritancegraph visible="$CLASS_GRAPH"/> <inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="yes"/> <collaborationgraph visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<nestedclasses visible="yes" title=""/> <nestedclasses visible="yes" title=""/>
<publictypes title=""/> <publictypes title=""/>
@@ -84,7 +84,6 @@
<related title="" subtitle=""/> <related title="" subtitle=""/>
<membergroups visible="yes"/> <membergroups visible="yes"/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
<memberdef> <memberdef>
<inlineclasses title=""/> <inlineclasses title=""/>
<typedefs title=""/> <typedefs title=""/>
@@ -106,6 +105,7 @@
<!-- Layout definition for a namespace page --> <!-- Layout definition for a namespace page -->
<namespace> <namespace>
<briefdescription visible="yes"/> <briefdescription visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<nestednamespaces visible="yes" title=""/> <nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/> <constantgroups visible="yes" title=""/>
@@ -122,7 +122,6 @@
<variables title=""/> <variables title=""/>
<membergroups visible="yes"/> <membergroups visible="yes"/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
<memberdef> <memberdef>
<inlineclasses title=""/> <inlineclasses title=""/>
<typedefs title=""/> <typedefs title=""/>
@@ -151,6 +150,7 @@
<includegraph visible="yes"/> <includegraph visible="yes"/>
<includedbygraph visible="yes"/> <includedbygraph visible="yes"/>
<sourcelink visible="yes"/> <sourcelink visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<interfaces visible="yes" title=""/> <interfaces visible="yes" title=""/>
<classes visible="yes" title=""/> <classes visible="yes" title=""/>
@@ -168,7 +168,6 @@
<variables title=""/> <variables title=""/>
<membergroups visible="yes"/> <membergroups visible="yes"/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
<memberdef> <memberdef>
<inlineclasses title=""/> <inlineclasses title=""/>
<defines title=""/> <defines title=""/>
@@ -186,6 +185,7 @@
<group> <group>
<briefdescription visible="yes"/> <briefdescription visible="yes"/>
<groupgraph visible="yes"/> <groupgraph visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<nestedgroups visible="yes" title=""/> <nestedgroups visible="yes" title=""/>
<modules visible="yes" title=""/> <modules visible="yes" title=""/>
@@ -211,7 +211,6 @@
<friends title=""/> <friends title=""/>
<membergroups visible="yes"/> <membergroups visible="yes"/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
<memberdef> <memberdef>
<pagedocs/> <pagedocs/>
<inlineclasses title=""/> <inlineclasses title=""/>
@@ -238,6 +237,7 @@
<module> <module>
<briefdescription visible="yes"/> <briefdescription visible="yes"/>
<exportedmodules visible="yes"/> <exportedmodules visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<concepts visible="yes" title=""/> <concepts visible="yes" title=""/>
<classes visible="yes" title=""/> <classes visible="yes" title=""/>
@@ -247,7 +247,6 @@
<variables title=""/> <variables title=""/>
<membergroups title=""/> <membergroups title=""/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<files visible="yes"/> <files visible="yes"/>
</memberdecl> </memberdecl>
@@ -257,10 +256,10 @@
<directory> <directory>
<briefdescription visible="yes"/> <briefdescription visible="yes"/>
<directorygraph visible="yes"/> <directorygraph visible="yes"/>
<detaileddescription title=""/>
<memberdecl> <memberdecl>
<dirs visible="yes"/> <dirs visible="yes"/>
<files visible="yes"/> <files visible="yes"/>
</memberdecl> </memberdecl>
<detaileddescription title=""/>
</directory> </directory>
</doxygenlayout> </doxygenlayout>

View File

@@ -68,7 +68,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If # entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used. # left blank the current directory will be used.
OUTPUT_DIRECTORY = C:/Users/msloc/Documents/Work/Personal/fennec/docs OUTPUT_DIRECTORY = /home/medusa/Documents/Work/Personal/fennec/docs
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format # sub-directories (in 2 levels) under the output directory of each output format
@@ -815,7 +815,7 @@ FILE_VERSION_FILTER =
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty. # tag is left empty.
LAYOUT_FILE = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/DoxyLayout.xml LAYOUT_FILE = /home/medusa/Documents/Work/Personal/fennec/doxy/DoxyLayout.xml
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib # the reference definitions. This must be a list of .bib files. The .bib
@@ -943,9 +943,9 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = "C:/Users/msloc/Documents/Work/Personal/fennec/include/" \ INPUT = "/home/medusa/Documents/Work/Personal/fennec/include" \
"C:/Users/msloc/Documents/Work/Personal/fennec/source/" \ "/home/medusa/Documents/Work/Personal/fennec/source" \
"C:/Users/msloc/Documents/Work/Personal/fennec/README.md" "/home/medusa/Documents/Work/Personal/fennec/README.md"
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1079,7 +1079,7 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include # that contain example code fragments that are included (see the \include
# command). # command).
EXAMPLE_PATH = "C:/Users/msloc/Documents/Work/Personal/fennec" EXAMPLE_PATH = "/home/medusa/Documents/Work/Personal/fennec/examples"
# If the value of the EXAMPLE_PATH tag contains directories, you can use the # 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 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
@@ -1099,7 +1099,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the # that contain images that are to be included in the documentation (see the
# \image command). # \image command).
IMAGE_PATH = IMAGE_PATH = "/home/medusa/Documents/Work/Personal/fennec/doxy/static"
# The INPUT_FILTER tag can be used to specify a program that doxygen should # 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 # invoke to filter for each input file. Doxygen will invoke the filter program
@@ -1160,7 +1160,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output. # and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = "C:/Users/msloc/Documents/Work/Personal/fennec/README.md" USE_MDFILE_AS_MAINPAGE = "/home/medusa/Documents/Work/Personal/fennec/README.md"
# The Fortran standard specifies that for fixed formatted Fortran code all # The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common # characters from position 72 are to be considered as comment. A common
@@ -1359,7 +1359,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation. # of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/header.html HTML_HEADER = /home/medusa/Documents/Work/Personal/fennec/doxy/header.html
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard # generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1369,7 +1369,7 @@ HTML_HEADER = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/head
# that doxygen normally uses. # that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/footer.html HTML_FOOTER = /home/medusa/Documents/Work/Personal/fennec/doxy/footer.html
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of # sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1381,7 +1381,7 @@ HTML_FOOTER = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/foot
# obsolete. # obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/style.css HTML_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/style.css
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets # cascading style sheets that are included after the standard style sheets
@@ -1399,10 +1399,10 @@ HTML_STYLESHEET = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/styl
# documentation. # documentation.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/doxygen-awesome.css \ HTML_EXTRA_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome.css \
C:/Users/msloc/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only.css \ /home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only.css \
C:/Users/msloc/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only-darkmode-toggle.css \ /home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only-darkmode-toggle.css \
C:/Users/msloc/Documents/Work/Personal/fennec/doxy/custom.css /home/medusa/Documents/Work/Personal/fennec/doxy/custom.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note # other source files which should be copied to the HTML output directory. Note
@@ -1412,7 +1412,7 @@ HTML_EXTRA_STYLESHEET = C:/Users/msloc/Documents/Work/Personal/fennec/doxy/doxy
# files will be copied as-is; there are no commands or markers available. # files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES = "C:/Users/msloc/Documents/Work/Personal/fennec/doxy/doxygen-awesome-darkmode-toggle.js" HTML_EXTRA_FILES = "/home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-darkmode-toggle.js"
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme. # should be rendered with a dark or light theme.
@@ -1981,7 +1981,7 @@ GENERATE_LATEX = NO
# The default directory is: latex. # The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES. # 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 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. # invoked.
@@ -2664,7 +2664,7 @@ TEMPLATE_RELATIONS = NO
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to 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 # 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 # set to YES then doxygen will generate a graph for each documented file showing
@@ -2676,7 +2676,7 @@ INCLUDE_GRAPH = YES
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to 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 # If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method. # dependency graph for every global function or class method.

View File

@@ -943,8 +943,8 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = "@PROJECT_SOURCE_DIR@/include/" \ INPUT = "@PROJECT_SOURCE_DIR@/include" \
"@PROJECT_SOURCE_DIR@/source/" \ "@PROJECT_SOURCE_DIR@/source" \
"@PROJECT_SOURCE_DIR@/README.md" "@PROJECT_SOURCE_DIR@/README.md"
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
@@ -1079,7 +1079,7 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include # that contain example code fragments that are included (see the \include
# command). # 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 # 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 # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
@@ -1099,7 +1099,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the # that contain images that are to be included in the documentation (see the
# \image command). # \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 # 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 # invoke to filter for each input file. Doxygen will invoke the filter program
@@ -1981,7 +1981,7 @@ GENERATE_LATEX = NO
# The default directory is: latex. # The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES. # 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 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. # invoked.
@@ -2664,7 +2664,7 @@ TEMPLATE_RELATIONS = NO
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to 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 # 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 # set to YES then doxygen will generate a graph for each documented file showing
@@ -2676,7 +2676,7 @@ INCLUDE_GRAPH = YES
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to 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 # If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method. # dependency graph for every global function or class method.

View File

@@ -287,3 +287,8 @@ html.dark-mode {
td.odd_c { td.odd_c {
background-color: var(--odd-color) background-color: var(--odd-color)
} }
a + h2.groupheader {
display:none;
}

71
doxy/retrieve-emojis.py Normal file
View 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)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 60 KiB

1
external/cpptrace vendored Submodule

Submodule external/cpptrace added at 787d8af6f6

1
external/sdl vendored

Submodule external/sdl deleted from d7939abf42

36
gdb/fennec/__init__.py Normal file
View File

@@ -0,0 +1,36 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
# GDB CODE =============================================================================================================
import gdb
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)

481
gdb/fennec/containers.py Normal file
View File

@@ -0,0 +1,481 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
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, i)
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)
# 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::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp
printer = register_printers()

60
gdb/fennec/filesystem.py Normal file
View File

@@ -0,0 +1,60 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
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
View File

@@ -0,0 +1,99 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
import os
import gdb
from . import utility
# VECTOR =================================================================================================================
class VectorPrinter:
def __init__(self, val):
self.val = val
self.base = val['data']['elements']
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']['elements']
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']['elements']
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
View File

@@ -0,0 +1,43 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
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
View File

@@ -0,0 +1,61 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
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
View File

@@ -0,0 +1,20 @@
# ======================================================================================================================
# 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/>.
# ======================================================================================================================
def printMembers(obj):
print(str(dir(obj)))

View File

@@ -18,7 +18,7 @@
/// ///
/// \file array.h /// \file array.h
/// \brief fennec::array definition & implementation /// \brief A header containing the definition for a static/stack allocated array
/// ///
/// ///
/// \details /// \details
@@ -40,62 +40,48 @@ namespace fennec
/// ///
/// ///
/// \brief wrapper for fixed size arrays /// \brief Data Structure that defines a compile-time allocated array
/// ///
/// \details /// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ✅ |
/// | dynamic | ⛔ |
/// | homogenous | ✅ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ✅ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(N)\f$ |
/// | insertion | ⛔ |
/// | deletion | ⛔ |
///
/// \tparam ValueT value type /// \tparam ValueT value type
/// \tparam ElemV number of elements /// \tparam ElemV number of elements
template<typename ValueT, size_t ElemV> template<typename ValueT, size_t ElemV>
struct array struct array {
{
///
/// \brief backing c-style array handle
ValueT elements[ElemV];
/// \name Element Access // Definitions =========================================================================================================
public:
using value_t = ValueT; ///< Alias for `ValueT`
// Public Members ======================================================================================================
/// \name Public Members
/// @{ /// @{
/// ///
/// \copydetails array::at(size_t) const /// \brief backing c-style array handle
constexpr ValueT& at(size_t i) { static_assert(i < ElemV); assert(i < ElemV); return elements[i]; } value_t data[ElemV];
///
/// \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 Space-Complexity
/// Constant
constexpr const ValueT& at(size_t i) const { static_assert(i < ElemV); assert(i < ElemV); return elements[i]; }
///
/// \copydetails array::operator[](size_t) const
constexpr ValueT& operator[](size_t i) { return elements[i]; }
///
/// \brief access specified element
/// \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 Space-Complexity
/// Constant
constexpr const ValueT& operator[](size_t i) const { return elements[i]; }
/// @} /// @}
/// \name Capacity // Properties ==========================================================================================================
/// \name Properties
/// @{ /// @{
/// ///
@@ -112,22 +98,128 @@ struct array
// Access ==============================================================================================================
/// \name Element Access
/// @{
///
/// \copydetails array::operator[](size_t) const
constexpr value_t& operator[](size_t i) {
assertd(i < ElemV, "Array Out of Bounds");
return data[i];
}
///
/// \brief access specified element
/// \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 Space-Complexity
/// Constant
constexpr const value_t& operator[](size_t i) const {
assertd(i < ElemV, "Array Out of Bounds");
return data[i];
}
///
/// \brief Access the first element
/// \returns A reference to the element at `elements[0]`
constexpr value_t& front() {
return data[0];
}
///
/// \brief Const Access the first element
/// \returns A const-qualified reference to the element at `elements[0]`
constexpr const value_t& front() const {
return data[0];
}
///
/// \brief Access the first element
/// \returns A reference to the element at `elements[ElemV - 1]`
constexpr value_t& back() {
return data[ElemV - 1];
}
///
/// \brief Const Access the first element
/// \returns A const-qualified reference to the element at `elements[ElemV - 1]`
constexpr const value_t& back() const {
return data[ElemV - 1];
}
/// @}
// Comparison ==========================================================================================================
/// \name Comparison Operators /// \name Comparison Operators
/// @{ /// @{
/// ///
/// \brief /// \brief Checks if all elements in the arrays are equal
friend constexpr bool_t operator==(const array& lhs, const array& rhs) friend constexpr bool_t operator==(const array& lhs, const array& rhs) {
{ return array::__compare(lhs, rhs, make_index_sequence<ElemV>{}); } return array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
}
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) ///
{ return not array::__compare(lhs, rhs, make_index_sequence<ElemV>{}); } /// \brief Checks if any element in the arrays is not equal
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) {
return not array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
}
/// @}
// Iteration ===========================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns A pointer to the first element of the array
constexpr value_t* begin() {
return data;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns A pointer to one after the end of the array
constexpr value_t* end() {
return data + ElemV;
}
///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const-qualified pointer to the first element of the array
constexpr const value_t* begin() const {
return data;
}
///
/// \brief Const C++ Iterator Specification `end()`
/// \returns A const-qualified pointer to one after the end of the array
constexpr const value_t* end() const {
return data + ElemV;
}
/// @} /// @}
private: private:
template<size_t...i> template<size_t...i>
static bool __compare(const array& lhs, const array& rhs, index_sequence<i...>) { return ((lhs[i] == rhs[i]) && ...); } static bool _compare(const array& lhs, const array& rhs, const_index_sequence<i...>) {
return ((lhs[i] == rhs[i]) && ...);
}
}; };
} }

View File

@@ -0,0 +1,94 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file containers.h
/// \brief fennec containers library main header
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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_cppstdlib C++ Standard Template Library
///
/// | Symbol | Implemented | Passed |
/// |:----------------------------------------------------------------------------|:-----------:|:------:|
/// | \ref fennec::any "fennec::any" | ⛔ | ⛔ |
/// | \ref fennec::array "fennec::array" | ✅ | ✅ |
/// | \ref fennec::bitset "fennec::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_fennec fennec
///
/// | Symbol | Implemented | Passed |
/// |:-------------------------|:-----------:|:------:|
/// | \ref fennec::graph | 🚧 | 🚧 |
/// | \ref fennec::object_pool | 🚧 | 🚧 |
/// | \ref fennec::rdtree | ✅ | ✅ |
///
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,345 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file deque.h
/// \brief A header containing the definition for a double-ended queue
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \tparam TypeT value type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct deque {
// Definitions =========================================================================================================
public:
using value_t = TypeT; ///< Alias for the value type
class iterator;
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;
};
public:
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
using elem_t = node*;
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty deque
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
deque(const alloc_t& alloc)
: _alloc(alloc)
, _first(nullptr)
, _last(nullptr)
, _size(0) {
}
///
/// \brief Copy Constructor
/// \param deque the deque to copy
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
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
~deque() {
clear();
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns `true` when the deque is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns the number of elements in size()
constexpr size_t size() const {
return _size;
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \returns a reference to the first element in the deque
value_t& front() {
assert(not empty(), "Attempted to access an empty deque.");
return _first->value;
}
///
/// \returns a const-qualified reference to the first element in the deque
const value_t& front() const {
assert(not empty(), "Attempted to access an empty deque.");
return _first->value;
}
///
/// \returns a reference to the last element in the deque
value_t& back() {
assert(not empty(), "Attempted to access an empty deque.");
return _last->value;
}
///
/// \returns a const-qualified reference to the last element in the deque
const value_t& back() const {
assert(not 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
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
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
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
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
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
template<typename...ArgsT>
void emplace_back(ArgsT&&...args) {
this->_push_back(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Clears the contents of the deque
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
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
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:
alloc_t _alloc;
node *_first, *_last;
size_t _size;
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;
}
};
}
#endif // FENNEC_CONTAINERS_DEQUE_H

View File

@@ -0,0 +1,61 @@
// =====================================================================================================================
// 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_CONTAINERS_DETAIL_TUPLE_H
#define FENNEC_CONTAINERS_DETAIL_TUPLE_H
#include <fennec/lang/const_sequences.h>
#include <fennec/lang/utility.h>
namespace fennec::detail
{
template <std::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<const_index_sequence<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

View File

@@ -18,7 +18,7 @@
/// ///
/// \file dynarray.h /// \file dynarray.h
/// \brief fennec::array definition & implementation /// \brief A header containing the definition for a dynamically allocated array
/// ///
/// ///
/// \details /// \details
@@ -38,126 +38,445 @@
namespace fennec namespace fennec
{ {
template<class TypeT, class Alloc> ///
class dynarray ///
{ /// \brief Wrapper for dynamically sized and allocated arrays
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// This structure prefers shallow moves and deep copies.
///
/// \tparam TypeT value type
template<class TypeT, class Alloc = allocator<TypeT>>
class dynarray {
public: public:
using element_t = TypeT;
using alloc_t = Alloc; // Definitions =========================================================================================================
using value_t = TypeT; ///< Alias for the value type
using alloc_t = Alloc; ///< Alias for the allocator type
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
/// ///
/// \brief Default Constructor, initializes an empty allocation. /// \brief Default Constructor, initializes an empty allocation.
dynarray() : _alloc(8), _size(0) {} constexpr 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) {}
///
/// \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); }
} }
/// ///
/// \brief Create an allocation of size `n`, with each element constructed using the copy constructor /// \brief Alloc Constructor, initialize empty allocation with allocator instance.
/// \brief n the number of elements /// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
dynarray(size_t n, const TypeT& val) /// data.
{ explicit constexpr dynarray(const alloc_t& alloc)
element_t* addr = _alloc.data(); : _alloc(8, alloc)
for(; n > 0; --n, ++addr) { fennec::construct(addr, val); } , _size(0) {
} }
///
/// \brief Alloc Move 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.
explicit constexpr dynarray(alloc_t&& alloc) noexcept
: _alloc(8, alloc)
, _size(0) {
}
///
/// \brief Sized Allocation, initializes a dynarray with `n` elements using the default constructor.
/// \param n The number of elements.
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 `alloc` and `n` elements
/// using the default constructor.
/// \param n The number of elements
/// \param alloc The allocator object to copy
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 Alloc Move Constructor, initializes a dynarray with allocator `alloc` and `n` elements
/// using the default constructor.
/// \param n The number of elements
/// \param alloc The allocator object to copy
constexpr dynarray(size_t n, 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 `n` elements, with each element
/// constructed using the copy constructor
/// \param n the number of elements
/// \param val the value to copy
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);
}
}
// 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&&) = delete;
///
/// \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
template<typename...ArgsT> template<typename...ArgsT>
dynarray(size_t n, ArgsT...args) constexpr explicit dynarray(size_t n, ArgsT&&...args)
{ : _alloc(n)
element_t* addr = _alloc.data(); , _size(n) {
for(; n > 0; --n, ++addr) { fennec::construct(addr, args...); } for(; n > 0; --n) {
fennec::construct(&_alloc[n], fennec::forward<ArgsT>(args)...);
}
} }
~dynarray() ///
{ /// \brief Copy Constructor, uses the copy constructor to copy each element
element_t* addr = _alloc.data(); /// \param arr the dynarray to copy
for(; n > 0; --n, ++addr) { fennec::destruct(addr); } 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]);
}
} }
size_t size() const { return _size; } ///
/// \brief Move Constructor, takes ownership of the allocation
/// \param arr the dynarray to move
constexpr dynarray(dynarray&& arr) noexcept
: _alloc(fennec::move(arr._alloc))
, _size(arr._size) {
arr._size = 0;
}
size_t capacity() const { return _alloc.capacity(); } ///
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
constexpr ~dynarray() {
value_t* addr = _alloc.data();
if (addr == nullptr) return;
for(int n = _size; n > 0; --n, ++addr) {
fennec::destruct(addr);
}
}
TypeT& operator[](size_t i) { return _alloc.data()[i]; } /// @}
const TypeT& operator[](size_t i) const { return _alloc.data()[i]; }
// Assignment ==========================================================================================================
/// \name Properties
/// @{
///
/// \brief Copy Assignment Operator
/// \param arr the array to copy
/// \returns A dynarray after having copied each element of `arr`
constexpr dynarray& operator=(const dynarray& arr) {
this->clear();
_alloc.creallocate(_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 `arr`
constexpr dynarray& operator=(dynarray&& arr) noexcept {
this->clear();
_alloc = fennec::move(arr._alloc);
_size = arr._size;
arr._size = 0;
return *this;
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The size of the dynarray in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns The current capacity, in elements, of the underlying allocation
constexpr size_t capacity() const {
return _alloc.capacity();
}
///
/// \returns True when there are no elements active, otherwise false
constexpr bool empty() const {
return _size == 0;
}
/// @}
// Element Access ======================================================================================================
/// \name Access
/// @{
///
/// \brief Array Access Operator
/// \param i The index to access
/// \returns A reference to the element at index `i`
constexpr TypeT& operator[](size_t i) {
assertd(i < _size, "Array Out of Bounds");
return _alloc.data()[i];
}
///
/// \brief Array Access Operator (const)
/// \param i The index to access
/// \returns A const qualified reference to the element at index `i`
constexpr const TypeT& operator[](size_t i) const {
assertd(i < _size, "Array Out of Bounds");
return _alloc.data()[i];
}
///
/// \returns Reference to the first element in the dynarray
constexpr TypeT& front() {
return this->operator[](0);
}
///
/// \returns A const-qualified reference to the first element in the dynarray
constexpr const TypeT& front() const {
return this->operator[](0);
}
///
/// \returns A reference to the last element in the dynarray
constexpr TypeT& back() {
return this->operator[](size() - 1);
}
///
/// \returns A const-qualified reference to the last element in the dynarray
constexpr const TypeT& back() const {
return this->operator[](size() - 1);
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion
/// \param i index to insert at
/// \param val the value to initialize with
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 // 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 // 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( fennec::memmove(
_alloc.data() + i (void*)(_alloc.data() + i + 1)
, _alloc.data() + i + 1 , (void*)(_alloc.data() + i)
, (_size - i) * sizeof(TypeT)); , (_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
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 // Insert the element
fennec::construct(_alloc.data() + i, val); fennec::construct(_alloc.data() + i, val);
++_size;
} }
void insert(size_t i, TypeT&& val) ///
{ /// \brief Emplace Insertion
// Grow if the size has reached the capacity of the allocation /// \param i index to insert at
if(_size == capacity()) _grow(); /// \param args Arguments to construct with
/// \tparam ArgsT Argument types
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 // Grow if the size has reached the capacity of the allocation
if((i = fennec::min(i, _size) < _size) { if(_size == capacity()) {
fennec::memmove( _grow();
_alloc.data() + i
, _alloc.data() + i + 1
, (_size - i) * sizeof(TypeT));
} }
// 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 // 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( fennec::memmove(
_alloc.data() + i (void*)(_alloc.data() + i)
, _alloc.data() + i + 1 , (void*)(_alloc.data() + i + 1)
, (_size - i) * sizeof(TypeT)); , (_size - i) * sizeof(TypeT));
} }
// Insert the element // Insert the element
fennec::construct(_alloc.data() + i, fennec::forward<ArgsT>(args)...); 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
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
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
template<typename...ArgsT>
constexpr void emplace_back(ArgsT...args) {
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase last element
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
constexpr void resize(size_t n) {
_alloc.creallocate(n);
while (_size < n) {
emplace_back();
}
}
///
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
constexpr void clear() {
while (_size > 0) {
fennec::destruct(&_alloc[--_size]);
}
_alloc.deallocate();
}
/// @}
// Iteration ===========================================================================================================
/// \name Iteration
/// @{
///
/// \brief "Iterator" Begin Function
/// \returns A pointer to the first element in the dynarray
constexpr TypeT* begin() { return _alloc.data(); }
///
/// \brief "Iterator" End Function
/// \return A pointer to the address after the last element in the dynarray
constexpr TypeT* end() { return begin() + _size; }
///
/// \brief Const "Iterator" Begin Function
/// \returns A const qualified pointer to the first element in the dynarray
constexpr const TypeT* begin() const { return _alloc; }
///
/// \brief Const "Iterator" End Function
/// \return A const qualified pointer to the address after the last element in the dynarray
constexpr const TypeT* end() const { return begin() + _size; }
/// @}
private: private:
void _grow() const { _alloc.reallocate(_alloc.capacity() * 2); } constexpr void _grow() {
_alloc.creallocate(_alloc.capacity() * 2);
}
allocation<element_t, alloc_t> _alloc; allocation<value_t, alloc_t> _alloc;
size_t _size; size_t _size;
}; };

View File

@@ -0,0 +1,518 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file graph.h
/// \brief A header containing the definition for a graph of vertices connected by edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ✅ |
/// | 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(N)\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 `u` &rarr; `v` *or* `v` &rarr; `u` for every
/// pair of vertices `u, v`. We will call this "unilateral"
///
/// A directed graph is strongly-connected if there is a directed path p for `u` &rarr; `v` *and* `v` &rarr; `u` for every pair
/// of vertices `u, v`. 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 =========================================================================================================
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
static constexpr size_t npos = -1; ///< Constant for a non-existent vertex
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty graph
constexpr graph() = default;
///
/// \brief Destructor
constexpr ~graph() = default;
/// @}
/// \name Assignment Operators
/// @{
///
/// \brief Copy Assignment Operator
/// \param g The graph to copy
/// \returns A reference to this after assigning g
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
constexpr graph& operator=(graph&& g) = default;
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The number of vertices in the graph
constexpr size_t num_vertices() const {
return _vertex_pool.size();
}
///
/// \returns The number of edges in the graph
constexpr size_t num_edges() const {
return _edge_pool.size();
}
///
/// \returns The capacity of the vertex pool
constexpr size_t capacity() const {
return _vertex_pool.capacity();
}
///
/// \returns `true` when there are no vertices in the graph, `false` otherwise
constexpr bool empty() const {
return num_vertices() == 0;
}
///
/// \brief Checks if there exists an edge `e` that starts from `a` and ends at `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if the edge exists, `false` otherwise
constexpr bool exists(size_t a, size_t b) const {
return _edge_map[a][b] != nullptr;
}
///
/// \brief Checks if there exists an edge `e0` that starts from `a` and ends at `b` and `e1` that starts from `b`
/// and ends at `a`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
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 `e` between `a` and `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
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
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
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, `nullptr` if not found
constexpr edge_t* operator[](size_t a, size_t b) {
if (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, `nullptr` if not found
constexpr const edge_t* operator[](size_t a, size_t b) const {
if (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 `x` that `vertex` has an edge to `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `vertex` to `x...`
list<size_t> outgoing(size_t vertex) {
list<size_t> res;
if (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 `x` that `vertex` has an edge from `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `x...` to `vertex`
list<size_t> incoming(size_t vertex) {
list<size_t> res;
if (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 `x` that `vertex` has an edge to and from `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` that have symmetric edges with `vertex`
list<size_t> symmetric(size_t vertex) {
list<size_t> res;
if (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 `x` that `vertex` has an edge to and from `x...` 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 `x` that have symmetric edges with `vertex`
list<size_t> undirected(size_t vertex) {
list<size_t> res;
if (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
const auto* edges(size_t vertex) {
if (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
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
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
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
constexpr void erase(size_t vertex) {
cut(vertex);
_vertex_pool.erase(vertex);
}
///
/// \brief Form an edge from vertex `a` to vertex `b`
/// \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
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 `a` and vertex `b`
/// \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
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 `a` to vertex `b`
/// \param a The first vertex id
/// \param b The second vertex id
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 `a` and `b`
/// \param a The first vertex id
/// \param b The second vertex id
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 to and from `n`
/// \param n The vertex id
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.
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

View File

@@ -0,0 +1,655 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file list.h
/// \brief A header containing the definition for a linked list of values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \tparam TypeT value type
template<class TypeT, class Alloc = allocator<TypeT>>
struct list {
// Definitions =========================================================================================================
private:
struct node;
public:
/// \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; ///< Iterator type for forward iteration over the list
class const_iterator; ///< Iterator type for forward iteration over a constant list
// Constructors & Destructor ===========================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty list.
constexpr list()
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
}
///
/// \brief Copy Constructor, copies all elements in `l` with optimized layout
/// \param l The list to copy
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
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.
constexpr ~list() {
for (size_t i = 0; i < capacity(); ++i) {
_table[i].value = nullopt;
}
}
/// @}
// Assignment ==========================================================================================================
/// \name Assignment
/// @{
///
/// \brief Copy Assignment Operator
/// \param l the list to copy
/// \returns `this` after having copied all elements of `l`
constexpr list& operator=(const list& l) {
this->clear();
for (const value_t& it : l) {
this->push_back(it);
}
}
///
/// \brief Move Assignment Operator
/// \param l the list to copy
/// \returns `this` after having taken ownership over the contents of `l`
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;
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The size of the list in elements.
constexpr size_t size() const { return _size; }
///
/// \returns The capacity of the list in elements.
constexpr size_t capacity() const { return _table.capacity(); }
///
/// \returns `true` when the list is empty, `false` otherwise.
constexpr bool empty() const { return _root == npos; }
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Array Access Operator
/// \param i Index to access
/// \returns A reference to the element at `i`
///
/// \details \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 `i`
///
/// \details \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
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
constexpr const value_t& front() const {
return *_table[_root].value;
}
///
/// \brief Access Back Element
/// \returns A reference to the last element in the list
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
constexpr const value_t& back() const {
return *_table[_last].value;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Copy Insertion
/// \param it Location to insert at
/// \param x value to copy
///
/// \details \f$O(1)\f$
constexpr size_t 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
///
/// \details \f$O(1)\f$
constexpr size_t 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
///
/// \details \f$O(N)\f$
constexpr size_t 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
///
/// \details \f$O(N)\f$
constexpr size_t 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
///
/// \details \f$O(N)\f$
template<typename...ArgsT>
constexpr size_t 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 Push Front Copy
/// \param x Value to copy
constexpr size_t push_front(const value_t& x) {
return this->_insert(_root, x);
}
///
/// \brief Push Front Move
/// \param x Value to move
constexpr size_t 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
template<typename...ArgsT>
constexpr size_t emplace_front(ArgsT&&...args) {
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Push Back Copy
/// \param x Value to copy
constexpr size_t push_back(const value_t& x) {
return this->_insert(npos, x);
}
///
/// \brief Push Back Move
/// \param x Value to move
constexpr size_t 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
template<typename...ArgsT>
constexpr size_t emplace_back(ArgsT&&...args) {
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase Element
/// \param i Index to erase
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
constexpr void erase(const iterator& it) {
_erase(it._n);
}
///
/// \brief Pop Front, erases first element
constexpr void pop_front() {
_erase(_root);
}
///
/// \brief Pop Back, erases first element
constexpr void pop_back() {
_erase(_last);
}
///
/// \brief Clears the list, destructing all elements in order
constexpr void clear() {
size_t i = _root;
while (i != npos) {
fennec::destruct(_table[i]);
i = this->_next(i);
}
_table.deallocate(_table);
}
/// @}
// ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for the first element in the list
constexpr iterator begin() {
return iterator(this, _root);
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator for the end of the list
constexpr iterator end() {
return iterator(this, npos);
}
///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const iterator for the first element in the list
constexpr const_iterator begin() const {
return const_iterator(this, _root);
}
///
/// \brief Const C++ Iterator Specification `end()`
/// \returns A const iterator for the end of the list
constexpr const_iterator end() const {
return const_iterator(this, npos);
}
/// @}
///
/// \brief Iterator Class
class iterator {
public:
~iterator() {
_list = nullptr;
}
// prefix operator
constexpr friend iterator& operator++(iterator& rhs) {
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
return rhs;
}
rhs._n = npos;
return rhs;
}
constexpr friend iterator operator++(iterator& lhs, int) {
iterator prev = lhs;
++lhs;
return prev;
}
constexpr value_t& operator*() {
return *(_list->_table[_n].value);
}
constexpr value_t* operator->() {
return &*(_list->_table[_n].value);
}
constexpr bool operator==(const iterator& it) {
return _list == it._list and _n == it._n;
}
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:
~const_iterator() {
_list = nullptr;
}
// prefix operator
constexpr friend const_iterator& operator++(const_iterator& rhs) {
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
return rhs;
}
rhs._n = npos;
return rhs;
}
constexpr friend const_iterator operator++(const_iterator& lhs, int) {
const_iterator prev = lhs;
++lhs;
return prev;
}
constexpr const value_t& operator*() {
return *(_list->_table[_n].value);
}
constexpr const value_t* operator->() {
return &*(_list->_table[_n].value);
}
constexpr bool operator==(const const_iterator& it) {
return _list == it._list and _n == it._n;
}
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:
allocation<node, alloc_t> _table;
dynarray<size_t> _freed;
size_t _root, _last, _size;
friend class iterator;
constexpr void _expand() {
_table.creallocate(fennec::max(_table.capacity(), size_t(1)) * 2);
}
struct node {
optional<value_t> value;
size_t prev, next;
constexpr node()
: value()
, prev(npos)
, next(npos) {
}
constexpr node(size_t p, size_t n)
: value()
, prev(p)
, next(n) {
}
constexpr node(size_t p, size_t n, value_t&& val)
: value(fennec::forward<value_t>(val))
, prev(p)
, next(n) {
}
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.empty()) {
size_t n = _freed.back();
_freed.pop_back();
return n;
}
return _size;
}
template<typename...ArgsT>
constexpr size_t _insert(size_t n, ArgsT&&...args) {
if (size() == capacity()) {
_expand();
}
size_t i = _next_free();
++_size;
fennec::construct(&_table[i].value, fennec::forward<ArgsT>(args)...);
if (_root == npos) {
_table[i].prev = npos;
_table[i].next = npos;
_root = _last = i;
return i;
}
if (n == npos) {
_table[_last].next = i;
_table[i].prev = _last;
_table[i].next = npos;
_last = i;
return i;
}
_table[i].prev = _prev(n);
_table[i].next = n;
_table[n].prev = i;
_root = n == _root ? i : _root;
return i;
}
constexpr void _erase(size_t n) {
if (n == npos) return;
fennec::destruct(&_table[n].value);
_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

View File

@@ -0,0 +1,312 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file map.h
/// \brief A header containing the definition for a mapping of keys to values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 `key` \f$KeyT\f$ to `value` \f$ValueT\f$
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \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:
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
// We only want to hash the key
struct key_hash : hash_t {
constexpr size_t operator()(const elem_t& p) const {
return hash_t::operator()(p.first);
}
};
// We only want to compare the keys
struct key_equals : equality<KeyT> {
constexpr bool operator()(const elem_t& a, const elem_t& b) const {
return equality<KeyT>::operator()(a.first, b.first);
}
};
// Constructors & Destructor ===========================================================================================
/// \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 ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The size of the set
constexpr size_t size() const {
return _set.size();
}
///
/// \returns `true` when there are no elements in the set, `false` otherwise
constexpr size_t empty() const {
return _set.size();
}
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const {
return _set.capacity();
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Key Access Operator
/// \param key Key value to access
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
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 `key`, `nullptr` if `key` is not present.
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 ArgT Argument Type
/// \param arg Argument to construct the key with
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
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 `key`, `nullptr` if `key` is not present.
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 ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Key-Value Insertion
/// \param pair a pair containing the key and its value
constexpr void insert(elem_t&& pair) {
this->_insert(fennec::forward<elem_t>(pair));
}
///
/// \brief Key-Value Insertion
/// \param args Arguments for constructing the key-value pair
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
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase a key
/// \param key key to erase
constexpr void erase(KeyT&& key) {
_set.erase(this->_find(fennec::forward<KeyT>(key)));
}
///
/// \brief Erase a key
/// \param key key to erase
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
template<typename...ArgsT>
constexpr void erase(ArgsT&&...args) {
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
}
///
/// \brief Clears the map destructing all elements
void clear() {
_set.clear();
}
/// @}
// Iteration ===========================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the start of the map
constexpr iterator begin() {
return _set.begin();
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns an iterator at the end of the map
constexpr iterator end() {
return _set.end();
}
/// @}
private:
set_t _set;
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

View File

@@ -0,0 +1,586 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file multiset.h
/// \brief A header containing the definition for a set of repeating values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_MULTISET_H
#define FENNEC_CONTAINERS_MULTISET_H
// https://programming.guide/robin-hood-hashing.html
#include <fennec/containers/optional.h>
#include <fennec/containers/multiset.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 A Data Structure that defines a set of elements that may repeat
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \tparam TypeT The type to contain
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
struct multiset {
// Definitions =========================================================================================================
public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
using hash_t = Hash;
using equal_t = Equals;
using elem_t = TypeT;
class iterator;
class value_iterator;
static constexpr size_t npos = -1;
static constexpr double default_load = 0.8;
private:
struct node {
optional<elem_t> value;
int psl;
constexpr node() = default;
constexpr ~node() = default;
};
// Constructors ========================================================================================================
public:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty multiset
constexpr multiset()
: _alloc()
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
};
///
/// \brief Hash Copy Constructor, initializes empty multiset with a hash
constexpr multiset(const hash_t& hash)
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Constructor, initializes empty multiset with a hash
constexpr multiset(hash_t&& hash) noexcept
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Copy Constructor, initializes empty multiset with an allocator
constexpr multiset(const alloc_t& alloc)
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Move Constructor, initializes empty multiset with an allocator
constexpr multiset(alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Alloc Copy Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(const hash_t& hash, const alloc_t& alloc)
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Copy Alloc Move Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(const hash_t& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Move Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(hash_t&& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Copy Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(hash_t&& hash, const alloc_t& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Set Copy Constructor
/// \param multiset Set to copy
constexpr multiset(const multiset& multiset)
: _alloc( multiset._alloc)
, _hash( multiset._hash)
, _size( multiset._size)
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Set Move Constructor
/// \param multiset Set to move
constexpr multiset(multiset&& multiset) noexcept
: _alloc(fennec::move( multiset._alloc))
, _hash(fennec::move( multiset._hash))
, _size(fennec::move( multiset._size))
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Destructor, destructs all elements and releases the allocation
constexpr ~multiset() {
for (size_t i = 0; i < capacity(); ++i) {
_alloc[i].value = nullopt;
}
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns Size of the multiset in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns `true` when the set is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns Capacity of the multiset in elements
constexpr size_t capacity() const {
return _alloc.capacity();
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Find an Element
/// \param val Value to find
/// \returns An iterator at the location of the first instance of `value`
constexpr iterator find(const elem_t& val, size_t c = 0) 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 (_alloc[i].psl >= psl && _alloc[i].value) {
if (*_alloc[i].value == val) {
return iterator(this, i);
}
}
// Loop while there is a value and its psl is greater than our probe
while (c > 0) {
++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 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
if (c0 && _alloc[i0].value) {
if (*_alloc[i0].value == val) {
if (c-- == 0) {
return iterator(this, i0);
}
}
}
if (c1 && _alloc[i1].value) {
if (*_alloc[i1].value == val) {
if (c-- == 0) {
return iterator(this, i1);
}
}
}
if (not(c0 or c1)) {
break;
}
}
return iterator(this, npos);
}
///
/// \brief Check if a multiset contains a value
/// \param val Value to check
/// \returns `true` if `val` can be found, `false` otherwise
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, `nullptr` if not found.
/// The value should not be changed in a manner that will change the hash of the element.
constexpr elem_t* at(const iterator& it) {
if (it == end()) {
return nullptr;
}
if (not _alloc[it._i].value) {
return nullptr;
}
return &*_alloc[it._i].value;
}
///
/// \brief Iterator Const Access
/// \param it Location to access
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
constexpr const elem_t* at(const iterator& it) const {
if (not _alloc[it._i].value) return nullptr;
return &*_alloc[it._i].value;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion
/// \param val Value to insert
constexpr void insert(elem_t&& val) {
this->_insert(fennec::forward<elem_t>(val));
}
///
/// \brief Copy Insertion
/// \param val Value to insert
constexpr void insert(const elem_t& val) {
this->_insert(val);
}
///
/// \brief Emplace Insertion
/// \tparam ArgsT Argument types
/// \param args Arguments to construct with
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Element Erase
/// \param it Location to erase
constexpr void erase(iterator it) {
size_t i = it._i;
if (i >= capacity()) {
return;
} // These are separated due to compilers being inconsistent
if (not _alloc[i].value) {
return;
}
_alloc[i].value = nullopt;
_sumpsl -= _alloc[i].psl;
--_size;
size_t p = i;
while (_alloc[i = (i + 1) % capacity()].value) {
if (_alloc[i].psl == 0) break;
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
--_alloc[p].psl, --_sumpsl;
p = i;
}
}
///
/// \brief Element Erase
/// \param val Value to erase
constexpr void erase(const elem_t& val) {
this->erase(this->find(val));
}
/// @}
// ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
///
/// \brief Class for Iterating the Set
class iterator {
public:
constexpr ~iterator() {
_set = nullptr;
}
// prefix operator
constexpr iterator& operator++() {
while (++_i < _set->capacity()) {
if (_set->_alloc[_i].value) {
return *this;
}
}
_i = npos;
return *this;
}
constexpr iterator operator++(int) {
iterator prev = *this;
this->operator++();
return prev;
}
constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value;
}
constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
private:
const multiset* _set;
size_t _i;
friend multiset;
constexpr iterator(const multiset* multiset, size_t i)
: _set(multiset)
, _i(i) {
}
};
class value_iterator {
public:
constexpr ~value_iterator() {
_set = nullptr;
}
// prefix operator
constexpr value_iterator& operator++() {
while (_psl <= _set->_alloc[_i].psl) {
if (not _set->_alloc[_i].value) {
break;
}
}
_i = npos;
return *this;
}
constexpr value_iterator operator++(int) {
value_iterator prev = *this;
this->operator++();
return prev;
}
constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value;
}
constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
private:
const multiset* _set;
size_t _i;
int _psl;
elem_t _value;
friend multiset;
constexpr value_iterator(const multiset* multiset, size_t i, int psl, const elem_t& value)
: _set(multiset)
, _i(i)
, _value(value) {
}
};
// PRIVATE =============================================================================================================
private:
constexpr void _expand() {
multiset cpy; // Create a new multiset
cpy._alloc.callocate(
fennec::next_prime2(_alloc.capacity())
);
// rehash
for (size_t i = 0; i < capacity(); ++i) {
if (_alloc[i].value) {
cpy.insert(fennec::move(*_alloc[i].value));
}
}
// Swap buffers
fennec::swap(_alloc, cpy._alloc);
}
template<typename...ArgsT>
constexpr void _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 (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
return;
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
}
allocation<node, alloc_t> _alloc;
hash_t _hash;
equal_t _equal;
size_t _size;
size_t _sumpsl;
float _load;
};
}
#endif // FENNEC_CONTAINERS_SET_H

View File

@@ -0,0 +1,209 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file object_pool.h
/// \brief A header containing the definition for a pool of objects associated by ids
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
/// \tparam TypeT The value type
/// \tparam AllocT The allocator type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct object_pool {
// Definitions =========================================================================================================
public:
using value_t = TypeT;
using elem_t = optional<TypeT>;
using table_t = dynarray<elem_t, AllocT>;
// Constructors & Destructor ===========================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty object pool
constexpr object_pool()
: _size(0) {
}
///
/// \brief Default Destructor, destructs objects then releases the allocation.
constexpr ~object_pool() = default;
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The number of active objects in the pool
constexpr size_t size() const {
return _size;
}
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const {
return _table.capacity();
}
///
/// \returns `true` when there are no objects in the pool, `false` otherwise
constexpr bool empty() const {
return size() == 0;
}
///
/// \brief Retrieve the next id `i` that would be assigned to an object `o` 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
constexpr size_t next_id() const {
size_t next = _size;
if (not _freed.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 `i`
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 `i`
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];
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion, inserts `x` into the pool
/// \param x the object to move
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(value_t&& x) {
return this->_insert(fennec::forward<value_t>(x));
}
///
/// \brief Move Insertion, inserts a copy of `x` into the pool
/// \param x the object to copy
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(const value_t& x) {
return this->_insert(x);
}
///
/// \brief Emplacement, constructs a new object using `args...`
/// \param args The arguments to construct the new object with
/// \returns An integer corresponding to the id of the node
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
constexpr void erase(size_t i) {
_table[i] = nullopt;
_freed.push_back(i);
--_size;
}
/// @}
private:
dynarray<elem_t, AllocT> _table;
list<size_t> _freed;
size_t _size;
size_t _next_free() {
size_t next = _size;
if (not _freed.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.emplace_back();
}
_table[i].emplace(fennec::forward<ArgsT>(args)...);
return i;
}
};
}
#endif // FENNEC_CONTAINERS_OBJECT_POOL_H

View File

@@ -0,0 +1,332 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file optional.h
/// \brief A header containing the definition for a container with an optionally present variable
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
{
struct nullopt_t {};
constexpr nullopt_t nullopt_v = {};
#define nullopt nullopt_v
///
/// \brief Structure to hold an optional value.
/// \tparam T
template<typename T>
struct optional {
// Definitions =========================================================================================================
public:
using reference_t = T&;
using pointer_t = T*;
using const_reference_t = T&;
using const_pointer_t = const T*;
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor
constexpr optional()
: _root(0)
, _set(false) {
}
///
/// \brief Default Constructor
constexpr optional(nullopt_t)
: _root(0)
, _set(false) {
}
///
/// \brief Type Copy Constructor
/// \param val the value to initialize the underlying object with
constexpr optional(const T& val)
: _val(val)
, _set(true) {
}
///
/// \brief Type Move Constructor
/// \param val the value to initialize the underlying object with
constexpr optional(T&& val)
: _val(fennec::forward<T>(val))
, _set(true) {
}
///
/// \brief Copy Constructor
/// \param opt the optional to copy
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
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
: optional() {
_set = opt._set;
if (_set) {
_val = fennec::move(opt._val);
}
opt = nullopt;
}
template<typename...ArgsT>
constexpr optional(ArgsT&&...args)
: _val(fennec::forward<ArgsT>(args)...)
, _set(true) {
}
constexpr ~optional() {
if constexpr(is_fundamental_v<T>) {
return;
}
if (_set) {
fennec::destruct(&_val);
}
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \brief Implicit Boolean Check, returns `true` when there is a value contained
constexpr operator bool() const {
return _set;
}
///
/// \returns `true` when there is no held value, `false` otherwise.
constexpr bool empty() const {
return not _set;
}
/// @}
// Assignment Operators ================================================================================================
/// \name Assignment
/// @{
///
/// \brief Fundamental Type Assignment
/// \param val The value to set with
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
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
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
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
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, `nullptr` if there is no value
constexpr pointer_t operator->() noexcept {
return _set ? &_val : nullptr;
}
///
/// \returns A const-qualified pointer to the value, `nullptr` if there is no value
constexpr const_pointer_t operator->() const noexcept {
return _set ? &_val : nullptr;
}
///
/// \brief Dereference Operator
/// \returns A reference to the value
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
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
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
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
/// \val The optional to move
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
void reset() {
this->operator=(nullopt);
}
/// @}
private:
union {
char _root;
T _val;
};
bool _set;
};
}
#endif // FENNEC_CONTAINERS_OPTIONAL_H

View File

@@ -0,0 +1,187 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file pair.h
/// \brief A header containing the definition for a container holding a pair of values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
/// \tparam TypeT0 The type of the first value
/// \tparam TypeT1 The type of the second value
template<typename TypeT0, typename TypeT1>
struct pair {
// Members =============================================================================================================
TypeT0 first; ///< The first value in the pair
TypeT1 second; ///< The second value in the pair
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, invokes default constructor for both elements
constexpr pair() = default;
///
/// \brief Destructor, invokes destructor for both elements
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
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
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
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
constexpr pair(const pair&) = default;
///
/// \brief Move Constructor, moves both elements
constexpr pair(pair&&) noexcept = default;
///
/// \brief Copy Assignment, copies both elements
constexpr pair& operator=(const pair&) = default;
///
/// \brief Move Assignment, moves both elements
constexpr pair& operator=(pair&&) noexcept = default;
/// @}
// Comparison ==========================================================================================================
/// \name Comparison
/// @{
///
/// \brief Equality Operator
/// \param p Pair to compare with
/// \returns `true` when both elements of each pair are equal
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 `true` when either element of each pair are equal
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 `true` when the first element is less, or they are
/// equal and the second element is less
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 `true` when the first element is less, or they are
/// equal and the second element is less or equal
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 `true` when the first element is greater, or they are
/// equal and the second element is greater
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 `true` when the first element is greater, or they are
/// equal and the second element is greater or equal
constexpr bool operator>=(const pair& p) const {
return first > p.first or (first == p.first and second >= p.second);
}
/// @}
};
template<typename TypeT0, typename TypeT1>
struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
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

View File

@@ -0,0 +1,626 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file rdtree.h
/// \brief A header containing the definition for a tree with a root and directed edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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/memory/allocator.h>
namespace fennec
{
///
/// \brief Rooted-Directed Tree
/// \tparam TypeT Data type
/// \tparam AllocT Allocator Type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct rdtree {
// Definitions =========================================================================================================
protected:
struct node;
public:
using value_t = TypeT;
using alloc_t = typename allocator_traits<AllocT>::template rebind<node>;
static constexpr size_t root = 0;
static constexpr size_t npos = -1;
protected:
struct node {
optional<TypeT> value;
size_t parent, child, prev, next;
size_t depth, num_children;
constexpr node()
: value(nullopt)
, 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;
}
};
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
template<typename...ArgsT>
explicit constexpr rdtree(ArgsT&&...args)
: _table(), _freed(), _size(1) {
_table.creallocate(8);
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Copy Constructor, copies the contents of `tree`
/// \param tree the rdtree to copy
constexpr rdtree(const rdtree& tree)
: _table(tree._table), _freed(tree._freed), _size(tree._size) {
}
///
/// \brief Move Constructor, takes ownership over the contents of `tree`
/// \param tree the rdtree to move
constexpr rdtree(rdtree&& tree) noexcept
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
}
/// @}
// Assignment ==========================================================================================================
/// \name Assignment
/// @{
///
/// \brief Copy Assignment Operator
/// \param rhs the rdtree to copy
/// \returns `this` after copying the contents of `rhs`
constexpr rdtree& operator=(const rdtree& rhs) {
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 `this` after taking ownership over the contents of `rhs`
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
constexpr size_t size() const {
return _size;
}
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const {
return _table.capacity();
}
///
/// \returns `true` when there are no nodes in the tree, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
// Access ==============================================================================================================
///
/// \param i The id of the node to check
/// \returns The id of the parent node
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
/// \returns The id of the child node
constexpr size_t child(size_t i, size_t n = 0) const {
if (i >= _table.capacity()) 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
/// \returns The id of the next node
constexpr size_t next(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 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
/// \returns The id of the previous node
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 `i`
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 `i`
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
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
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 `insert` or `emplace` to be called
constexpr size_t next_id() const {
size_t i = _size;
if (not _freed.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
constexpr value_t* operator[](size_t i) {
auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
}
///
/// \param i The id of the node to access
/// \returns A const-qualified reference to the value of the node wrapped in an optional
constexpr const value_t* operator[](size_t i) const {
const auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
}
// Insertion & Deletion ================================================================================================
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` 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
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 `parent`
/// \param parent the parent node, if `npos` 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
constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
return this->_insert(parent, next, fennec::forward<value_t>(val));
}
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param args the args to construct the value to insert
/// \returns the index of the created node
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
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 ===========================================================================================================
///
/// \brief Traverse the tree using a specified order and visiting functor
///
/// \details
/// The visitor should accept a reference to a value of type `TypeT` and a `size_t` 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 `uint8_t visit(TypeT&, size_t)`
/// \param visit The visiting object
/// \param i The node to start at
template<typename OrderT, typename VisitorT>
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];
}
}
struct pre_order {
list<size_t> visit;
size_t head;
size_t operator()(const rdtree&, size_t start) {
head = start;
return start;
}
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.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
struct in_order {
list<size_t> visit;
size_t head;
size_t operator()(const rdtree& tree, size_t start) {
head = start;
return tree.left_most(start);
}
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.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
struct post_order {
list<size_t> visit;
size_t head;
size_t operator()(const rdtree& tree, size_t start) {
head = start;
return tree.left_most(start);
}
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.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
protected:
allocation<node, alloc_t> _table;
list<size_t> _freed;
size_t _size;
void _expand() {
_table.creallocate(_table.capacity() * 2);
}
size_t _next_free() {
size_t next = _size;
if (not _freed.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.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

View File

@@ -0,0 +1,492 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file set.h
/// \brief A header containing the definition for a set of unique values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \tparam TypeT The type to contain
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
struct set {
// Definitions =========================================================================================================
public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
using hash_t = Hash;
using equal_t = Equals;
using elem_t = TypeT;
class iterator;
static constexpr size_t npos = -1;
static constexpr double default_load = 0.8;
private:
struct node {
optional<elem_t> value;
int psl;
constexpr node() = default;
constexpr ~node() = default;
};
// Constructors ========================================================================================================
public:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty set
constexpr set()
: _alloc()
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
};
///
/// \brief Hash Copy Constructor, initializes empty set with a hash
/// \param hash the hash object
constexpr set(const hash_t& hash)
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
/// \param alloc the allocator object
constexpr set(const alloc_t& alloc)
: _alloc(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
constexpr set(const hash_t& hash, const alloc_t& alloc)
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Set Copy Constructor
/// \param set Set to copy
constexpr set(const set& set)
: _alloc(set._alloc)
, _hash(set._hash)
, _size(set._size)
, _sumpsl(set._sumpsl)
, _load(set._load) {
}
///
/// \brief Set Move Constructor
/// \param set Set to move
constexpr set(set&& set) noexcept
: _alloc(fennec::move(set._alloc))
, _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
constexpr ~set() {
for (size_t i = 0; i < capacity(); ++i) {
_alloc[i].value = nullopt;
}
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns Size of the set in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns `true` when the set is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns Capacity of the set in elements
constexpr size_t capacity() const {
return _alloc.size();
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Find an Element
/// \param val Value to find
/// \returns An iterator at the location of the value
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 (_alloc[i].psl >= psl && _alloc[i].value) {
if (_equal(*_alloc[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 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
if (c0 && _alloc[i0].value) {
if (_equal(*_alloc[i0].value, val)) {
return iterator(this, i0);
}
}
if (c1 && _alloc[i1].value) {
if (_equal(*_alloc[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 `true` if `val` can be found, `false` otherwise
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, `nullptr` if not found.
/// The value should not be changed in a manner that will change the hash of the element.
constexpr elem_t* at(const iterator& it) {
if (it == end()) {
return nullptr;
}
if (not _alloc[it._i].value) {
return nullptr;
}
return &*_alloc[it._i].value;
}
///
/// \brief Iterator Const Access
/// \param it Location to access
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
constexpr const elem_t* at(const iterator& it) const {
if (not _alloc[it._i].value) return nullptr;
return &*_alloc[it._i].value;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion
/// \param val Value to insert
constexpr iterator insert(elem_t&& val) {
return this->_insert(fennec::forward<elem_t>(val));
}
///
/// \brief Copy Insertion
/// \param val Value to insert
constexpr iterator insert(const elem_t& val) {
return this->_insert(val);
}
///
/// \brief Emplace Insertion
/// \tparam ArgsT Argument types
/// \param args Arguments to construct with
template<typename...ArgsT>
constexpr iterator emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Element Erase
/// \param it Location to erase
constexpr void erase(iterator it) {
size_t i = it._i;
if (i >= capacity()) {
return;
} // These are separated due to compilers being inconsistent
if (not _alloc[i].value) {
return;
}
_alloc[i].value = nullopt;
_sumpsl -= _alloc[i].psl;
--_size;
size_t p = i;
while (_alloc[i = (i + 1) % capacity()].value) {
if (_alloc[i].psl == 0) break;
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
--_alloc[p].psl, --_sumpsl;
p = i;
}
}
///
/// \brief Element Erase
/// \param val Value to erase
constexpr void erase(const elem_t& val) {
this->erase(this->find(val));
}
///
/// \brief
constexpr void clear() {
for (size_t i = 0; i < _alloc.capacity(); ++i) {
}
}
/// @}
// ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
///
/// \brief Class for Iterating the Set
class iterator {
public:
constexpr iterator(const set* set, size_t i)
: _set(set)
, _i(i) {
}
constexpr ~iterator() {
_set = nullptr;
}
// prefix operator
constexpr friend iterator& operator++(iterator& rhs) {
while (++rhs._i < rhs._set->capacity()) {
if (rhs._set->_alloc[rhs._i].value) {
return rhs;
}
}
rhs._i = npos;
return rhs;
}
constexpr friend iterator operator++(iterator& lhs, int) {
iterator prev = lhs;
++lhs;
return prev;
}
constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value;
}
constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
constexpr size_t index() const { return _i; }
private:
const set* _set;
size_t _i;
friend set;
};
// PRIVATE =============================================================================================================
private:
constexpr void _expand() {
set cpy; // Create a new set
cpy._alloc.resize(
fennec::next_prime2(_alloc.capacity())
);
// rehash
for (size_t i = 0; i < capacity(); ++i) {
if (_alloc[i].value) {
cpy.insert(fennec::move(*_alloc[i].value));
}
}
// Swap buffers
fennec::swap(_alloc, cpy._alloc);
}
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 (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
return iterator(this, i);
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
return iterator(this, npos);
}
dynarray<node, alloc_t> _alloc;
hash_t _hash;
equal_t _equal;
size_t _size;
size_t _sumpsl;
float _load;
};
}
#endif // FENNEC_CONTAINERS_SET_H

View File

@@ -0,0 +1,47 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file traversal.h
/// \brief a header containing constants and utilities related to traversal
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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,
traversal_control_break = 1,
traversal_control_jump_over = 2,
};
}
#endif // FENNEC_CONTAINERS_TRAVERSAL_H

View File

@@ -0,0 +1,106 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file tuple.h
/// \brief A header containing the definition for a container with multiple values of differing types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ⛔ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ✅ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(1)\f$ |
/// | insertion | ⛔ |
/// | deletion | ⛔ |
///
/// \tparam TypesT The types to store
template<typename...TypesT> struct 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;
}
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_sequence_t<sizeof...(TypesT)>, TypesT...>
{
using base_t = detail::_tuple<make_index_sequence_t<sizeof...(TypesT)>, TypesT...>;
template<size_t i>
using elem_t = typename nth_element<i, TypesT...>::type;
template<typename...ArgsT>
tuple(ArgsT&&...args)
: base_t(fennec::forward<ArgsT>(args)...) {
}
tuple(const tuple& cpy)
: base_t(cpy) {
}
tuple(tuple&& cpy)
: base_t(cpy) {
}
};
// This is needed for
template<typename...TypesT>
tuple(TypesT...) -> tuple<TypesT...>;
}
#endif // FENNEC_CONTAINERS_TUPLE_H

View File

@@ -0,0 +1,34 @@
// =====================================================================================================================
// 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/>.
// =====================================================================================================================
///
/// \file variant.h
/// \brief Contains the definition for a structure that holds a single value from multiple types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_VARIANT_H
#define FENNEC_CONTAINERS_VARIANT_H
#endif // FENNEC_CONTAINERS_VARIANT_H

View File

@@ -29,19 +29,25 @@
/// ///
/// ///
/// \page documentation Documentation /// \page contents Contents
/// ///
/// 1. \ref introduction "Introduction" /// 1. \ref introduction "Introduction"
/// 1. \ref coding-standards "Coding Standards" /// 1. \ref coding-standards "Coding Standards"
/// 2. \ref building-from-source "Building from Source" /// 2. \ref building-from-source "Building from Source"
/// 1. \ref building-from-terminal "Building from Terminal" /// 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" /// 2. \ref building-on-windows "Building on Windows"
/// 3. \ref running-the-test-suite "Running the Test Suite" /// 3. \ref running-the-test-suite "Running the Test Suite"
/// 4. \ref usage "Usage" /// 4. \ref usage "Usage"
/// 1. \ref licensing "Licensing"
/// 5. \ref contribution "Contribution" /// 5. \ref contribution "Contribution"
/// 6. \subpage libraries /// 6. \subpage libraries
/// 1. \ref fennec_lang "C++ Language Library" /// 1. \ref fennec_lang "C++ Language Library"
/// 2. \ref fennec_math "Math 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 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
/// ///
@@ -49,10 +55,16 @@
/// ///
/// \page libraries Libraries /// \page libraries Libraries
/// ///
/// | Library | Brief | /// | 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_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_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 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
#ifndef FENNEC_CORE_ENGINE_H #ifndef FENNEC_CORE_ENGINE_H
#define FENNEC_CORE_ENGINE_H #define FENNEC_CORE_ENGINE_H

View File

@@ -0,0 +1,61 @@
// =====================================================================================================================
// 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_CORE_EVENT_H
#define FENNEC_CORE_EVENT_H
#include <fennec/lang/types.h>
#include <fennec/lang/typeuuid.h>
namespace fennec
{
struct event;
class event_listener {
public:
virtual ~event_listener() = default;
virtual void handle_event(event* event) = 0;
};
struct event {
const uint64_t type;
event() = delete;
template<typename EventT>
event() : type(typeuuid<EventT>()) { }
template<typename EventT>
static void add_listener(event_listener* listener) {
event::add_listener(listener, typeuuid<EventT, event>());
}
template<typename EventT>
static void dispatch(EventT* event) {
dispatch(event);
}
static void add_listener(event_listener* listener, uint64_t type);
static void remove_listener(event_listener* listener);
static void dispatch(event* event);
};
}
#endif // FENNEC_CORE_EVENT_H

View File

@@ -0,0 +1,47 @@
// =====================================================================================================================
// 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_CORE_SYSTEM_H
#define FENNEC_CORE_SYSTEM_H
#include <fennec/langproc/strings/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

View File

@@ -16,17 +16,77 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
// ===================================================================================================================== // =====================================================================================================================
///
/// \file assert.h
/// \brief \ref fennec_lang_assert
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_LANG_ASSERT_H #ifndef FENNEC_LANG_ASSERT_H
#define FENNEC_LANG_ASSERT_H #define FENNEC_LANG_ASSERT_H
#if _MSC_VER ///
/// \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 `expr` and provide a description `desc`. 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 `expr` and provide a description `desc`. 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 `expr` and provide a description `desc`.
/// This should be used when the branching caused by `assert` would hinder performance in release mode.
///
/// </table>
///
///
///
#if FENNEC_COMPILER_MSVC
#define __PRETTY_FUNCTION__ __FUNCSIG__ #define __PRETTY_FUNCTION__ __FUNCSIG__
#endif #endif
using assert_handler = void (*)(const char *, const char *, int , const char *); using assert_handler = void (*)(const char *, const char *, int , const char *);
extern void __assert_impl(const char* expression, const char* file, int line, const char* function); void _assert_impl(const char* expression, const char* file, int line, const char* function, const char* desc, bool halt);
#define assert(expression) if(not(expression)) { __assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__); } // flagged unlikely to optimize branch prediction
#define assert(expression, description) \
if(not(expression)) [[unlikely]] { \
_assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, not FENNEC_RELEASE); \
}
#define assertf(expression, description) \
if(not(expression)) [[unlikely]] { \
_assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
}
#if FENNEC_RELEASE
#define assertd(expression, description)
#else
#define assertd(expression, description) assert(expression, description)
#endif
#endif // FENNEC_LANG_ASSERT_H #endif // FENNEC_LANG_ASSERT_H

View File

@@ -1,6 +1,6 @@
// ===================================================================================================================== // =====================================================================================================================
// fennec, a free and open source game engine // fennec, a free and open source game engine
// Copyright (C) 2025 Medusa Slockbower // Copyright © 2025 Medusa Slockbower
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@@ -80,8 +80,8 @@
/// ///
#include <fennec/lang/intrinsics.h> #include <fennec/lang/intrinsics.h>
#include <fennec/memory/memory.h> #include <fennec/memory/common.h>
#include <fennec/lang/detail/__bits.h> #include <fennec/lang/detail/_bits.h>
namespace fennec namespace fennec
{ {
@@ -95,12 +95,10 @@ namespace fennec
/// \param from Value to bit cast /// \param from Value to bit cast
/// \returns A value containing a bitwise copy of the input /// \returns A value containing a bitwise copy of the input
template<typename ToT, typename FromT> requires(sizeof(ToT) == sizeof(FromT)) template<typename ToT, typename FromT> requires(sizeof(ToT) == sizeof(FromT))
constexpr ToT bit_cast(const FromT& from) constexpr ToT bit_cast(const FromT& from) {
{ if constexpr(FENNEC_HAS_BUILTIN_BIT_CAST) {
if constexpr(FENNEC_HAS_BUILTIN_BIT_CAST)
return FENNEC_BUILTIN_BIT_CAST(ToT, from); return FENNEC_BUILTIN_BIT_CAST(ToT, from);
else } else {
{
ToT to; ToT to;
fennec::memcpy(&to, &from, sizeof(ToT)); fennec::memcpy(&to, &from, sizeof(ToT));
return to; return to;
@@ -119,15 +117,16 @@ constexpr ToT bit_cast(const FromT& from)
/// \param mask the mask to and against arr /// \param mask the mask to and against arr
/// \param n the number of bytes /// \param n the number of bytes
/// \returns the pointer \f$arr\f$ /// \returns the pointer \f$arr\f$
constexpr void* bit_and(void* arr, const void* mask, size_t n) constexpr void* bit_and(void* arr, const void* mask, size_t n) {
{ if (arr == mask) {
if (arr == mask) return arr; return arr;
}
uint8_t* d = static_cast<uint8_t*>(arr); uint8_t* d = static_cast<uint8_t*>(arr);
const uint8_t* s = static_cast<const uint8_t*>(mask); const uint8_t* s = static_cast<const uint8_t*>(mask);
while (n > 0) while (n > 0) {
{ const size_t step = detail::_bit_and(d, s, n);
size_t step = detail::__bit_and(d, s, n);
d += step; s += step; n -= step; d += step; s += step; n -= step;
} }
@@ -138,11 +137,14 @@ constexpr void* bit_and(void* arr, const void* mask, size_t n)
/// \brief Safe version of fennec::bit_and /// \brief Safe version of fennec::bit_and
/// ///
/// \details 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 n0 the size of arr in bytes
/// \param mask the mask to and against arr
/// \param n1 the size of mask in bytes /// \param n1 the size of mask in bytes
constexpr void* bit_and_s(void* arr, size_t n0, const void* mask, size_t n1) /// \returns the pointer arr
{ return bit_and(arr, mask, n0 < n1 ? n0 : n1); } 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);
}
@@ -156,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 mask the mask to or against arr
/// \param n the number of bytes /// \param n the number of bytes
/// \returns the pointer arr /// \returns the pointer arr
constexpr void* bit_or(void* arr, const void* mask, size_t n) constexpr void* bit_or(void* arr, const void* mask, size_t n) {
{ if (arr == mask) {
if (arr == mask) return arr; return arr;
}
uint8_t* d = static_cast<uint8_t*>(arr); uint8_t* d = static_cast<uint8_t*>(arr);
const uint8_t* s = static_cast<const uint8_t*>(mask); const uint8_t* s = static_cast<const uint8_t*>(mask);
while (n > 0) 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; d += step; s += step; n -= step;
} }
@@ -175,11 +178,14 @@ constexpr void* bit_or(void* arr, const void* mask, size_t n)
/// \brief Safe version of fennec::bit_or /// \brief Safe version of fennec::bit_or
/// ///
/// \details 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 n0 the size of arr in bytes
/// \param mask the mask to or against arr
/// \param n1 the size of mask in bytes /// \param n1 the size of mask in bytes
constexpr void* bit_or_s(void* arr, size_t n0, const void* mask, size_t n1) /// \returns the pointer arr
{ return bit_or(arr, mask, n0 < n1 ? n0 : n1); } 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);
}
@@ -193,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 mask the mask to or against arr
/// \param n the number of bytes /// \param n the number of bytes
/// \returns the pointer arr /// \returns the pointer arr
constexpr void* bit_xor(void* arr, const void* mask, size_t n) constexpr void* bit_xor(void* arr, const void* mask, size_t n) {
{ if (arr == mask) {
if (arr == mask) return arr; return arr;
}
uint8_t* d = static_cast<uint8_t*>(arr); uint8_t* d = static_cast<uint8_t*>(arr);
const uint8_t* s = static_cast<const uint8_t*>(mask); const uint8_t* s = static_cast<const uint8_t*>(mask);
while (n > 0) {
while (n > 0) const size_t step = detail::_bit_xor(d, s, n);
{
size_t step = detail::__bit_xor(d, s, n);
d += step; s += step; n -= step; d += step; s += step; n -= step;
} }
@@ -212,11 +218,14 @@ constexpr void* bit_xor(void* arr, const void* mask, size_t n)
/// \brief Safe version of fennec::bit_xor /// \brief Safe version of fennec::bit_xor
/// ///
/// \details 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 n0 the size of arr in bytes
/// \param mask the mask to or against arr
/// \param n1 the size of mask in bytes /// \param n1 the size of mask in bytes
constexpr void* bit_xor_s(void* arr, size_t n0, const void* mask, size_t n1) /// \returns the pointer arr
{ return bit_xor(arr, mask, n0 < n1 ? n0 : n1); } 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);
}
} }

View File

@@ -0,0 +1,123 @@
// =====================================================================================================================
// 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_COMPARE_H
#define FENNEC_LANG_COMPARE_H
#include <fennec/lang/type_operators.h>
namespace fennec
{
// equality ============================================================================================================
template<typename T0, typename T1 = T0> struct equality;
template<typename T0, typename T1> requires has_equals_v<T0, T1>
struct equality<T0, T1> {
constexpr bool operator()(const T0& x, const T1& y) const {
return x == y;
}
};
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> {
constexpr bool operator()(const T0& x, const T1& y) const {
return not(x < y) and not(y < x);
}
};
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> {
constexpr bool operator()(const T0& x, const T1& y) const {
return not(x > y) and not(y > x);
}
};
// inequality ==========================================================================================================
template<typename T0, typename T1 = T0> struct inequality;
template<typename T0, typename T1> requires has_nequals_v<T0, T1>
struct inequality<T0, T1> {
constexpr bool operator()(const T0& x, const T1& y) const {
return x != y;
}
};
template<typename T0, typename T1> requires has_less_v<T0, T1> and has_less_v<T1, T0>
struct inequality<T0, T1> {
constexpr bool operator()(const T0& x, const T1& y) const {
return (x < y) or (y < x);
}
};
template<typename T0, typename T1> requires has_greater_v<T0, T1> and has_greater_v<T1, T0>
struct inequality<T0, T1> {
constexpr bool operator()(const T0& x, const T1& y) const {
return (x > y) or (y > x);
}
};
// less ================================================================================================================
template<typename T0, typename T1 = T0> requires has_less_v<T0, T1>
struct less {
constexpr bool operator()(const T0& x, const T1& y) const {
return x < y;
}
};
// less_equal ==========================================================================================================
template<typename T0, typename T1 = T0> requires has_less_equals_v<T0, T1>
struct less_equals {
constexpr bool operator()(const T0& x, const T1& y) const {
return x <= y;
}
};
// less ================================================================================================================
template<typename T0, typename T1 = T0> requires has_greater_v<T0, T1>
struct greater {
constexpr bool operator()(const T0& x, const T1& y) const {
return x < y;
}
};
// less_equal ==========================================================================================================
template<typename T0, typename T1 = T0> requires has_greater_equals_v<T0, T1>
struct greater_equals {
constexpr bool operator()(const T0& x, const T1& y) const {
return x <= y;
}
};
}
#endif // FENNEC_LANG_COMPARE_H

View File

@@ -32,6 +32,8 @@
#ifndef FENNEC_LANG_CONDITIONAL_TYPES_H #ifndef FENNEC_LANG_CONDITIONAL_TYPES_H
#define FENNEC_LANG_CONDITIONAL_TYPES_H #define FENNEC_LANG_CONDITIONAL_TYPES_H
#include <fennec/lang/type_identity.h>
/// ///
/// \page fennec_lang_conditional_types Conditional Types /// \page fennec_lang_conditional_types Conditional Types
/// ///
@@ -44,8 +46,8 @@
/// <tr><th style="vertical-align: top">Syntax /// <tr><th style="vertical-align: top">Syntax
/// <th style="vertical-align: top">Description /// <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::conditional "typename conditional<bool_t B, TrueT, FalseT>::type"<br> /// \ref fennec::conditional "typename conditional<bool B, TrueT, FalseT>::type"<br>
/// \ref fennec::conditional_t "conditional_t<bool_t B, TrueT, FalseT>" /// \ref fennec::conditional_t "conditional_t<bool B, TrueT, FalseT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::conditional /// \copydetails fennec::conditional
/// ///
@@ -56,17 +58,14 @@
/// \copydetails fennec::detect /// \copydetails fennec::detect
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <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 "typename enable_if<bool B, TypeT>::type"<br>
/// \ref fennec::enable_if_t "enable_if_t<bool_t B, TypeT>" /// \ref fennec::enable_if_t "enable_if_t<bool B, TypeT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::enable_if /// \copydetails fennec::enable_if
/// </table> /// </table>
/// ///
/// ///
#include <fennec/lang/type_transforms.h>
#include <fennec/lang/types.h>
namespace fennec namespace fennec
{ {
@@ -80,27 +79,25 @@ namespace fennec
/// \tparam B the value of the condition /// \tparam B the value of the condition
/// \tparam TrueT type to use when \f$B == true\f$ /// \tparam TrueT type to use when \f$B == true\f$
/// \tparam FalseT type to use when \f$B == false\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; struct conditional;
/// ///
/// \brief Shorthand for ```typename conditional<ConditionV, TrueT, FalseT>::type``` /// \brief Shorthand for ```typename conditional<ConditionV, TrueT, FalseT>::type```
template<bool_t B, typename TrueT, typename FalseT> template<bool B, typename TrueT, typename FalseT>
using conditional_t using conditional_t
= typename conditional<B, TrueT, FalseT>::type; = typename conditional<B, TrueT, FalseT>::type;
// specialization of fennec::conditional for `true` case // specialization of fennec::conditional for `true` case
template<typename T, typename F> template<typename T, typename F>
struct conditional<true, T, F> struct conditional<true, T, F> : type_identity<T>{};
: type_transform<T>{};
// specialization of fennec::conditional for `false` case // specialization of fennec::conditional for `false` case
template<typename T, typename F> template<typename T, typename F>
struct conditional<false, T, F> struct conditional<false, T, F> : type_identity<F>{};
: type_transform<F>{};
// fennec::detect ====================================================================================================== // fennec::detect ======================================================================================================
@@ -123,8 +120,7 @@ struct detect
/// ///
/// \brief Shorthand for ```typename detect<DefaultT, DetectT, ArgsT...>::type``` /// \brief Shorthand for ```typename detect<DefaultT, DetectT, ArgsT...>::type```
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT> template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
using detect_t using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;
= typename detect<DefaultT, DetectT, ArgsT...>::type;
// true case // true case
@@ -134,7 +130,6 @@ struct detect<DefaultT, DetectT, ArgsT...>
{ {
using type = DetectT<ArgsT...>; using type = DetectT<ArgsT...>;
static constexpr bool is_detected = true; static constexpr bool is_detected = true;
}; };
@@ -153,12 +148,12 @@ struct detect<DefaultT, DetectT, ArgsT...>
/// ///
/// \tparam B A boolean value /// \tparam B A boolean value
/// \tparam T The type to conditionally define /// \tparam T The type to conditionally define
template<bool_t B, typename T = void> template<bool B, typename T = void>
struct enable_if {}; struct enable_if {};
/// ///
/// \brief Shorthand for ```typename enable_if<B, T>::type``` /// \brief Shorthand for ```typename enable_if<B, T>::type```
template<bool_t B, typename T = void> template<bool B, typename T = void>
using enable_if_t = typename enable_if<B, T>::type; using enable_if_t = typename enable_if<B, T>::type;
// true case // true case

View File

@@ -91,19 +91,21 @@ namespace fennec
/// \endcode /// \endcode
/// \tparam ValueT type of the values /// \tparam ValueT type of the values
/// \tparam Values sequence values /// \tparam Values sequence values
template<typename ValueT, ValueT...Values> struct sequence template<typename ValueT, ValueT...Values> struct const_sequence
{ {
/// \brief type of the sequence /// \brief type of the sequence
using value_type = ValueT; using value_type = ValueT;
/// \brief self-referential type /// \brief self-referential type
using type = sequence; using type = const_sequence;
/// ///
/// \brief returns the number of elements /// \brief returns the number of elements
/// ///
/// \return number of elements in the array /// \return number of elements in the array
inline static constexpr size_t size() noexcept { return sizeof...(Values); } inline static constexpr size_t size() noexcept {
return sizeof...(Values);
}
}; };
@@ -117,19 +119,21 @@ template<typename ValueT, ValueT...Values> struct sequence
/// \tparam IntT type of the values, must satisfy ```fennec::is_integral<T>``` /// \tparam IntT type of the values, must satisfy ```fennec::is_integral<T>```
/// \tparam Values sequence values /// \tparam Values sequence values
template<typename IntT, IntT...Values> requires(is_integral_v<IntT>) template<typename IntT, IntT...Values> requires(is_integral_v<IntT>)
struct integer_sequence : sequence<IntT, Values...> struct const_integer_sequence : const_sequence<IntT, Values...>
{ {
/// \brief type of the sequence /// \brief type of the sequence
using value_type = IntT; using value_type = IntT;
/// \brief self-referential type /// \brief self-referential type
using type = integer_sequence; using type = const_integer_sequence;
/// ///
/// \brief returns the number of elements /// \brief returns the number of elements
/// ///
/// \return number of elements in the array /// \return number of elements in the array
inline static constexpr size_t size() noexcept { return sizeof...(Values); } inline static constexpr size_t size() noexcept {
return sizeof...(Values);
}
}; };
@@ -154,19 +158,21 @@ template<typename IntT, size_t N> using make_integer_sequence_t = typename make
/// ///
/// \details A `fennec::integer_sequence` specialized for sequences of `size_t` indices. /// \details A `fennec::integer_sequence` specialized for sequences of `size_t` indices.
/// \tparam Indices sequence values /// \tparam Indices sequence values
template<size_t...Indices> struct index_sequence : integer_sequence<size_t, Indices...> template<size_t...Indices> struct const_index_sequence : const_integer_sequence<size_t, Indices...>
{ {
/// \brief type of the sequence /// \brief type of the sequence
using value_type = size_t; using value_type = size_t;
/// \brief self-referential type /// \brief self-referential type
using type = index_sequence; using type = const_index_sequence;
/// ///
/// \brief returns the number of elements /// \brief returns the number of elements
/// ///
/// \return number of elements in the array /// \return number of elements in the array
inline static constexpr size_t size() noexcept { return sizeof...(Indices); } inline static constexpr size_t size() noexcept {
return sizeof...(Indices);
}
}; };
@@ -207,31 +213,31 @@ template<typename SequenceT0, typename SequenceT1> using concat_sequence_t
template<typename T, size_t N> struct make_integer_sequence : concat_sequence_t<make_integer_sequence_t<T, N / 2>, make_integer_sequence_t<T, N - N / 2>>{}; template<typename T, size_t N> struct make_integer_sequence : concat_sequence_t<make_integer_sequence_t<T, N / 2>, make_integer_sequence_t<T, N - N / 2>>{};
// Base Case of N=0 // Base Case of N=0
template<typename T> struct make_integer_sequence<T, 0> : integer_sequence<T> {}; template<typename T> struct make_integer_sequence<T, 0> : const_integer_sequence<T> {};
// Base Case of N=1 // Base Case of N=1
template<typename T> struct make_integer_sequence<T, 1> : integer_sequence<T, 0>{}; template<typename T> struct make_integer_sequence<T, 1> : const_integer_sequence<T, 0>{};
// Implementation for Generating an index_sequence // Implementation for Generating an index_sequence
template<size_t N> struct make_index_sequence : concat_sequence_t<make_index_sequence_t<N / 2>, make_index_sequence_t<N - N / 2>>{}; template<size_t N> struct make_index_sequence : concat_sequence_t<make_index_sequence_t<N / 2>, make_index_sequence_t<N - N / 2>>{};
// Base Case of N=0 // Base Case of N=0
template<> struct make_index_sequence<0> : index_sequence<> {}; template<> struct make_index_sequence<0> : const_index_sequence<> {};
// Base Case of N=1 // Base Case of N=1
template<> struct make_index_sequence<1> : index_sequence<0>{}; template<> struct make_index_sequence<1> : const_index_sequence<0>{};
// Specialization for integer sequences // Specialization for integer sequences
template<typename T, T...SequenceV0, T...SequenceV1> template<typename T, T...SequenceV0, T...SequenceV1>
struct concat_sequence<integer_sequence<T, SequenceV0...>, integer_sequence<T, SequenceV1...>> struct concat_sequence<const_integer_sequence<T, SequenceV0...>, const_integer_sequence<T, SequenceV1...>>
: integer_sequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{}; : const_integer_sequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
// Specialization for index sequences // Specialization for index sequences
template<size_t...SequenceV0, size_t...SequenceV1> template<size_t...SequenceV0, size_t...SequenceV1>
struct concat_sequence<index_sequence<SequenceV0...>, index_sequence<SequenceV1...>> struct concat_sequence<const_index_sequence<SequenceV0...>, const_index_sequence<SequenceV1...>>
: index_sequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{}; : const_index_sequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};

View File

@@ -31,6 +31,8 @@
#ifndef FENNEC_LANG_CONSTANTS_H #ifndef FENNEC_LANG_CONSTANTS_H
#define FENNEC_LANG_CONSTANTS_H #define FENNEC_LANG_CONSTANTS_H
#include <fennec/lang/types.h>
/// ///
/// ///
/// \page fennec_lang_constants Constants /// \page fennec_lang_constants Constants
@@ -49,20 +51,18 @@
/// \copydetails fennec::integral_constant /// \copydetails fennec::integral_constant
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <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"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::bool_constant /// \copydetails fennec::bool_constant
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <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"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::bool_constant /// \copydetails fennec::bool_constant
/// ///
/// </table> /// </table>
/// ///
#include <fennec/lang/types.h>
namespace fennec namespace fennec
{ {
@@ -90,7 +90,7 @@ template<typename IntT, IntT ValueV> struct integral_constant
/// ///
/// \details /// \details
/// \tparam ValueV value of the constant /// \tparam ValueV value of the constant
template<bool_t ValueV> template<bool ValueV>
struct bool_constant struct bool_constant
: integral_constant<bool_t, ValueV> {}; : integral_constant<bool_t, ValueV> {};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,139 @@
// =====================================================================================================================
// 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::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

View File

@@ -19,18 +19,16 @@
#ifndef FENNEC_LANG_DETAIL_INT_H #ifndef FENNEC_LANG_DETAIL_INT_H
#define FENNEC_LANG_DETAIL_INT_H #define FENNEC_LANG_DETAIL_INT_H
#if _MSC_VER #if FENNEC_COMPILER_MSVC
#pragma warning(push) #pragma warning(push)
#pragma warning(disable:4117) #pragma warning(disable:4117)
#define __PTRDIFF_TYPE__ ptrdiff_t
#endif #endif
#pragma push_macro("__cplusplus") // Include math since stdint will define its own versions of isinf and isnan
#undef __cplusplus #include <math.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#pragma pop_macro("__cplusplus")
#if _MSC_VER
#pragma warning(pop)
#endif
#endif // FENNEC_LANG_DETAIL_INT_H #endif // FENNEC_LANG_DETAIL_INT_H

View File

@@ -0,0 +1,59 @@
// =====================================================================================================================
// 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::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

View File

@@ -19,20 +19,6 @@
#ifndef FENNEC_LANG_DETAIL_STDLIB_H #ifndef FENNEC_LANG_DETAIL_STDLIB_H
#define FENNEC_LANG_DETAIL_STDLIB_H #define FENNEC_LANG_DETAIL_STDLIB_H
#if _MSC_VER
#pragma warning(push)
#pragma warning(disable:4117)
#endif
#pragma push_macro("__cplusplus")
#undef __cplusplus
extern "C" {
#include <stdlib.h> #include <stdlib.h>
}
#pragma pop_macro("__cplusplus")
#if _MSC_VER
#pragma warning(pop)
#endif
#endif // FENNEC_LANG_DETAIL_STDLIB_H #endif // FENNEC_LANG_DETAIL_STDLIB_H

View File

@@ -0,0 +1,40 @@
// =====================================================================================================================
// 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_SEQUENCES_H
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
#include <fennec/lang/type_transforms.h>
namespace fennec::detail
{
template<typename FirstT, typename... RestT> struct _first_element : type_identity<FirstT> {};
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
> {};
}
#endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H

View File

@@ -0,0 +1,75 @@
// =====================================================================================================================
// 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::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 {};
// 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 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_TYPE_TRAITS_H

View File

@@ -0,0 +1,50 @@
// =====================================================================================================================
// 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_TRANSFORMS_H
#define FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
#include <fennec/lang/types.h>
namespace fennec::detail
{
template<typename _Tp, typename = void>
struct _add_lvalue_reference {
using type = _Tp;
};
template<typename _Tp>
struct _add_lvalue_reference<_Tp, void_t<_Tp&>> {
using type = _Tp&;
};
template<typename _Tp, typename = void>
struct _add_rvalue_reference {
using type = _Tp;
};
template<typename _Tp>
struct _add_rvalue_reference<_Tp, void_t<_Tp&&>> {
using type = _Tp&&;
};
}
#endif // FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H

View File

@@ -0,0 +1,35 @@
// =====================================================================================================================
// 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_TYPEUUID_H
#define FENNEC_LANG_DETAIL_TYPEUUID_H
#include <fennec/lang/types.h>
namespace fennec::detail
{
template<typename RootT>
FENNEC_NO_INLINE uint64_t _typeuuid() {
static uint64_t i = 0;
return ++i;
}
}
#endif // FENNEC_LANG_DETAIL_TYPEUUID_H

View File

@@ -80,7 +80,7 @@
#define FLT_EPSILON fennec::bit_cast<float>(0x34000000) #define FLT_EPSILON fennec::bit_cast<float>(0x34000000)
#define FLT_INF fennec::bit_cast<float>(0x7f800000) #define FLT_INF fennec::bit_cast<float>(0x7f800000)
#define FLT_QUIET_NAN fennec::bit_cast<float>(0x7fc00000) #define FLT_QUIET_NAN fennec::bit_cast<float>(0x7fc00000)
#define FLT_SIGNALING_NAN fennec::bit_cast<float>(0x7fc00001) #define FLT_SIGNALING_NAN fennec::bit_cast<float>(0x7fa00000)
#define FLT_DENORM_MIN fennec::bit_cast<float>(0x1) #define FLT_DENORM_MIN fennec::bit_cast<float>(0x1)
#define FLT_ROUND_ERR fennec::bit_cast<float>(0x3f000000) #define FLT_ROUND_ERR fennec::bit_cast<float>(0x3f000000)
@@ -132,7 +132,7 @@
#define DBL_EPSILON fennec::bit_cast<double>(0x3cb0000000000000ll) #define DBL_EPSILON fennec::bit_cast<double>(0x3cb0000000000000ll)
#define DBL_INF fennec::bit_cast<double>(0x7ff0000000000000ll) #define DBL_INF fennec::bit_cast<double>(0x7ff0000000000000ll)
#define DBL_QUIET_NAN fennec::bit_cast<double>(0x7ff8000000000000ll) #define DBL_QUIET_NAN fennec::bit_cast<double>(0x7ff8000000000000ll)
#define DBL_SIGNALING_NAN fennec::bit_cast<double>(0x7ff0000000000001ll) #define DBL_SIGNALING_NAN fennec::bit_cast<double>(0x7ff4000000000000ll)
#define DBL_DENORM_MIN fennec::bit_cast<double>(0x1ll) #define DBL_DENORM_MIN fennec::bit_cast<double>(0x1ll)
#define DBL_ROUND_ERR fennec::bit_cast<double>(0x3fe0000000000000ll) #define DBL_ROUND_ERR fennec::bit_cast<double>(0x3fe0000000000000ll)

View File

@@ -0,0 +1,91 @@
// =====================================================================================================================
// 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_HASHING_H
#define FENNEC_LANG_HASHING_H
#include <fennec/lang/types.h>
#include <fennec/lang/type_traits.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;
// Murmur3 Hash for 64-bit ints
template<>
struct hash<uint64_t> {
using type_t = uint64_t;
constexpr size_t operator()(uint64_t x) const {
// Murmur3
x ^= x >> 33U;
x *= 0xff51afd7ed558ccd;
x ^= x >> 33U;
x *= 0xc4ceb9fe1a85ec53;
x ^= x >> 33U;
return x;
}
};
// Wrapper for casting ints
template<typename IntT>
requires is_integral_v<IntT>
struct hash<IntT> : hash<uint64_t> {
using type_t = IntT;
};
// Wrapper for pointers
template<typename PtrT>
struct hash<PtrT*> : hash<uintptr_t> {
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> {
constexpr size_t operator()(float x) const {
return hash<uint32_t>::operator()(bit_cast<uint32_t>(x));
}
};
template<>
struct hash<double> : hash<uint64_t> {
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

View File

@@ -57,26 +57,26 @@
#define CHAR_DIG 0x2 #define CHAR_DIG 0x2
#define CHAR_DECIMAL_DIG 0x0 #define CHAR_DECIMAL_DIG 0x0
#define CHAR_RADIX 0x2 #define CHAR_RADIX 0x2
#define CHAR_TRAPS 0xfalse #define CHAR_TRAPS 0xtrue
#define CHAR_MIN 0x80 #define CHAR_MIN 0x80
#define CHAR_MAX 0x7f #define CHAR_MAX 0x7f
#define WCHAR_IS_SIGNED false #define WCHAR_IS_SIGNED true
#define WCHAR_ROUNDS 0x0 #define WCHAR_ROUNDS 0x0
#define WCHAR_RADIX_DIG 0x10 #define WCHAR_RADIX_DIG 0x1f
#define WCHAR_DIG 0x4 #define WCHAR_DIG 0x9
#define WCHAR_DECIMAL_DIG 0x0 #define WCHAR_DECIMAL_DIG 0x0
#define WCHAR_RADIX 0x2 #define WCHAR_RADIX 0x2
#define WCHAR_TRAPS 0xfalse #define WCHAR_TRAPS 0xtrue
#define WCHAR_MIN 0x0 #define WCHAR_MIN 0x80000000
#define WCHAR_MAX 0xffff #define WCHAR_MAX 0x7fffffff
#define SCHAR_ROUNDS 0x0 #define SCHAR_ROUNDS 0x0
#define SCHAR_RADIX_DIG 0x7 #define SCHAR_RADIX_DIG 0x7
#define SCHAR_DIG 0x2 #define SCHAR_DIG 0x2
#define SCHAR_DECIMAL_DIG 0x0 #define SCHAR_DECIMAL_DIG 0x0
#define SCHAR_RADIX 0x2 #define SCHAR_RADIX 0x2
#define SCHAR_TRAPS 0xfalse #define SCHAR_TRAPS 0xtrue
#define SCHAR_MIN 0x80 #define SCHAR_MIN 0x80
#define SCHAR_MAX 0x7f #define SCHAR_MAX 0x7f
@@ -85,7 +85,7 @@
#define UCHAR_DIG 0x2 #define UCHAR_DIG 0x2
#define UCHAR_DECIMAL_DIG 0x0 #define UCHAR_DECIMAL_DIG 0x0
#define UCHAR_RADIX 0x2 #define UCHAR_RADIX 0x2
#define UCHAR_TRAPS 0xfalse #define UCHAR_TRAPS 0xtrue
#define UCHAR_MIN 0x0 #define UCHAR_MIN 0x0
#define UCHAR_MAX 0xff #define UCHAR_MAX 0xff
@@ -94,7 +94,7 @@
#define SHORT_DIG 0x4 #define SHORT_DIG 0x4
#define SHORT_DECIMAL_DIG 0x0 #define SHORT_DECIMAL_DIG 0x0
#define SHORT_RADIX 0x2 #define SHORT_RADIX 0x2
#define SHORT_TRAPS 0xfalse #define SHORT_TRAPS 0xtrue
#define SHORT_MIN 0x8000 #define SHORT_MIN 0x8000
#define SHORT_MAX 0x7fff #define SHORT_MAX 0x7fff
@@ -103,7 +103,7 @@
#define USHORT_DIG 0x4 #define USHORT_DIG 0x4
#define USHORT_DECIMAL_DIG 0x0 #define USHORT_DECIMAL_DIG 0x0
#define USHORT_RADIX 0x2 #define USHORT_RADIX 0x2
#define USHORT_TRAPS 0xfalse #define USHORT_TRAPS 0xtrue
#define USHORT_MIN 0x0 #define USHORT_MIN 0x0
#define USHORT_MAX 0xffff #define USHORT_MAX 0xffff
@@ -112,7 +112,7 @@
#define INT_DIG 0x9 #define INT_DIG 0x9
#define INT_DECIMAL_DIG 0x0 #define INT_DECIMAL_DIG 0x0
#define INT_RADIX 0x2 #define INT_RADIX 0x2
#define INT_TRAPS 0xfalse #define INT_TRAPS 0xtrue
#define INT_MIN 0x80000000 #define INT_MIN 0x80000000
#define INT_MAX 0x7fffffff #define INT_MAX 0x7fffffff
@@ -121,34 +121,34 @@
#define UINT_DIG 0x9 #define UINT_DIG 0x9
#define UINT_DECIMAL_DIG 0x0 #define UINT_DECIMAL_DIG 0x0
#define UINT_RADIX 0x2 #define UINT_RADIX 0x2
#define UINT_TRAPS 0xfalse #define UINT_TRAPS 0xtrue
#define UINT_MIN 0x0 #define UINT_MIN 0x0
#define UINT_MAX 0xffffffff #define UINT_MAX 0xffffffff
#define LONG_ROUNDS 0x0 #define LONG_ROUNDS 0x0
#define LONG_RADIX_DIG 0x1f #define LONG_RADIX_DIG 0x3f
#define LONG_DIG 0x9 #define LONG_DIG 0x12
#define LONG_DECIMAL_DIG 0x0 #define LONG_DECIMAL_DIG 0x0
#define LONG_RADIX 0x2 #define LONG_RADIX 0x2
#define LONG_TRAPS 0xfalse #define LONG_TRAPS 0xtrue
#define LONG_MIN 0x80000000 #define LONG_MIN 0x8000000000000000
#define LONG_MAX 0x7fffffff #define LONG_MAX 0x7fffffffffffffff
#define ULONG_ROUNDS 0x0 #define ULONG_ROUNDS 0x0
#define ULONG_RADIX_DIG 0x20 #define ULONG_RADIX_DIG 0x40
#define ULONG_DIG 0x9 #define ULONG_DIG 0x13
#define ULONG_DECIMAL_DIG 0x0 #define ULONG_DECIMAL_DIG 0x0
#define ULONG_RADIX 0x2 #define ULONG_RADIX 0x2
#define ULONG_TRAPS 0xfalse #define ULONG_TRAPS 0xtrue
#define ULONG_MIN 0x0 #define ULONG_MIN 0x0
#define ULONG_MAX 0xffffffff #define ULONG_MAX 0xffffffffffffffff
#define LLONG_ROUNDS 0x0 #define LLONG_ROUNDS 0x0
#define LLONG_RADIX_DIG 0x3f #define LLONG_RADIX_DIG 0x3f
#define LLONG_DIG 0x12 #define LLONG_DIG 0x12
#define LLONG_DECIMAL_DIG 0x0 #define LLONG_DECIMAL_DIG 0x0
#define LLONG_RADIX 0x2 #define LLONG_RADIX 0x2
#define LLONG_TRAPS 0xfalse #define LLONG_TRAPS 0xtrue
#define LLONG_MIN 0x8000000000000000 #define LLONG_MIN 0x8000000000000000
#define LLONG_MAX 0x7fffffffffffffff #define LLONG_MAX 0x7fffffffffffffff
@@ -157,7 +157,7 @@
#define ULLONG_DIG 0x13 #define ULLONG_DIG 0x13
#define ULLONG_DECIMAL_DIG 0x0 #define ULLONG_DECIMAL_DIG 0x0
#define ULLONG_RADIX 0x2 #define ULLONG_RADIX 0x2
#define ULLONG_TRAPS 0xfalse #define ULLONG_TRAPS 0xtrue
#define ULLONG_MIN 0x0 #define ULLONG_MIN 0x0
#define ULLONG_MAX 0xffffffffffffffff #define ULLONG_MAX 0xffffffffffffffff

View File

@@ -1,6 +1,6 @@
// ===================================================================================================================== // =====================================================================================================================
// fennec, a free and open source game engine // fennec, a free and open source game engine
// Copyright (C) 2025 Medusa Slockbower // Copyright © 2025 Medusa Slockbower
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@@ -126,14 +126,60 @@
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 0 # define FENNEC_HAS_BUILTIN_IS_ABSTRACT 0
#endif #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_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
// CONSTRUCTORS ========================================================================================================
// Difficult and Inconsistent without intrinsics // Difficult and Inconsistent without intrinsics
#if __has_builtin(__is_constructible) #if __has_builtin(__is_constructible)
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1 # define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 1
# define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(type, ...) __is_constructible(type, __VA_ARGS__) # define FENNEC_BUILTIN_IS_CONSTRUCTIBLE(type, ...) __is_constructible(type __VA_OPT__(,) __VA_ARGS__)
#else #else
# define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 0 # define FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE 0
#endif #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)
#else
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_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
// Type Traits // Type Traits
// can_convert is also very difficult to implement without intrinsics // can_convert is also very difficult to implement without intrinsics
#if __has_builtin(__is_convertible) #if __has_builtin(__is_convertible)
@@ -159,6 +205,30 @@
#endif #endif
#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_final)
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
# define FENNEC_BUILTIN_IS_FINAL(arg) __is_final(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
#endif
// Inconsistent with dynamic intrinsics, requires a massive table for static intrinsics
#if __has_builtin(__is_fundamental)
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 1
# define FENNEC_BUILTIN_IS_FUNDAMENTAL(arg) __is_fundamental(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 0
#endif
// Inconsistent without intrinsics // Inconsistent without intrinsics
#if __has_builtin(__is_polymorphic) #if __has_builtin(__is_polymorphic)
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1 # define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 1
@@ -181,7 +251,7 @@
// TODO: More compiler support // TODO: More compiler support
#if _MSC_VER #if FENNEC_COMPILER_MSVC
# define FENNEC_HAS_BUILTIN_ADDRESS_OF 1 # define FENNEC_HAS_BUILTIN_ADDRESS_OF 1
# define FENNEC_BUILTIN_ADDRESS_OF(arg) __builtin_addressof(arg) # define FENNEC_BUILTIN_ADDRESS_OF(arg) __builtin_addressof(arg)

View File

@@ -31,9 +31,8 @@
#ifndef FENNEC_LANG_H #ifndef FENNEC_LANG_H
#define FENNEC_LANG_H #define FENNEC_LANG_H
#include <fennec/lang/assert.h>
#include <fennec/lang/bits.h> #include <fennec/lang/bits.h>
#include <fennec/lang/intrinsics.h>
#include <fennec/lang/limits.h>
#include <fennec/lang/types.h> #include <fennec/lang/types.h>
#include <fennec/lang/utility.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. /// 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_bit_manipulation
/// - \subpage fennec_lang_intrinsics /// - \subpage fennec_lang_intrinsics
/// - \subpage fennec_lang_limits /// - \subpage fennec_lang_limits

View File

@@ -1,6 +1,6 @@
// ===================================================================================================================== // =====================================================================================================================
// fennec, a free and open source game engine // fennec, a free and open source game engine
// Copyright (C) 2025 Medusa Slockbower // Copyright © 2025 Medusa Slockbower
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by

View File

@@ -55,7 +55,7 @@
/// </table> /// </table>
#include <fennec/lang/type_transforms.h> #include <fennec/lang/type_transforms.h>
#include <fennec/lang/detail/__numeric_transforms.h> #include <fennec/lang/detail/_numeric_transforms.h>
namespace fennec namespace fennec
{ {
@@ -63,7 +63,7 @@ namespace fennec
/// ///
/// \brief Get the corresponding signed integral type of TypeT /// \brief Get the corresponding signed integral type of TypeT
/// \tparam TypeT the integral type to transform /// \tparam TypeT the integral type to transform
template<typename TypeT> struct make_signed : detail::__make_signed<remove_cv_t<TypeT>> {}; template<typename TypeT> struct make_signed : detail::_make_signed<remove_cv_t<TypeT>> {};
/// ///
/// \brief Shorthand for `typename make_signed<TypeT>::type` /// \brief Shorthand for `typename make_signed<TypeT>::type`
@@ -73,7 +73,7 @@ template<typename TypeT> using make_signed_t = typename make_signed<TypeT>::type
/// ///
/// \brief Get the corresponding unsigned integral type of TypeT /// \brief Get the corresponding unsigned integral type of TypeT
/// \tparam TypeT the integral type to transform /// \tparam TypeT the integral type to transform
template<typename TypeT> struct make_unsigned : detail::__make_unsigned<remove_cv_t<TypeT>> {}; template<typename TypeT> struct make_unsigned : detail::_make_unsigned<remove_cv_t<TypeT>> {};
/// ///
/// \brief Shorthand for `typename make_unsigned<TypeT>::type` /// \brief Shorthand for `typename make_unsigned<TypeT>::type`

View File

@@ -0,0 +1,28 @@
// =====================================================================================================================
// 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_STARTUP_H
#define FENNEC_LANG_STARTUP_H
// Helper for running a function before main()
#define STATIC_CONSTRUCTOR(f) \
inline static void f(void); \
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
inline static void f(void)
#endif // FENNEC_LANG_STARTUP_H

View File

@@ -0,0 +1,57 @@
// =====================================================================================================================
// 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_TYPE_IDENTITY_H
#define FENNEC_LANG_TYPE_IDENTITY_H
///
/// \page fennec_lang_type_identity Type Identity
///
/// \brief Part of the fennec metaprogramming library. This header defines structures for copying types with different traits
/// or rather, transform them, at compile time.
///
/// \code #include <fennec/lang/type_identity.h> \endcode
///
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_type_identity">
/// <tr><th style="vertical-align: top">Syntax
/// <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::type_identity "type_identity<TypeT>::type"<br>
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::type_identity
/// </table>
///
namespace fennec
{
///
/// \brief Base Class for Type Transformations
///
/// \details resembles a transformation from one type to T, the result is stored in the member type_transform::type
/// \tparam T Resultant Type
template<typename T> struct type_identity {
/// \brief the type to transform into
using type = T;
};
}
#endif // FENNEC_LANG_TYPE_IDENTITY_H

View File

@@ -0,0 +1,112 @@
// =====================================================================================================================
// 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_TYPE_OPERATORS_H
#define FENNEC_LANG_TYPE_OPERATORS_H
namespace fennec
{
// https://stackoverflow.com/questions/6534041/how-to-check-whether-operator-exists
// has_equals ==========================================================================================================
template<typename T0, typename T1 = T0>
struct has_equals {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() == declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_equals_v = has_equals<T0, T1>::value;
// has_nequals =========================================================================================================
template<typename T0, typename T1 = T0>
struct has_nequals {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() != declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_nequals_v = has_nequals<T0, T1>::value;
// has_less ============================================================================================================
template<typename T0, typename T1 = T0>
struct has_less {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() < declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_less_v = has_less<T0, T1>::value;
// has_less_equals =====================================================================================================
template<typename T0, typename T1 = T0>
struct has_less_equals {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() <= declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_less_equals_v = has_less_equals<T0, T1>::value;
// has_greater =========================================================================================================
template<typename T0, typename T1 = T0>
struct has_greater {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() > declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_greater_v = has_greater<T0, T1>::value;
// has_greater_equals ==================================================================================================
template<typename T0, typename T1 = T0>
struct has_greater_equals {
// Use SFINAE to check for the operator
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() >= declval<V>());
template<typename, typename> static auto test(...) -> false_type;
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
};
template<typename T0, typename T1 = T0> constexpr bool has_greater_equals_v = has_greater_equals<T0, T1>::value;
}
#endif // FENNEC_LANG_TYPE_OPERATORS_H

View File

@@ -54,7 +54,7 @@
/// </table> /// </table>
/// ///
#include <fennec/lang/detail/__type_sequences.h> #include <fennec/lang/detail/_type_sequences.h>
namespace fennec namespace fennec
{ {
@@ -62,13 +62,15 @@ namespace fennec
/// ///
/// \brief Get the first element of a template parameter pack /// \brief Get the first element of a template parameter pack
/// \tparam TypesT the Parameter Pack /// \tparam TypesT the Parameter Pack
template<typename...TypesT> struct first_element : detail::__first_element<TypesT...> {}; template<typename...TypesT> struct first_element : detail::_first_element<TypesT...> {};
/// ///
/// \brief alias for first_element<TypesT>::type /// \brief alias for first_element<TypesT>::type
template<typename...TypesT> using first_element_t = typename first_element<TypesT...>::type; template<typename...TypesT> using first_element_t = typename first_element<TypesT...>::type;
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};
/// ///
/// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first `ArgT` of `ArgsT...` with `SubT` /// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first `ArgT` of `ArgsT...` with `SubT`

View File

@@ -92,10 +92,10 @@
/// \copydetails fennec::is_same /// \copydetails fennec::is_same
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::can_convert "can_convert<TypeT0, TypeT1>::value"<br> /// \ref fennec::is_convertible "is_convertible<TypeT0, TypeT1>::value"<br>
/// \ref fennec::can_convert_v "can_convert_v<TypeT>" /// \ref fennec::is_convertible_v "is_convertible_v<TypeT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::can_convert /// \copydetails fennec::is_convertible
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::is_constructible "is_constructible<ClassT, ArgsT...>::value"<br> /// \ref fennec::is_constructible "is_constructible<ClassT, ArgsT...>::value"<br>
@@ -107,11 +107,27 @@
/// ///
#include <fennec/lang/type_transforms.h> #include <fennec/lang/type_transforms.h>
#include <fennec/lang/detail/__type_traits.h> #include <fennec/lang/detail/_type_traits.h>
namespace fennec namespace fennec
{ {
// fennec::declval =====================================================================================================
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);
}
constexpr inline bool is_constant_evaluated() noexcept {
if consteval {
return true;
} else {
return false;
}
}
// fennec::is_void ===================================================================================================== // fennec::is_void =====================================================================================================
/// ///
@@ -120,13 +136,12 @@ namespace fennec
/// \details Stores a boolean value in `is_void::value`, representing whether the provided type is of base type void. /// \details Stores a boolean value in `is_void::value`, representing whether the provided type is of base type void.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_void template<typename T> struct is_void
: detail::__is_void<remove_cvr_t<T>>{}; : detail::_is_void<remove_cvr_t<T>>{};
/// ///
/// \brief shorthand for ```is_void<T>::value``` /// \brief shorthand for ```is_void<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_void_v template<typename T> constexpr bool_t is_void_v = is_void<T>::value;
= is_void<T>::value;
@@ -138,13 +153,77 @@ template<typename T> constexpr bool_t is_void_v
/// \details Stores a boolean value in `is_bool::value`, representing whether the provided type is of base type bool. /// \details Stores a boolean value in `is_bool::value`, representing whether the provided type is of base type bool.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_bool template<typename T> struct is_bool
: detail::__is_bool<remove_cvr_t<T>>{}; : detail::_is_bool<remove_cvr_t<T>>{};
/// ///
/// \brief shorthand for ```is_bool<T>::value``` /// \brief shorthand for ```is_bool<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_bool_v template<typename T> constexpr bool_t is_bool_v = is_bool<T>::value;
= is_bool<T>::value;
// fennec::is_null_pointer =============================================================================================
///
/// \brief check if \p T is of type nullptr_t
///
/// \details Stores a boolean value in `is_null_pointer::value`, representing whether the provided type is of base type nullptr_t.
/// \tparam T type to check
template<typename T> struct is_null_pointer
: detail::_is_null_pointer<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_null_pointer<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_null_pointer_v = is_null_pointer<T>::value;
// fennec::is_array ====================================================================================================
#if FENNEC_HAS_BUILTIN_IS_ARRAY
///
/// \brief check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
: bool_constant<FENNEC_BUILTIN_IS_ARRAY(T)> {};
#else
///
/// \brief check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
: false_type {};
// overload for a sized array type
template<typename T, size_t N> struct is_array<T[N]>
: true_type {};
// overload for a generic array type
template<typename T> struct is_array<T[]>
: true_type {};
#endif
///
/// \brief shorthand for ```is_array<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_array_v = is_array<T>::value;
// fennec::is_class ====================================================================================================
///
/// \brief check if \p T is a class
/// \tparam T type to check
template<typename T> struct is_class
: bool_constant<FENNEC_BUILTIN_IS_CLASS(T)> {};
///
/// \brief check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_class_v = is_class<T>::value;
@@ -156,13 +235,12 @@ template<typename T> constexpr bool_t is_bool_v
/// \details Stores a boolean value in `is_integral::value`, representing whether the provided type is of a base integer type. /// \details Stores a boolean value in `is_integral::value`, representing whether the provided type is of a base integer type.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_integral template<typename T> struct is_integral
: detail::__is_integral<remove_cvr_t<T>> {}; : detail::_is_integral<remove_cvr_t<T>> {};
/// ///
/// \brief shorthand for ```is_integral<T>::value``` /// \brief shorthand for ```is_integral<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_integral_v template<typename T> constexpr bool_t is_integral_v = is_integral<T>::value;
= is_integral<T>::value;
/// ///
@@ -171,13 +249,12 @@ template<typename T> constexpr bool_t is_integral_v
/// \details Checks if type `T` is a signed type i.e. `T(-1) < T(0)` and stores it in `is_same::value`. /// \details Checks if type `T` is a signed type i.e. `T(-1) < T(0)` and stores it in `is_same::value`.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_signed template<typename T> struct is_signed
: detail::__is_signed<remove_cvr_t<T>> {}; : detail::_is_signed<remove_cvr_t<T>> {};
/// ///
/// \brief shorthand for ```is_signed<T>::value``` /// \brief shorthand for ```is_signed<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_signed_v template<typename T> constexpr bool_t is_signed_v = is_signed<T>::value;
= is_signed<T>::value;
/// ///
@@ -186,13 +263,12 @@ template<typename T> constexpr bool_t is_signed_v
/// \details Checks if type `T` is an unsigned type i.e. `T(-1) > T(0)` and stores it in `is_same::value`. /// \details Checks if type `T` is an unsigned type i.e. `T(-1) > T(0)` and stores it in `is_same::value`.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_unsigned template<typename T> struct is_unsigned
: detail::__is_unsigned<remove_cvr_t<T>> {}; : detail::_is_unsigned<remove_cvr_t<T>> {};
/// ///
/// \brief shorthand for ```is_unsigned<T>::value``` /// \brief shorthand for ```is_unsigned<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_unsigned_v template<typename T> constexpr bool_t is_unsigned_v = is_unsigned<T>::value;
= is_unsigned<T>::value;
@@ -204,13 +280,28 @@ template<typename T> constexpr bool_t is_unsigned_v
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`. /// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_floating_point template<typename T> struct is_floating_point
: detail::__is_floating_point<remove_cvr_t<T>>{}; : detail::_is_floating_point<remove_cvr_t<T>>{};
/// ///
/// \brief shorthand for ```is_floating_point<T>::value``` /// \brief shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_floating_point_v template<typename T> constexpr bool_t is_floating_point_v = is_floating_point<T> {};
= is_floating_point<T> {};
// Pointer Types =======================================================================================================
///
/// \brief check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_pointer
: detail::_is_pointer<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_pointer_v = is_pointer<T> {};
@@ -227,8 +318,22 @@ template<typename T> struct is_arithmetic
/// ///
/// \brief shorthand for ```is_arithmetic<T>::value``` /// \brief shorthand for ```is_arithmetic<T>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_arithmetic_v template<typename T> constexpr bool_t is_arithmetic_v = is_arithmetic<T>::value;
= is_arithmetic<T>::value;
// fennec::is_fundamental ==============================================================================================
///
/// \brief check if \p T is a fundamental type, i.e. arithmetic, void, or nullptr_t
/// \tparam T type to check
template<typename T> struct is_fundamental
: bool_constant<is_arithmetic_v<T> || is_void_v<T> || is_null_pointer_v<T>>{};
///
/// \brief shorthand for ```is_fundamental<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_fundamental_v = is_fundamental<T>::value;
// fennec::is_same ===================================================================================================== // fennec::is_same =====================================================================================================
@@ -238,20 +343,17 @@ template<typename T> constexpr bool_t is_arithmetic_v
/// \details Checks if `T0` and `T1` are identical and store it in `is_same::value` /// \details Checks if `T0` and `T1` are identical and store it in `is_same::value`
/// \tparam T0 first type to check /// \tparam T0 first type to check
/// \tparam T1 second type to check /// \tparam T1 second type to check
template<typename T0, typename T1> struct is_same template<typename T0, typename T1> struct is_same : false_type {};
: false_type {};
// true case // true case
template<typename T> struct is_same<T, T> template<typename T> struct is_same<T, T> : true_type {};
: true_type {};
/// ///
/// \brief shorthand for ```is_same<T0, T1>::value``` /// \brief shorthand for ```is_same<T0, T1>::value```
/// \tparam T type to check /// \tparam T type to check
template<typename T0, typename T1> constexpr bool_t is_same_v template<typename T0, typename T1> constexpr bool_t is_same_v = is_same<T0, T1> {};
= is_same<T0, T1> {};
// fennec::can_convert ================================================================================================= // fennec::is_convertible ==============================================================================================
/// ///
/// \brief check if type `T0` can be converted `T1` /// \brief check if type `T0` can be converted `T1`
@@ -266,11 +368,10 @@ template<typename FromT, typename ToT> struct is_convertible
/// \brief shorthand for `can_convert<TypeT0, TypeT1>::value` /// \brief shorthand for `can_convert<TypeT0, TypeT1>::value`
/// \param FromT First type /// \param FromT First type
/// \param ToT Second type /// \param ToT Second type
template<typename FromT, typename ToT> using is_convertible_v template<typename FromT, typename ToT> constexpr bool_t is_convertible_v = is_convertible<FromT, ToT>{};
= typename is_convertible<FromT, ToT>::type;
// fennec::is_constructible =============================================================================================== // fennec::is_constructible ============================================================================================
/// ///
/// \brief Check if `ClassT` can be constructed with `ArgsT,` i.e. `ClassT(ArgsT...)`. /// \brief Check if `ClassT` can be constructed with `ArgsT,` i.e. `ClassT(ArgsT...)`.
@@ -282,11 +383,109 @@ template<typename ClassT, typename...ArgsT> struct is_constructible
/// ///
/// \brief Shorthand for `is_constructible<ClassT, ArgsT...>::value` /// \brief Shorthand for `is_constructible<ClassT, ArgsT...>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_constructible_v template<typename ClassT, typename...ArgsT> constexpr bool_t is_constructible_v = is_constructible<ClassT, ArgsT...>{};
= is_constructible<ClassT, ArgsT...>{};
// fennec::
///
/// \brief Check if `ClassT` is default constructible
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_default_constructible
: bool_constant<FENNEC_BUILTIN_IS_CONSTRUCTIBLE(ClassT,)> {};
///
/// \brief Shorthand for `is_default_constructible<ClassT>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_default_constructible_v = is_default_constructible<ClassT>{};
///
/// \brief Check if `ClassT` is copy constructible
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_copy_constructible
: bool_constant<FENNEC_BUILTIN_IS_CONSTRUCTIBLE(ClassT, add_lvalue_reference_t<const ClassT>)> {};
///
/// \brief Shorthand for `is_copy_constructible<ClassT>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_copy_constructible_v = is_copy_constructible<ClassT>{};
///
/// \brief Check if `ClassT` is copy constructible
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_move_constructible
: bool_constant<FENNEC_BUILTIN_IS_CONSTRUCTIBLE(ClassT, add_rvalue_reference_t<ClassT>)> {};
///
/// \brief Shorthand for `is_copy_constructible<ClassT>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_move_constructible_v = is_move_constructible<ClassT>{};
///
/// \brief Check if `ClassT` is trivially constructible
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_trivially_constructible
: bool_constant<FENNEC_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE(ClassT)> {};
///
/// \brief Shorthand for `is_trivially_constructible<ClassT, ArgsT...>::value`
template<typename ClassT> constexpr bool_t is_trivially_constructible_v = is_trivially_constructible<ClassT>{};
// fennec::is_trivially_destructible ===================================================================================
///
/// \brief Check if `ClassT` is trivially destructible
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_trivially_destructible
: bool_constant<FENNEC_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE(ClassT)> {};
///
/// \brief Shorthand for `is_trivially_destructible<ClassT, ArgsT...>::value`
template<typename ClassT> constexpr bool_t is_trivially_destructible_v = is_trivially_destructible<ClassT>{};
// fennec::is_assignable ===============================================================================================
///
/// \brief Check if `ClassT` can be constructed with `ArgsT,` i.e. `ClassT(ArgsT...)`.
/// This may be read as "is `ClassT` constructible with `ArgsT`"
/// \tparam ClassAT The class type to test
/// \tparam ClassBT The arguments for the specific constructor
template<typename ClassAT, typename ClassBT> struct is_assignable
: bool_constant<FENNEC_BUILTIN_IS_ASSIGNABLE(ClassAT, ClassBT)> {};
///
/// \brief Shorthand for `is_constructible<ClassT, ArgsT...>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_assignable_v = is_assignable<ClassT, ArgsT...>{};
// fennec::is_copy_assignable ==========================================================================================
///
/// \brief Check if `ClassT` is copy assignable
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_copy_assignable
: bool_constant<FENNEC_BUILTIN_IS_ASSIGNABLE(add_lvalue_reference_t<ClassT>, add_lvalue_reference_t<const ClassT>)> {};
///
/// \brief Shorthand for `is_copy_assignable<ClassT>::value`
template<typename ClassT> constexpr bool_t is_copy_assignable_v = is_copy_assignable<ClassT>{};
// fennec::is_move_assignable ==========================================================================================
///
/// \brief Check if `ClassT` is move assignable
/// \tparam ClassT The class type to test
template<typename ClassT> struct is_move_assignable
: bool_constant<FENNEC_BUILTIN_IS_ASSIGNABLE(add_lvalue_reference_t<ClassT>, add_rvalue_reference_t<ClassT>)> {};
///
/// \brief Shorthand for `is_move_assignable<ClassT>::value`
template<typename ClassT> constexpr bool_t is_move_assignable_v = is_move_assignable<ClassT>{};
// //

View File

@@ -31,6 +31,9 @@
#ifndef FENNEC_LANG_TYPE_TRANSFORMS_H #ifndef FENNEC_LANG_TYPE_TRANSFORMS_H
#define FENNEC_LANG_TYPE_TRANSFORMS_H #define FENNEC_LANG_TYPE_TRANSFORMS_H
#include <fennec/lang/type_identity.h>
#include <fennec/lang/detail/_type_transforms.h>
/// ///
/// \page fennec_lang_type_transforms Type Transforms /// \page fennec_lang_type_transforms Type Transforms
/// ///
@@ -44,11 +47,6 @@
/// <tr><th style="vertical-align: top">Syntax /// <tr><th style="vertical-align: top">Syntax
/// <th style="vertical-align: top">Description /// <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::type_transform "type_transform<TypeT>::type"<br>
/// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::type_transform
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::add_pointer "add_pointer<TypeT>::type"<br> /// \ref fennec::add_pointer "add_pointer<TypeT>::type"<br>
/// \ref fennec::add_pointer_t "add_pointer_t<TypeT>" /// \ref fennec::add_pointer_t "add_pointer_t<TypeT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
@@ -127,22 +125,6 @@
namespace fennec namespace fennec
{ {
// fennec::type_transform ==============================================================================================
///
/// \brief Base Class for Type Transformations
///
/// \details resembles a transformation from one type to T, the result is stored in the member type_transform::type
/// \tparam T Resultant Type
template<typename T> struct type_transform {
///
/// \brief the type to transform into
using type = T;
};
// Pointer Conversions ================================================================================================= // Pointer Conversions =================================================================================================
/// ///
@@ -150,7 +132,7 @@ template<typename T> struct type_transform {
/// ///
/// \details adds a pointer to the provided type such that `T` becomes `T*` /// \details adds a pointer to the provided type such that `T` becomes `T*`
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct add_pointer : type_transform<T*>{}; template<typename T> struct add_pointer : type_identity<T*>{};
/// ///
/// \brief shorthand for `typename add_pointer<T>::type` /// \brief shorthand for `typename add_pointer<T>::type`
@@ -162,10 +144,10 @@ template<typename T> using add_pointer_t = typename add_pointer<T>::type;
/// ///
/// \details removes a pointer from the provided type such that `T*` becomes `T` /// \details removes a pointer from the provided type such that `T*` becomes `T`
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct remove_pointer : type_transform<T> {}; template<typename T> struct remove_pointer : type_identity<T> {};
// specialization for T* // specialization for T*
template<typename T> struct remove_pointer<T*> : type_transform<T> {}; template<typename T> struct remove_pointer<T*> : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_pointer<T>::type` /// \brief shorthand for `typename remove_pointer<T>::type`
@@ -180,7 +162,7 @@ template<typename T> using remove_pointer_t = typename remove_pointer<T>::type;
/// ///
/// \details adds a pointer to the provided type such that `T` becomes `T&` /// \details adds a pointer to the provided type such that `T` becomes `T&`
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct add_reference : type_transform<T&> {}; template<typename T> struct add_reference : type_identity<T&> {};
/// ///
/// \brief shorthand for `typename add_reference<T>::type` /// \brief shorthand for `typename add_reference<T>::type`
@@ -192,19 +174,43 @@ template<typename T> using add_reference_t = typename add_reference<T>::type;
/// ///
/// \details removes references from the provided type such that `T&` and `T&&` become `T` /// \details removes references from the provided type such that `T&` and `T&&` become `T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_reference : type_transform<T> {}; template<typename T> struct remove_reference : type_identity<T> {};
// specialization for `T&` // specialization for `T&`
template<typename T> struct remove_reference<T&> : type_transform<T> {}; template<typename T> struct remove_reference<T&> : type_identity<T> {};
// specialization for `T&&` // specialization for `T&&`
template<typename T> struct remove_reference<T&&> : type_transform<T> {}; template<typename T> struct remove_reference<T&&> : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_reference<T>::type` /// \brief shorthand for `typename remove_reference<T>::type`
template<typename T> using remove_reference_t = typename remove_reference<T>::type; template<typename T> using remove_reference_t = typename remove_reference<T>::type;
///
/// \brief add a lvalue reference to \p T
///
/// \details adds a lvalue reference to the provided type such that 'T' becomes 'T&'
/// \tparam T Reference Type
template<typename T> struct add_lvalue_reference : detail::_add_lvalue_reference<T> {};
///
/// \brief shorthand for `typename remove_reference<T>::type`
template<typename T> using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
///
/// \brief add a rvalue reference to \p T
///
/// \details adds a rvalue reference to the provided type such that 'T' becomes 'T&&'
/// \tparam T Reference Type
template<typename T> struct add_rvalue_reference : detail::_add_rvalue_reference<T> {};
///
/// \brief shorthand for `typename remove_reference<T>::type`
template<typename T> using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;
// Const & Volatile Conversions ======================================================================================== // Const & Volatile Conversions ========================================================================================
@@ -213,14 +219,14 @@ template<typename T> using remove_reference_t = typename remove_reference<T>::t
/// ///
/// \details adds const qualification to the provided type such that `T` becomes `const T` /// \details adds const qualification to the provided type such that `T` becomes `const T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_const : type_transform<const T> {}; template<typename T> struct add_const : type_identity<const T> {};
/// ///
/// \brief shorthand for `typename add_const<T>::type` /// \brief shorthand for `typename add_const<T>::type`
template<typename T> using add_const_t = typename add_const<T>::type; template<typename T> using add_const_t = typename add_const<T>::type;
// specialization for const types // specialization for const types
template<typename T> struct add_const<const T> : type_transform<const T> {}; template<typename T> struct add_const<const T> : type_identity<const T> {};
/// ///
@@ -228,14 +234,14 @@ template<typename T> struct add_const<const T> : type_transform<const T> {};
/// ///
/// \details removes const qualification from the provided type such that `const T` becomes `T` /// \details removes const qualification from the provided type such that `const T` becomes `T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_const : type_transform<T> {}; template<typename T> struct remove_const : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_const<T>::type` /// \brief shorthand for `typename remove_const<T>::type`
template<typename T> using remove_const_t = typename remove_const<T>::type; template<typename T> using remove_const_t = typename remove_const<T>::type;
// specialization for const types // specialization for const types
template<typename T> struct remove_const<const T> : type_transform<T> {}; template<typename T> struct remove_const<const T> : type_identity<T> {};
@@ -244,14 +250,14 @@ template<typename T> struct remove_const<const T> : type_transform<T> {};
/// ///
/// \details removes references from the provided type such that `T` becomes `volatile T` /// \details removes references from the provided type such that `T` becomes `volatile T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_volatile : type_transform<volatile T> {}; template<typename T> struct add_volatile : type_identity<volatile T> {};
/// ///
/// \brief shorthand for `typename add_volatile<T>::type` /// \brief shorthand for `typename add_volatile<T>::type`
template<typename T> using add_volatile_t = typename add_volatile<T>::type; template<typename T> using add_volatile_t = typename add_volatile<T>::type;
// specialization for volatile types // specialization for volatile types
template<typename T> struct add_volatile<volatile T> : type_transform<volatile T> {}; template<typename T> struct add_volatile<volatile T> : type_identity<volatile T> {};
/// ///
@@ -259,14 +265,14 @@ template<typename T> struct add_volatile<volatile T> : type_transform<volatile T
/// ///
/// \details removes references from the provided type such that `volatile T` becomes `T` /// \details removes references from the provided type such that `volatile T` becomes `T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_volatile : type_transform<T> {}; template<typename T> struct remove_volatile : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_volatile<T>::type` /// \brief shorthand for `typename remove_volatile<T>::type`
template<typename T> using remove_volatile_t = typename remove_volatile<T>::type; template<typename T> using remove_volatile_t = typename remove_volatile<T>::type;
// specialization for volatile types // specialization for volatile types
template<typename T> struct remove_volatile<volatile T> : type_transform<T> {}; template<typename T> struct remove_volatile<volatile T> : type_identity<T> {};
@@ -276,20 +282,20 @@ template<typename T> struct remove_volatile<volatile T> : type_transform<T> {};
/// \details removes references from the provided type such that `T`, `const T`, and `volatile T` become /// \details removes references from the provided type such that `T`, `const T`, and `volatile T` become
/// `const volatile T` /// `const volatile T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_cv : type_transform<const volatile T> {}; template<typename T> struct add_cv : type_identity<const volatile T> {};
/// ///
/// \brief shorthand for `typename add_cv<T>::type` /// \brief shorthand for `typename add_cv<T>::type`
template<typename T> using add_cv_t = typename add_cv<T>::type; template<typename T> using add_cv_t = typename add_cv<T>::type;
// specialization for const types // specialization for const types
template<typename T> struct add_cv<const T> : type_transform<const volatile T> {}; template<typename T> struct add_cv<const T> : type_identity<const volatile T> {};
// specialization for volatile types // specialization for volatile types
template<typename T> struct add_cv<volatile T> : type_transform<const volatile T> {}; template<typename T> struct add_cv<volatile T> : type_identity<const volatile T> {};
// specialization for const volatile types // specialization for const volatile types
template<typename T> struct add_cv<const volatile T> : type_transform<const volatile T> {}; template<typename T> struct add_cv<const volatile T> : type_identity<const volatile T> {};
@@ -299,16 +305,16 @@ template<typename T> struct add_cv<const volatile T> : type_transform<const vola
/// \details removes const and volatile from the provided type such that `const T`, `volatile T`, and /// \details removes const and volatile from the provided type such that `const T`, `volatile T`, and
/// `const volatile T` become `T` /// `const volatile T` become `T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_cv : type_transform<T> {}; template<typename T> struct remove_cv : type_identity<T> {};
// specialization for const types // specialization for const types
template<typename T> struct remove_cv<const T> : type_transform<T> {}; template<typename T> struct remove_cv<const T> : type_identity<T> {};
// specialization for volatile types // specialization for volatile types
template<typename T> struct remove_cv<volatile T> : type_transform<T> {}; template<typename T> struct remove_cv<volatile T> : type_identity<T> {};
// specialization for const volatile types // specialization for const volatile types
template<typename T> struct remove_cv<const volatile T> : type_transform<T> {}; template<typename T> struct remove_cv<const volatile T> : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_cv<T>::type` /// \brief shorthand for `typename remove_cv<T>::type`
@@ -321,7 +327,7 @@ template<typename T> using remove_cv_t = typename remove_cv<T>::type;
/// ///
/// \details adds references and const volatile qualifiers to the provided type. /// \details adds references and const volatile qualifiers to the provided type.
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_cvr : type_transform<add_reference_t<add_cv_t<T>>> {}; template<typename T> struct add_cvr : type_identity<add_reference_t<add_cv_t<T>>> {};
/// ///
/// \brief shorthand for `typename add_cvr<T>::type` /// \brief shorthand for `typename add_cvr<T>::type`
@@ -334,7 +340,7 @@ template<typename T> using add_cvr_t = typename add_cvr<T>::type;
/// ///
/// \details removes const and volatile from the provided type such that /// \details removes const and volatile from the provided type such that
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_cvr : type_transform<remove_cv_t<remove_reference_t<T>>> {}; template<typename T> struct remove_cvr : type_identity<remove_cv_t<remove_reference_t<T>>> {};
/// ///

View File

@@ -34,6 +34,8 @@
/// ///
/// \page fennec_lang_types Types /// \page fennec_lang_types Types
/// ///
/// \code #include <fennec/lang/types.h> \endcode
///
/// \brief This header contains definitions for the built-in types of the C++ language. /// \brief This header contains definitions for the built-in types of the C++ language.
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_types"> /// <table width="100%" class="fieldtable" id="table_fennec_lang_types">
@@ -153,17 +155,6 @@
/// \copybrief fennec::int64_t /// \copybrief fennec::int64_t
/// ///
/// ///
///// <tr><td width="50%" style="vertical-align: top"> <br>
///// <tt>\ref fennec::float32_t "float32_t"</tt>
///// <td width="50%" style="vertical-align: top">
///// \copybrief fennec::float32_t
/////
///// <tr><td width="50%" style="vertical-align: top"> <br>
///// <tt>\ref fennec::float64_t "float64_t"</tt>
///// <td width="50%" style="vertical-align: top">
///// \copybrief fennec::float64_t
///
///
/// <tr><th colspan=2 style="text-align: center;">Special Types /// <tr><th colspan=2 style="text-align: center;">Special Types
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// <tt>\ref fennec::nullptr_t "nullptr_t"</tt> /// <tt>\ref fennec::nullptr_t "nullptr_t"</tt>
@@ -210,7 +201,9 @@
/// ///
/// ///
#include <fennec/lang/detail/__int.h> #include <fennec/lang/detail/_int.h>
#include <fennec/lang/conditional_types.h>
namespace fennec namespace fennec
{ {
@@ -249,25 +242,21 @@ namespace fennec
/// \name Special Types /// \name Special Types
/// @{ /// @{
using nullptr_t = decltype(nullptr); ///< \brief Null Pointer Type using nullptr_t = decltype(nullptr); ///< \brief Null Pointer Type
using intptr_t = intptr_t; ///< \brief Signed Integer Capable of Holding a Pointer to void using intptr_t = intptr_t; ///< \brief Signed Integer Capable of Holding a Pointer to void
using uintptr_t = uintptr_t; ///< \brief Unsigned Integer Capable of Holding a Pointer to void using uintptr_t = uintptr_t; ///< \brief Unsigned Integer Capable of Holding a Pointer to void
using intmax_t = intmax_t; ///< \brief Maximum Width Signed Integer Type using intmax_t = intmax_t; ///< \brief Maximum Width Signed Integer Type
using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type
using size_t = size_t; ///< \brief Unsigned Integer Type Returned By `sizeof`, `sizeof...`, and `alignof` using size_t = size_t; ///< \brief Unsigned Integer Type Returned By `sizeof`, `sizeof...`, and `alignof`
using ptrdiff_t = ptrdiff_t; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers using ptrdiff_t = __PTRDIFF_TYPE__; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers
struct empty_t {};
class undefined_t; ///< \brief undefined class for SFINAE class undefined_t; ///< \brief undefined class for SFINAE
template<typename...> using void_t = void; ///< \brief Void type used for SFINAE template<typename...> using void_t = void; ///< \brief Void type used for SFINAE
/// @} /// @}
}
#include <fennec/lang/conditional_types.h>
namespace fennec
{
// Sized Arithmetic Types ============================================================================================== // Sized Arithmetic Types ==============================================================================================
@@ -275,15 +264,15 @@ namespace fennec
/// \name Sized Integer Types /// \name Sized Integer Types
/// @{ /// @{
using int8_t = schar_t; ///< \brief Signed 8-bit integer using int8_t = ::int8_t; ///< \brief Signed 8-bit integer
using int16_t = short_t; ///< \brief Signed 16-bit integer using int16_t = ::int16_t; ///< \brief Signed 16-bit integer
using int32_t = conditional_t<sizeof(int_t) == 4, int_t, long_t>; ///< \brief Signed 32-bit integer using int32_t = ::int32_t; ///< \brief Signed 32-bit integer
using int64_t = llong_t; ///< \brief Signed 64-bit integer using int64_t = ::int64_t; ///< \brief Signed 64-bit integer
using uint8_t = uchar_t; ///< \brief Unsigned 8-bit integer using uint8_t = ::uint8_t; ///< \brief Unsigned 8-bit integer
using uint16_t = ushort_t; ///< \brief Unsigned 16-bit integer using uint16_t = ::uint16_t; ///< \brief Unsigned 16-bit integer
using uint32_t = conditional_t<sizeof(uint_t) == 4, uint_t, ulong_t>; ///< \brief Unsigned 32-bit integer using uint32_t = ::uint32_t; ///< \brief Unsigned 32-bit integer
using uint64_t = ullong_t; ///< \brief Unsigned 64-bit integer using uint64_t = ::uint64_t; ///< \brief Unsigned 64-bit integer
/// @} /// @}

View File

@@ -0,0 +1,63 @@
// =====================================================================================================================
// 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_TYPEUUID_H
#define FENNEC_LANG_TYPEUUID_H
#include <fennec/lang/assert.h>
#include <fennec/lang/types.h>
#include <fennec/lang/type_traits.h>
#include <fennec/lang/detail/_typeuuid.h>
namespace fennec
{
constexpr uint64_t nullid = 0;
template<typename TypeT, typename RootT = void>
FENNEC_NO_INLINE uint64_t typeuuid() {
assertf(not is_constant_evaluated(), "Type UUIDs Cannot Be Obtained at Compile Time");
static bool init = false;
static uint64_t id = nullid;
if (init) return id;
init = true;
return id = detail::_typeuuid<RootT>();
}
template<typename RootT = void>
struct typed {
public:
const uint64_t type;
template<typename TypeT>
bool is_type() const {
return type == typeuuid<TypeT, RootT>();
}
template<typename TypeT>
typed(TypeT*)
: type(typeuuid<TypeT, RootT>()) {
}
};
}
#endif // FENNEC_LANG_TYPEUUID_H

View File

@@ -60,6 +60,7 @@
/// ///
#include <fennec/lang/type_transforms.h> #include <fennec/lang/type_transforms.h>
#include <fennec/lang/type_traits.h>
namespace fennec namespace fennec
{ {
@@ -71,10 +72,14 @@ namespace fennec
/// \tparam T base type of the object /// \tparam T base type of the object
/// \param x reference to the object /// \param x reference to the object
/// \returns /// \returns
template<typename T> constexpr T&& forward(remove_reference_t<T>& x) noexcept { return static_cast<T&&>(x); } template<typename T> constexpr T&& forward(remove_reference_t<T>& x) noexcept {
return static_cast<T&&>(x);
}
// specialization for T&& // specialization for T&&
template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept { return static_cast<T&&>(x); } template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept {
return static_cast<T&&>(x);
}
/// ///
@@ -84,7 +89,9 @@ template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept {
/// \tparam T base type of the object /// \tparam T base type of the object
/// \param x object to be moved /// \param x object to be moved
/// \returns `static_cast<remove_reference_t<T>&&>(x)` /// \returns `static_cast<remove_reference_t<T>&&>(x)`
template<typename T> constexpr remove_reference_t<T>&& move(T&& x) noexcept { return static_cast<remove_reference_t<T>&&>(x); } template<typename T> constexpr remove_reference_t<T>&& move(T&& x) noexcept {
return static_cast<remove_reference_t<T>&&>(x);
}
/// ///
@@ -94,7 +101,32 @@ template<typename T> constexpr remove_reference_t<T>&& move(T&& x) noexcept { re
/// \tparam T base type of the object /// \tparam T base type of the object
/// \param x object to be copied /// \param x object to be copied
/// \returns const r-value /// \returns const r-value
template<typename T> constexpr const remove_reference_t<T>& copy(T&& x) noexcept { return x; } template<typename T> constexpr const remove_reference_t<T>& copy(T&& x) noexcept {
return x;
}
///
/// \brief Swaps x and y
/// \tparam T the fundamental type
/// \param x first value
/// \param y second value
template<typename T> requires is_fundamental_v<T>
constexpr void swap(T& x, T& y) noexcept {
T a = x;
x = y;
y = a;
}
///
/// \brief Swaps x and y without invoking destructor
/// \tparam T the type
/// \param x first value
/// \param y second value
template<typename T> constexpr void swap(T& x, T& y) noexcept {
T a = fennec::move(x);
x = fennec::move(y);
y = fennec::move(a);
}
} }

View File

@@ -0,0 +1,24 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_DETAIL_CTYPE_H
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
#include <stdio.h>
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H

View File

@@ -0,0 +1,319 @@
// =====================================================================================================================
// 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_LANGPROC_IO_FILE_H
#define FENNEC_LANGPROC_IO_FILE_H
#include <fennec/langproc/filesystem/path.h>
#include <fennec/langproc/strings/cstring.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/langproc/strings/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">`read`
/// <td style="vertical-align: top">Opens file as read-only, reading from start
///
/// <tr><td style="vertical-align: top">`write`
/// <td style="vertical-align: top">Opens file as write-only, writing to end
///
/// <tr><td style="vertical-align: top">`read | write`
/// <td style="vertical-align: top">Opens file as read-write, reading from start
///
/// <tr><td style="vertical-align: top">`write | trunc`
/// <td style="vertical-align: top">Opens file as write-only, destroying contents
///
/// <tr><td style="vertical-align: top">`read | write | trunc`
/// <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().
class file
{
public:
/// \brief value of an invalid position
static constexpr size_t npos = -1;
///
/// \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);
}
///
/// \returns the c stdout
static file& cout();
///
/// \returns the c stdin
static file& cin();
///
/// \returns the c stderr
static file& cerr();
///
/// \brief default constructor, initializes an empty stream
file();
///
/// \brief default destructor, cleans up an open stream
~file();
///
/// \brief move constructor
/// \param file the stream to take ownership of
file(file&& file) noexcept;
file& operator=(file&& file) noexcept;
// don't allow copying streams
file(const file&) = delete;
// 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 true if there is a valid, open stream.
bool is_open() const {
return _handle != nullptr;
}
// File Access =========================================================================================================
///
/// \brief open a file
/// \param path the path to the file
/// \param mode the mode flags to open the file with
/// \returns false on success, true on error
bool open(const cstring& 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 false on success, true 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 false on success, true on error
bool open(const path& path, uint8_t mode);
///
/// \brief close a stream
/// \returns false on success, true on error
bool close();
///
/// \brief commit the streams buffer to the file
/// \returns false on success, true on error
bool commit();
// File Operations =====================================================================================================
///
/// \brief closes the stream and erases the file
/// \returns false on success, true on error
bool erase();
///
/// \brief rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details 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);
///
/// \brief rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details 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 rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details 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);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const cstring& path);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const string& path);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const path& path);
// File Positioning ====================================================================================================
size_t get_pos() const;
bool set_pos(size_t i);
bool rewind();
bool eof() const;
// Binary Read Operations ==============================================================================================
char getc();
wchar_t getwc();
size_t read(void* data, size_t size, size_t n);
template<typename T>
size_t read(T* data, size_t n) {
return read(static_cast<void*>(data), sizeof(T), n);
}
template<typename T, size_t n>
size_t read(T (&data)[n]) {
return read(static_cast<void*>(data), sizeof(T), n);
}
string getline();
wstring getwline();
// Binary Write Operations =============================================================================================
bool putc(char c);
bool putwc(wchar_t c);
size_t write(const void* data, size_t size, size_t n);
template<size_t n>
size_t write(const char (&data)[n]) {
return write(data, sizeof(char), n - 1);
}
template<size_t n>
size_t write(const wchar_t (&data)[n]) {
return write(data, sizeof(wchar_t), n - 1);
}
template<typename T>
size_t write(const T* data, size_t n) {
return write(static_cast<const void*>(data), sizeof(T), n);
}
template<typename T, size_t n>
size_t write(const T (&data)[n]) {
return write(static_cast<const void*>(data), sizeof(T), n);
}
// Printing Operations =================================================================================================
// Error Handling ======================================================================================================
const char* get_error() const { return _error; }
void clear_error() { _error = nullptr; }
private:
FILE* _handle;
path _path;
uint8_t _mode;
char* _error;
};
}
#endif // FENNEC_LANGPROC_IO_FILE_H

View File

@@ -0,0 +1,247 @@
// =====================================================================================================================
// 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_LANGPROC_IO_PATH_H
#define FENNEC_LANGPROC_IO_PATH_H
#include <fennec/langproc/strings/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
{
public:
// Static Functions ====================================================================================================
/// \brief Get the current working directory
/// \returns a path containing the absolute path to the working directory
static path 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 current(const path& path);
// Constructors ========================================================================================================
///
/// \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)) { }
// Assignment Operators ================================================================================================
///
/// \brief C-String Assignment Operator
/// \param str the cstring to assign
/// \returns a reference to `this` after assigning `p`
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 `this` after assigning `p`
path& operator=(const cstring& p) {
_str = p;
return *this;
}
///
/// \brief String Assignment Operator
/// \param p the cstring to assign
/// \returns a reference to `this` after assigning `p`
path& operator=(const string& p) {
_str = p;
return *this;
}
///
/// \brief Path Copy Assignment Operator
/// \param p the path to copy
/// \returns a reference to `this` after copying `p`
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 `this` after taking ownership of `p`
path& operator=(path&& p) noexcept {
_str = move(p._str);
return *this;
}
// Append Operators ====================================================================================================
///
/// \brief
/// \param str
/// \return
path operator/(const cstring& str) const {
return path(_str + '/' + str);
}
path operator/(const string& str) const {
return path(_str + '/' + str);
}
path operator/(const path& p) const {
return path(_str + '/' + p._str);
}
bool operator==(const path& p) const {
return _str == p._str;
}
const string& str() const { return _str; }
const char* cstr() const { return _str.cstr(); }
bool 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
}
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() - 1;
start = _str[start] == '/' ? start - 1 : start;
return path(_str.substring(0, _str.rfind('/', start)));
#endif
}
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 = current();
}
while (not parse.empty()) {
// Handle dots
while (not parse.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.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;
}
private:
string _str;
};
}
#endif // FENNEC_LANGPROC_IO_PATH_H

View File

@@ -0,0 +1,351 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_CSTRING_H
#define FENNEC_LANGPROC_STRINGS_CSTRING_H
#include <fennec/langproc/strings/detail/_ctype.h>
#include <fennec/memory/detail/_string.h>
#include <fennec/lang/assert.h>
#include <fennec/math/common.h>
#include <fennec/memory/bytes.h>
namespace fennec
{
// TODO: Document
using ::isalnum;
using ::isalpha;
using ::islower;
using ::isupper;
using ::isdigit;
using ::isxdigit;
using ::iscntrl;
using ::isgraph;
using ::isspace;
using ::isblank;
using ::isprint;
using ::ispunct;
using ::tolower;
using ::toupper;
///
/// \brief This struct wraps c-style strings
///
/// \details Requires that the string is null-terminated.
/// Prevents const qualified memory blocks from being manipulated.
/// This struct should be used when fennec::string would make unnecessary dynamic buffers.
struct cstring
{
public:
static constexpr size_t npos = -1;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, initializes with nullptr
constexpr cstring()
: _str(nullptr), _size(0), _const(true) {
}
///
/// \brief Default Constructor, initializes with nullptr
constexpr cstring(nullptr_t)
: _str(nullptr), _size(0), _const(true) {
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \param n the number of characters in the buffer plus the null terminator
constexpr cstring(char* str, size_t n)
: _str(str)
, _size(n - 1)
, _const(false) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer plus the null terminator
template<size_t n>
constexpr cstring(char(&str)[n])
: _str(str)
, _size(n - 1)
, _const(false) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Const Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \param n the number of characters in the buffer plus the null terminator
constexpr cstring(const char* str, size_t n)
: _cstr(str)
, _size(n - 1)
, _const(true) {
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer plus the null terminator
template<size_t n>
constexpr cstring(const char(&str)[n])
: _cstr(str)
, _size(n - 1)
, _const(true) {
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Move Constructor
/// \param str object to move
constexpr cstring(cstring&& str) noexcept
: _cstr(str._cstr)
, _size(str._size)
, _const(str._const) {
str._cstr = nullptr;
str._size = 0;
str._const = true;
}
// TODO: Document
constexpr cstring(const cstring&) = delete;
constexpr ~cstring() = default;
constexpr cstring& operator=(nullptr_t) {
_str = nullptr, _size = 0, _const = true;
return *this;
}
// TODO: Document
template<size_t n>
constexpr cstring& operator=(char(&str)[n]) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
_str = str, _size = n - 1, _const = false;
return *this;
}
// TODO: Document
template<size_t n>
constexpr cstring& operator=(const char(&str)[n]) {
assert(str[n - 1] == '\0', "Invalid NTBS.");
_cstr = str; _size = n - 1; _const = true;
return *this;
}
// TODO: Document
constexpr cstring& operator=(cstring&& str) noexcept {
_cstr = str._cstr; str._cstr = nullptr;
_size = str._size; str._size = 0;
_const = str._const; str._const = true;
return *this;
}
// Properties ==========================================================================================================
///
/// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'`
constexpr size_t size() const { return _size; }
///
/// \returns the size of the string including its null terminator, i.e. `(*str)[capacity() - 1] == '\0'`
constexpr size_t capacity() const { return _size + 1; }
constexpr bool empty() const {
return _cstr == nullptr || _size == 0;
}
// Access ==============================================================================================================
///
/// \brief Array Access Operator
/// \param i the index to access
/// \returns a reference to the character
constexpr char& operator[](size_t i) {
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
assertd(i < size(), "Array Out of Bounds");
return _str[i];
}
///
/// \brief Const-Array Access Operator
/// \param i the index to access
/// \returns a copy of the character
constexpr char operator[](size_t i) const {
assertd(i < size(), "Array Out of Bounds");
return _cstr[i];
}
///
/// \brief Data Access
/// \returns A const qualified pointer to the underlying allocation
constexpr char* data() {
return _str;
}
///
/// \brief Data Access
/// \returns A const qualified pointer to the underlying allocation
constexpr const char* data() const {
return _cstr;
}
///
/// \brief Implicit Dereference Cast
constexpr operator const char*() const {
return _cstr;
}
// Examination =========================================================================================================
///
/// \returns The length of the string to the first null-terminator
constexpr size_t length() const {
return find('\0');
}
///
/// \brief String Comparison
/// \param str the string to compare against
/// \param i the index to start at
/// \param n the number of characters to compare
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const cstring& str, size_t i = 0, size_t n = npos) const {
if (i >= _size) {
return -1;
}
n = min(n, max(_size, str._size) + 1);
return ::strncmp(_cstr + i, str, n);
}
///
/// \brief String Equality
/// \param str the string to compare against
/// \returns True if all characters are equal, false otherwise
template<size_t n>
constexpr bool operator==(const char (&str)[n]) const {
return compare(cstring(str)) == 0;
}
///
/// \brief String Equality
/// \param str the string to compare against
/// \returns True if all characters are equal, false otherwise
constexpr bool operator==(const cstring& str) const {
return compare(str) == 0;
}
///
/// \brief Finds the index of the first occurrence of `c` in the string
/// \param c the character to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(char c, size_t i = 0) const {
if (i >= _size) { // bounds check
return _size;
}
const char* loc = ::strchr(_cstr + i, c); // get location using strchr
return loc ? loc - _cstr : _size; // return size if not found
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const cstring& str, size_t i = 0) const {
if (i + str._size > _size) { // bounds check
return _size;
}
const char* loc = ::strstr(_cstr + i, str); // get location using strstr
return loc ? loc - _cstr : _size; // return size if not found
}
///
/// \brief Finds the index of the last occurrence of `c` in the string.
/// \param c the string to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(char c, size_t i = npos) const {
if (_size == 0) {
return _size;
}
i = min(i, _size - 1); // clamp i to bounds
do {
if (_cstr[i] == c) return i; // loop backwards looking for c
} while (i--);
return _size; // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const cstring& str, size_t i = npos) const {
if (_size == 0) {
return _size;
}
const char first = str[0];
i = min(i, _size - str._size);
do {
if(_cstr[i] == first) {
if (compare(str, i, str._size - 1) == 0) {
return i;
}
} // loop backwards looking for str
} while (i--);
return _size; // base case
}
private:
union { // hack to allow both const qualified and non-const strings
char* _str;
const char* _cstr;
};
size_t _size;
bool _const;
};
template<>
struct hash<cstring> : hash<byte_array> {
constexpr size_t operator()(const cstring& str) const {
return hash<byte_array>::operator()(byte_array(str, str.size()));
}
};
}
#endif // FENNEC_LANGPROC_STRINGS_CSTRING_H

View File

@@ -0,0 +1,25 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_DETAIL_CTYPE_H
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
#include <ctype.h>
#include <wctype.h>
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H

View File

@@ -0,0 +1,24 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_DETAIL_CTYPE_H
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
#include <locale.h>
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H

View File

@@ -0,0 +1,44 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_LOCALE_H
#define FENNEC_LANGPROC_STRINGS_LOCALE_H
#include <fennec/langproc/strings/detail/_locale.h>
namespace fennec
{
enum locale : int
{
lc_all = LC_ALL
, lc_collate = LC_COLLATE
, lc_ctype = LC_CTYPE
, lc_monetary = LC_MONETARY
, lc_numeric = LC_NUMERIC
, lc_time = LC_TIME
};
using ::lconv;
using ::setlocale;
using ::localeconv;
}
#endif // FENNEC_LANGPROC_STRINGS_LOCALE_H

View File

@@ -0,0 +1,437 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_STRING_H
#define FENNEC_LANGPROC_STRINGS_STRING_H
#include <fennec/langproc/strings/detail/_ctype.h>
#include <fennec/langproc/strings/cstring.h>
#include <fennec/lang/assert.h>
#include <fennec/memory/allocator.h>
#include <fennec/memory/common.h>
#include <fennec/math/common.h>
namespace fennec
{
// Forward def
template<typename AllocT = allocator<char>> struct _string;
// Alias for default allocator
using string = _string<>;
///
/// \brief Struct for wrapping c-style strings
///
/// \details behaviour guarantees that the underlying string is null-terminated
template<typename AllocT>
struct _string
{
public:
static constexpr size_t npos = -1;
using alloc_t = allocation<char, AllocT>;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, initializes empty string
constexpr _string()
: _str() {
}
///
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...`
/// \param n the number of characters
/// \param c the character to fill with
///
/// \details adds additional character for null termination.
constexpr _string(size_t n, char c = '\0')
: _str(n + 1) {
fennec::memset(_str, c, n);
}
constexpr _string(const alloc_t& alloc)
: _str(alloc) {
}
constexpr _string(size_t n, char c, const alloc_t& alloc)
: _str(n + 1, alloc) {
fennec::memset(_str, c, n);
}
constexpr _string(const cstring& cstr)
: _str(cstr, cstr.size() + 1) {
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer including the null-terminator, if present
template<size_t n>
explicit constexpr _string(const char (&str)[n])
: _str(str[n - 1] != '\0' ? n + 1 : n) {
fennec::memcpy(_str, str, n);
if (str[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \param n the number of characters in the buffer including the null-terminator, if present
constexpr _string(const char* buf, size_t n)
: _str(buf[n - 1] != '\0' ? n + 1 : n) {
fennec::memcpy(_str, buf, n);
if (buf[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief String Copy Constructor
/// \param str the string to copy
constexpr _string(const _string& str) = default;
///
/// \brief String Move Constructor
/// \param str the string to take ownership of
constexpr _string(_string&& str) noexcept = default;
///
/// \brief Destructor, cleans up underlying allocation
constexpr ~_string() = default;
// Assignment ==========================================================================================================
constexpr _string& operator=(const cstring& cstr) {
_str.callocate(cstr.capacity());
fennec::memcpy(_str, cstr, cstr.capacity());
return *this;
}
constexpr _string& operator=(const _string& str) = default;
constexpr _string& operator=(_string&& str) noexcept = default;
// Properties ==========================================================================================================
///
/// \returns The size of the string excluding null terminator
constexpr size_t size() const {
return _str.capacity() > 0 ? _str.capacity() - 1 : 0;
}
///
/// \returns The size of the string including null terminator
constexpr size_t capacity() const {
return _str.capacity();
}
constexpr bool empty() const {
return size() == 0;
}
// Access ==============================================================================================================
///
/// \brief Array Access Operator
/// \param i the index to access
/// \returns a reference to the character
constexpr char& operator[](size_t i) {
return _str[i];
}
///
/// \brief Const-Array Access Operator
/// \param i the index to access
/// \returns a copy of the character
constexpr const char& operator[](size_t i) const {
return _str[i];
}
constexpr char* data() {
return _str;
}
constexpr const char* data() const {
return _str;
}
constexpr const char* cstr() const {
return _str;
}
// Examination =========================================================================================================
///
/// \returns The length of the string to the first null-terminator
constexpr size_t length() const {
return find('\0');
}
///
/// \brief String Comparison
/// \param ostr the string to compare against
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const cstring& str, size_t i = 0, size_t n = npos) const {
if (i >= size()) { // bounds check
return -1;
}
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
return ::strncmp(_str + i, str, n);
}
///
/// \brief String Comparison
/// \param ostr the string to compare against
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const string& str, size_t i = 0, size_t n = npos) const {
if (i >= size()) { // bounds check
return -1;
}
n = min(n, max(size(), str.size()) + 1);
return ::strncmp(_str + i, str.data(), n);
}
constexpr bool operator==(const _string& str) const {
return compare(str) == 0;
}
///
/// \brief Finds the index of the first occurrence of `c` in the string
/// \param c the character to find
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(char c, size_t i = 0) const {
if (i >= size()) { // bounds check
return size();
}
const char* loc = ::strchr(_str + i, c); // get location using strchr
return loc ? loc - _str : size(); // return size if not found
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const string& str, size_t i = 0) const { // bounds check
if (i >= size()) { // bounds check
return size();
}
const char* loc = ::strstr(_str, str);
return loc ? loc - _str : size();
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const cstring& str, size_t i = 0) const {
if (i + str.size() > size()) { // bounds check
return size();
}
const char* loc = ::strstr(_str + i, str); // get location using strstr
return loc ? loc - _str : size(); // return size if not found
}
///
/// \brief Finds the index of the last occurrence of `c` in the string.
/// \param c the string to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(char c, size_t i = npos) const {
if (size() == 0) {
return size();
}
i = min(i, size() - 1); // clamp i to bounds
do {
if (_str[i] == c) { // loop backwards looking for c
return i;
}
} while (i--);
return size(); // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const cstring& str, size_t i = npos) const {
if (size() == 0) {
return size();
}
const char first = str[0];
i = min(i, size() - str.size());
do {
if(_str[i] == first) {
if (compare(str, i) == 0) {
return i;
}
}
} while (i--);
return size(); // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const string& str, size_t i = npos) const {
if (size() == 0) {
return size();
}
const char first = str[0];
i = min(i, size() - str.size());
do {
if(_str[i] == first) {
if (compare(str, i) == 0) { // loop backwards looking for str
return i;
}
}
} while (i--);
return size(); // base case
}
///
/// \brief Retrieve a substring of a string
/// \param i the start index
/// \param n the number of characters
/// \return
constexpr _string substring(size_t i, size_t n = npos) const {
if (i >= size()) {
return _string("");
}
n = fennec::min(n, size() - i);
_string res;
res._str.callocate(n + 1);
fennec::memcpy(res.data(), _str + i, n);
return res;
}
// Modifiers ===========================================================================================================
constexpr void resize(size_t n) {
_str.creallocate(n + 1);
_str[size()] = '\0';
}
constexpr _string operator+(char c) const {
if (_str == nullptr) {
return _string(1, c);
}
_string res;
res._str.callocate(capacity() + 1);
fennec::memcpy(res.data(), _str, size());
res[size()] = c;
return res;
}
friend constexpr _string operator+(char c, const _string& str) {
_string res(1, c);
return res += str;
}
constexpr _string operator+(const cstring& cstr) const {
if (_str == nullptr) {
return _string(cstr);
}
_string res;
res._str.callocate(size() + cstr.size() + 1);
fennec::memcpy(res.data(), _str, size());
fennec::memcpy(res.data() + size(), cstr, cstr.size());
return res;
}
constexpr _string operator+(const _string& str) const {
if (_str == nullptr) {
return _string(str);
}
if (str.data() == nullptr) {
return _string(*this);
}
_string res;
res._str.callocate(size() + str.size() + 1);
fennec::memcpy(res.data(), _str, size());
fennec::memcpy(res.data() + size(), str.data(), str.size());
return res;
}
constexpr _string& operator+=(char c) {
if (_str == nullptr) {
_str.callocate(2);
_str[0] = c;
return *this;
}
_str.creallocate(capacity() + 1);
_str[size() - 1] = c;
return *this;
}
constexpr _string& operator+=(const cstring& cstr) {
if (_str == nullptr) {
return *this = cstr;
}
size_t middle = size();
_str.creallocate(middle + cstr.size() + 1);
fennec::memcpy(_str + middle, cstr, cstr.size());
return *this;
}
constexpr _string& operator+=(const _string& str) {
if (_str == nullptr) {
return *this = str;
}
if (str.data() == nullptr) {
return *this;
}
size_t middle = size();
_str.creallocate(middle + str.size() + 1);
fennec::memcpy(_str + middle, str.data(), str.size());
return *this;
}
private:
alloc_t _str;
};
template<>
struct hash<string> : hash<byte_array> {
constexpr size_t operator()(const string& str) const {
return hash<byte_array>::operator()(byte_array(str.data(), str.size()));
}
};
}
#endif // FENNEC_LANGPROC_STRINGS_STRING_H

View File

@@ -0,0 +1,353 @@
// =====================================================================================================================
// 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_LANGPROC_STRINGS_wcstring_H
#define FENNEC_LANGPROC_STRINGS_wcstring_H
#include <fennec/langproc/strings/detail/_ctype.h>
#include <fennec/memory/detail/_string.h>
#include <fennec/lang/assert.h>
#include <fennec/math/common.h>
namespace fennec
{
// TODO: Document
using ::iswalnum;
using ::iswalpha;
using ::iswlower;
using ::iswupper;
using ::iswdigit;
using ::iswxdigit;
using ::iswcntrl;
using ::iswgraph;
using ::iswspace;
using ::iswblank;
using ::iswprint;
using ::iswpunct;
using ::towlower;
using ::towupper;
using ::towctrans;
using ::wctrans;
///
/// \brief This struct wraps c-style strings
///
/// \details Requires that the string is null-terminated.
/// Prevents const qualified memory blocks from being manipulated.
/// This struct should be used when fennec::string would make unnecessary dynamic buffers.
struct wcstring
{
public:
static constexpr size_t npos = -1;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, initializes with nullptr
constexpr wcstring()
: _str(nullptr), _size(0), _const(true) {
}
///
/// \brief Default Constructor, initializes with nullptr
constexpr wcstring(nullptr_t)
: _str(nullptr), _size(0), _const(true) {
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \param n the number of characters in the buffer plus the null terminator
constexpr wcstring(wchar_t* str, size_t n)
: _str(str)
, _size(n - 1)
, _const(false) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer plus the null terminator
template<size_t n>
constexpr wcstring(wchar_t (&str)[n])
: _str(str)
, _size(n - 1)
, _const(false) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Const Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \param n the number of characters in the buffer plus the null terminator
constexpr wcstring(const wchar_t* str, size_t n)
: _cstr(str)
, _size(n - 1)
, _const(true) {
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer plus the null terminator
template<size_t n>
constexpr wcstring(const wchar_t (&str)[n])
: _cstr(str)
, _size(n - 1)
, _const(true) {
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
}
///
/// \brief Move Constructor
/// \param str object to move
constexpr wcstring(wcstring&& str) noexcept
: _cstr(str._cstr)
, _size(str._size)
, _const(str._const) {
str._cstr = nullptr;
str._size = 0;
str._const = true;
}
// TODO: Document
constexpr wcstring(const wcstring&) = delete;
constexpr ~wcstring() = default;
constexpr wcstring& operator=(nullptr_t) {
_str = nullptr, _size = 0, _const = true;
return *this;
}
// TODO: Document
template<size_t n>
constexpr wcstring& operator=(wchar_t(&str)[n]) {
assert(_str[n - 1] == '\0', "Invalid NTBS.");
_str = str, _size = n - 1, _const = false;
return *this;
}
// TODO: Document
template<size_t n>
constexpr wcstring& operator=(const wchar_t(&str)[n]) {
assert(str[n - 1] == '\0', "Invalid NTBS.");
_cstr = str; _size = n - 1; _const = true;
return *this;
}
// TODO: Document
constexpr wcstring& operator=(wcstring&& str) noexcept {
_cstr = str._cstr; str._cstr = nullptr;
_size = str._size; str._size = 0;
_const = str._const; str._const = true;
return *this;
}
// Properties ==========================================================================================================
///
/// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'`
constexpr size_t size() const { return _size; }
///
/// \returns the size of the string including its null terminator, i.e. `(*str)[capacity() - 1] == '\0'`
constexpr size_t capacity() const { return _size + 1; }
constexpr bool empty() const {
return _cstr == nullptr || _size == 0;
}
// Access ==============================================================================================================
///
/// \brief Array Access Operator
/// \param i the index to access
/// \returns a reference to the character
constexpr wchar_t& operator[](size_t i) {
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
assertd(i < size(), "Array Out of Bounds");
return _str[i];
}
///
/// \brief Const-Array Access Operator
/// \param i the index to access
/// \returns a copy of the character
constexpr const wchar_t& operator[](size_t i) const {
assertd(i < size(), "Array Out of Bounds");
return _cstr[i];
}
///
/// \brief Data Access
/// \returns A const qualified pointer to the underlying allocation
constexpr wchar_t* data() {
return _str;
}
///
/// \brief Data Access
/// \returns A const qualified pointer to the underlying allocation
constexpr const wchar_t* data() const {
return _cstr;
}
///
/// \brief Implicit Dereference Cast
constexpr operator const wchar_t*() const {
return _cstr;
}
// Examination =========================================================================================================
///
/// \returns The length of the string to the first null-terminator
constexpr size_t length() const {
return find('\0');
}
///
/// \brief String Comparison
/// \param str the string to compare against
/// \param i the index to start at
/// \param n the number of characters to compare
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const wcstring& str, size_t i = 0, size_t n = npos) const {
if (i >= _size) {
return -1;
}
n = min(n, max(_size, str._size) + 1);
return ::wcsncmp(_cstr + i, str, n);
}
///
/// \brief String Equality
/// \param str the string to compare against
/// \returns True if all characters are equal, false otherwise
template<size_t n>
constexpr bool operator==(const wchar_t (&str)[n]) const {
return compare(wcstring(str)) == 0;
}
///
/// \brief String Equality
/// \param str the string to compare against
/// \returns True if all characters are equal, false otherwise
constexpr bool operator==(const wcstring& str) const {
return compare(str) == 0;
}
///
/// \brief Finds the index of the first occurrence of `c` in the string
/// \param c the character to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(wchar_t c, size_t i = 0) const {
if (i >= _size) { // bounds check
return _size;
}
const wchar_t* loc = ::wcschr(_cstr + i, c); // get location using strchr
return loc ? loc - _cstr : _size; // return size if not found
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const wcstring& str, size_t i = 0) const {
if (i + str._size > _size) { // bounds check
return _size;
}
const wchar_t* loc = ::wcsstr(_cstr + i, str); // get location using strstr
return loc ? loc - _cstr : _size; // return size if not found
}
///
/// \brief Finds the index of the last occurrence of `c` in the string.
/// \param c the string to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(char c, size_t i = npos) const {
if (_size == 0) {
return _size;
}
i = min(i, _size - 1); // clamp i to bounds
do {
if (_cstr[i] == c) return i; // loop backwards looking for c
} while (i--);
return _size; // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const wcstring& str, size_t i = npos) const {
if (_size == 0) {
return _size;
}
const char first = str[0];
i = min(i, _size - str._size);
do {
if(_cstr[i] == first) {
if (compare(str, i, str._size - 1) == 0) {
return i;
}
} // loop backwards looking for str
} while (i--);
return _size; // base case
}
private:
union { // hack to allow both const qualified and non-const strings
wchar_t* _str;
const wchar_t* _cstr;
};
size_t _size;
bool _const;
};
template<>
struct hash<wcstring> : hash<byte_array> {
constexpr size_t operator()(const wcstring& str) const {
return hash<byte_array>::operator()(byte_array(str, str.size()));
}
};
}
#endif // FENNEC_LANGPROC_STRINGS_wcstring_H

View File

@@ -0,0 +1,437 @@
// =====================================================================================================================
// 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_LANGPROC_wstringS_WSTRING_H
#define FENNEC_LANGPROC_wstringS_WSTRING_H
#include <fennec/langproc/strings/detail/_ctype.h>
#include <fennec/langproc/strings/wcstring.h>
#include <fennec/lang/assert.h>
#include <fennec/memory/allocator.h>
#include <fennec/memory/common.h>
#include <fennec/math/common.h>
namespace fennec
{
// Forward def
template<typename AllocT = allocator<wchar_t>> struct _wstring;
// Alias for default allocator
using wstring = _wstring<>;
///
/// \brief Struct for wrapping c-style strings
///
/// \details behaviour guarantees that the underlying string is null-terminated
template<typename AllocT>
struct _wstring
{
public:
static constexpr size_t npos = -1;
using alloc_t = allocation<wchar_t, AllocT>;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, initializes empty string
constexpr _wstring()
: _str() {
}
///
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...`
/// \param n the number of wchar_tacters
/// \param c the wchar_tacter to fill with
///
/// \details adds additional wchar_tacter for null termination.
constexpr _wstring(size_t n, wchar_t c = '\0')
: _str(n + 1) {
fennec::wmemset(_str, c, n);
}
constexpr _wstring(const alloc_t& alloc)
: _str(alloc) {
}
constexpr _wstring(size_t n, wchar_t c, const alloc_t& alloc)
: _str(n + 1, alloc) {
fennec::wmemset(_str, c, n);
}
constexpr _wstring(const wcstring& cstr)
: _str(cstr, cstr.size() + 1) {
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \tparam n the number of wchar_tacters in the buffer including the null-terminator, if present
template<size_t n>
explicit constexpr _wstring(const wchar_t (&str)[n])
: _str(str[n - 1] != '\0' ? n + 1 : n) {
fennec::wmemcpy(_str, str, n);
if (str[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \param n the number of wchar_tacters in the buffer including the null-terminator, if present
constexpr _wstring(const wchar_t* buf, size_t n)
: _str(buf[n - 1] != '\0' ? n + 1 : n) {
fennec::wmemcpy(_str, buf, n);
if (buf[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief String Copy Constructor
/// \param str the string to copy
constexpr _wstring(const _wstring& str) = default;
///
/// \brief String Move Constructor
/// \param str the string to take ownership of
constexpr _wstring(_wstring&& str) noexcept = default;
///
/// \brief Destructor, cleans up underlying allocation
constexpr ~_wstring() = default;
// Assignment ==========================================================================================================
constexpr _wstring& operator=(const wcstring& cstr) {
_str.callocate(cstr.capacity());
fennec::wmemcpy(_str, cstr, cstr.capacity());
return *this;
}
constexpr _wstring& operator=(const _wstring& str) = default;
constexpr _wstring& operator=(_wstring&& str) noexcept = default;
// Properties ==========================================================================================================
///
/// \returns The size of the string excluding null terminator
constexpr size_t size() const {
return _str.capacity() > 0 ? _str.capacity() - 1 : 0;
}
///
/// \returns The size of the string including null terminator
constexpr size_t capacity() const {
return _str.capacity();
}
constexpr bool empty() const {
return size() == 0;
}
// Access ==============================================================================================================
///
/// \brief Array Access Operator
/// \param i the index to access
/// \returns a reference to the wchar_tacter
constexpr wchar_t& operator[](size_t i) {
return _str[i];
}
///
/// \brief Const-Array Access Operator
/// \param i the index to access
/// \returns a copy of the wchar_tacter
constexpr const wchar_t& operator[](size_t i) const {
return _str[i];
}
constexpr wchar_t* data() {
return _str;
}
constexpr const wchar_t* data() const {
return _str;
}
constexpr const wchar_t* cstr() const {
return _str;
}
// Examination =========================================================================================================
///
/// \returns The length of the string to the first null-terminator
constexpr size_t length() const {
return find('\0');
}
///
/// \brief String Comparison
/// \param ostr the string to compare against
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const wcstring& str, size_t i = 0, size_t n = npos) const {
if (i >= size()) { // bounds check
return -1;
}
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
return ::wcsncmp(_str + i, str, n);
}
///
/// \brief String Comparison
/// \param ostr the string to compare against
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
/// current locale, otherwise a positive value.
constexpr int compare(const _wstring& str, size_t i = 0, size_t n = npos) const {
if (i >= size()) { // bounds check
return -1;
}
n = min(n, max(size(), str.size()) + 1);
return ::wcsncmp(_str + i, str.data(), n);
}
constexpr bool operator==(const _wstring& str) const {
return compare(str) == 0;
}
///
/// \brief Finds the index of the first occurrence of `c` in the string
/// \param c the wchar_tacter to find
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(wchar_t c, size_t i = 0) const {
if (i >= size()) { // bounds check
return size();
}
const wchar_t* loc = ::wcschr(_str + i, c); // get location using strchr
return loc ? loc - _str : size(); // return size if not found
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const _wstring& str, size_t i = 0) const { // bounds check
if (i >= size()) { // bounds check
return size();
}
const wchar_t* loc = ::wcsstr(_str, str);
return loc ? loc - _str : size();
}
///
/// \brief Finds the index of the first occurrence of `str` in the string.
/// \param str the string to find
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(const wcstring& str, size_t i = 0) const {
if (i + str.size() > size()) { // bounds check
return size();
}
const wchar_t* loc = ::wcsstr(_str + i, str); // get location using strstr
return loc ? loc - _str : size(); // return size if not found
}
///
/// \brief Finds the index of the last occurrence of `c` in the string.
/// \param c the string to find
/// \param i the index to start at
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(wchar_t c, size_t i = npos) const {
if (size() == 0) {
return size();
}
i = min(i, size() - 1); // clamp i to bounds
do {
if (_str[i] == c) { // loop backwards looking for c
return i;
}
} while (i--);
return size(); // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const wcstring& str, size_t i = npos) const {
if (size() == 0) {
return size();
}
const wchar_t first = str[0];
i = min(i, size() - str.size());
do {
if(_str[i] == first) {
if (compare(str, i) == 0) {
return i;
}
}
} while (i--);
return size(); // base case
}
///
/// \brief Finds the index of the last occurrence of `str` in the string.
/// \param str the string to find
/// \param i the index to start at
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
constexpr size_t rfind(const string& str, size_t i = npos) const {
if (size() == 0) {
return size();
}
const wchar_t first = str[0];
i = min(i, size() - str.size());
do {
if(_str[i] == first) {
if (compare(str, i) == 0) { // loop backwards looking for str
return i;
}
}
} while (i--);
return size(); // base case
}
///
/// \brief Retrieve a substring of a string
/// \param i the start index
/// \param n the number of wchar_tacters
/// \return
constexpr _wstring substring(size_t i, size_t n = npos) const {
if (i >= size()) {
return _wstring("");
}
n = fennec::min(n, size() - i);
_wstring res;
res._str.callocate(n + 1);
fennec::wmemcpy(res.data(), _str + i, n);
return res;
}
// Modifiers ===========================================================================================================
constexpr void resize(size_t n) {
_str.creallocate(n + 1);
_str[size()] = '\0';
}
constexpr _wstring operator+(wchar_t c) const {
if (_str == nullptr) {
return _wstring(1, c);
}
_wstring res;
res._str.callocate(capacity() + 1);
fennec::wmemcpy(res.data(), _str, size());
res[size()] = c;
return res;
}
friend constexpr _wstring operator+(wchar_t c, const _wstring& str) {
_wstring res(1, c);
return res += str;
}
constexpr _wstring operator+(const wcstring& cstr) const {
if (_str == nullptr) {
return _wstring(cstr);
}
_wstring res;
res._str.callocate(size() + cstr.size() + 1);
fennec::wmemcpy(res.data(), _str, size());
fennec::wmemcpy(res.data() + size(), cstr, cstr.size());
return res;
}
constexpr _wstring operator+(const _wstring& str) const {
if (_str == nullptr) {
return _wstring(str);
}
if (str.data() == nullptr) {
return _wstring(*this);
}
_wstring res;
res._str.callocate(size() + str.size() + 1);
fennec::wmemcpy(res.data(), _str, size());
fennec::wmemcpy(res.data() + size(), str.data(), str.size());
return res;
}
constexpr _wstring& operator+=(wchar_t c) {
if (_str == nullptr) {
_str.callocate(2);
_str[0] = c;
return *this;
}
_str.creallocate(capacity() + 1);
_str[size() - 1] = c;
return *this;
}
constexpr _wstring& operator+=(const wcstring& cstr) {
if (_str == nullptr) {
return *this = cstr;
}
size_t middle = size();
_str.creallocate(middle + cstr.size() + 1);
fennec::wmemcpy(_str + middle, cstr, cstr.size());
return *this;
}
constexpr _wstring& operator+=(const _wstring& str) {
if (_str == nullptr) {
return *this = str;
}
if (str.data() == nullptr) {
return *this;
}
size_t middle = size();
_str.creallocate(middle + str.size() + 1);
fennec::wmemcpy(_str + middle, str.data(), str.size());
return *this;
}
private:
alloc_t _str;
};
template<>
struct hash<wstring> : hash<byte_array> {
constexpr size_t operator()(const string& str) const {
return hash<byte_array>::operator()(byte_array(str.data(), str.size()));
}
};
}
#endif // FENNEC_LANGPROC_wstringS_WSTRING_H

View File

@@ -1,6 +1,6 @@
// ===================================================================================================================== // =====================================================================================================================
// fennec, a free and open source game engine // fennec, a free and open source game engine
// Copyright (C) 2025 Medusa Slockbower // Copyright © 2025 Medusa Slockbower
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@@ -269,11 +269,13 @@
/// ///
/// ///
#include <fennec/math/detail/__math.h> #include <fennec/math/detail/_math.h>
#include <fennec/lang/limits.h>
#include <fennec/math/vector.h> #include <fennec/math/vector.h>
#if _MSC_VER #if FENNEC_COMPILER_MSVC
#define isnanf(x) isnan(x) #define isnanf(x) isnan(x)
#define isinff(x) isinf(x) #define isinff(x) isinf(x)
#endif #endif
@@ -301,15 +303,17 @@ namespace fennec
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType sign(genType x) constexpr genType sign(genType x) {
{ return (x < genType(0) ? genType(-1) : genType(1)) * static_cast<genType>(x != 0); } // reduces to cmove return (x < genType(0) ? genType(-1) : genType(1)) * static_cast<genType>(x != 0); // reduces to cmove
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> sign(const vector<genType, i...>& x) constexpr vector<genType, i...> sign(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::sign(x[i]) ...); } return vector<genType, i...>(fennec::sign(x[i]) ...);
}
// Absolute Value ====================================================================================================== // Absolute Value ======================================================================================================
@@ -323,15 +327,17 @@ constexpr vector<genType, i...> sign(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType abs(genType x) constexpr genType abs(genType x) {
{ return x * fennec::sign(x); } return x * fennec::sign(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> abs(const vector<genType, i...>& x) constexpr vector<genType, i...> abs(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::abs(x[i]) ...); } return vector<genType, i...>(fennec::abs(x[i]) ...);
}
/// @} /// @}
@@ -355,15 +361,17 @@ constexpr vector<genType, i...> abs(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType floor(genType x) constexpr genType floor(genType x) {
{ return ::floor(x); } return ::floor(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> floor(const vector<genType, i...>& x) constexpr vector<genType, i...> floor(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::floor(x[i]) ...); } return vector<genType, i...>(fennec::floor(x[i]) ...);
}
@@ -378,15 +386,17 @@ constexpr vector<genType, i...> floor(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType ceil(genType x) constexpr genType ceil(genType x) {
{ return ::ceil(x); } return ::ceil(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> ceil(const vector<genType, i...>& x) constexpr vector<genType, i...> ceil(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::ceil(x[i]) ...); } return vector<genType, i...>(fennec::ceil(x[i]) ...);
}
@@ -401,15 +411,17 @@ constexpr vector<genType, i...> ceil(const vector<genType, i...>& x)
/// \f$\text{round}(x) = \text{sgn}(x) \cdot \lfloor \left| x \right| + 0.5 \rfloor\f$<br> <br> /// \f$\text{round}(x) = \text{sgn}(x) \cdot \lfloor \left| x \right| + 0.5 \rfloor\f$<br> <br>
/// ///
/// \param x input value /// \param x input value
template<typename genType> constexpr genType round(genType x) template<typename genType> constexpr genType round(genType x) {
{ return ::round(x); } return ::round(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> round(const vector<genType, i...>& x) constexpr vector<genType, i...> round(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::round(x[i]) ...); } return vector<genType, i...>(fennec::round(x[i]) ...);
}
@@ -424,15 +436,17 @@ constexpr vector<genType, i...> round(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType trunc(genType x) constexpr genType trunc(genType x) {
{ return ::trunc(x); } return ::trunc(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> trunc(const vector<genType, i...>& x) constexpr vector<genType, i...> trunc(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::trunc(x[i]) ...); } return vector<genType, i...>(fennec::trunc(x[i]) ...);
}
@@ -449,24 +463,35 @@ constexpr vector<genType, i...> trunc(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType roundEven(genType x) constexpr genType roundEven(genType x) {
{ static const genType e = numeric_limits<genType>::epsilon();
const float e = numeric_limits<genType>::epsilon(); int I = static_cast<int>(x);
float f = x - fennec::floor(x); genType i = static_cast<genType>(I);
if (fennec::abs(f - genType(0.5)) > e) genType f = x - i;
if (abs(f - genType(0.5)) > e)
return fennec::round(x); return fennec::round(x);
float i = fennec::floor(x); bool dir = (I % 2);
float r = i / 2; return dir ? i + 1 : i;
bool up = r - fennec::floor(r) > e;
return i + static_cast<genType>(up); // Older version that generates significantly more branching and asm instructions
//const genType e = numeric_limits<genType>::epsilon();
//genType f = x - fennec::floor(x);
//if (fennec::abs(f - genType(0.5)) > e)
// return fennec::round(x);
//
//genType i = fennec::floor(x);
//genType r = i / 2;
//bool up = r - fennec::floor(r) > e; // This will cause a branch in most circumstances
//return i + static_cast<genType>(up);
} }
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x) constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::roundEven(x[i]) ...); } return vector<genType, i...>(fennec::roundEven(x[i]) ...);
}
/// @} /// @}
@@ -489,16 +514,17 @@ constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType> template<typename genType>
constexpr genType fract(genType x) constexpr genType fract(genType x) {
{ return x - ::floor(x); } return x - ::floor(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> fract(const vector<genType, i...>& x) constexpr vector<genType, i...> fract(const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::fract(x[i]) ...); } return vector<genType, i...>(fennec::fract(x[i]) ...);
}
// Mod ================================================================================================================= // Mod =================================================================================================================
@@ -513,20 +539,22 @@ constexpr vector<genType, i...> fract(const vector<genType, i...>& x)
/// \param x dividend /// \param x dividend
/// \param y divisor /// \param y divisor
template<typename genType> template<typename genType>
constexpr genType mod(genType x, genType y) constexpr genType mod(genType x, genType y) {
{ return x - y * fennec::floor(x / y); } return x - y * fennec::floor(x / y);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y) constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y) {
{ return x - y * fennec::floor(x / y); } return x - y * fennec::floor(x / y);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector<genType, i...>& y) constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector<genType, i...>& y) {
{ return x - y * fennec::floor(x / y); } return x - y * fennec::floor(x / y);
}
// ModF ================================================================================================================ // ModF ================================================================================================================
@@ -541,16 +569,17 @@ constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector
/// \param x input value /// \param x input value
/// \param i integral out /// \param i integral out
template<typename genType> template<typename genType>
constexpr genType modf(genType x, genType& i) constexpr genType modf(genType x, genType& i) {
{ i = fennec::floor(x); return fennec::fract(x); } i = fennec::floor(x); return fennec::fract(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genType, i...>& I) constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genType, i...>& I) {
{ I = fennec::floor(x); return fennec::fract(x); } I = fennec::floor(x); return fennec::fract(x);
}
// Is NaN ============================================================================================================== // Is NaN ==============================================================================================================
@@ -569,16 +598,17 @@ constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genT
/// ///
/// \param x input value /// \param x input value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>) template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>)
constexpr genBType isnan(genType x) constexpr genBType isnan(genType x) {
{ return ::isnanf(x); } return ::isnanf(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>) template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x) constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x) {
{ return vector<genBType, i...>(fennec::isnan(x[i]) ...); } return vector<genBType, i...>(fennec::isnan(x[i]) ...);
}
// Is Inf ============================================================================================================== // Is Inf ==============================================================================================================
@@ -592,15 +622,17 @@ constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x)
/// ///
/// \param x input value /// \param x input value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>) template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType>)
constexpr genBType isinf(genType x) constexpr genBType isinf(genType x) {
{ return ::isinff(x); } return ::isinff(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>) template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x) constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x) {
{ return vector<genBType, i...>(fennec::isinf(x[i]) ...); } return vector<genBType, i...>(fennec::isinf(x[i]) ...);
}
@@ -609,8 +641,10 @@ constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x)
/// ///
/// \copydetails fennec::floatBitsToUint(fennec::genFType) /// \copydetails fennec::floatBitsToUint(fennec::genFType)
template<typename genType, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genIType floatBitsToInt(genType x) constexpr genIType floatBitsToInt(genType x) {
{ return fennec::bit_cast<genIType>(x); } return fennec::bit_cast<genIType>(x);
}
/// ///
/// \brief Returns a signed or unsigned integer value representing the encoding of a floating-point value. /// \brief Returns a signed or unsigned integer value representing the encoding of a floating-point value.
@@ -629,14 +663,17 @@ constexpr genIType floatBitsToInt(genType x)
/// ///
/// \param x value to convert /// \param x value to convert
template<typename genType, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType)) template<typename genType, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr genUType floatBitsToUint(genType x) constexpr genUType floatBitsToUint(genType x) {
{ return fennec::bit_cast<genUType>(x); } return fennec::bit_cast<genUType>(x);
}
/// ///
/// \copydetails fennec::uintBitsToFloat(fennec::genUType) /// \copydetails fennec::uintBitsToFloat(fennec::genUType)
template<typename genType = float_t, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType = float_t, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genType intBitsToFloat(genIType x) constexpr genType intBitsToFloat(genIType x) {
{ return fennec::bit_cast<genType>(x); } return fennec::bit_cast<genType>(x);
}
/// ///
@@ -654,27 +691,32 @@ constexpr genType intBitsToFloat(genIType x)
/// ///
/// \param x value to convert /// \param x value to convert
template<typename genType = float_t, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType)) template<typename genType = float_t, typename genUType = uint_t> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr genType uintBitsToFloat(genUType x) constexpr genType uintBitsToFloat(genUType x) {
{ return fennec::bit_cast<genType>(x); } return fennec::bit_cast<genType>(x);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genIType, i...> floatBitsToInt(const vector<genType, i...>& x) constexpr vector<genIType, i...> floatBitsToInt(const vector<genType, i...>& x) {
{ return vector<genIType, i...>(fennec::bit_cast<genIType>(x[i])...); } return vector<genIType, i...>(fennec::bit_cast<genIType>(x[i])...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType)) template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genUType, i...> floatBitsToUint(const vector<genType, i...>& x) constexpr vector<genUType, i...> floatBitsToUint(const vector<genType, i...>& x) {
{ return vector<genUType, i...>(fennec::bit_cast<genUType>(x[i])...); } return vector<genUType, i...>(fennec::bit_cast<genUType>(x[i])...);
}
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genType, i...> intBitsToFloat(const vector<genIType, i...>& x) constexpr vector<genType, i...> intBitsToFloat(const vector<genIType, i...>& x) {
{ return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...); } return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType)) template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x) constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x) {
{ return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...); } return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
@@ -691,15 +733,17 @@ constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x)
/// \param b the multiplier /// \param b the multiplier
/// \param c the addend /// \param c the addend
template<typename genType> template<typename genType>
constexpr genType fma(genType a, genType b, genType c) constexpr genType fma(genType a, genType b, genType c) {
{ return genType(::fma(a, b, c)); } return genType(::fma(a, b, c));
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector<genType, i...>& b, const vector<genType, i...>& c) constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector<genType, i...>& b, const vector<genType, i...>& c) {
{ return vector<genType, i...>(fennec::fma(a[i], b[i], c[i]) ...); } return vector<genType, i...>(fennec::fma(a[i], b[i], c[i]) ...);
}
@@ -720,15 +764,17 @@ constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector
/// \param x The floating-point value to split /// \param x The floating-point value to split
/// \param exp The variable to store the exponent in /// \param exp The variable to store the exponent in
template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>) template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>)
constexpr genType frexp(genType x, genIType& exp) constexpr genType frexp(genType x, genIType& exp) {
{ return ::frexp(x, &exp); } return ::frexp(x, &exp);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>) template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<genIType, i...>& exp) constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<genIType, i...>& exp) {
{ return vector<genType, i...>(fennec::frexp(x[i], exp[i])...); } return vector<genType, i...>(fennec::frexp(x[i], exp[i])...);
}
@@ -751,15 +797,17 @@ constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<gen
/// \param x The significand /// \param x The significand
/// \param exp The exponent /// \param exp The exponent
template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>) template<typename genType, typename genIType = int_t> requires(is_integral_v<genIType>)
constexpr genType ldexp(genType x, genIType exp) constexpr genType ldexp(genType x, genIType exp) {
{ return ::ldexp(x, exp); } return ::ldexp(x, exp);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>) template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vector<genType, i...>& exp) constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vector<genIType, i...>& exp) {
{ return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...); } return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...);
}
/// @} /// @}
@@ -784,24 +832,27 @@ constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vect
/// \param x input value \f$x\f$ /// \param x input value \f$x\f$
/// \param y input value \f$y\f$ /// \param y input value \f$y\f$
template<typename genType> template<typename genType>
constexpr genType min(genType x, genType y) constexpr genType min(genType x, genType y) {
{ return (y < x) ? y : x; } return (y < x) ? y : x;
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> min(genType x, const vector<genType, i...>& y) constexpr vector<genType, i...> min(genType x, const vector<genType, i...>& y) {
{ return vector<genType, i...>(fennec::min(x, y[i]) ...); } return vector<genType, i...>(fennec::min(x, y[i]) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, genType y) constexpr vector<genType, i...> min(const vector<genType, i...>& x, genType y) {
{ return vector<genType, i...>(fennec::min(x[i], y) ...); } return vector<genType, i...>(fennec::min(x[i], y) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector<genType, i...>& y) constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector<genType, i...>& y) {
{ return vector<genType, i...>(fennec::min(x[i], y[i]) ...); } return vector<genType, i...>(fennec::min(x[i], y[i]) ...);
}
// Max ================================================================================================================= // Max =================================================================================================================
@@ -816,23 +867,27 @@ constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector
/// \param x first input value /// \param x first input value
/// \param y second input value /// \param y second input value
template<typename genType> template<typename genType>
constexpr genType max(genType x, genType y) constexpr genType max(genType x, genType y) {
{ return (x < y) ? y : x; } return (x < y) ? y : x;
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> max(genType x, const vector<genType, i...>& y) constexpr vector<genType, i...> max(genType x, const vector<genType, i...>& y) {
{ return vector<genType, i...>(fennec::max(x, y[i]) ...); } return vector<genType, i...>(fennec::max(x, y[i]) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, genType y) constexpr vector<genType, i...> max(const vector<genType, i...>& x, genType y) {
{ return vector<genType, i...>(fennec::max(x[i], y) ...); } return vector<genType, i...>(fennec::max(x[i], y) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector<genType, i...>& y) constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector<genType, i...>& y) {
{ return vector<genType, i...>(fennec::max(x[i], y[i]) ...); } return vector<genType, i...>(fennec::max(x[i], y[i]) ...);
}
// Clamp =============================================================================================================== // Clamp ===============================================================================================================
@@ -848,19 +903,22 @@ constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector
/// \param minVal minimum value /// \param minVal minimum value
/// \param maxVal maximum value /// \param maxVal maximum value
template<typename genType> template<typename genType>
constexpr genType clamp(genType x, genType minVal, genType maxVal) constexpr genType clamp(genType x, genType minVal, genType maxVal) {
{ return min(max(x, minVal), maxVal); } return min(max(x, minVal), maxVal);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, genType minVal, genType maxVal) constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, genType minVal, genType maxVal) {
{ return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal), maxVal)...); } return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal), maxVal)...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vector<genType, i...>& minVal, const vector<genType, i...>& maxVal) constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vector<genType, i...>& minVal, const vector<genType, i...>& maxVal) {
{ return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal[i]), maxVal[i])...); } return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal[i]), maxVal[i])...);
}
/// @} /// @}
@@ -885,19 +943,22 @@ constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vect
/// \param edge The \f$x\f$ coordinate of the discontinuity /// \param edge The \f$x\f$ coordinate of the discontinuity
/// \param x The coordinate of the sample location /// \param x The coordinate of the sample location
template<typename genType> requires(is_floating_point_v<genType>) template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType step(genType edge, genType x) constexpr genType step(genType edge, genType x) {
{ return static_cast<genType>(not(x < edge)); } return static_cast<genType>(not(x < edge));
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(genType edge, const vector<genType, i...>& x) constexpr vector<genType, i...> step(genType edge, const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::step(edge, x[i]) ...); } return vector<genType, i...>(fennec::step(edge, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const vector<genType, i...>& x) constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::step(edge[i], x[i]) ...); } return vector<genType, i...>(fennec::step(edge[i], x[i]) ...);
}
// Smoothstep ========================================================================================================== // Smoothstep ==========================================================================================================
@@ -923,19 +984,22 @@ constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const ve
/// \param edge1 \f$x\f$ value where the function returns \f$1.0\f$ /// \param edge1 \f$x\f$ value where the function returns \f$1.0\f$
/// \param x \f$x\f$ coordinate input /// \param x \f$x\f$ coordinate input
template<typename genType> requires(is_floating_point_v<genType>) template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType smoothstep(genType edge0, genType edge1, genType x) constexpr genType smoothstep(genType edge0, genType edge1, genType x) {
{ genType t = fennec::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); return t * t * (3 - 2 * t); } genType t = fennec::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); return t * t * (3 - 2 * t);
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(genType edge0, genType edge1, const vector<genType, i...>& x) constexpr vector<genType, i...> smoothstep(genType edge0, genType edge1, const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::smoothstep(edge0, edge1, x[i]) ...); } return vector<genType, i...>(fennec::smoothstep(edge0, edge1, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, const vector<genType, i...>& edge1, const vector<genType, i...>& x) constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, const vector<genType, i...>& edge1, const vector<genType, i...>& x) {
{ return vector<genType, i...>(fennec::smoothstep(edge0[i], edge1[i], x[i]) ...); } return vector<genType, i...>(fennec::smoothstep(edge0[i], edge1[i], x[i]) ...);
}
// Mix ================================================================================================================= // Mix =================================================================================================================
@@ -954,18 +1018,21 @@ constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, c
/// \param y Second value /// \param y Second value
/// \param a Interpolant /// \param a Interpolant
template<typename genType> requires(is_floating_point_v<genType>) template<typename genType> requires(is_floating_point_v<genType>)
constexpr genType mix(genType x, genType y, genType a) constexpr genType mix(genType x, genType y, genType a) {
{ return x * (genType(1.0) - a) + y * a; } return x * (genType(1.0) - a) + y * a;
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genType a) constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genType a) {
{ return x * (genType(1.0) - a) + y * a; } return x * (genType(1.0) - a) + y * a;
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>) template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genType, i...>& a) constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genType, i...>& a) {
{ return x * (genType(1.0) - a) + y * a; } return x * (genType(1.0) - a) + y * a;
}
// Mix (Bool) ========================================================================================================== // Mix (Bool) ==========================================================================================================
@@ -986,24 +1053,28 @@ constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector
/// \param y False Value /// \param y False Value
/// \param a Boolean Value /// \param a Boolean Value
template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType> and is_floating_point_v<genType>) template<typename genType, typename genBType = bool_t> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr genType mix(genType x, genType y, genBType a) constexpr genType mix(genType x, genType y, genBType a) {
{ return a ? y : x; } return a ? y : x;
}
// Vector Specializations ---------------------------------------------------------------------------------------------- // Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>) template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genBType a) constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genBType a) {
{ return genDType((a ? y[i] : x[i])...); } return genDType((a ? y[i] : x[i])...);
}
template<typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>) template<typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> mix(const vector<genBType, i...>& x, const vector<genBType, i...>& y, const vector<genBType, i...>& a) constexpr vector<genBType, i...> mix(const vector<genBType, i...>& x, const vector<genBType, i...>& y, const vector<genBType, i...>& a) {
{ return genBType((a[i] ? y[i] : x[i])...); } return genBType((a[i] ? y[i] : x[i])...);
}
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>) template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genBType, i...>& a) constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genBType, i...>& a) {
{ return genDType((a[i] ? y[i] : x[i])...); } return genDType((a[i] ? y[i] : x[i])...);
}
/// @} /// @}

Some files were not shown because too many files have changed in this diff Show More