Compare commits
47 Commits
2117e4347c
...
custom_pla
| Author | SHA1 | Date | |
|---|---|---|---|
| 086c73f058 | |||
| 339f5c8cd8 | |||
| 18c0a7099d | |||
| 540c7fbce8 | |||
| cbcd699ab0 | |||
| ff27caab4f | |||
| fe4c49d092 | |||
| 037c62bf12 | |||
| 494d766741 | |||
| 83f0c01e29 | |||
| 4ff739d625 | |||
| 7cd38604a7 | |||
| 733fca41ef | |||
| 55a8c54119 | |||
| 27754a56d7 | |||
| fcf9c6adcb | |||
| e6c0a60ea9 | |||
| 5252ba84c9 | |||
| 73041e994d | |||
| 3ddc2b3d97 | |||
| e91c2aa9f1 | |||
| 38b7221fa0 | |||
| 8bfb59cd20 | |||
| 2535e1ac4b | |||
| f173c3e7cd | |||
| cc4d85c393 | |||
| d6e31a89b0 | |||
| 74fb525453 | |||
| b9de039a10 | |||
| 9f96155856 | |||
| d2be083a8f | |||
| efe56b3699 | |||
| b7d8426e86 | |||
| 2cb41e1437 | |||
| 0f721f57ea | |||
| 4a3639ecb4 | |||
| ff4d6efedc | |||
| 9dc9ed4ed1 | |||
| 5e04eb0ca6 | |||
| 3d42dea9eb | |||
| 3d4ea4398a | |||
| 7aafa4c9aa | |||
| 8124ea2ae5 | |||
| d02a51fd8d | |||
| 7493b5252a | |||
| 7ea2710ee0 | |||
| f9de242b87 |
7
.gdbinit
Normal file
7
.gdbinit
Normal file
@@ -0,0 +1,7 @@
|
||||
python
|
||||
import sys, os.path
|
||||
print(os.path.abspath("./gdb"))
|
||||
sys.path.insert(0, os.path.abspath("./gdb"))
|
||||
import fennec
|
||||
fennec.register_printers(gdb.current_objfile())
|
||||
end
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
/docs/
|
||||
/bin/
|
||||
/lib/
|
||||
/doxy/README.md
|
||||
|
||||
116
CMakeLists.txt
116
CMakeLists.txt
@@ -20,19 +20,38 @@ cmake_minimum_required(VERSION 3.30)
|
||||
project(fennec)
|
||||
|
||||
# External dependencies should be loaded here
|
||||
|
||||
# CppTrace is a dependency of the project, added as a git submodule
|
||||
add_subdirectory(external/cpptrace)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_target(fennec-dependencies
|
||||
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_package(Doxygen)
|
||||
fennec_check_platform()
|
||||
@@ -55,15 +74,36 @@ add_library(fennec STATIC
|
||||
|
||||
# CORE =================================================================================================================
|
||||
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 ===========================================================================================================
|
||||
include/fennec/containers/containers.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/traversal.h
|
||||
include/fennec/containers/tuple.h
|
||||
include/fennec/containers/variant.h
|
||||
|
||||
|
||||
include/fennec/containers/detail/_tuple.h
|
||||
|
||||
|
||||
# LANG =================================================================================================================
|
||||
@@ -77,26 +117,31 @@ add_library(fennec STATIC
|
||||
include/fennec/lang/intrinsics.h
|
||||
include/fennec/lang/limits.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_transforms.h
|
||||
include/fennec/lang/typeuuid.h
|
||||
include/fennec/lang/types.h
|
||||
include/fennec/lang/utility.h
|
||||
include/fennec/lang/integer.h
|
||||
|
||||
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/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 ===============================================================================================================
|
||||
include/fennec/memory/new.h source/memory/new.cpp
|
||||
|
||||
@@ -105,9 +150,9 @@ add_library(fennec STATIC
|
||||
include/fennec/memory/common.h
|
||||
include/fennec/memory/memory.h
|
||||
include/fennec/memory/pointers.h
|
||||
include/fennec/memory/ptr_traits.h
|
||||
include/fennec/memory/pointer_traits.h
|
||||
|
||||
include/fennec/memory/detail/__ptr_traits.h
|
||||
include/fennec/memory/detail/_ptr_traits.h
|
||||
|
||||
# DEBUG ================================================================================================================
|
||||
source/debug/assert_impl.cpp
|
||||
@@ -142,41 +187,42 @@ add_library(fennec STATIC
|
||||
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
|
||||
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
|
||||
|
||||
# FPROC ================================================================================================================
|
||||
|
||||
# langproc ================================================================================================================
|
||||
|
||||
# Strings
|
||||
include/fennec/fproc/strings/cstring.h
|
||||
include/fennec/fproc/strings/locale.h
|
||||
include/fennec/fproc/strings/string.h
|
||||
include/fennec/langproc/strings/cstring.h
|
||||
include/fennec/langproc/strings/locale.h
|
||||
include/fennec/langproc/strings/string.h
|
||||
|
||||
include/fennec/fproc/strings/detail/__ctype.h
|
||||
include/fennec/langproc/strings/detail/_ctype.h
|
||||
|
||||
# Filesystem
|
||||
include/fennec/fproc/filesystem/file.h source/fproc/filesystem/file.cpp
|
||||
include/fennec/fproc/filesystem/path.h source/fproc/filesystem/path.cpp
|
||||
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/dialog.h
|
||||
include/fennec/platform/interface/display.h
|
||||
include/fennec/platform/interface/window.h source/platform/interface/window.cpp
|
||||
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}
|
||||
include/fennec/lang/type_operators.h
|
||||
include/fennec/containers/tuple.h
|
||||
include/fennec/containers/detail/__tuple.h
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming)
|
||||
add_dependencies(fennec metaprogramming fennec-dependencies)
|
||||
|
||||
target_compile_definitions(fennec PUBLIC
|
||||
${FENNEC_COMPILE_DEFINITIONS}
|
||||
@@ -188,8 +234,9 @@ target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_OPTIONS}) # Do not comp
|
||||
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
||||
|
||||
target_link_libraries(fennec PRIVATE
|
||||
cpptrace::cpptrace
|
||||
${FENNEC_LINK_LIBRARIES}
|
||||
|
||||
cpptrace::cpptrace
|
||||
)
|
||||
|
||||
|
||||
@@ -202,6 +249,7 @@ file(COPY logo DESTINATION docs/logo)
|
||||
if(DOXYGEN_FOUND)
|
||||
add_dependencies(fennec fennecdocs)
|
||||
set(DOXY_OUTPUT_DIR "${FENNEC_SOURCE_DIR}/docs")
|
||||
set(DOXY_EXAMPLES_DIR "${FENNEC_SOURCE_DIR}/examples")
|
||||
get_filename_component(DOXYGEN_PROJECT_NAME ${FENNEC_SOURCE_DIR} NAME) # Set Doxy Project name to the name of the root dir
|
||||
set(DOXYGEN_CONFIG_IN "${FENNEC_SOURCE_DIR}/doxy/Doxyfile.in") # Input config file with preprocessor arguments
|
||||
set(DOXYGEN_CONFIG_OUT "${FENNEC_SOURCE_DIR}/doxy/Doxyfile") # Generated config file from input
|
||||
|
||||
658
PLANNING.md
658
PLANNING.md
@@ -1,658 +0,0 @@
|
||||
|
||||
|
||||
# Planning Documentation for fennec
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [TODO](#todo)
|
||||
1. [Security Ramblings](#file-security-ramblings)
|
||||
2. [Platform Support](#platform--api-support)
|
||||
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. [Platform Support Layer](#platform-support-layer-platform)
|
||||
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`)
|
||||
|
||||
|
||||
### File Security Ramblings:
|
||||
|
||||
Windows is starting to piss me off, so I am considering dropping official support for MSVC. MinGW and Cygwin
|
||||
will still work for compiling on Windows if this ends up being the case. The reason for this is that there are
|
||||
*a lot* of platform dependent security issues. MinGW and Cygwin wrap Linux and glibc headers for Windows, which would
|
||||
push the security onus onto the compiler and end-user.
|
||||
|
||||
The biggest blocker at the moment in terms of this is the filesystem. If we want to implement a filesystem that
|
||||
is safe across platforms, stdc++ *and* iso libc have no guarantees about the safety of their functions.
|
||||
|
||||
The crux of this issue falls at the following specific behaviour:
|
||||
- User selects an existing file to write to
|
||||
- Application interface confirms overwrite action
|
||||
- Application writes to the file after confirmation
|
||||
|
||||
A threat actor can introduce a malicious file or symlink to the file that was attempted access between the check and
|
||||
usage of the file. This is called TOCTOU (time of check, time of use).
|
||||
|
||||
This issue can be solved using `fopen("<file>", "a+")` and `ftell`, however this specific behaviour is not intuitive to
|
||||
those first learning how to work with file systems. We can attempt to abstract this away with another wrapper, or simply
|
||||
write the file structure to handle this behaviour properly. The downside to this method overall is that it will break
|
||||
common conventions of how humans interpret filesystems and the related control flow logic. What we can do is force the
|
||||
`'+'` flag to always be present for write operations, and raise an error when desired, if the file is not empty. This
|
||||
unfortunately would have the downside of being unable to open a file as write only.
|
||||
|
||||
Using `"wx"` in this instance would not be sufficient since it would require a second call to fopen, which would
|
||||
create the conditions for the TOCTOU error described above.
|
||||
|
||||
Another issue arises when we are parsing a directory tree. The best we can do is take ownership of the directory that
|
||||
is opened as the root. However, this requires `dirent.h` which is not implemented in MSVC. A custom implementation of
|
||||
`dirent.h` may be written for MSVC, however this is one of the few things I am not willing to outsource to another
|
||||
library. Developing our own implementation would take a non-insignificant amount of time, between writing the library,
|
||||
debugging it, and testing for vulnerabilities. As stated above, this implementation is native to MinGW and Cygwin,
|
||||
so we would not have to entirely drop support for Windows. However, MSVC is the most widely used compiler for Windows
|
||||
applications and is native to Visual Studio and VSCode.
|
||||
|
||||
What is probably the best solution is to wrap everything in a file interface that does not allow the direct setting of
|
||||
these flags. Then we set our own usage type for the file that informs which flags should be used.
|
||||
|
||||
We need to be able to handle the following types of files:
|
||||
- Assets, such as scenes, audio, textures, metadata, meshes, etc.
|
||||
- Save files, setting files, etc.
|
||||
|
||||
One of the nice things about the assets is that they are guaranteed to be read-only once an application is installed
|
||||
on the computer of the end-user. Therefore, this issue only arises with save files and custom file formats.
|
||||
|
||||
When the editor is run, all these files should be opened in read/write mode.
|
||||
|
||||
Naming conventions should exist for the types of files and how they are read. For example, in release mode,
|
||||
most assets should be opened once, and then closed immediately. However, this does not make sense for formats
|
||||
that are continuous and too large to be kept around in memory, such as video formats.
|
||||
|
||||
Perhaps the following conventions:
|
||||
- Static Asset
|
||||
- Stream Asset
|
||||
- Resource
|
||||
|
||||
We can turn this into an object-oriented approach by having different formats inherit these base types. We may still
|
||||
have a base file type that wraps C functionality, but discourage developers from using the interface.
|
||||
|
||||
We could also declare the file interface extern so that only internal files know the implementation. However, I would
|
||||
not be satisfied by doing this since it would prevent developers from implementing custom file type implementations.
|
||||
|
||||
Conserving memory is not really an issue here as long as we are smart about our implementation. Files should only be
|
||||
open when necessary and be closed when it is no longer necessary to have them open. Data should be streamed unless the
|
||||
all the data in the file is required.
|
||||
|
||||
When built in release mode, we also need to pack static assets into some sort of archive that is mountable to reduce
|
||||
disk space consumption of a program.
|
||||
|
||||
I was considering encryption for archives, however it does not make much sense. Assuming someone intends to pirate the
|
||||
game, there is not much stopping them from running the files. I will add Steam support at some point which would allow
|
||||
you to use Steam's DRM to prevent the executable from being run. Otherwise, there is no point in attempting to encrypt
|
||||
game files. Even Unreal PAK files can be cracked in seconds, and even if I managed to write something that cannot be
|
||||
trivially cracked locally, you can scrape most assets from the GPU and Audio Card.
|
||||
|
||||
I have managed to solve the specific case provided at the top of this section, which was done by wrapping C I/O calls
|
||||
into a file wrapper. This wrapper can handle a few different mode flags and has specific conditions for the flags. See
|
||||
the documentation for `fennec/fproc/io/file.h` for more info.
|
||||
|
||||
One question remains unanswered on this front; should a read/write file open as `r+` or `a+`. `rewind` is slightly faster
|
||||
than `fseek(SEEK_END)`, however for the case of save files and editor assets, `r+` makes more sense from a usage perspective
|
||||
|
||||
Directories remain an issue, with `dirent.h` being the only sensible option at time of writing. The issue with using
|
||||
`dirent.h` boils back down to security issues on Windows. However, the only option is to write a custom implementation
|
||||
for MSVC.
|
||||
|
||||
|
||||
### Platform & API Support
|
||||
|
||||
I have decided to forgo SDL, this is so the engine can provide specific support for specific platforms.
|
||||
Also, SDL implements a lot of things that will need to be implemented specifically for the engine, so only the window
|
||||
management would be used.
|
||||
|
||||
Platform support will be implemented in the following order:
|
||||
- Linux/BSD
|
||||
- Wayland
|
||||
- XKB
|
||||
- OpenGL (EGL)
|
||||
- PulseAudio
|
||||
- Vulkan
|
||||
- X11
|
||||
- OpenGL (EGL)
|
||||
- ALSA
|
||||
- Vulkan
|
||||
- Microsoft Windows
|
||||
- XInput
|
||||
- OpenGL
|
||||
- DirectSound
|
||||
- Vulkan
|
||||
- Android
|
||||
- OpenGL ES
|
||||
- macOS
|
||||
- iOS
|
||||
|
||||
Most consoles will never get official platform support due to NDAs which conflict with the principles of this engine.
|
||||
fennec will avoid using proprietary libraries except when strictly necessary, such as support for Windows and MacOS.
|
||||
fennec will interact with any drivers required for the listed operating systems above, even if proprietary.
|
||||
|
||||
|
||||
## C++ Language Library (`lang`)
|
||||
|
||||
Implement header files for standard functions relating to the C++ Language.
|
||||
|
||||
So far this is implemented on an as-needed basis. A full implementation should be worked on continuously.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Math Library (`math`)
|
||||
|
||||
Implement math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf).
|
||||
|
||||
"Extensions" has a different meaning here. Extensions for the math library are any functions that are not defined within
|
||||
the Specification.
|
||||
|
||||
Additional extensions should be implemented to provide standard definitions for functions predominantly related
|
||||
to Linear Algebra, Mathematical Analysis, and more specifically Discrete Analysis. Additional extensions will be
|
||||
implemented on an as-needed basis.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Memory Library (`memory`)
|
||||
|
||||
Implement headers related to memory allocation in C++.
|
||||
|
||||
* Smart Pointers
|
||||
* Unique Pointer
|
||||
* Shared Pointer
|
||||
|
||||
- Memory Allocation
|
||||
- Allocation
|
||||
-
|
||||
|
||||
|
||||
|
||||
|
||||
## Containers Library (`containers`)
|
||||
|
||||
All containers of the [C++ Standard Library](https://cppreference.com/w/cpp/container.html) should be implemented.
|
||||
|
||||
Here are essential data-structures not specified in the C++ stdlib:
|
||||
- Graph → AI `graph`
|
||||
- Necessary for 2D and 3D navigation.
|
||||
- Rooted Directed Tree → Scene `rd_tree`
|
||||
- Defines the scene structure.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Format Processing (`fproc`)
|
||||
|
||||
This library contains information for any data that is formatted. This includes basic string formats, file formats,
|
||||
and eventually programming languages
|
||||
|
||||
fennec should be able to use Doxygen and LaTeX externally. Consider including binaries with releases.
|
||||
|
||||
### Notes
|
||||
|
||||
* 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
|
||||
- Audio Formats
|
||||
- MP3
|
||||
- WAV
|
||||
- AAC
|
||||
- Graphics Formats
|
||||
- Textures
|
||||
- BMP
|
||||
- DDS
|
||||
- JPG
|
||||
- PNG
|
||||
- TIFF
|
||||
- Vectors
|
||||
- OTF
|
||||
- SVG
|
||||
- TTF
|
||||
- Models
|
||||
- FBX
|
||||
- Wavefront OBJ
|
||||
- Video Formats
|
||||
- MP4
|
||||
- AVI
|
||||
- MPG
|
||||
- MOV
|
||||
|
||||
**TODO LATER**
|
||||
* 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**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Platform Support Layer (`platform`)
|
||||
|
||||
This is the core part of platform support for fennec. All necessary drivers
|
||||
and OS specific functionality will be wrapped up nicely into these interfaces.
|
||||
|
||||
See implementation order [here](#platform--api-support)
|
||||
|
||||
|
||||
## Scene (`scene`)
|
||||
|
||||
* In-Array Directed Tree
|
||||
* Elegant method for providing `O(1)` insertions and `O(log(n))` deletions.
|
||||
* Bounding Volume Hierarchy
|
||||
* Octree
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2D Graphics (`gfx2d`)
|
||||
|
||||
Links:
|
||||
- https://en.wikipedia.org/wiki/Quadtree
|
||||
- https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-25-rendering-vector-art-gpu
|
||||
|
||||
Object Structure. The mesh is implicit data.
|
||||
|
||||
|
||||
### Structures (`gfx2d`)
|
||||
|
||||
For the 2d rendering framework, Materials need to be rendered independently because we have
|
||||
no size constraints for images. This disallows us from using a meta-shader like in
|
||||
the 3d rendering framework.
|
||||
|
||||
```c++
|
||||
struct Object
|
||||
{
|
||||
vec2 location, scale; // A matrix would be 36 bytes, this is instead 20 bytes
|
||||
float rotation;
|
||||
}
|
||||
```
|
||||
|
||||
- BVH
|
||||
- Quadtree
|
||||
- Leaf Size and Tree Depth should be calculated by the scene, constraints are as follows:
|
||||
- Min Object Size
|
||||
- Max Object Size
|
||||
- Scene Center
|
||||
- Scene Edge
|
||||
- Insertions and Updates are done on the CPU
|
||||
- Nodes
|
||||
- Start Index 32-bits
|
||||
- Object Count 32-bits
|
||||
- Objects
|
||||
- Buffer of Object IDs grouped by Octree Node
|
||||
- Culling
|
||||
- Starting at each Octree Leaf, traverse upwards.
|
||||
- Insert Visible Leaf IDs
|
||||
- Track using atomic buffer
|
||||
- Generate the Command Buffer for Culled Meshes from the Visible Leaf Buffer
|
||||
- Count Materials
|
||||
- Count Meshes per Material
|
||||
- Generate the Culled Object Buffer by copying objects from the Object Buffer
|
||||
- Adjust Buffer Size using the counts
|
||||
- Insert using another atomic buffer
|
||||
|
||||
- Translucent objects will be sorted. We can cheat by using a z-index instead of a z-coordinate.
|
||||
This will allow us to sort objects as they are created. We can still bulk render each z-index,
|
||||
with meshes and objects being grouped by material.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3D Graphics (`gfx3d`)
|
||||
|
||||
Links:
|
||||
- https://en.wikipedia.org/wiki/Octree
|
||||
- https://www.adriancourreges.com/blog/2015/11/02/gta-v-graphics-study/
|
||||
- https://learnopengl.com/PBR/Lighting
|
||||
- https://learnopengl.com/PBR/IBL/Diffuse-irradiance
|
||||
- https://en.wikipedia.org/wiki/Schlick%27s_approximation
|
||||
- https://pixelandpoly.com/ior.html
|
||||
- https://developer.download.nvidia.com/SDK/10/opengl/screenshots/samples/dual_depth_peeling.html
|
||||
|
||||
**DirectX will never have official support.**
|
||||
If you would like to make a fork, have at it, but know that I will hold a deep disdain for you.
|
||||
|
||||
The graphics pipeline will have a buffer with a list of objects and their rendering data.
|
||||
This will be referred to as the Object Buffer. There will be two, for both the Deferred and Forward Passes.
|
||||
|
||||
The buffers will be optimized by scene prediction.
|
||||
This involves tracking the meshes and textures directly and indirectly used by a scene.
|
||||
A callback function in the graphics system for scene loading can do this.
|
||||
|
||||
|
||||
Materials and Lighting models will be run via a shader metaprogram to make the pipeline independent of this aspect.
|
||||
This allows the GPU to draw every single deferred rendered mesh in a single draw call for each stage of the renderer.
|
||||
|
||||
Specifications for debugging views via early breaks are included in the stages.
|
||||
|
||||
|
||||
|
||||
### Structures (`gfx3d`)
|
||||
|
||||
Object Structure. The mesh is implicit data.
|
||||
|
||||
```c++
|
||||
struct Object
|
||||
{
|
||||
vec3 location, scale; // A matrix would be 64 bytes, this is instead 28 bytes
|
||||
quat rotation;
|
||||
int material;
|
||||
}
|
||||
```
|
||||
|
||||
Textures for 3D rendering are stored in various buffers with sizes of powers of 2.
|
||||
Ratios of `1:1` and `2:1` are allowed. The `2:1` ratio is specifically for spherical and cylindrical projection.
|
||||
UVs may be transformed to use a `2:1` as if it were `1:2`.
|
||||
Cubemaps may only be `1:1`, I would be concerned if you are using any other ratio.
|
||||
|
||||
- 8-Bit R Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RG Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGB Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGBA Texture `4096, 2048, 1024, 512` (8)
|
||||
- 8-Bit RGB Cubemap `1024, 512, 256, 128` (4)
|
||||
|
||||
* 16-Bit HDR RGB Texture `4096, 2048, 1024, 512` (8)
|
||||
* 16-Bit HDR RGBA Texture `4096, 2048, 1024, 512` (8)
|
||||
* 16-Bit HDR RGB Cubemap `1024, 512, 256, 128` (4)
|
||||
|
||||
- 16-Bit Shadow Texture `4096, 2048, 1024, 512` (8)
|
||||
- 16-Bit Shadow Cubemap `2048, 1024, 512, 256` (4)
|
||||
|
||||
Documentation should provide guidelines on categories of Art Assets and the resolution of textures to use.
|
||||
|
||||
Textures are identified by an 8-bit integer and 16-bit integer.
|
||||
- `int8` → the texture buffer
|
||||
- `int16` → the layer in the buffer
|
||||
|
||||
Artists should be informed on the texture structure of the engine and its limitations.
|
||||
However, these principles should be followed in other game engines as these are
|
||||
guided by what is most efficient for typical GPU hardware.
|
||||
|
||||
|
||||
Materials are, for the most part, user-defined. Documentation should make the user aware of this.
|
||||
Material buffers will be a sequence of the Material Struct instances.
|
||||
They will at the very least contain the id of their shader.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Stages (`gfx3d`)
|
||||
|
||||
This is the set of stages for the graphics pipeline that runs every frame:
|
||||
Unless otherwise specified, each stage will be run on the GPU.
|
||||
|
||||
- BVH
|
||||
- Octree `(8 Bpn, 64 bpn) [6-Layers ≈ 2.1MB]`
|
||||
- Leaf Size and Tree Depth should be calculated by the scene, constraints are as follows:
|
||||
- Min Object Size
|
||||
- Max Object Size
|
||||
- Scene Center
|
||||
- Scene Edge
|
||||
- Buffer has implicit locations due to the tree having 8 children.
|
||||
- Insertions and Updates are done on the CPU
|
||||
- Nodes
|
||||
- Start Index `int32`
|
||||
- Object Count `int32`
|
||||
- Objects
|
||||
- Buffer of Object IDs grouped by Octree Node
|
||||
- Leaf Culling
|
||||
- Starting at each Octree Leaf, traverse upwards.
|
||||
- Insert Visible Leaf IDs
|
||||
- Track using atomic buffer
|
||||
- Generate the Command Buffer for Culled Mesh LODs from the Visible Leaf Buffer
|
||||
- Track counts using atomic buffers
|
||||
- To avoid double counting due to the construction of the Octree output, we have some options
|
||||
- Ignore Leaf Instances based on occurrences of the mesh in the surrounding 8 Quadtree Leaves. This would require
|
||||
a bias towards a specific corner of the filter.
|
||||
- Perform a preprocessing step on the CPU to erase duplicate elements and fix the buffer continuity.
|
||||
- Let the duplicates be rendered.
|
||||
- Generate the Culled Object Buffer with the respective object IDs
|
||||
- Adjust Buffer Size using the counts
|
||||
- Insert by reusing the count buffer, clipped to only contain used meshes
|
||||
|
||||
Debug View: Object ID, Mesh ID, LOD
|
||||
|
||||
- Visibility
|
||||
- Buffer `(15 Bpp, 120 bpp) [1920x1080] ≈ 39.4MB`
|
||||
- Depth Buffer → `D24`
|
||||
- Visibility Info → `RGB32I`
|
||||
- R = Object ID
|
||||
- G = Mesh ID
|
||||
- B = Material ID
|
||||
- Regenerate the Command Buffer for Visible Mesh LODs
|
||||
- Regenerate the Culled Object Buffer
|
||||
|
||||
Debug View: Visibility Buffer
|
||||
|
||||
* G-Buffer Pass `(17 Bpp, 136 bpp) [1920x1080] ≈ 35.3MB`
|
||||
* Depth - Stencil → `D24_S8`
|
||||
* S → used to represent the lighting model.
|
||||
* Diffuse → `RGBA8`
|
||||
* A → Ambient Occlusion
|
||||
* Emission → `RGB8`
|
||||
* Normal → `RGB8`
|
||||
* Specular → `RGB8`
|
||||
* R → Roughness
|
||||
* G → Specularity (sometimes called the Metallicness)
|
||||
* B → Index of Refraction (IOR)
|
||||
|
||||
Debug View: Depth, Stencil, Diffuse, Emission, Normal, Specularity
|
||||
|
||||
- Deferred Lighting Pass `(10 Bpp, 80 bpp) [1920x1080] ≈ 2 x 16.3MB + 8.3MB ≈ 24.6MB`
|
||||
- Depth Buffer → `D24`
|
||||
- Lighting Buffer → `RGB16` (w/ Mipmapping when Bloom or DoF are enabled)
|
||||
- Stencil Buffer $rarr; `S8`
|
||||
- Generate Dynamic Shadows
|
||||
- Generate Dynamic Reflections (Optional)
|
||||
- SSAO (Optional)
|
||||
- Apply Lighting Model
|
||||
|
||||
Debug View: Shadows, Reflections, SSAO, Deferred Lighting
|
||||
|
||||
* Forward Pass
|
||||
* BVH, Same as Above
|
||||
* LOD Selection, Same as Above
|
||||
* Translucent Materials
|
||||
* Dual Depth Peeling
|
||||
|
||||
Debug View: Forward Mask
|
||||
|
||||
- Post Processing
|
||||
- Depth of Field (Optional)
|
||||
- When enabled, the Visiblity Buffer, G-Buffer, and Deferred Lighting Pass will be double layered.
|
||||
- At this point the Lighting Buffers will be Flattened
|
||||
- Bloom (Optional) → Mipmap Blurring `(6Bpp, 48bpp) [1920x1080] ≈ 16.3MB`
|
||||
- Tonemapping (Optional)
|
||||
- HDR Correction
|
||||
|
||||
|
||||
|
||||
|
||||
## 3D Physics `(physics3d)`
|
||||
|
||||
Links:
|
||||
- https://www.researchgate.net/publication/264839743_Simulating_Ocean_Water
|
||||
- https://arxiv.org/pdf/2109.00104
|
||||
- https://www.youtube.com/watch?v=rSKMYc1CQHE
|
||||
- https://tflsguoyu.github.io/webpage/pdf/2013ICIA.pdf
|
||||
- https://animation.rwth-aachen.de/publication/0557/
|
||||
- https://github.com/InteractiveComputerGraphics/PositionBasedDynamics?tab=readme-ov-file
|
||||
- https://www.cs.umd.edu/class/fall2019/cmsc828X/LEC/PBD.pdf
|
||||
|
||||
Systems
|
||||
|
||||
* Rigid Body Physics
|
||||
* Newtonian Physics and Collision Resolution
|
||||
* Articulated Skeletal Systems
|
||||
* Inverse Kinematics
|
||||
* Stiff Rods
|
||||
- Particle Physics
|
||||
* Soft Body Physics
|
||||
* Elastics → Finite Element Simulation
|
||||
* Cloth → Position-Based Dynamics
|
||||
* Water
|
||||
* Oceans → iWave
|
||||
* Reasoning: iWave provides interactive lightweight fluid dynamics suitable for flat planes of water.
|
||||
<br><br>
|
||||
|
||||
* 3D Fluid Dynamics → Smoothed-Particle Hydrodynamics
|
||||
* Reasoning: This is the simplest method for simulating 3D bodies of water. This should exclusively be
|
||||
used for small scale simulations where self-interactive fluids are necessary. I.E. pouring water into
|
||||
a glass.
|
||||
<br><br>
|
||||
|
||||
* 2D Fluid Dynamics → Force-Based Dynamics
|
||||
* Reasoning: This model, like iWave, provides lightweight interactive fluid dynamics, but is more easily
|
||||
adapted to flowing surfaces such as streams and rivers.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Artificial Intelligence (`ai`)
|
||||
|
||||
This artificial intelligence method only differs in static generation between 2D and 3D.
|
||||
The solvers are dimension independent since they work on a graph.
|
||||
|
||||
The general process is;
|
||||
|
||||
Static:
|
||||
- generate a static navigation graph (sometimes called a NavMesh)
|
||||
|
||||
Update:
|
||||
* resolve dynamic blockers
|
||||
* update paths using dijkstra's algorithm
|
||||
* apply rigid-body forces with constraints
|
||||
|
||||
The update loop for artificial intelligence should only update every `n` ticks. Where `n <= k`, with `k` being the
|
||||
tick rate of the physics engine.
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# Readings
|
||||
|
||||
Here is a list of relevant books and articles on various concepts related to
|
||||
Here is a list of relevant books and articles on various concepts related to
|
||||
developing a game engine and its subsystems.
|
||||
|
||||
- Game Engine Architecture, Ed. 3 – Jason Gregory
|
||||
|
||||
194
README.md
194
README.md
@@ -8,17 +8,23 @@
|
||||
|
||||
<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) → `apt`
|
||||
* [Arch](#arch) → `pacman`
|
||||
* [Fedora](#fedora) → `dnf`
|
||||
* [Building on Windows](#building-on-windows)
|
||||
* [Running the Test Suite](#running-the-test-suite)
|
||||
* [Usage](#usage)
|
||||
* [Licensing](#licensing)
|
||||
* [Contribution](#contribution)
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
1. [Coding Standards](#coding-standards)
|
||||
2. [Building from Source](#building-from-source)
|
||||
1. [Building from Terminal](#building-from-terminal)
|
||||
2. [Building on Windows](#building-on-windows)
|
||||
3. [Running the Test Suite](#running-the-test-suite)
|
||||
4. [Usage](#usage)
|
||||
5. [Contribution](#contribution)
|
||||
6. [Documentation](./documentation.html)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -27,8 +33,8 @@
|
||||
<a id="introduction"></a>
|
||||
## Introduction
|
||||
|
||||
  fennec is designed to be a general purpose, educational game engine. fennec
|
||||
may be used through the provided editor application, or as a standalone library to
|
||||
  fennec is designed to be a general purpose, educational game engine. fennec
|
||||
may be used through the provided editor application, or as a standalone library to
|
||||
link against your application.
|
||||
|
||||
|
||||
@@ -38,7 +44,7 @@ link against your application.
|
||||
<a id="coding-standards"></a>
|
||||
### Coding Standards
|
||||
|
||||
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
Some main areas where the engine strays from the GNU standard includes the following:
|
||||
|
||||
* [Section 4.7, Standards for Graphical Interfaces](https://www.gnu.org/prep/standards/html_node/Graphical-Interfaces.html).
|
||||
@@ -46,23 +52,23 @@ Some main areas where the engine strays from the GNU standard includes the follo
|
||||
- [Section 6.1, GNU Manuals](https://www.gnu.org/prep/standards/html_node/GNU-Manuals.html)
|
||||
fennec does not use Texinfo and instead uses Doxygen. Otherwise, it follows the other standards of this section.
|
||||
* [Section 7, The Release Process](https://www.gnu.org/prep/standards/html_node/Managing-Releases.html)
|
||||
fennec follows most of the conventions in this section, however the build system used is CMake and not
|
||||
fennec follows most of the conventions in this section, however the build system used is CMake and not
|
||||
Makefile. CMake, although overwhelming at first, is much more friendly to those who are learning build systems for the first time.
|
||||
|
||||
<br>
|
||||
|
||||
fennec Standards:
|
||||
|
||||
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
|
||||
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
|
||||
`<APP_NAME>_`. Macros that wrap C-Style functions may use normal `snake_case`.
|
||||
|
||||
- Header Guards should be implemented using `#ifndef`, `#define`, and `#endif` for portability.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`.
|
||||
I.E. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
- Header Guards should be implemented using `#ifndef`, `#define`, and `#endif` for portability.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`.
|
||||
I.E. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
|
||||
* Helper Functions, in the case of classes, should be private.
|
||||
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
||||
* Helper Functions, in the case of classes, should be private.
|
||||
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
||||
Doxygen documentation.
|
||||
|
||||
- **DO NOT USE C++ EXCEPTIONS** they will not be supported because they are shit.<sup>[[1]](#f1)</sup>
|
||||
@@ -72,19 +78,19 @@ fennec Standards:
|
||||
<br><br>
|
||||
|
||||
<a id="f1"></a>
|
||||
<sup>[1]</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
|
||||
that the state will not be corrupted when an exception is thrown. The behaviour afterward is undefined
|
||||
because of this, and we also don't really know when, how, or where that exception will be handled.
|
||||
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
|
||||
code and how it is handled. In a debug build we can immediately halt the program, we don't care about
|
||||
the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
|
||||
so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
|
||||
fennec defines its own `assert` macro to be used, defining a hook in the private version of the
|
||||
function. This hook is used to clean up any state information within the engine and may be used to send
|
||||
immediate events to listeners so that outside functionality may decide how to handle the impending crash.
|
||||
In Debug Mode there is nothing that can be done to stop the crash, as soon as the branch finishes,
|
||||
<sup>[1]</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
|
||||
that the state will not be corrupted when an exception is thrown. The behaviour afterward is undefined
|
||||
because of this, and we also don't really know when, how, or where that exception will be handled.
|
||||
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
|
||||
code and how it is handled. In a debug build we can immediately halt the program, we don't care about
|
||||
the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
|
||||
so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
|
||||
fennec defines its own `assert` macro to be used, defining a hook in the private version of the
|
||||
function. This hook is used to clean up any state information within the engine and may be used to send
|
||||
immediate events to listeners so that outside functionality may decide how to handle the impending crash.
|
||||
In Debug Mode there is nothing that can be done to stop the crash, as soon as the branch finishes,
|
||||
`abort()` will be called.
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
@@ -104,11 +110,11 @@ There are a few reasons for this:
|
||||
<a id="building-from-source"></a>
|
||||
## Building from Source
|
||||
|
||||
  fennec uses the CMake build manager. The CMake build script provides several
|
||||
  fennec uses the CMake build manager. The CMake build script provides several
|
||||
targets for building parts of the engine.
|
||||
|
||||
  Using an IDE will streamline the build process for you and add additional configuration
|
||||
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
||||
  Using an IDE will streamline the build process for you and add additional configuration
|
||||
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
||||
is also a viable IDE but involves some extra setup.
|
||||
|
||||
| Target | Description |
|
||||
@@ -125,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 |
|
||||
| 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 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 |
|
||||
@@ -138,24 +145,66 @@ is also a viable IDE but involves some extra setup.
|
||||
`build.sh` provides profiles for building the main engine. Run `./build.sh --help`
|
||||
for more info.
|
||||
|
||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
||||
build scripts to use another build manager, see the [CMake documentation for available generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
|
||||
|
||||
  I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
|
||||
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
|
||||
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
|
||||
|
||||
  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>
|
||||
|
||||
<a id="building-on-windows"></a>
|
||||
### Building on Windows
|
||||
|
||||
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
||||
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
||||
the build [dependencies](#dependencies) installed and configured to be available on the `PATH` environment variable.
|
||||
|
||||
Fore more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
For more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
|
||||
Otherwise, follow the sequence of commands provided in the bash script.
|
||||
|
||||
@@ -178,8 +227,8 @@ The value of `<profile>` may be one of the following:
|
||||
|
||||
<br>
|
||||
|
||||
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
||||
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
||||
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||
of the CMake docs. Running the following command will generate the Visual Studio project.
|
||||
|
||||
```commandline
|
||||
@@ -194,9 +243,9 @@ cmake -G "Visual Studio 17 2022" -A x64
|
||||
|
||||
`test.sh` provides profiles for building the test suite and executes them.
|
||||
|
||||
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
||||
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
||||
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
||||
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
||||
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
||||
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
||||
information displayed is correct.
|
||||
|
||||
<br>
|
||||
@@ -206,33 +255,36 @@ information displayed is correct.
|
||||
## 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
|
||||
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.
|
||||
|
||||
  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.
|
||||
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.
|
||||
|
||||
  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
|
||||
  fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from
|
||||
modifying fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially,
|
||||
however it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
|
||||
fennec however you wish according to the terms of the license, which does not bar you from commercializing software based
|
||||
on fennec.
|
||||
|
||||
  If you wish to protect your game files, assets and generated content do not constitute a covered work and may be
|
||||
copyrighted under a non-compliant license. Think of it in terms of using Blender to create a mesh for a game, then
|
||||
licensing that mesh under another license.
|
||||
|
||||
Any code that is linked against fennec or uses any part is by definition a covered work must be licensed under GPLv3.
|
||||
|
||||
  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
|
||||
  Later down the line, I plan on implementing scripts in a manner that allows the script itself to remain proprietary.
|
||||
The scripts will likely be trans-compiled to another language before being compiled to binary, but this is only an
|
||||
intermediate step and will be erased when no longer needed.
|
||||
|
||||
  As long as you use the official editor, it will properly include licenses in project content when provided a license
|
||||
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
|
||||
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
|
||||
  As long as you use the official editor, it will properly include licenses in project content when provided a license
|
||||
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
|
||||
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
|
||||
licensed under GPLv3 must be available upon request.
|
||||
|
||||
  A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
|
||||
  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.
|
||||
@@ -248,7 +300,7 @@ The following practices are more likely to get my attention and enforcement:
|
||||
- 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
|
||||
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:
|
||||
@@ -272,23 +324,23 @@ The fact that a work is unpublished shall not itself bar a finding of fair use i
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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>
|
||||
|
||||
8
build.sh
8
build.sh
@@ -38,7 +38,7 @@ Help()
|
||||
Debug()
|
||||
{
|
||||
mkdir -p build/debug
|
||||
cd ./build/debug
|
||||
cd ./build/debug || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -47,7 +47,7 @@ Debug()
|
||||
Release()
|
||||
{
|
||||
mkdir -p build/release
|
||||
cd ./build/release
|
||||
cd ./build/release || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -56,7 +56,7 @@ Release()
|
||||
RelWithDebInfo()
|
||||
{
|
||||
mkdir -p build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -65,7 +65,7 @@ RelWithDebInfo()
|
||||
MinSizeRel()
|
||||
{
|
||||
mkdir -p build/minsizerel
|
||||
cd ./build/minsizerel
|
||||
cd ./build/minsizerel || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
# 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}")
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
# 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")
|
||||
|
||||
35
cmake/default_user.cmake
Normal file
35
cmake/default_user.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 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()
|
||||
@@ -16,8 +16,10 @@
|
||||
# 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 FENNEC_COMPILER_GCC=1)
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS _GLIBCXX_INCLUDE_NEXT_C_HEADERS=1 FENNEC_COMPILER_GCC=1 FENNEC_NO_INLINE=[[gnu::noinline]])
|
||||
@@ -16,25 +16,28 @@
|
||||
# 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
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_NAME="Linux"
|
||||
FENNEC_PLATFORM_LINUX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/linux/platform.h source/platform/linux/platform.cpp
|
||||
)
|
||||
|
||||
# includes
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/wayland.cmake")
|
||||
if(FENNEC_USER_CLIENT)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/wayland.cmake")
|
||||
|
||||
fennec_check_wayland()
|
||||
fennec_init_graphics()
|
||||
endif()
|
||||
|
||||
# tests
|
||||
fennec_check_wayland()
|
||||
endmacro()
|
||||
57
cmake/opengl.cmake
Normal file
57
cmake/opengl.cmake
Normal 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()
|
||||
@@ -16,8 +16,17 @@
|
||||
# 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")
|
||||
|
||||
@@ -16,7 +16,14 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
# generic unix functionality
|
||||
|
||||
# compile definitions
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_UNIX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
|
||||
)
|
||||
33
cmake/version.cmake
Normal file
33
cmake/version.cmake
Normal file
@@ -0,0 +1,33 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 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}
|
||||
)
|
||||
@@ -17,6 +17,24 @@
|
||||
# ======================================================================================================================
|
||||
|
||||
# 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)
|
||||
@@ -25,39 +43,80 @@ macro(fennec_check_wayland)
|
||||
WAYLAND_CLIENT_INCLUDE_DIR
|
||||
NAMES wayland-client.h
|
||||
)
|
||||
|
||||
find_library(
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
NAMES wayland-client libwayland-client
|
||||
)
|
||||
get_filename_component(
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
${WAYLAND_CLIENT_LIBRARY}
|
||||
NAME
|
||||
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)
|
||||
set(WAYLAND_CLIENT_FOUND 1)
|
||||
add_library(wayland::client UNKNOWN IMPORTED)
|
||||
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_target_properties(
|
||||
wayland::client PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${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
|
||||
)
|
||||
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
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/fwd.h
|
||||
include/fennec/platform/linux/wayland/lib/sym.h
|
||||
include/fennec/platform/linux/wayland/lib/sym_def.h
|
||||
include/fennec/platform/linux/wayland/lib/dyn.h source/platform/linux/wayland/lib/dyn.cpp
|
||||
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
|
||||
)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}")
|
||||
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
58
cmake/xkb.cmake
Normal 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()
|
||||
@@ -49,6 +49,7 @@
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
@@ -83,7 +84,6 @@
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -105,6 +105,7 @@
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
@@ -121,7 +122,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -150,6 +150,7 @@
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -167,7 +168,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
@@ -185,6 +185,7 @@
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
@@ -210,7 +211,6 @@
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
@@ -237,6 +237,7 @@
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -246,7 +247,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
@@ -256,10 +256,10 @@
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = fennec
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.0.2
|
||||
PROJECT_NUMBER =
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -944,7 +944,7 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = "/home/medusa/Documents/Work/Personal/fennec/include" \
|
||||
"/home/medusa/Documents/Work/Personal/fennec/source" \
|
||||
"/home/medusa/Documents/Work/Personal/fennec/source" \
|
||||
"/home/medusa/Documents/Work/Personal/fennec/README.md"
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
@@ -1099,7 +1099,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \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
|
||||
# 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.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_OUTPUT = ./latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
@@ -2664,7 +2664,7 @@ TEMPLATE_RELATIONS = NO
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDE_GRAPH = NO
|
||||
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
@@ -2676,7 +2676,7 @@ INCLUDE_GRAPH = YES
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
|
||||
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
|
||||
# dependency graph for every global function or class method.
|
||||
|
||||
@@ -944,7 +944,7 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = "@PROJECT_SOURCE_DIR@/include" \
|
||||
"@PROJECT_SOURCE_DIR@/source" \
|
||||
"@PROJECT_SOURCE_DIR@/source" \
|
||||
"@PROJECT_SOURCE_DIR@/README.md"
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
@@ -1099,7 +1099,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = "@PROJECT_SOURCE_DIR@/doxy/static"
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -1981,7 +1981,7 @@ GENERATE_LATEX = NO
|
||||
# The default directory is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_OUTPUT = ./latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
@@ -2664,7 +2664,7 @@ TEMPLATE_RELATIONS = NO
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDE_GRAPH = NO
|
||||
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
@@ -2676,7 +2676,7 @@ INCLUDE_GRAPH = YES
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
|
||||
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
|
||||
# dependency graph for every global function or class method.
|
||||
|
||||
@@ -286,4 +286,9 @@ html.dark-mode {
|
||||
|
||||
td.odd_c {
|
||||
background-color: var(--odd-color)
|
||||
}
|
||||
}
|
||||
|
||||
a + h2.groupheader {
|
||||
display:none;
|
||||
}
|
||||
|
||||
|
||||
71
doxy/retrieve-emojis.py
Normal file
71
doxy/retrieve-emojis.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# script to download the emoticons from GitHub and to produce a table for
|
||||
# inclusion in Doxygen. Works with python 2.7+ and python 3.x
|
||||
import json
|
||||
import os
|
||||
import argparse
|
||||
import re
|
||||
try:
|
||||
import urllib.request as urlrequest
|
||||
except ImportError:
|
||||
import urllib as urlrequest
|
||||
|
||||
unicode_re = re.compile(r'.*?/unicode/(.*?).png\?.*')
|
||||
|
||||
def get_emojis():
|
||||
response = urlrequest.urlopen('https://api.github.com/emojis')
|
||||
raw_data = response.read()
|
||||
return json.loads(raw_data)
|
||||
|
||||
def download_images(dir_name, silent):
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
json_data = get_emojis()
|
||||
num_items = len(json_data)
|
||||
cur_item=0
|
||||
for image,url in sorted(json_data.items()):
|
||||
image_name = image+'.png'
|
||||
cur_item=cur_item+1
|
||||
if url.find('/unicode/')==-1 or not os.path.isfile(dir_name+'/'+image_name):
|
||||
success = True
|
||||
with open(dir_name+'/'+image_name,'wb') as file:
|
||||
if not silent:
|
||||
print('%s/%s: fetching %s' % (cur_item,num_items,image_name))
|
||||
try:
|
||||
file.write(urlrequest.urlopen(url).read())
|
||||
except:
|
||||
print('Unable to fetch %s' % (image_name))
|
||||
success = False
|
||||
if not success:
|
||||
os.remove(dir_name+'/'+image_name)
|
||||
else:
|
||||
if not silent:
|
||||
print('%s/%s: skipping %s' % (cur_item,num_items,image_name))
|
||||
|
||||
def produce_table():
|
||||
json_data = get_emojis()
|
||||
lines = []
|
||||
for image,url in sorted(json_data.items()):
|
||||
match = unicode_re.match(url)
|
||||
if match:
|
||||
unicodes = match.group(1).split('-')
|
||||
unicodes_html = ''.join(["&#x"+x+";" for x in unicodes])
|
||||
image_str = "\":"+image+":\","
|
||||
unicode_str = "\""+unicodes_html+"\""
|
||||
lines.append(' { %-42s %-38s }' % (image_str,unicode_str))
|
||||
out_str = ',\n'.join(lines)
|
||||
print("{")
|
||||
print(out_str)
|
||||
print("};")
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-d','--dir',help='directory to place images in')
|
||||
parser.add_argument('-t','--table',help='generate code fragment',action='store_true')
|
||||
parser.add_argument('-s','--silent',help='silent mode',action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.table:
|
||||
produce_table()
|
||||
if args.dir:
|
||||
download_images(args.dir, args.silent)
|
||||
4
doxy/static/graphs/containers/rdtree.svg
Normal file
4
doxy/static/graphs/containers/rdtree.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 60 KiB |
2
external/cpptrace
vendored
2
external/cpptrace
vendored
Submodule external/cpptrace updated: 9133b90a99...787d8af6f6
36
gdb/fennec/__init__.py
Normal file
36
gdb/fennec/__init__.py
Normal 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
481
gdb/fennec/containers.py
Normal 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
60
gdb/fennec/filesystem.py
Normal 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
99
gdb/fennec/math.py
Normal 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
43
gdb/fennec/memory.py
Normal 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
61
gdb/fennec/strings.py
Normal 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
20
gdb/fennec/utility.py
Normal 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)))
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
///
|
||||
/// \file array.h
|
||||
/// \brief statically allocated array wrapper
|
||||
/// \brief A header containing the definition for a static/stack allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
@@ -40,49 +40,48 @@ namespace fennec
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for fixed size arrays
|
||||
/// \brief Data Structure that defines a compile-time allocated array
|
||||
///
|
||||
/// \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 ElemV number of elements
|
||||
template<typename ValueT, size_t ElemV>
|
||||
struct array
|
||||
{
|
||||
///
|
||||
/// \brief backing c-style array handle
|
||||
ValueT elements[ElemV];
|
||||
struct array {
|
||||
|
||||
/// \name Element Access
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using value_t = ValueT; ///< Alias for `ValueT`
|
||||
|
||||
// Public Members ======================================================================================================
|
||||
|
||||
/// \name Public Members
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \copydetails array::operator[](size_t) const
|
||||
constexpr ValueT& operator[](size_t i) {
|
||||
assertd(i < ElemV, "Array Out of Bounds");
|
||||
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 {
|
||||
assertd(i < ElemV, "Array Out of Bounds");
|
||||
return elements[i];
|
||||
}
|
||||
/// \brief backing c-style array handle
|
||||
value_t data[ElemV];
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
/// \name Capacity
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
@@ -99,24 +98,126 @@ 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
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \brief Checks if all elements in the arrays are equal
|
||||
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>{});
|
||||
}
|
||||
|
||||
///
|
||||
/// \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>{});
|
||||
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:
|
||||
template<size_t...i>
|
||||
static bool __compare(const array& lhs, const array& rhs, index_sequence<i...>) {
|
||||
static bool _compare(const array& lhs, const array& rhs, const_index_sequence<i...>) {
|
||||
return ((lhs[i] == rhs[i]) && ...);
|
||||
}
|
||||
};
|
||||
|
||||
94
include/fennec/containers/containers.h
Normal file
94
include/fennec/containers/containers.h
Normal 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
|
||||
345
include/fennec/containers/deque.h
Normal file
345
include/fennec/containers/deque.h
Normal 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
|
||||
@@ -18,40 +18,42 @@
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#define FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#include <fennec/lang/sequences.h>
|
||||
#include <fennec/lang/const_sequences.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
// leaves
|
||||
template<size_t i, typename T>
|
||||
struct __tuple_leaf {
|
||||
T value;
|
||||
|
||||
template<typename...ArgsT>
|
||||
__tuple_leaf(ArgsT&&...args) : value(args...) {
|
||||
}
|
||||
template <std::size_t I, typename T>
|
||||
struct _tuple_leaf
|
||||
{
|
||||
template <typename ArgT>
|
||||
constexpr _tuple_leaf(ArgT&& arg) : value(fennec::forward<ArgT>(arg)) {}
|
||||
|
||||
constexpr operator T&() {
|
||||
return value;
|
||||
}
|
||||
constexpr ~_tuple_leaf() = default;
|
||||
|
||||
constexpr operator const T&() const {
|
||||
return value;
|
||||
}
|
||||
constexpr _tuple_leaf& operator=(const _tuple_leaf&) = default;
|
||||
constexpr _tuple_leaf& operator=(_tuple_leaf&&) noexcept = default;
|
||||
|
||||
T value;
|
||||
};
|
||||
|
||||
// proxy
|
||||
template<typename, typename...TypesT>
|
||||
struct __tuple;
|
||||
template <typename, typename...>
|
||||
struct _tuple;
|
||||
|
||||
template<size_t...i, typename...TypesT>
|
||||
struct __tuple<index_sequence<i...>, TypesT...> : __tuple_leaf<i, TypesT>... {
|
||||
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))... {
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
__tuple(ArgsT&&...args) : __tuple_leaf<i, TypesT>(args)... {
|
||||
}
|
||||
constexpr _tuple& operator=(const _tuple&) = default;
|
||||
constexpr _tuple& operator=(_tuple&&) noexcept = default;
|
||||
|
||||
constexpr ~_tuple() = default;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
///
|
||||
/// \file dynarray.h
|
||||
/// \brief dynamically allocated array wrapper
|
||||
/// \brief A header containing the definition for a dynamically allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
@@ -38,95 +38,290 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \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
|
||||
{
|
||||
class dynarray {
|
||||
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.
|
||||
dynarray() : _alloc(8), _size(0) {}
|
||||
constexpr dynarray()
|
||||
: _alloc(8)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Constructor, initialize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
||||
dynarray(const alloc_t& alloc)
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
|
||||
/// data.
|
||||
explicit constexpr dynarray(const alloc_t& alloc)
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation, create an allocation with a size of `n` elements, initialized with the default constructor.
|
||||
dynarray(size_t n)
|
||||
/// \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)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \param n
|
||||
/// \param alloc
|
||||
dynarray(size_t n, const alloc_t& alloc)
|
||||
/// \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) {
|
||||
element_t* addr = _alloc.data();
|
||||
value_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 n the number of elements
|
||||
dynarray(size_t n, const TypeT& val)
|
||||
/// \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) {
|
||||
element_t* addr = _alloc.data();
|
||||
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>
|
||||
dynarray(size_t n, ArgsT&&...args) {
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr, fennec::forward<ArgsT>(args)...);
|
||||
constexpr explicit dynarray(size_t n, ArgsT&&...args)
|
||||
: _alloc(n)
|
||||
, _size(n) {
|
||||
for(; n > 0; --n) {
|
||||
fennec::construct(&_alloc[n], fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
~dynarray() {
|
||||
element_t* addr = _alloc.data();
|
||||
///
|
||||
/// \brief Copy Constructor, uses the copy constructor to copy each element
|
||||
/// \param arr the dynarray to copy
|
||||
constexpr dynarray(const dynarray& arr)
|
||||
: _alloc(arr._size)
|
||||
, _size(arr._size) {
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of the allocation
|
||||
/// \param arr the dynarray to move
|
||||
constexpr dynarray(dynarray&& arr) noexcept
|
||||
: _alloc(fennec::move(arr._alloc))
|
||||
, _size(arr._size) {
|
||||
arr._size = 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
||||
constexpr ~dynarray() {
|
||||
value_t* addr = _alloc.data();
|
||||
if (addr == nullptr) return;
|
||||
for(int n = _size; n > 0; --n, ++addr) {
|
||||
fennec::destruct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
/// @}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
///
|
||||
/// \returns The current capacity, in elements, of the underlying allocation
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
TypeT& operator[](size_t i) {
|
||||
assertd(i < _size, "Array Out of Bounds"); return _alloc.data()[i];
|
||||
///
|
||||
/// \returns True when there are no elements active, otherwise false
|
||||
constexpr bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
const TypeT& operator[](size_t i) const {
|
||||
assertd(i < _size, "Array Out of Bounds"); return _alloc.data()[i];
|
||||
/// @}
|
||||
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
void insert(size_t i, const TypeT& val) {
|
||||
///
|
||||
/// \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) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) {
|
||||
@@ -136,28 +331,8 @@ public:
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i
|
||||
, _alloc.data() + i + 1
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, val);
|
||||
++_size;
|
||||
}
|
||||
|
||||
void insert(size_t i, TypeT&& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) {
|
||||
_grow();
|
||||
}
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i + 1
|
||||
, _alloc.data() + i
|
||||
(void*)(_alloc.data() + i + 1)
|
||||
, (void*)(_alloc.data() + i)
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
}
|
||||
|
||||
@@ -166,8 +341,11 @@ public:
|
||||
++_size;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void emplace(size_t i, ArgsT&&...args) {
|
||||
///
|
||||
/// \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()) {
|
||||
@@ -177,8 +355,35 @@ public:
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
_alloc.data() + i
|
||||
, _alloc.data() + i + 1
|
||||
(void*)(_alloc.data() + i),
|
||||
(void*)(_alloc.data() + i + 1),
|
||||
(_size - i) * sizeof(TypeT)
|
||||
);
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
fennec::construct(_alloc.data() + i, val);
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(size_t i, ArgsT&&...args) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
if(_size == capacity()) {
|
||||
_grow();
|
||||
}
|
||||
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
(void*)(_alloc.data() + i)
|
||||
, (void*)(_alloc.data() + i + 1)
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
}
|
||||
|
||||
@@ -187,28 +392,91 @@ public:
|
||||
++_size;
|
||||
}
|
||||
|
||||
void push_back(const TypeT& val) {
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param val Value to initialize with
|
||||
constexpr void push_back(const TypeT& val) {
|
||||
dynarray::insert(_size, val);
|
||||
}
|
||||
|
||||
void push_back(TypeT&& val) {
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param val Value to initialize with
|
||||
constexpr void push_back(TypeT&& val) {
|
||||
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
||||
}
|
||||
|
||||
template<typename...ArgsT> void emplace_back(ArgsT...args) {
|
||||
///
|
||||
/// \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)...);
|
||||
}
|
||||
|
||||
void resize(size_t n) {
|
||||
_alloc.reallocate(n);
|
||||
///
|
||||
/// \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:
|
||||
void _grow() {
|
||||
_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;
|
||||
};
|
||||
|
||||
|
||||
518
include/fennec/containers/graph.h
Normal file
518
include/fennec/containers/graph.h
Normal 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` → `v` *or* `v` → `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` → `v` *and* `v` → `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
|
||||
655
include/fennec/containers/list.h
Normal file
655
include/fennec/containers/list.h
Normal 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
|
||||
@@ -16,6 +16,18 @@
|
||||
// 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
|
||||
|
||||
@@ -25,14 +37,12 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
/* Ramblings
|
||||
*
|
||||
* Definitions:
|
||||
* user = Programmer using this data structure
|
||||
*
|
||||
* The STL unordered-map is very contrived. Some of its functionality encourages younger programmers to use
|
||||
* 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
|
||||
@@ -40,19 +50,46 @@ namespace fennec
|
||||
* 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 an optional which forces user validation.
|
||||
* 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:
|
||||
using key_t = KeyT;
|
||||
using value_t = ValueT;
|
||||
using elem_t = pair<KeyT, ValueT>;
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>;
|
||||
using hash_t = Hash;
|
||||
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 {
|
||||
@@ -62,128 +99,212 @@ public:
|
||||
};
|
||||
|
||||
// We only want to compare the keys
|
||||
struct node_equals : equality<KeyT> {
|
||||
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) {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { key, 0 } };
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_set.at(it)->second;
|
||||
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 {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { key, 0 } }; // Only initialize root
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_set.at(it)->second;
|
||||
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) {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { key_t(fennec::forward<ArgsT>(args)...), 0 } }; // Only initialize root
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_set.at(it)->second;
|
||||
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 {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { key_t(fennec::forward<ArgsT>(args)...), 0 } }; // Only initialize root
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_set.at(it)->second;
|
||||
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) {
|
||||
auto it = _set.find(pair);
|
||||
if (it == _set.end()) {
|
||||
_set.at(it)->second = fennec::move(pair.second);
|
||||
return;
|
||||
}
|
||||
_set.insert(fennec::forward<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) {
|
||||
_set.insert(elem_t(args...));
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
constexpr void erase(KeyT&& key) {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { fennec::forward<KeyT>(key), 0 } };
|
||||
_set.erase(trick.val);
|
||||
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
constexpr void erase(const KeyT& key) {
|
||||
KeyT val = key;
|
||||
erase(fennec::move(val));
|
||||
_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) {
|
||||
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() {
|
||||
root.~pair<KeyT, char[sizeof(ValueT)]>();
|
||||
}
|
||||
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
|
||||
_set.erase(trick.val);
|
||||
_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<elem_t, key_hash, node_equals, alloc_t> _set;
|
||||
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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
586
include/fennec/containers/multiset.h
Normal file
586
include/fennec/containers/multiset.h
Normal 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
|
||||
209
include/fennec/containers/object_pool.h
Normal file
209
include/fennec/containers/object_pool.h
Normal 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
|
||||
@@ -16,6 +16,18 @@
|
||||
// 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
|
||||
|
||||
@@ -37,12 +49,19 @@ constexpr nullopt_t nullopt_v = {};
|
||||
/// \tparam T
|
||||
template<typename T>
|
||||
struct optional {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
// Constructors ========================================================================================================
|
||||
using reference_t = T&;
|
||||
using pointer_t = add_pointer_t<remove_reference_t<T>>;
|
||||
using const_reference_t = const T&;
|
||||
using const_pointer_t = const add_pointer_t<remove_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
|
||||
@@ -59,18 +78,18 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Fundamental Type Constructor
|
||||
/// \brief Type Copy Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
constexpr optional(T val) requires is_fundamental_v<T>
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Copy Constructor
|
||||
/// \brief Type Move Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
constexpr optional(T&& val)
|
||||
: _val(fennec::forward<T>(val))
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
@@ -97,25 +116,56 @@ public:
|
||||
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) {
|
||||
_val.~T();
|
||||
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
|
||||
/// \val The value to set with
|
||||
/// \param val The value to set with
|
||||
constexpr optional& operator=(nullopt_t) {
|
||||
if constexpr(not is_fundamental_v<T>) {
|
||||
if (_set) {
|
||||
_val.~T();
|
||||
fennec::destruct(&_val);
|
||||
}
|
||||
}
|
||||
_root = '\0';
|
||||
@@ -125,7 +175,7 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Type Copy Assignment
|
||||
/// \val The value to set with
|
||||
/// \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;
|
||||
@@ -138,12 +188,12 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Type Move Assignment
|
||||
/// \val The value to set with
|
||||
/// \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::move(val);
|
||||
_val = fennec::forward<T>(val);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::move(val));
|
||||
fennec::construct(&_val, fennec::forward<T>(val));
|
||||
_set = true;
|
||||
}
|
||||
return *this;
|
||||
@@ -151,14 +201,14 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment
|
||||
/// \val The optional to copy
|
||||
/// \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
|
||||
_val.~T();
|
||||
fennec::destruct(&_val);
|
||||
_root = 0;
|
||||
}
|
||||
} else if (_set) { // Copy Assignment
|
||||
@@ -169,14 +219,14 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Move Assignment
|
||||
/// \val The optional to move
|
||||
/// \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
|
||||
_val.~T();
|
||||
fennec::destruct(&_val);
|
||||
_root = 0;
|
||||
}
|
||||
} else if (_set) { // Copy Assignment
|
||||
@@ -185,6 +235,68 @@ public:
|
||||
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) {
|
||||
@@ -196,44 +308,13 @@ public:
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Reset the Optional
|
||||
void reset() {
|
||||
this->operator=(nullopt);
|
||||
}
|
||||
|
||||
|
||||
// Operators ===========================================================================================================
|
||||
|
||||
constexpr operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
|
||||
constexpr pointer_t operator->() noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
constexpr const pointer_t operator->() const noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
constexpr T& operator*() & noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
constexpr T&& operator*() && noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
constexpr const T&& operator*() const&& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,18 @@
|
||||
// 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
|
||||
|
||||
@@ -28,67 +40,144 @@ namespace fennec
|
||||
|
||||
// TODO: Document
|
||||
|
||||
template<typename T0, typename T1>
|
||||
///
|
||||
/// \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;
|
||||
|
||||
constexpr pair(const T0& x, const T1& y)
|
||||
///
|
||||
/// \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) {
|
||||
}
|
||||
|
||||
constexpr pair(T0&& x, T1&& y) noexcept
|
||||
: first(fennec::forward<T0>(x))
|
||||
, second(fennec::forward<T1>(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;
|
||||
|
||||
pair& operator=(const pair&) = default;
|
||||
pair& operator=(pair&&) noexcept = default;
|
||||
///
|
||||
/// \brief Copy Assignment, copies both elements
|
||||
constexpr pair& operator=(const pair&) = default;
|
||||
|
||||
bool operator==(const pair& p) const {
|
||||
///
|
||||
/// \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;
|
||||
}
|
||||
|
||||
bool operator!=(const pair& p) const {
|
||||
///
|
||||
/// \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;
|
||||
}
|
||||
|
||||
bool operator<(const pair& p) const {
|
||||
///
|
||||
/// \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);
|
||||
}
|
||||
|
||||
bool operator<=(const pair& p) const {
|
||||
///
|
||||
/// \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);
|
||||
}
|
||||
|
||||
bool operator>(const pair& p) const {
|
||||
///
|
||||
/// \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);
|
||||
}
|
||||
|
||||
bool operator>=(const pair& p) const {
|
||||
///
|
||||
/// \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);
|
||||
}
|
||||
|
||||
T0 first;
|
||||
T1 second;
|
||||
/// @}
|
||||
};
|
||||
|
||||
template<typename T0, typename T1>
|
||||
struct hash<pair<T0, T1>> : hash<T0>, hash<T1> {
|
||||
constexpr size_t operator()(const pair<T0, T1>& p) const {
|
||||
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<T0>::operator()(p.first),
|
||||
hash<T1>::operator()(p.second)
|
||||
hash<TypeT0>::operator()(p.first),
|
||||
hash<TypeT1>::operator()(p.second)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
626
include/fennec/containers/rdtree.h
Normal file
626
include/fennec/containers/rdtree.h
Normal 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
|
||||
@@ -16,12 +16,25 @@
|
||||
// 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>
|
||||
@@ -30,15 +43,36 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
template<typename T, class Hash = hash<T>, class Equals = equality<T>, class Alloc = allocator<T>>
|
||||
///
|
||||
///
|
||||
/// \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<T>;
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using hash_t = Hash;
|
||||
using equal_t = Equals;
|
||||
using elem_t = T;
|
||||
using elem_t = TypeT;
|
||||
|
||||
class iterator;
|
||||
static constexpr size_t npos = -1;
|
||||
@@ -53,160 +87,233 @@ private:
|
||||
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) {
|
||||
}
|
||||
|
||||
constexpr set(hash_t&& hash) noexcept
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
, _size(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) {
|
||||
}
|
||||
|
||||
constexpr set(alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
, _size(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) {
|
||||
}
|
||||
|
||||
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(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)
|
||||
, _load(default_load) {
|
||||
, _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)) {
|
||||
, _size(fennec::move(set._size))
|
||||
, _sumpsl(set._sumpsl)
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
constexpr ~set() = default;
|
||||
///
|
||||
/// \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.capacity();
|
||||
return _alloc.size();
|
||||
}
|
||||
|
||||
constexpr void insert(elem_t&& val) {
|
||||
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
/// @}
|
||||
|
||||
elem_t value = fennec::forward<elem_t>(val);
|
||||
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, val)) { // Check to see if this element is already inserted
|
||||
return;
|
||||
}
|
||||
if (psl >= _alloc[i].psl) { // When psl is higher, swap
|
||||
fennec::swap(*_alloc[i].value, value);
|
||||
fennec::swap(_alloc[i].psl, psl);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_alloc[i].value = fennec::move(value);
|
||||
_alloc[i].psl = psl;
|
||||
++_size;
|
||||
}
|
||||
|
||||
constexpr void insert(const elem_t& val) {
|
||||
elem_t value = val; // Copy Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
// Access ==============================================================================================================
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
elem_t value = elem_t(fennec::forward<ArgsT>(args)...); // Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
/// \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 {
|
||||
size_t i = _hash(val) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
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;
|
||||
|
||||
// Loop while there is a value and its psl is greater than our probe
|
||||
while (_alloc[i].value && _alloc[i].psl <= psl) {
|
||||
// Check the first element;
|
||||
if (_alloc[i].psl >= psl && _alloc[i].value) {
|
||||
if (_equal(*_alloc[i].value, val)) {
|
||||
return iterator(this, i);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) return nullptr;
|
||||
if (not _alloc[i].value) return nullptr;
|
||||
return &*_alloc[i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) return nullopt;
|
||||
if (not _alloc[i].value) return nullopt;
|
||||
return &*_alloc[i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \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()) {
|
||||
@@ -217,25 +324,61 @@ public:
|
||||
}
|
||||
|
||||
_alloc[i].value = nullopt;
|
||||
_sumpsl -= _alloc[i].psl;
|
||||
--_size;
|
||||
size_t p = i;
|
||||
while (_alloc[i = (i + 1) % capacity()].value) {
|
||||
size_t psl = _alloc[i].psl;
|
||||
if (psl == 0) break;
|
||||
if (_alloc[i].psl == 0) break;
|
||||
|
||||
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
|
||||
_alloc[p].psl = psl - 1;
|
||||
--_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)
|
||||
@@ -249,8 +392,8 @@ public:
|
||||
|
||||
// prefix operator
|
||||
constexpr friend iterator& operator++(iterator& rhs) {
|
||||
while (++rhs._i < capacity()) {
|
||||
if (rhs._set->_alloc[rhs._i]) {
|
||||
while (++rhs._i < rhs._set->capacity()) {
|
||||
if (rhs._set->_alloc[rhs._i].value) {
|
||||
return rhs;
|
||||
}
|
||||
}
|
||||
@@ -268,39 +411,34 @@ public:
|
||||
return *_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator& it) {
|
||||
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) {
|
||||
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;
|
||||
};
|
||||
|
||||
constexpr iterator begin() const {
|
||||
iterator it(this, 0);
|
||||
if (not _alloc[it._i].value) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr iterator end() const {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE =============================================================================================================
|
||||
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
set cpy; // Create a new set
|
||||
cpy._alloc.callocate(
|
||||
cpy._alloc.resize(
|
||||
fennec::next_prime2(_alloc.capacity())
|
||||
);
|
||||
|
||||
@@ -315,11 +453,38 @@ private:
|
||||
fennec::swap(_alloc, cpy._alloc);
|
||||
}
|
||||
|
||||
allocation<node, alloc_t> _alloc;
|
||||
hash_t _hash;
|
||||
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;
|
||||
double _load;
|
||||
size_t _size;
|
||||
size_t _sumpsl;
|
||||
float _load;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
47
include/fennec/containers/traversal.h
Normal file
47
include/fennec/containers/traversal.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 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
|
||||
@@ -16,10 +16,22 @@
|
||||
// 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/containers/detail/_tuple.h>
|
||||
#include <fennec/lang/type_sequences.h>
|
||||
|
||||
namespace fennec
|
||||
@@ -27,38 +39,68 @@ 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>
|
||||
typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
||||
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;
|
||||
auto& it = *static_cast<detail::_tuple_leaf<i, elem_t>*>(&x);
|
||||
return it.value;
|
||||
}
|
||||
|
||||
template<size_t i, typename...TypesT>
|
||||
const typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
||||
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const 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;
|
||||
const auto& it = *static_cast<const detail::_tuple_leaf<i, elem_t>*>(&x);
|
||||
return it.value;
|
||||
}
|
||||
|
||||
|
||||
template<typename...TypesT>
|
||||
struct tuple : detail::__tuple<make_index_sequence<sizeof...(TypesT)>, TypesT...> {
|
||||
|
||||
public:
|
||||
using base_t = detail::__tuple<make_index_sequence<sizeof...(TypesT)>, TypesT...>;
|
||||
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 = nth_element<i, TypesT...>;
|
||||
using elem_t = typename nth_element<i, TypesT...>::type;
|
||||
|
||||
template<typename...ArgsT>
|
||||
tuple(ArgsT&&...args) : base_t(args...) {
|
||||
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
|
||||
|
||||
34
include/fennec/containers/variant.h
Normal file
34
include/fennec/containers/variant.h
Normal 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
|
||||
@@ -29,19 +29,25 @@
|
||||
///
|
||||
|
||||
///
|
||||
/// \page documentation Documentation
|
||||
/// \page contents Contents
|
||||
///
|
||||
/// 1. \ref introduction "Introduction"
|
||||
/// 1. \ref coding-standards "Coding Standards"
|
||||
/// 2. \ref building-from-source "Building from Source"
|
||||
/// 1. \ref building-from-terminal "Building from Terminal"
|
||||
/// 1. \ref debian "Debian"
|
||||
/// 2. \ref arch "Arch"
|
||||
/// 3. \ref fedora "Fedora"
|
||||
/// 2. \ref building-on-windows "Building on Windows"
|
||||
/// 3. \ref running-the-test-suite "Running the Test Suite"
|
||||
/// 4. \ref usage "Usage"
|
||||
/// 1. \ref licensing "Licensing"
|
||||
/// 5. \ref contribution "Contribution"
|
||||
/// 6. \subpage libraries
|
||||
/// 1. \ref fennec_lang "C++ Language Library"
|
||||
/// 2. \ref fennec_math "Math Library"
|
||||
/// 2. \ref fennec_memory "Memory Management Library"
|
||||
/// 2. \ref fennec_containers "Containers Library"
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
@@ -49,10 +55,16 @@
|
||||
///
|
||||
/// \page libraries Libraries
|
||||
///
|
||||
/// | Library | Brief |
|
||||
/// | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
/// | \subpage fennec_lang | Implementation for functions and classes related to the C++ Language, including base types, common utility functions, and metaprogramming templates |
|
||||
/// | \subpage fennec_math | Implementation of math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). Additional extensions are provided for other common math functions. |
|
||||
/// | Library | Brief |
|
||||
/// |:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// | \subpage fennec_lang | Implementation for functions and classes related to the C++ Language, including base types, common utility functions, and metaprogramming templates |
|
||||
/// | \subpage fennec_math | Implementation of math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). Additional extensions are provided for other common math functions. |
|
||||
/// | \subpage fennec_memory | Implementation of functions related to memory management. |
|
||||
/// | \subpage fennec_containers | Implementation of common data structures, those that are specified in the C++ STD Library, and custom data structures that fennec uses. |
|
||||
///
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CORE_ENGINE_H
|
||||
#define FENNEC_CORE_ENGINE_H
|
||||
|
||||
61
include/fennec/core/event.h
Normal file
61
include/fennec/core/event.h
Normal 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
|
||||
47
include/fennec/core/system.h
Normal file
47
include/fennec/core/system.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 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
|
||||
@@ -64,27 +64,27 @@
|
||||
///
|
||||
///
|
||||
|
||||
#if _MSC_VER
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
using assert_handler = void (*)(const char *, const char *, int , const char *);
|
||||
|
||||
void __assert_impl(const char* expression, const char* file, int line, const char* function, const char* desc, bool halt);
|
||||
void _assert_impl(const char* expression, const char* file, int line, const char* function, const char* desc, bool halt);
|
||||
|
||||
// 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); \
|
||||
_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); \
|
||||
_assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
|
||||
}
|
||||
|
||||
#if FENNEC_RELEASE
|
||||
#define assertd(expression, description) (0)
|
||||
#define assertd(expression, description)
|
||||
#else
|
||||
#define assertd(expression, description) assert(expression, description)
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
#include <fennec/lang/intrinsics.h>
|
||||
#include <fennec/memory/common.h>
|
||||
#include <fennec/lang/detail/__bits.h>
|
||||
#include <fennec/lang/detail/_bits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -126,7 +126,7 @@ constexpr void* bit_and(void* arr, const void* mask, size_t n) {
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
|
||||
while (n > 0) {
|
||||
const size_t step = detail::__bit_and(d, s, n);
|
||||
const size_t step = detail::_bit_and(d, s, n);
|
||||
d += step; s += step; n -= step;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ constexpr void* bit_or(void* arr, const void* mask, size_t n) {
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
while (n > 0)
|
||||
{
|
||||
const 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;
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ constexpr void* bit_xor(void* arr, const void* mask, size_t n) {
|
||||
uint8_t* d = static_cast<uint8_t*>(arr);
|
||||
const uint8_t* s = static_cast<const uint8_t*>(mask);
|
||||
while (n > 0) {
|
||||
const size_t step = detail::__bit_xor(d, s, n);
|
||||
const size_t step = detail::_bit_xor(d, s, n);
|
||||
d += step; s += step; n -= step;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,13 +91,13 @@ namespace fennec
|
||||
/// \endcode
|
||||
/// \tparam ValueT type of the 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
|
||||
using value_type = ValueT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = sequence;
|
||||
using type = const_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
@@ -119,13 +119,13 @@ template<typename ValueT, ValueT...Values> struct sequence
|
||||
/// \tparam IntT type of the values, must satisfy ```fennec::is_integral<T>```
|
||||
/// \tparam Values sequence values
|
||||
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
|
||||
using value_type = IntT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = integer_sequence;
|
||||
using type = const_integer_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
@@ -158,13 +158,13 @@ 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.
|
||||
/// \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
|
||||
using value_type = size_t;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = index_sequence;
|
||||
using type = const_index_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
@@ -213,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>>{};
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
template<> struct make_index_sequence<0> : index_sequence<> {};
|
||||
template<> struct make_index_sequence<0> : const_index_sequence<> {};
|
||||
|
||||
// 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
|
||||
template<typename T, T...SequenceV0, T...SequenceV1>
|
||||
struct concat_sequence<integer_sequence<T, SequenceV0...>, integer_sequence<T, SequenceV1...>>
|
||||
: integer_sequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
struct concat_sequence<const_integer_sequence<T, SequenceV0...>, const_integer_sequence<T, SequenceV1...>>
|
||||
: const_integer_sequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
// Specialization for index sequences
|
||||
template<size_t...SequenceV0, size_t...SequenceV1>
|
||||
struct concat_sequence<index_sequence<SequenceV0...>, index_sequence<SequenceV1...>>
|
||||
: index_sequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
struct concat_sequence<const_index_sequence<SequenceV0...>, const_index_sequence<SequenceV1...>>
|
||||
: const_index_sequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#ifndef FENNEC_LANG_CONSTANTS_H
|
||||
#define FENNEC_LANG_CONSTANTS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
///
|
||||
///
|
||||
/// \page fennec_lang_constants Constants
|
||||
@@ -61,8 +63,6 @@
|
||||
/// </table>
|
||||
///
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
|
||||
@@ -1,59 +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::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
|
||||
@@ -1,75 +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::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
|
||||
@@ -25,112 +25,112 @@ namespace fennec::detail
|
||||
{
|
||||
|
||||
// helper for bitwise and for 1 byte
|
||||
constexpr size_t __bit_and_8(void* dst, const void* src) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
return _bit_and_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_and_16(dst, src);
|
||||
return _bit_and_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_and_32(dst, src);
|
||||
return _bit_and_32(dst, src);
|
||||
default:
|
||||
return __bit_and_64(dst, src);
|
||||
return _bit_and_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise or for 1 byte
|
||||
constexpr size_t __bit_or_8(void* dst, const void* src) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
return _bit_or_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_or_16(dst, src);
|
||||
return _bit_or_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_or_32(dst, src);
|
||||
return _bit_or_32(dst, src);
|
||||
default:
|
||||
return __bit_or_64(dst, src);
|
||||
return _bit_or_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper for bitwise and 1 byte
|
||||
constexpr size_t __bit_xor_8(void* dst, const void* src) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
return _bit_xor_8(dst, src);
|
||||
case 2: case 3:
|
||||
return __bit_xor_16(dst, src);
|
||||
return _bit_xor_16(dst, src);
|
||||
case 4: case 5: case 6: case 7:
|
||||
return __bit_xor_32(dst, src);
|
||||
return _bit_xor_32(dst, src);
|
||||
default:
|
||||
return __bit_xor_64(dst, src);
|
||||
return _bit_xor_64(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
#ifndef FENNEC_LANG_DETAIL_INT_H
|
||||
#define FENNEC_LANG_DETAIL_INT_H
|
||||
|
||||
#if _MSC_VER
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4117)
|
||||
|
||||
#define __PTRDIFF_TYPE__ ptrdiff_t
|
||||
#endif
|
||||
|
||||
// Include math since stdint will define its own versions of isinf and isnan
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
59
include/fennec/lang/detail/_numeric_transforms.h
Normal file
59
include/fennec/lang/detail/_numeric_transforms.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
#define FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename> struct _make_unsigned : type_identity<undefined_t> {};
|
||||
|
||||
template<> struct _make_unsigned<char_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<uchar_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<schar_t> : type_identity<uchar_t> {};
|
||||
template<> struct _make_unsigned<short_t> : type_identity<ushort_t> {};
|
||||
template<> struct _make_unsigned<ushort_t> : type_identity<ushort_t> {};
|
||||
template<> struct _make_unsigned<uint_t> : type_identity<uint_t> {};
|
||||
template<> struct _make_unsigned<int_t> : type_identity<uint_t> {};
|
||||
template<> struct _make_unsigned<long_t> : type_identity<ulong_t> {};
|
||||
template<> struct _make_unsigned<ulong_t> : type_identity<ulong_t> {};
|
||||
template<> struct _make_unsigned<llong_t> : type_identity<ullong_t> {};
|
||||
template<> struct _make_unsigned<ullong_t> : type_identity<ullong_t> {};
|
||||
|
||||
|
||||
template<typename> struct _make_signed : type_identity<undefined_t> {};
|
||||
|
||||
template<> struct _make_signed<char_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<uchar_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<schar_t> : type_identity<schar_t> {};
|
||||
template<> struct _make_signed<short_t> : type_identity<short_t> {};
|
||||
template<> struct _make_signed<ushort_t> : type_identity<short_t> {};
|
||||
template<> struct _make_signed<uint_t> : type_identity<int_t> {};
|
||||
template<> struct _make_signed<int_t> : type_identity<int_t> {};
|
||||
template<> struct _make_signed<long_t> : type_identity<long_t> {};
|
||||
template<> struct _make_signed<ulong_t> : type_identity<long_t> {};
|
||||
template<> struct _make_signed<llong_t> : type_identity<llong_t> {};
|
||||
template<> struct _make_signed<ullong_t> : type_identity<llong_t> {};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_NUMERIC_TRANSFORMS_H
|
||||
@@ -24,16 +24,16 @@
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename FirstT, typename... RestT> struct __first_element : type_identity<FirstT> {};
|
||||
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, 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> 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, type_identity<HeadT>,
|
||||
__nth_element<n, i + 1, RestT...>
|
||||
struct _nth_element<n, i, HeadT, RestT...> : conditional<
|
||||
n == i, HeadT,
|
||||
typename _nth_element<n, i + 1, RestT...>::type
|
||||
> {};
|
||||
}
|
||||
|
||||
75
include/fennec/lang/detail/_type_traits.h
Normal file
75
include/fennec/lang/detail/_type_traits.h
Normal 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
|
||||
@@ -25,23 +25,23 @@ namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
struct __add_lvalue_reference {
|
||||
struct _add_lvalue_reference {
|
||||
using type = _Tp;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct __add_lvalue_reference<_Tp, void_t<_Tp&>> {
|
||||
struct _add_lvalue_reference<_Tp, void_t<_Tp&>> {
|
||||
using type = _Tp&;
|
||||
};
|
||||
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
struct __add_rvalue_reference {
|
||||
struct _add_rvalue_reference {
|
||||
using type = _Tp;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct __add_rvalue_reference<_Tp, void_t<_Tp&&>> {
|
||||
struct _add_rvalue_reference<_Tp, void_t<_Tp&&>> {
|
||||
using type = _Tp&&;
|
||||
};
|
||||
|
||||
35
include/fennec/lang/detail/_typeuuid.h
Normal file
35
include/fennec/lang/detail/_typeuuid.h
Normal 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
|
||||
@@ -54,8 +54,10 @@ struct hash<IntT> : hash<uint64_t> {
|
||||
|
||||
// Wrapper for pointers
|
||||
template<typename PtrT>
|
||||
requires is_pointer_v<PtrT>
|
||||
struct hash<PtrT> : hash<uintptr_t> {
|
||||
struct hash<PtrT*> : hash<uintptr_t> {
|
||||
constexpr size_t operator()(PtrT* ptr) const {
|
||||
return hash<uintptr_t>::operator()((uintptr_t)(const void*)ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// Float
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -251,7 +251,7 @@
|
||||
|
||||
// TODO: More compiler support
|
||||
|
||||
#if _MSC_VER
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
|
||||
# define FENNEC_HAS_BUILTIN_ADDRESS_OF 1
|
||||
# define FENNEC_BUILTIN_ADDRESS_OF(arg) __builtin_addressof(arg)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
/// </table>
|
||||
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
#include <fennec/lang/detail/__numeric_transforms.h>
|
||||
#include <fennec/lang/detail/_numeric_transforms.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -63,7 +63,7 @@ namespace fennec
|
||||
///
|
||||
/// \brief Get the corresponding signed integral type of TypeT
|
||||
/// \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`
|
||||
@@ -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
|
||||
/// \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`
|
||||
|
||||
28
include/fennec/lang/startup.h
Normal file
28
include/fennec/lang/startup.h
Normal 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
|
||||
@@ -54,7 +54,7 @@
|
||||
/// </table>
|
||||
///
|
||||
|
||||
#include <fennec/lang/detail/__type_sequences.h>
|
||||
#include <fennec/lang/detail/_type_sequences.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -62,14 +62,14 @@ namespace fennec
|
||||
///
|
||||
/// \brief Get the first element of a template 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
|
||||
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...> {};
|
||||
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};
|
||||
|
||||
|
||||
///
|
||||
|
||||
@@ -107,16 +107,24 @@
|
||||
///
|
||||
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
#include <fennec/lang/detail/__type_traits.h>
|
||||
#include <fennec/lang/detail/_type_traits.h>
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +136,7 @@ template<typename T> auto declval() noexcept -> decltype(detail::__declval<T>(0)
|
||||
/// \details Stores a boolean value in `is_void::value`, representing whether the provided type is of base type void.
|
||||
/// \tparam T type to check
|
||||
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```
|
||||
@@ -145,7 +153,7 @@ template<typename T> constexpr bool_t is_void_v = is_void<T>::value;
|
||||
/// \details Stores a boolean value in `is_bool::value`, representing whether the provided type is of base type bool.
|
||||
/// \tparam T type to check
|
||||
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```
|
||||
@@ -162,7 +170,7 @@ template<typename T> constexpr bool_t is_bool_v = is_bool<T>::value;
|
||||
/// \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>>{};
|
||||
: detail::_is_null_pointer<remove_cvr_t<T>>{};
|
||||
|
||||
///
|
||||
/// \brief shorthand for ```is_null_pointer<T>::value```
|
||||
@@ -227,7 +235,7 @@ template<typename T> constexpr size_t is_class_v = is_class<T>::value;
|
||||
/// \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
|
||||
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```
|
||||
@@ -241,7 +249,7 @@ template<typename T> constexpr bool_t is_integral_v = is_integral<T>::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
|
||||
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```
|
||||
@@ -255,7 +263,7 @@ template<typename T> constexpr bool_t is_signed_v = is_signed<T>::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
|
||||
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```
|
||||
@@ -272,7 +280,7 @@ template<typename T> constexpr bool_t is_unsigned_v = is_unsigned<T>::value;
|
||||
/// \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_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```
|
||||
@@ -288,7 +296,7 @@ template<typename T> constexpr bool_t is_floating_point_v = is_floating_point<T>
|
||||
/// \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>>{};
|
||||
: detail::_is_pointer<remove_cvr_t<T>>{};
|
||||
|
||||
///
|
||||
/// \brief shorthand for ```is_floating_point<T>::value```
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define FENNEC_LANG_TYPE_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/type_identity.h>
|
||||
#include <fennec/lang/detail/__type_transforms.h>
|
||||
#include <fennec/lang/detail/_type_transforms.h>
|
||||
|
||||
///
|
||||
/// \page fennec_lang_type_transforms Type Transforms
|
||||
@@ -192,7 +192,7 @@ template<typename T> using remove_reference_t = typename remove_reference<T>::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> {};
|
||||
template<typename T> struct add_lvalue_reference : detail::_add_lvalue_reference<T> {};
|
||||
|
||||
///
|
||||
/// \brief shorthand for `typename remove_reference<T>::type`
|
||||
@@ -204,7 +204,7 @@ template<typename T> using add_lvalue_reference_t = typename add_lvalue_referen
|
||||
///
|
||||
/// \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> {};
|
||||
template<typename T> struct add_rvalue_reference : detail::_add_rvalue_reference<T> {};
|
||||
|
||||
///
|
||||
/// \brief shorthand for `typename remove_reference<T>::type`
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/lang/detail/__int.h>
|
||||
#include <fennec/lang/detail/_int.h>
|
||||
|
||||
#include <fennec/lang/conditional_types.h>
|
||||
|
||||
@@ -249,6 +249,7 @@ namespace fennec
|
||||
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 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
|
||||
@@ -263,15 +264,15 @@ namespace fennec
|
||||
/// \name Sized Integer Types
|
||||
/// @{
|
||||
|
||||
using int8_t = schar_t; ///< \brief Signed 8-bit integer
|
||||
using int16_t = short_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 int64_t = conditional_t<sizeof(long_t) == 8, long_t, llong_t>; ///< \brief Signed 64-bit integer
|
||||
using int8_t = ::int8_t; ///< \brief Signed 8-bit integer
|
||||
using int16_t = ::int16_t; ///< \brief Signed 16-bit integer
|
||||
using int32_t = ::int32_t; ///< \brief Signed 32-bit integer
|
||||
using int64_t = ::int64_t; ///< \brief Signed 64-bit integer
|
||||
|
||||
using uint8_t = uchar_t; ///< \brief Unsigned 8-bit integer
|
||||
using uint16_t = ushort_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 uint64_t = conditional_t<sizeof(ulong_t) == 8, ulong_t, ullong_t>; ///< \brief Unsigned 64-bit integer
|
||||
using uint8_t = ::uint8_t; ///< \brief Unsigned 8-bit integer
|
||||
using uint16_t = ::uint16_t; ///< \brief Unsigned 16-bit integer
|
||||
using uint32_t = ::uint32_t; ///< \brief Unsigned 32-bit integer
|
||||
using uint64_t = ::uint64_t; ///< \brief Unsigned 64-bit integer
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
63
include/fennec/lang/typeuuid.h
Normal file
63
include/fennec/lang/typeuuid.h
Normal 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
|
||||
@@ -16,9 +16,9 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
@@ -16,14 +16,14 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_IO_FILE_H
|
||||
#define FENNEC_FPROC_IO_FILE_H
|
||||
#ifndef FENNEC_LANGPROC_IO_FILE_H
|
||||
#define FENNEC_LANGPROC_IO_FILE_H
|
||||
|
||||
#include <fennec/fproc/filesystem/path.h>
|
||||
#include <fennec/langproc/filesystem/path.h>
|
||||
|
||||
#include <fennec/fproc/strings/cstring.h>
|
||||
#include <fennec/fproc/strings/string.h>
|
||||
#include <fennec/fproc/strings/wstring.h>
|
||||
#include <fennec/langproc/strings/cstring.h>
|
||||
#include <fennec/langproc/strings/string.h>
|
||||
#include <fennec/langproc/strings/wstring.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace fennec
|
||||
/// fmode_binary and fmode_wide are independent of the other modes
|
||||
///
|
||||
/// \details Valid Flag Combinations
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_fproc_io_fmode">
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_LANGPROC_io_fmode">
|
||||
/// <tr><th style="vertical-align: top">Flags
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
@@ -248,7 +248,7 @@ public:
|
||||
bool eof() const;
|
||||
|
||||
|
||||
// Read Operations =====================================================================================================
|
||||
// Binary Read Operations ==============================================================================================
|
||||
|
||||
char getc();
|
||||
wchar_t getwc();
|
||||
@@ -269,13 +269,23 @@ public:
|
||||
wstring getwline();
|
||||
|
||||
|
||||
// Write Operations ====================================================================================================
|
||||
// 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);
|
||||
@@ -287,6 +297,11 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// Printing Operations =================================================================================================
|
||||
|
||||
|
||||
|
||||
|
||||
// Error Handling ======================================================================================================
|
||||
|
||||
const char* get_error() const { return _error; }
|
||||
@@ -301,4 +316,4 @@ private:
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FPROC_IO_FILE_H
|
||||
#endif // FENNEC_LANGPROC_IO_FILE_H
|
||||
@@ -16,10 +16,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_IO_PATH_H
|
||||
#define FENNEC_FPROC_IO_PATH_H
|
||||
#ifndef FENNEC_LANGPROC_IO_PATH_H
|
||||
#define FENNEC_LANGPROC_IO_PATH_H
|
||||
|
||||
#include <fennec/fproc/strings/string.h>
|
||||
#include <fennec/langproc/strings/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
/// \param str the cstring to convert
|
||||
path(const cstring& str)
|
||||
: _str(str) {
|
||||
while (not _str.empty() && _str[_str.size() - 1] == '/') {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,8 @@ public:
|
||||
/// \brief String Conversion Constructor
|
||||
/// \param str the string to convert
|
||||
path(const string& str)
|
||||
: _str(str) {
|
||||
while (_str[_str.size() - 1] == '/') {
|
||||
: _str(str) {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,16 @@ public:
|
||||
|
||||
// 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
|
||||
@@ -145,11 +155,12 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
#ifdef _WIN32
|
||||
#if FENNEC_PLATFORM_WINDOWS
|
||||
return (_str[1] == ':' && size == 3);
|
||||
#else
|
||||
return (_str[0] == '/' && size == 1);
|
||||
@@ -157,7 +168,7 @@ public:
|
||||
}
|
||||
|
||||
path parent() const {
|
||||
#ifdef _WIN32
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
size_t start = _str.size() - 1;
|
||||
start = _str[start] == '/' || _str[start] == '\\' ? start - 1 : start;
|
||||
|
||||
@@ -176,7 +187,7 @@ public:
|
||||
#else
|
||||
size_t start = _str.size() - 1;
|
||||
start = _str[start] == '/' ? start - 1 : start;
|
||||
return _str.substring(0, _str.rfind('/', start));
|
||||
return path(_str.substring(0, _str.rfind('/', start)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -185,7 +196,7 @@ public:
|
||||
path working; working._str.resize(0);
|
||||
|
||||
// Check if this is a rooted path;
|
||||
#ifdef _WIN32
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
if (_str[1] != ':') {
|
||||
#else
|
||||
if (_str[0] != '/') {
|
||||
@@ -195,7 +206,7 @@ public:
|
||||
|
||||
while (not parse.empty()) {
|
||||
// Handle dots
|
||||
while (parse._str[0] == '.') {
|
||||
while (not parse.empty() && parse._str[0] == '.') {
|
||||
// Check for ".."
|
||||
if (parse._str[1] == '.') {
|
||||
// ".."
|
||||
@@ -233,4 +244,4 @@ private:
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FPROC_IO_PATH_H
|
||||
#endif // FENNEC_LANGPROC_IO_PATH_H
|
||||
@@ -16,11 +16,11 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_CSTRING_H
|
||||
#define FENNEC_FPROC_STRINGS_CSTRING_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_CSTRING_H
|
||||
#define FENNEC_LANGPROC_STRINGS_CSTRING_H
|
||||
|
||||
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||
#include <fennec/memory/detail/__string.h>
|
||||
#include <fennec/langproc/strings/detail/_ctype.h>
|
||||
#include <fennec/memory/detail/_string.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
@@ -67,6 +67,12 @@ public:
|
||||
: _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
|
||||
@@ -129,11 +135,16 @@ public:
|
||||
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;
|
||||
_str = str, _size = n - 1, _const = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -160,6 +171,14 @@ public:
|
||||
/// \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 ==============================================================================================================
|
||||
|
||||
@@ -183,9 +202,16 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \brief Data Access
|
||||
/// \returns A const qualified pointer to the underlying allocation
|
||||
constexpr const char* operator*() const {
|
||||
constexpr char* data() {
|
||||
return _str;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Data Access
|
||||
/// \returns A const qualified pointer to the underlying allocation
|
||||
constexpr const char* data() const {
|
||||
return _cstr;
|
||||
}
|
||||
|
||||
@@ -220,6 +246,15 @@ public:
|
||||
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
|
||||
@@ -306,11 +341,11 @@ private:
|
||||
|
||||
template<>
|
||||
struct hash<cstring> : hash<byte_array> {
|
||||
constexpr size_t operator()(const cstring& str) {
|
||||
return hash<byte_array>::operator()(byte_array(*str, str.size()));
|
||||
constexpr size_t operator()(const cstring& str) const {
|
||||
return hash<byte_array>::operator()(byte_array(str, str.size()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_CSTRING_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_CSTRING_H
|
||||
@@ -16,10 +16,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
|
||||
#include <ctype.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
@@ -16,9 +16,9 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
@@ -16,10 +16,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_LOCALE_H
|
||||
#define FENNEC_FPROC_STRINGS_LOCALE_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_LOCALE_H
|
||||
#define FENNEC_LANGPROC_STRINGS_LOCALE_H
|
||||
|
||||
#include <fennec/fproc/strings/detail/__locale.h>
|
||||
#include <fennec/langproc/strings/detail/_locale.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -41,4 +41,4 @@ using ::localeconv;
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_LOCALE_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_LOCALE_H
|
||||
@@ -16,11 +16,11 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_STRING_H
|
||||
#define FENNEC_FPROC_STRINGS_STRING_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_STRING_H
|
||||
#define FENNEC_LANGPROC_STRINGS_STRING_H
|
||||
|
||||
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||
#include <fennec/fproc/strings/cstring.h>
|
||||
#include <fennec/langproc/strings/detail/_ctype.h>
|
||||
#include <fennec/langproc/strings/cstring.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
@@ -59,93 +59,104 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null characters
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...`
|
||||
/// \param n the number of characters
|
||||
///
|
||||
/// \details adds additional character for null termination.
|
||||
constexpr _string(size_t n)
|
||||
: _string('\0', n) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` filled with the character `c`
|
||||
/// \param c the character to fill with
|
||||
/// \param n the number of characters
|
||||
///
|
||||
/// \details adds additional character for null termination.
|
||||
constexpr _string(char c, size_t n)
|
||||
constexpr _string(size_t n, char c = '\0')
|
||||
: _str(n + 1) {
|
||||
fennec::memset(_str.data(), c, n);
|
||||
_str[n] = '\0';
|
||||
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 Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
/// \tparam n number of characters in the buffer
|
||||
///
|
||||
/// \details adds additional character for null termination. Ignores whether str is null-terminated.
|
||||
/// This constructor makes the assumption that `len` is the intended number of characters.
|
||||
/// \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>
|
||||
constexpr _string(const char str[n])
|
||||
: _str(str, n + 1) {
|
||||
_str[n] = '\0';
|
||||
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 Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
/// \param n number of characters in the buffer
|
||||
///
|
||||
/// \details adds additional character for null termination. Ignores whether str is null-terminated.
|
||||
/// This constructor makes the assumption that `n` is the intended number of characters.
|
||||
constexpr _string(const char* str, size_t n)
|
||||
: _str(n + 1) {
|
||||
fennec::memcpy(_str.data(), str, n);
|
||||
_str[n] = '\0';
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Buffer Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
constexpr _string(const cstring& str)
|
||||
: _str(str, str.size() + 1) {
|
||||
/// \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)
|
||||
: _str(str._str) {
|
||||
}
|
||||
|
||||
constexpr _string(_string&& str) noexcept
|
||||
: _str(fennec::move(str._str)) {
|
||||
}
|
||||
constexpr _string(const _string& str) = default;
|
||||
|
||||
///
|
||||
/// \brief String Destructor, cleans up the underlying allocation
|
||||
constexpr ~_string() = default; // allocation cleans up itself
|
||||
/// \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() - 1;
|
||||
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[](int i) {
|
||||
constexpr char& operator[](size_t i) {
|
||||
return _str[i];
|
||||
}
|
||||
|
||||
@@ -153,24 +164,21 @@ public:
|
||||
/// \brief Const-Array Access Operator
|
||||
/// \param i the index to access
|
||||
/// \returns a copy of the character
|
||||
constexpr char operator[](int i) const {
|
||||
assertd(i >= 0 && (size_t)i < size(), "Array Out of Bounds");
|
||||
constexpr const char& operator[](size_t i) const {
|
||||
return _str[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A const qualified pointer to the underlying allocation
|
||||
constexpr const char* operator*() const {
|
||||
return _str.data();
|
||||
constexpr char* data() {
|
||||
return _str;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Implicit Dereference Cast
|
||||
constexpr operator const char*() const {
|
||||
return _str.data();
|
||||
constexpr const char* data() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
constexpr const char* cstr() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
// Examination =========================================================================================================
|
||||
|
||||
@@ -191,7 +199,7 @@ public:
|
||||
}
|
||||
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
|
||||
|
||||
return ::strncmp(_str.data() + i, str, n);
|
||||
return ::strncmp(_str + i, str, n);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -205,7 +213,7 @@ public:
|
||||
}
|
||||
n = min(n, max(size(), str.size()) + 1);
|
||||
|
||||
return ::strncmp(_str.data() + i, str, n);
|
||||
return ::strncmp(_str + i, str.data(), n);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const _string& str) const {
|
||||
@@ -221,8 +229,8 @@ public:
|
||||
return size();
|
||||
}
|
||||
|
||||
const char* loc = ::strchr(_str.data() + i, c); // get location using strchr
|
||||
return loc ? loc - _str.data() : size(); // return size if not found
|
||||
const char* loc = ::strchr(_str + i, c); // get location using strchr
|
||||
return loc ? loc - _str : size(); // return size if not found
|
||||
}
|
||||
|
||||
///
|
||||
@@ -311,59 +319,6 @@ public:
|
||||
return size(); // base case
|
||||
}
|
||||
|
||||
// Manipulation ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Resize the string, filling additional bytes with `'\0'`
|
||||
/// \param n the new size of the string
|
||||
constexpr void resize(size_t n) {
|
||||
size_t i = size();
|
||||
_str.reallocate(n + 1);
|
||||
if (n > i) fennec::memset(_str.data() + i, '\0', n + 1 - i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param str the string to copy
|
||||
/// \returns a reference to `this`
|
||||
constexpr _string& operator=(const cstring& str) {
|
||||
resize(str.size());
|
||||
fennec::memcpy(_str.data(), str, str.size());
|
||||
_str[str.size()] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param str the string to copy
|
||||
/// \returns a reference to `this`
|
||||
constexpr _string& operator=(const string& str) {
|
||||
resize(str.size());
|
||||
fennec::memcpy(_str.data(), str, str.size());
|
||||
_str[str.size()] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param str the string to move
|
||||
/// \returns a reference to `this`
|
||||
constexpr _string& operator=(string&& str) noexcept {
|
||||
_str = move(str._str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Replace all instances of `x` with `y`
|
||||
/// \param x the character to search for
|
||||
/// \param y the character to replace with
|
||||
void replace(char x, char y) {
|
||||
size_t i = 0;
|
||||
while ((i = find(x, 0)) != size()) {
|
||||
_str[i] = y;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Retrieve a substring of a string
|
||||
/// \param i the start index
|
||||
@@ -373,60 +328,98 @@ public:
|
||||
if (i >= size()) {
|
||||
return _string("");
|
||||
}
|
||||
n = min(n, size() - i);
|
||||
return _string(_str.data() + i, n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Returns a string with `c` appended to it
|
||||
/// \param c
|
||||
/// \returns
|
||||
constexpr _string operator+(char c) const {
|
||||
// Copy contents with one additional byte.
|
||||
_string res(_str.data(), _str.size());
|
||||
res[size()] = c; // Set the last character to c
|
||||
n = fennec::min(n, size() - i);
|
||||
_string res;
|
||||
res._str.callocate(n + 1);
|
||||
fennec::memcpy(res.data(), _str + i, n);
|
||||
return res;
|
||||
}
|
||||
|
||||
friend constexpr _string operator+(char c, _string& str) {
|
||||
return _string(c, 1) + str;
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
constexpr void resize(size_t n) {
|
||||
_str.creallocate(n + 1);
|
||||
_str[size()] = '\0';
|
||||
}
|
||||
|
||||
constexpr _string operator+(const cstring& str) const {
|
||||
_string res(size() + str.size()); // Make a new string with the size of this + str
|
||||
fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||
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 {
|
||||
_string res(size() + str.size()); // Make a new string with the size of this + str
|
||||
fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||
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) {
|
||||
size_t x = size();
|
||||
resize(x + 1);
|
||||
_str[x] = 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& str) {
|
||||
size_t x = size();
|
||||
resize(x + str.size());
|
||||
fennec::memcpy(&_str[x], str, str.size());
|
||||
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) {
|
||||
size_t x = size();
|
||||
resize(x + str.size());
|
||||
fennec::memcpy(&_str[x], str, str.size());
|
||||
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;
|
||||
};
|
||||
@@ -434,11 +427,11 @@ private:
|
||||
template<>
|
||||
struct hash<string> : hash<byte_array> {
|
||||
constexpr size_t operator()(const string& str) const {
|
||||
return hash<byte_array>::operator()(byte_array(*str, str.size()));
|
||||
return hash<byte_array>::operator()(byte_array(str.data(), str.size()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // FENNEC_FPROC_STRINGS_STRING_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_STRING_H
|
||||
@@ -16,11 +16,11 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_STRINGS_wcstring_H
|
||||
#define FENNEC_FPROC_STRINGS_wcstring_H
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_wcstring_H
|
||||
#define FENNEC_LANGPROC_STRINGS_wcstring_H
|
||||
|
||||
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||
#include <fennec/memory/detail/__string.h>
|
||||
#include <fennec/langproc/strings/detail/_ctype.h>
|
||||
#include <fennec/memory/detail/_string.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
@@ -66,9 +66,13 @@ public:
|
||||
///
|
||||
/// \brief Default Constructor, initializes with nullptr
|
||||
constexpr wcstring()
|
||||
: _str(nullptr)
|
||||
, _size(0)
|
||||
, _const(true) {
|
||||
: _str(nullptr), _size(0), _const(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes with nullptr
|
||||
constexpr wcstring(nullptr_t)
|
||||
: _str(nullptr), _size(0), _const(true) {
|
||||
}
|
||||
|
||||
///
|
||||
@@ -87,7 +91,7 @@ public:
|
||||
/// \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])
|
||||
constexpr wcstring(wchar_t (&str)[n])
|
||||
: _str(str)
|
||||
, _size(n - 1)
|
||||
, _const(false) {
|
||||
@@ -110,7 +114,7 @@ public:
|
||||
/// \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])
|
||||
constexpr wcstring(const wchar_t (&str)[n])
|
||||
: _cstr(str)
|
||||
, _size(n - 1)
|
||||
, _const(true) {
|
||||
@@ -133,11 +137,16 @@ public:
|
||||
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;
|
||||
_str = str, _size = n - 1, _const = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -164,6 +173,14 @@ public:
|
||||
/// \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 ==============================================================================================================
|
||||
|
||||
@@ -181,15 +198,22 @@ public:
|
||||
/// \brief Const-Array Access Operator
|
||||
/// \param i the index to access
|
||||
/// \returns a copy of the character
|
||||
constexpr wchar_t operator[](size_t i) const {
|
||||
constexpr const wchar_t& operator[](size_t i) const {
|
||||
assertd(i < size(), "Array Out of Bounds");
|
||||
return _cstr[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \brief Data Access
|
||||
/// \returns A const qualified pointer to the underlying allocation
|
||||
constexpr const wchar_t* operator*() const {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -205,12 +229,14 @@ public:
|
||||
///
|
||||
/// \returns The length of the string to the first null-terminator
|
||||
constexpr size_t length() const {
|
||||
return find(L'\0');
|
||||
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 {
|
||||
@@ -222,6 +248,15 @@ public:
|
||||
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
|
||||
@@ -263,7 +298,7 @@ public:
|
||||
/// \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 {
|
||||
constexpr size_t rfind(char c, size_t i = npos) const {
|
||||
if (_size == 0) {
|
||||
return _size;
|
||||
}
|
||||
@@ -285,7 +320,7 @@ public:
|
||||
return _size;
|
||||
}
|
||||
|
||||
const wchar_t first = str[0];
|
||||
const char first = str[0];
|
||||
i = min(i, _size - str._size);
|
||||
do {
|
||||
if(_cstr[i] == first) {
|
||||
@@ -306,6 +341,13 @@ private:
|
||||
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_FPROC_STRINGS_wcstring_H
|
||||
#endif // FENNEC_LANGPROC_STRINGS_wcstring_H
|
||||
@@ -16,11 +16,11 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FPROC_wstringS_wstring_H
|
||||
#define FENNEC_FPROC_wstringS_wstring_H
|
||||
#ifndef FENNEC_LANGPROC_wstringS_WSTRING_H
|
||||
#define FENNEC_LANGPROC_wstringS_WSTRING_H
|
||||
|
||||
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||
#include <fennec/fproc/strings/wcstring.h>
|
||||
#include <fennec/langproc/strings/detail/_ctype.h>
|
||||
#include <fennec/langproc/strings/wcstring.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
@@ -59,98 +59,104 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null wchar_tacters
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...`
|
||||
/// \param n the number of wchar_tacters
|
||||
///
|
||||
/// \details adds additional wchar_tacter for null termination.
|
||||
constexpr _wstring(size_t n)
|
||||
: _wstring('\0', n) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Constructor, initializes a null-terminated string of size `n` filled with the wchar_tacter `c`
|
||||
/// \param c the wchar_tacter to fill with
|
||||
/// \param n the number of wchar_tacters
|
||||
///
|
||||
/// \details adds additional wchar_tacter for null termination.
|
||||
constexpr _wstring(wchar_t c, size_t n)
|
||||
constexpr _wstring(size_t n, wchar_t c = '\0')
|
||||
: _str(n + 1) {
|
||||
fennec::wmemset(_str.data(), c, n); _str[n] = '\0';
|
||||
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 Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
/// \param len number of wchar_tacters in the buffer
|
||||
///
|
||||
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||
/// \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>
|
||||
constexpr _wstring(const wchar_t str[n])
|
||||
: _str(str, n + 1) {
|
||||
::wcsncpy(_str.data(), str, n);
|
||||
_str[n] = '\0';
|
||||
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 Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
/// \param len number of wchar_tacters in the buffer
|
||||
///
|
||||
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||
constexpr _wstring(const wchar_t* str, size_t n)
|
||||
: _str(str, n + 1) {
|
||||
::wcsncpy(_str.data(), str, n);
|
||||
_str[n] = '\0';
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Buffer Copy Constructor
|
||||
/// \param str the buffer to copy
|
||||
/// \param len number of wchar_tacters in the buffer
|
||||
///
|
||||
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||
constexpr _wstring(const wcstring& str)
|
||||
: _str(str, str.size() + 1) {
|
||||
_str[str.size()] = '\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)
|
||||
: _wstring(str, str.size() - 1) {
|
||||
}
|
||||
|
||||
constexpr _wstring(_wstring&& str) noexcept
|
||||
: _str(fennec::move(str._str)) {
|
||||
}
|
||||
constexpr _wstring(const _wstring& str) = default;
|
||||
|
||||
///
|
||||
/// \brief String Destructor, cleans up the underlying allocation
|
||||
constexpr ~_wstring() = default; // allocation cleans up itself
|
||||
/// \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() - 1;
|
||||
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[](int i) {
|
||||
constexpr wchar_t& operator[](size_t i) {
|
||||
return _str[i];
|
||||
}
|
||||
|
||||
@@ -158,24 +164,21 @@ public:
|
||||
/// \brief Const-Array Access Operator
|
||||
/// \param i the index to access
|
||||
/// \returns a copy of the wchar_tacter
|
||||
constexpr wchar_t operator[](int i) const {
|
||||
assertd(i >= 0 && i < size(), "Array Out of Bounds");
|
||||
constexpr const wchar_t& operator[](size_t i) const {
|
||||
return _str[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A const qualified pointer to the underlying allocation
|
||||
constexpr const wchar_t* operator*() const {
|
||||
return _str.data();
|
||||
constexpr wchar_t* data() {
|
||||
return _str;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Implicit Dereference Cast
|
||||
constexpr operator const wchar_t*() const {
|
||||
return _str.data();
|
||||
constexpr const wchar_t* data() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
constexpr const wchar_t* cstr() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
// Examination =========================================================================================================
|
||||
|
||||
@@ -196,7 +199,7 @@ public:
|
||||
}
|
||||
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
|
||||
|
||||
return ::wcsncmp(_str.data() + i, str, n);
|
||||
return ::wcsncmp(_str + i, str, n);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -204,13 +207,13 @@ public:
|
||||
/// \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 {
|
||||
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.data() + i, str, n);
|
||||
return ::wcsncmp(_str + i, str.data(), n);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const _wstring& str) const {
|
||||
@@ -226,15 +229,15 @@ public:
|
||||
return size();
|
||||
}
|
||||
|
||||
const wchar_t* loc = ::wcschr(_str.data() + i, c); // get location using strchr
|
||||
return loc ? loc - _str.data() : size(); // return size if not found
|
||||
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
|
||||
constexpr size_t find(const _wstring& str, size_t i = 0) const { // bounds check
|
||||
if (i >= size()) { // bounds check
|
||||
return size();
|
||||
}
|
||||
@@ -300,7 +303,7 @@ public:
|
||||
/// \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 wstring& str, size_t i = npos) const {
|
||||
constexpr size_t rfind(const string& str, size_t i = npos) const {
|
||||
if (size() == 0) {
|
||||
return size();
|
||||
}
|
||||
@@ -316,112 +319,119 @@ public:
|
||||
return size(); // base case
|
||||
}
|
||||
|
||||
// Manipulation ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Resize the string, filling additional bytes with `'\0'`
|
||||
/// \param n the new size of the string
|
||||
constexpr void resize(size_t n) {
|
||||
size_t i = size();
|
||||
_str.reallocate(n + 1);
|
||||
if (n > i) fennec::wmemset(_str.data() + i, L'\0', n - i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param str the string to copy
|
||||
/// \returns a reference to `this`
|
||||
constexpr wstring& operator=(const wcstring& str) {
|
||||
if (str.size() > size()) resize(str.size());
|
||||
fennec::wmemcpy(_str.data(), str, str.size());
|
||||
_str[str.size()] = L'\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param str the string to copy
|
||||
/// \returns a reference to `this`
|
||||
constexpr wstring& operator=(const wstring& str) {
|
||||
if (str.size() > size()) resize(str.size());
|
||||
fennec::wmemcpy(_str.data(), str, str.size());
|
||||
_str[str.size()] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param str the string to move
|
||||
/// \returns a reference to `this`
|
||||
constexpr wstring& operator=(wstring&& str) noexcept {
|
||||
_str = fennec::move(str._str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \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 {
|
||||
constexpr _wstring substring(size_t i, size_t n = npos) const {
|
||||
if (i >= size()) {
|
||||
return wstring("");
|
||||
return _wstring("");
|
||||
}
|
||||
n = min(n, size() - i);
|
||||
return wstring(_str.data() + i, n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Returns a string with `c` appended to it
|
||||
/// \param c
|
||||
/// \returns
|
||||
constexpr wstring operator+(wchar_t c) const {
|
||||
// Copy contents with one additional byte.
|
||||
wstring res(_str, _str.size());
|
||||
res[size()] = c; // Set the last wchar_tacter to c
|
||||
n = fennec::min(n, size() - i);
|
||||
_wstring res;
|
||||
res._str.callocate(n + 1);
|
||||
fennec::wmemcpy(res.data(), _str + i, n);
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr wstring operator+(const wcstring& str) const {
|
||||
wstring res(size() + str.size()); // Make a new string with the size of this + str
|
||||
fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||
fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
constexpr wstring operator+(const wstring& str) const {
|
||||
wstring res(size() + str.size()); // Make a new string with the size of this + str
|
||||
fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||
fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||
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+=(wchar_t c) {
|
||||
size_t x = size();
|
||||
resize(x + 1);
|
||||
_str[x] = c;
|
||||
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& str) {
|
||||
size_t x = size();
|
||||
resize(x + str.size());
|
||||
fennec::wmemcpy(&_str[x], str, str.size());
|
||||
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) {
|
||||
size_t x = size();
|
||||
resize(x + str.size());
|
||||
fennec::wmemcpy(&_str[x], str, str.size());
|
||||
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_FPROC_wstringS_wstring_H
|
||||
#endif // FENNEC_LANGPROC_wstringS_WSTRING_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -269,13 +269,13 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/math/detail/__math.h>
|
||||
#include <fennec/math/detail/_math.h>
|
||||
|
||||
#include <fennec/lang/limits.h>
|
||||
|
||||
#include <fennec/math/vector.h>
|
||||
|
||||
#if _MSC_VER
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#define isnanf(x) isnan(x)
|
||||
#define isinff(x) isinf(x)
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef FENNEC_MATH_DETAIL_FWD_H
|
||||
#define FENNEC_MATH_DETAIL_FWD_H
|
||||
|
||||
#include <fennec/math/detail/__types.h>
|
||||
#include <fennec/math/detail/_types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -29,10 +29,10 @@ template<typename ScalarT, size_t RowsV, size_t...ColIndicesV> struct matrix; //
|
||||
|
||||
// Simplified interface for creating sized vectors or matrices
|
||||
template<typename ScalarT, size_t SizeV> using vec
|
||||
= decltype(detail::__gen_vector<vector, ScalarT>(make_index_sequence<SizeV>{})); // Gets the type returned by this function
|
||||
= decltype(detail::_gen_vector<vector, ScalarT>(make_index_sequence<SizeV>{})); // Gets the type returned by this function
|
||||
|
||||
template<typename ScalarT, size_t ColsV, size_t RowsV> using mat
|
||||
= decltype(detail::__gen_matrix<matrix, ScalarT, RowsV>(make_index_sequence<ColsV>{})); // Gets the type returned by this function
|
||||
= decltype(detail::_gen_matrix<matrix, ScalarT, RowsV>(make_index_sequence<ColsV>{})); // Gets the type returned by this function
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef FENNEC_MATH_DETAIL_MATRIX_H
|
||||
#define FENNEC_MATH_DETAIL_MATRIX_H
|
||||
|
||||
#include <fennec/math/detail/__fwd.h>
|
||||
#include <fennec/math/detail/_fwd.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef FENNEC_MATH_DETAIL_TYPES_H
|
||||
#define FENNEC_MATH_DETAIL_TYPES_H
|
||||
|
||||
#include <fennec/lang/sequences.h>
|
||||
#include <fennec/lang/const_sequences.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -28,11 +28,11 @@ namespace detail
|
||||
{
|
||||
|
||||
template<template<typename, size_t...> typename VectorT, typename ScalarT, size_t...IndicesV>
|
||||
VectorT<ScalarT, IndicesV...> __gen_vector(index_sequence<IndicesV...>); // Helper for substituting a size N with sequence of integers
|
||||
VectorT<ScalarT, IndicesV...> _gen_vector(const_index_sequence<IndicesV...>); // Helper for substituting a size N with sequence of integers
|
||||
|
||||
|
||||
template<template<typename, size_t...> typename MatrixT, typename ScalarT, size_t RowsV, size_t...IndicesV>
|
||||
MatrixT<ScalarT, RowsV, IndicesV...> __gen_matrix(index_sequence<IndicesV...>); // Helper for substituting a size Columns with sequence of integers
|
||||
MatrixT<ScalarT, RowsV, IndicesV...> _gen_matrix(const_index_sequence<IndicesV...>); // Helper for substituting a size Columns with sequence of integers
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef FENNEC_MATH_DETAIL_VECTOR_TRAITS_H
|
||||
#define FENNEC_MATH_DETAIL_VECTOR_TRAITS_H
|
||||
|
||||
#include <fennec/math/detail/__fwd.h>
|
||||
#include <fennec/math/detail/_fwd.h>
|
||||
|
||||
#include <fennec/lang/type_traits.h>
|
||||
#include <fennec/math/swizzle.h>
|
||||
@@ -33,46 +33,46 @@ namespace detail
|
||||
// Helpers for vector traits
|
||||
|
||||
template<typename>
|
||||
struct __is_vector_helper
|
||||
struct _is_vector_helper
|
||||
: false_type {}; // Default false case
|
||||
|
||||
|
||||
template<typename ScalarT, size_t...IndicesV>
|
||||
struct __is_vector_helper<vector<ScalarT, IndicesV...>>
|
||||
struct _is_vector_helper<vector<ScalarT, IndicesV...>>
|
||||
: true_type {}; // True for vectors
|
||||
|
||||
|
||||
template<typename VectorT, typename DataT, typename ScalarT, size_t...IndicesV>
|
||||
struct __is_vector_helper<swizzle<VectorT, DataT, ScalarT, IndicesV...>>
|
||||
struct _is_vector_helper<swizzle<VectorT, DataT, ScalarT, IndicesV...>>
|
||||
: true_type {}; // True for swizzles
|
||||
|
||||
// get number of components of a type
|
||||
template<typename>
|
||||
struct __component_count_helper;
|
||||
struct _component_count_helper;
|
||||
|
||||
// numeric types reduce to 1
|
||||
template<typename TypeT> requires(is_arithmetic_v<TypeT>)
|
||||
struct __component_count_helper<TypeT>
|
||||
struct _component_count_helper<TypeT>
|
||||
: integral_constant<size_t, 1> {};
|
||||
|
||||
// Vectors reduce to the number of elements
|
||||
template<typename ScalarT, size_t...IndicesV>
|
||||
struct __component_count_helper<vector<ScalarT, IndicesV...>>
|
||||
struct _component_count_helper<vector<ScalarT, IndicesV...>>
|
||||
: integral_constant<size_t, sizeof...(IndicesV)> {};
|
||||
|
||||
// Swizzles reduce to number of elements
|
||||
template<typename VectorT, typename DataT, typename ScalarT, size_t...IndicesV>
|
||||
struct __component_count_helper<swizzle<VectorT, DataT, ScalarT, IndicesV...>>
|
||||
struct _component_count_helper<swizzle<VectorT, DataT, ScalarT, IndicesV...>>
|
||||
: integral_constant<size_t, sizeof...(IndicesV)> {};
|
||||
|
||||
// Matrices reduce to the number of cells
|
||||
template<typename ScalarT, size_t RowsV, size_t...ColIndicesV>
|
||||
struct __component_count_helper<matrix<ScalarT, RowsV, ColIndicesV...>>
|
||||
struct _component_count_helper<matrix<ScalarT, RowsV, ColIndicesV...>>
|
||||
: integral_constant<size_t, RowsV * sizeof...(ColIndicesV)> {};
|
||||
|
||||
// default case reduces to 0
|
||||
template<typename>
|
||||
struct __component_count_helper
|
||||
struct _component_count_helper
|
||||
: integral_constant<size_t, 0> {};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -87,7 +87,7 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/math/detail/__math.h>
|
||||
#include <fennec/math/detail/_math.h>
|
||||
#include <fennec/math/vector.h>
|
||||
|
||||
namespace fennec
|
||||
|
||||
@@ -531,7 +531,7 @@
|
||||
///
|
||||
///
|
||||
|
||||
#if _MSC_VER
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4305)
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -43,8 +43,8 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/math/detail/__fwd.h>
|
||||
#include <fennec/math/detail/__matrix.h>
|
||||
#include <fennec/math/detail/_fwd.h>
|
||||
#include <fennec/math/detail/_matrix.h>
|
||||
|
||||
#include <fennec/containers/array.h>
|
||||
|
||||
@@ -292,7 +292,7 @@ struct matrix
|
||||
/// \param args
|
||||
template<typename...ArgsT> requires(total_component_count_v<ArgsT...> == num_components)
|
||||
constexpr matrix(ArgsT&&...args) {
|
||||
matrix::__construct(fennec::forward<ArgsT>(args)...);
|
||||
matrix::_construct(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -579,7 +579,7 @@ struct matrix
|
||||
/// \param rhs the vector
|
||||
/// \returns a vector containing the dot products of \f$rhs\f$ with each row of \f$lhs\f$
|
||||
constexpr friend column_t operator*(const matrix_t& lhs, const row_t& rhs) {
|
||||
return __mul(lhs, rhs);
|
||||
return _mul(lhs, rhs);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -625,7 +625,7 @@ struct matrix
|
||||
template<size_t ORowsV, size_t...OColIndicesV> requires(columns == ORowsV)
|
||||
constexpr friend matrix<scalar_t, RowsV, OColIndicesV...> operator*(const matrix_t& lhs, const matrix<scalar_t, ORowsV, OColIndicesV...>& rhs) {
|
||||
return matrix<scalar_t, RowsV, OColIndicesV...>(
|
||||
matrix::__mul(lhs, rhs[OColIndicesV])...
|
||||
matrix::_mul(lhs, rhs[OColIndicesV])...
|
||||
);
|
||||
}
|
||||
|
||||
@@ -645,43 +645,43 @@ private:
|
||||
|
||||
// ReSharper disable once CppMemberFunctionMayBeStatic
|
||||
template<size_t i0 = 0>
|
||||
constexpr void __construct() {
|
||||
constexpr void _construct() {
|
||||
// base case, does nothing, this will get optimized away
|
||||
}
|
||||
|
||||
// helper for parsing parameter packs
|
||||
template<size_t i0 = 0, typename HeadT, typename...RestT>
|
||||
constexpr void __construct(HeadT&& head, RestT&&...rest) {
|
||||
matrix::__insert<i0>(head); // insert the head element
|
||||
matrix::__construct<i0 + component_count_v<HeadT>>(std::forward<RestT>(rest)...); // propagate to the rest of the arguments
|
||||
constexpr void _construct(HeadT&& head, RestT&&...rest) {
|
||||
matrix::_insert<i0>(head); // insert the head element
|
||||
matrix::_construct<i0 + component_count_v<HeadT>>(std::forward<RestT>(rest)...); // propagate to the rest of the arguments
|
||||
}
|
||||
|
||||
// helper for inserting a scalar value
|
||||
template<size_t i0 = 0>
|
||||
constexpr void __insert(scalar_t s) {
|
||||
constexpr void _insert(scalar_t s) {
|
||||
data[i0 / rows][i0 % rows] = s;
|
||||
}
|
||||
|
||||
// helper for inserting a scalar value of differing type
|
||||
template<size_t i0 = 0, typename OScalarT> requires(is_arithmetic_v<OScalarT>)
|
||||
constexpr void __insert(OScalarT s) {
|
||||
constexpr void _insert(OScalarT s) {
|
||||
data[i0 / rows][i0 % rows] = scalar_t(s);
|
||||
}
|
||||
|
||||
// helper for inserting a vector
|
||||
template<size_t i0 = 0, size_t...i>
|
||||
constexpr void __insert(const vector<scalar_t, i...>& v) {
|
||||
(matrix::__insert<i0 + i>(v[i]), ...);
|
||||
constexpr void _insert(const vector<scalar_t, i...>& v) {
|
||||
(matrix::_insert<i0 + i>(v[i]), ...);
|
||||
}
|
||||
|
||||
// helper for inserting a vector of differing type
|
||||
template<size_t i0 = 0, typename OScalarT, size_t...i>
|
||||
constexpr void __insert(const vector<OScalarT, i...>& v) {
|
||||
(matrix::__insert<i0 + i>(v[i]), ...);
|
||||
constexpr void _insert(const vector<OScalarT, i...>& v) {
|
||||
(matrix::_insert<i0 + i>(v[i]), ...);
|
||||
}
|
||||
|
||||
// helper for a linear algebraic multiply
|
||||
static constexpr column_t __mul(const matrix_t& lhs, const row_t& rhs) {
|
||||
static constexpr column_t _mul(const matrix_t& lhs, const row_t& rhs) {
|
||||
// the compiler will optimize this better than writing out a specific definition
|
||||
// when compared to glm or CxxSwizzle, this is faster by a significant margin
|
||||
// all implementations provide 7 decimal places of precision
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -59,15 +59,16 @@
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
#include <fennec/math/detail/__fwd.h>
|
||||
#include <fennec/math/detail/__types.h>
|
||||
#include <fennec/math/detail/_fwd.h>
|
||||
#include <fennec/math/detail/_types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief an unsigned integer
|
||||
using uint = unsigned int;
|
||||
using byte = uint8_t;
|
||||
using ubyte = uint8_t;
|
||||
using ushort = uint16_t;
|
||||
using uint = uint32_t;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/lang/sequences.h>
|
||||
#include <fennec/lang/const_sequences.h>
|
||||
|
||||
#include <fennec/math/swizzle_storage.h>
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
|
||||
private:
|
||||
template<size_t...VecIndicesV>
|
||||
constexpr VectorT& decay_impl(VectorT& vec, index_sequence<VecIndicesV...>) {
|
||||
constexpr VectorT& decay_impl(VectorT& vec, const_index_sequence<VecIndicesV...>) {
|
||||
return ((vec[VecIndicesV] = this->data[IndicesV]), ..., vec);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
///
|
||||
///
|
||||
|
||||
#include <fennec/math/detail/__math.h>
|
||||
#include <fennec/math/detail/_math.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user