Compare commits

..

39 Commits

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

7
.gdbinit Normal file
View File

@@ -0,0 +1,7 @@
python
import sys, os.path
print(os.path.abspath("./gdb"))
sys.path.insert(0, os.path.abspath("./gdb"))
import fennec
fennec.register_printers(gdb.current_objfile())
end

1
.gitignore vendored
View File

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

View File

@@ -20,20 +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()
@@ -58,16 +76,32 @@ add_library(fennec STATIC
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
@@ -83,7 +117,7 @@ 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
@@ -95,6 +129,9 @@ add_library(fennec STATIC
include/fennec/lang/utility.h
include/fennec/lang/integer.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
@@ -104,8 +141,6 @@ add_library(fennec STATIC
include/fennec/lang/detail/_type_sequences.h
include/fennec/lang/detail/_typeuuid.h
include/fennec/lang/assert.h source/lang/assert.cpp
# MEMORY ===============================================================================================================
include/fennec/memory/new.h source/memory/new.cpp
@@ -115,15 +150,10 @@ 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
# CONCURRENCY ==========================================================================================================
include/fennec/concurrency/thread.h
include/fennec/concurrency/mutex.h
include/fennec/concurrency/atomic.h
# DEBUG ================================================================================================================
source/debug/assert_impl.cpp
@@ -184,19 +214,15 @@ add_library(fennec STATIC
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/renderers/interface/renderer.h
include/fennec/containers/rdtree.h
include/fennec/scene/scene.h
include/fennec/scene/component.h
include/fennec/core/system.h
)
add_dependencies(fennec metaprogramming)
add_dependencies(fennec metaprogramming fennec-dependencies)
target_compile_definitions(fennec PUBLIC
${FENNEC_COMPILE_DEFINITIONS}
@@ -208,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
)
@@ -222,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

View File

@@ -1,761 +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-langproc)
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/langproc/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
- OpenGL (EGL) ✔
- XKB
- PulseAudio
- Vulkan
- X11
- ALSA
- Vulkan
- Microsoft Windows
- XInput
- OpenGL (WGL)
- WASAPI
- Vulkan
- Android
- OpenGL ES
- AAudio
- openslES
- macOS/iOS
- cocoa
- OpenGL
- Core Audio
- Vulkan
- Metal
Linux Wayland will be implemented first. Once setup, the core engine will be implemented and tested on top of Wayland.
Once the engine is in a stable state, then support for other platforms will be resumed.
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 &rarr; AI `graph`
- Necessary for 2D and 3D navigation.
- Rooted Directed Tree &rarr; Scene `rd_tree`
- Defines the scene structure.
## Language Processing (`langproc`)
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 (`langproc/strings`)
* Search
* Manipulation
* Delimiting
* Regex
- File Formats (`langproc/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
unfortunately, most formats are esoteric due to copyright/trademark/etc. I will be using assimp for the
time being, below is a list of formats supported by assimp.
- 3D
- 3DS
- 3MF
- AC
- AC3D
- ACC
- AMJ
- ASE
- ASK
- B3D
- BVH
- CSM
- COB
- DAE/Collada
- DXF
- ENFF
- FBX
- glTF 1.0 + GLB
- glTF 2.0
- HMB
- IFC-STEP
- IQM
- IRR / IRRMESH
- LWO
- LWS
- LXO
- M3D
- MD2
- MD3
- MD5
- MDC
- MDL
- MESH / MESH.XML
- MOT
- MS3D
- NDO
- NFF
- OBJ
- OFF
- OGEX
- PLY
- PMX
- PRJ
- Q3O
- Q3S
- RAW
- SCN
- SIB
- SMD
- STP
- STL
- TER
- UC
- USD
- VTA
- X
- X3D
- XGL
- ZGL
- Video Formats
- MP4
- AVI
- MPG
- MOV
**TODO LATER**
* Compilation (`langproc/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 Accelerations)
- 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.
There will be three profiles for OpenGL implementation:
- modern
- fallback
- legacy
All profiles will have the same feature set, however their implementations will differ.
The modern context will use up-to-date features to get as much performance out of the pipeline as
possible.
### 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;
uint32 material, lighting;
}
```
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 and 3D textures 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 R Cubemap `1024, 512, 256, 128` (4)
- 8-Bit RGB Cubemap `1024, 512, 256, 128` (4)
* 16-Bit HDR R Texture `4096, 2048, 1024, 512` (8)
* 16-Bit HDR RGB Texture `4096, 2048, 1024, 512` (8)
* 16-Bit HDR RGBA Texture `4096, 2048, 1024, 512` (8)
* 16-Bit HDR R Cubemap `1024, 512, 256, 128` (4)
* 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)
* 8-Bit 3D R Texture `256, 128, 64, 32` (4)
* 8-Bit 3D RGB Texture `256, 128, 64, 32` (4)
* 16-Bit 3D HDR R Texture `256, 128, 64, 32` (4)
* 16-Bit 3D HDR RGB Texture `256, 128, 64, 32` (4)
Documentation should provide guidelines on categories of Art Assets and the resolution of textures to use.
Textures are identified by a 32-bit integer.
- `8 bits` &rarr; the texture buffer
- `24 bits` &rarr; the layer in the buffer
Artists should be informed on the texture structure of the engine and its limitations.
However, these principles should be followed in other game engines as these are
guided by what is most efficient for typical GPU hardware.
Materials are, for the most part, user-defined. Documentation should make the user aware of this.
Material buffers will be a sequence of the Material Struct instances.
They will at the very least contain the id of their shader.
Types of materials:
- Surface (Vertex & Fragment)
- Opaque / Masked
- Translucent (Forward)
- Volume (Path Traced Volumetrics / Forward)
- Light (Lighting Model)
- Post-Process
### Stages (`gfx3d`)
This is the set of stages for the graphics pipeline that runs every frame:
Unless otherwise specified, each stage will be run on the GPU.
- BVH
- Octree `(8 Bpn, 64 bpn) [6-Layers ≈ 2.1MB]`
- Leaf Size and Tree Depth should be calculated by the scene, constraints are as follows:
- Min Object Size
- Max Object Size
- Scene Center
- Scene Edge
- Buffer has implicit locations due to the tree having 8 children.
- Insertions and Updates are done on the CPU
- Nodes
- Start Index `int32`
- Object Count `int32`
- Objects
- Buffer of Object IDs grouped by Octree Node
- Leaf Culling
- Starting at each Octree Leaf, traverse upwards.
- Insert Visible Leaf IDs
- Track using atomic buffer
- Generate the Command Buffer for Culled Mesh LODs from the Visible Leaf Buffer
- Track counts using atomic buffers
- To avoid double counting due to the construction of the Octree output, we have some options
- Ignore Leaf Instances based on occurrences of the mesh in the surrounding 8 Quadtree Leaves. This would require
a bias towards a specific corner of the filter.
- Perform a preprocessing step on the CPU to erase duplicate elements and fix the buffer continuity.
- Let the duplicates be rendered.
- Generate the Culled Object Buffer with the respective object IDs
- Adjust Buffer Size using the counts
- Insert by reusing the count buffer, clipped to only contain used meshes
Debug View: Object ID, Mesh ID, LOD
- Visibility
- Buffer `(15 Bpp, 120 bpp) [1920x1080] ≈ 39.4MB`
- Depth Buffer &rarr; `D24`
- Visibility Info &rarr; `RGB32I`
- R = Object ID
- G = Mesh ID
- B = Material ID
- Regenerate the Command Buffer for Visible Mesh LODs
- Regenerate the Culled Object Buffer
Debug View: Visibility Buffer
* G-Buffer Pass `(17 Bpp, 136 bpp) [1920x1080] ≈ 35.3MB`
* Depth - Stencil &rarr; `D24_S8`
* S &rarr; used to represent the lighting model.
* Diffuse &rarr; `RGBA8`
* A &rarr; Ambient Occlusion
* Emission &rarr; `RGB8`
* Normal &rarr; `RGB8`
* Specular &rarr; `RGB8`
* R &rarr; Roughness
* G &rarr; Specularity (sometimes called the Metallicness)
* B &rarr; Index of Refraction (IOR)
Debug View: Depth, Stencil, Diffuse, Emission, Normal, Specularity
- Generate Dynamic Shadows
- Generate Dynamic Reflections (Optional)
- SSAO (Optional)
- Deferred Lighting Pass `(10 Bpp, 80 bpp) [1920x1080] ≈ 2 x 16.3MB + 8.3MB ≈ 24.6MB`
- Depth Buffer &rarr; `D24`
- Lighting Buffer &rarr; `RGB16` w/ Mipmapping
- Stencil Buffer $rarr; `S8`
- Apply Lighting Model
Debug View: Shadows, Reflections, SSAO, Deferred Lighting
We can combine all of these into one framebuffer:
- Depth - Stencil &rarr; `D24_S8`
- Visibility Info &rarr; `RGB32I`
- Diffuse &rarr; `RGBA8`
- Emission &rarr; `RGB8`
- Normal &rarr; `RGB8`
- Specular &rarr; `RGB8`
- Lighting Buffer &rarr; `RGB16` w/ Mipmapping
- One more slot left open for another
* Forward Pass
* BVH, Same as Above
* LOD Selection, Same as Above
* Translucent Materials
* Dual Depth Peeling
Debug View: Forward Mask
- Post Processing
- Depth of Field (Optional)
- When enabled, the Visiblity Buffer, G-Buffer, and Deferred Lighting Pass will be double layered.
- At this point the Lighting Buffers will be Flattened
- Bloom (Optional) &rarr; Mipmap Blurring `(6Bpp, 48bpp) [1920x1080] ≈ 16.3MB`
- Tonemapping (Optional)
- HDR Correction
## 3D Physics `(physics3d)`
Links:
- https://www.researchgate.net/publication/264839743_Simulating_Ocean_Water
- https://arxiv.org/pdf/2109.00104
- https://www.youtube.com/watch?v=rSKMYc1CQHE
- https://tflsguoyu.github.io/webpage/pdf/2013ICIA.pdf
- https://animation.rwth-aachen.de/publication/0557/
- https://github.com/InteractiveComputerGraphics/PositionBasedDynamics?tab=readme-ov-file
- https://www.cs.umd.edu/class/fall2019/cmsc828X/LEC/PBD.pdf
Systems
* Rigid Body Physics
* Newtonian Physics and Collision Resolution
* Articulated Skeletal Systems
* Inverse Kinematics
* Stiff Rods
- Particle Physics
* Soft Body Physics
* Elastics &rarr; Finite Element Simulation
* Cloth &rarr; Position-Based Dynamics
* Water
* Oceans &rarr; iWave
* Reasoning: iWave provides interactive lightweight fluid dynamics suitable for flat planes of water.
<br><br>
* 3D Fluid Dynamics &rarr; Smoothed-Particle Hydrodynamics
* Reasoning: This is the simplest method for simulating 3D bodies of water. This should exclusively be
used for small scale simulations where self-interactive fluids are necessary. I.E. pouring water into
a glass.
<br><br>
* 2D Fluid Dynamics &rarr; Force-Based Dynamics
* Reasoning: This model, like iWave, provides lightweight interactive fluid dynamics, but is more easily
adapted to flowing surfaces such as streams and rivers.
## Artificial Intelligence (`ai`)
This artificial intelligence method only differs in static generation between 2D and 3D.
The solvers are dimension independent since they work on a graph.
The general process is;
Static:
- generate a static navigation graph (sometimes called a NavMesh)
Update:
* resolve dynamic blockers
* update paths using dijkstra's algorithm
* apply rigid-body forces with constraints
The update loop for artificial intelligence should only update every `n` ticks. Where `n <= k`, with `k` being the
tick rate of the physics engine.

View File

@@ -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 &ndash; Jason Gregory

194
README.md
View File

@@ -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) &rarr; `apt`
* [Arch](#arch) &rarr; `pacman`
* [Fedora](#fedora) &rarr; `dnf`
* [Building on Windows](#building-on-windows)
* [Running the Test Suite](#running-the-test-suite)
* [Usage](#usage)
* [Licensing](#licensing)
* [Contribution](#contribution)
1. [Introduction](#introduction)
1. [Coding Standards](#coding-standards)
2. [Building from Source](#building-from-source)
1. [Building from Terminal](#building-from-terminal)
2. [Building on Windows](#building-on-windows)
3. [Running the Test Suite](#running-the-test-suite)
4. [Usage](#usage)
5. [Contribution](#contribution)
6. [Documentation](./documentation.html)
<br>
<br>
@@ -27,8 +33,8 @@
<a id="introduction"></a>
## Introduction
&ensp; 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
&ensp; 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
&ensp; fennec uses the CMake build manager. The CMake build script provides several
&ensp; fennec uses the CMake build manager. The CMake build script provides several
targets for building parts of the engine.
&ensp; 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
&ensp; 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.
&ensp; By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
&ensp; By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
build scripts to use another build manager, see the [CMake documentation for available generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
&ensp; I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
&ensp; I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
<a id="debian"></a>
#### Debian
On Debian-based distributions, you can install dependencies using the following command:
```shell
sudo apt install build-essential cmake ninja-build libglew-dev valgrind
git submodule update --force --init --recursive --remote
```
for Doxygen run:
```shell
sudo apt install doxygen graphviz
```
<a id="arch"></a>
#### Arch
On Arch-based distributions, you can install dependencies using the following command:
```shell
sudo pacman -S base-devel cmake ninja glew valgrind
git submodule update --force --init --recursive --remote
```
for Doxygen run:
```shell
sudo pacman -S doxygen graphviz
```
<a id="fedora"></a>
#### Fedora
On Fedora-based distributions, you can install dependencies using the following command:
```shell
sudo dnf install build-essential g++ cmake ninja-build glew-devel valgrind
git submodule update --force --init --recursive --remote
```
for Doxygen run:
```shell
sudo dnf install doxygen graphviz
```
<br>
<a id="building-on-windows"></a>
### Building on Windows
&ensp; 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
&ensp; 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>
&ensp; 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)
&ensp; 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.
&ensp; 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
&ensp; 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.
&ensp; fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from modifying
fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially, however
it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
fennec however you wish according to the terms of the license, which does not bar you from commercializing software
based on fennec.
TLDR; You may license your game under whichever license you please. Any C++ code that is by definition a derivative work
must be licensed under GPLv3 and freely available, everything else; assets, scripts, design documents, etc. may be under
the license of your choosing and remain proprietary.
&ensp; 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
&ensp; fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from
modifying fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially,
however it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
fennec however you wish according to the terms of the license, which does not bar you from commercializing software based
on fennec.
&ensp; If you wish to protect your game files, assets and generated content do not constitute a covered work and may be
copyrighted under a non-compliant license. Think of it in terms of using Blender to create a mesh for a game, then
licensing that mesh under another license.
Any code that is linked against fennec or uses any part is by definition a covered work must be licensed under GPLv3.
&ensp; Later down the line, I plan on implementing scripts in a manner that allows the script itself to remain proprietary.
The scripts will likely be trans-compiled to another language before being compiled to binary, but this is only an
&ensp; Later down the line, I plan on implementing scripts in a manner that allows the script itself to remain proprietary.
The scripts will likely be trans-compiled to another language before being compiled to binary, but this is only an
intermediate step and will be erased when no longer needed.
&ensp; As long as you use the official editor, it will properly include licenses in project content when provided a license
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
&ensp; As long as you use the official editor, it will properly include licenses in project content when provided a license
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
licensed under GPLv3 must be available upon request.
&ensp; A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
&ensp; A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
The executable and shared library are under the GPLv3 license, while the archive pak and streamable assets are under your license.
It is to my discretion whether I enforce the terms of the license on a party.
@@ -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>

View File

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

View File

@@ -22,4 +22,4 @@ 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 FENNEC_NO_INLINE=[[gnu::noinline]])
list(APPEND FENNEC_COMPILE_DEFINITIONS _GLIBCXX_INCLUDE_NEXT_C_HEADERS=1 FENNEC_COMPILER_GCC=1 FENNEC_NO_INLINE=[[gnu::noinline]])

View File

@@ -23,13 +23,13 @@ macro(fennec_check_platform)
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
)

View File

@@ -18,14 +18,17 @@
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)
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::GL)
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_OPENGL=1)
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()
@@ -35,12 +38,20 @@ if(FENNEC_GRAPHICS_WANT_EGL)
message(FATAL_ERROR "EGL Library not found.")
endif()
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::EGL)
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_EGL=1)
list(APPEND FENNEC_EXTRA_SOURCES
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}")
include/fennec/renderers/opengl/modern.h
include/fennec/renderers/opengl/fallback.h
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()

View File

@@ -19,11 +19,11 @@
# generic unix functionality
# compile definitions
list(APPEND FENNEC_COMPILE_DEFINITIONS
fennec_add_definitions(
FENNEC_PLATFORM_UNIX=1
)
# extra source files
list(APPEND FENNEC_EXTRA_SOURCES
fennec_add_sources(
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
)

View File

@@ -19,6 +19,23 @@
# 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)
@@ -30,6 +47,7 @@ macro(fennec_check_wayland)
WAYLAND_CLIENT_LIBRARY
NAMES wayland-client libwayland-client
)
find_program(WAYLAND_SCANNER NAMES wayland-scanner)
# EGL is required
find_path(
@@ -41,13 +59,34 @@ macro(fennec_check_wayland)
NAMES wayland-egl libwayland-egl
)
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY)
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY AND WAYLAND_SCANNER)
AND (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARY))
message(STATUS "Found Wayland: ${WAYLAND_CLIENT_LIBRARY}")
set(WAYLAND_PROTOCOLS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/protocols)
set(WAYLAND_HEADERS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/headers)
set(WAYLAND_SOURCES_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/sources)
# Search for base protocol xml
find_file(WAYLAND_PROTOCOL NAMES wayland.xml PATHS /usr/share/wayland /usr/share/wayland-protocols)
file(COPY ${WAYLAND_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
# search for xdg protocols
find_file(XDG_SHELL_PROTOCOL NAMES xdg-shell.xml PATHS /usr/share/wayland-protocols/stable/xdg-shell)
file(COPY ${XDG_SHELL_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
# include sub-dependencies
include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake")
fennec_check_xkb()
# generate protocols, based on SDL3
file(GLOB WAYLAND_PROTOCOLS_XML RELATIVE "${WAYLAND_PROTOCOLS_DIR}" "${WAYLAND_PROTOCOLS_DIR}/*.xml")
foreach(_XML IN LISTS WAYLAND_PROTOCOLS_XML)
get_filename_component(_FILE ${_XML} NAME_WLE)
fennec_wayland_get_header("${WAYLAND_SCANNER}" "${WAYLAND_PROTOCOLS_DIR}/${_XML}" "${_FILE}")
endforeach()
# Add sources and libraries
get_filename_component(
WAYLAND_CLIENT_LIBRARY
${WAYLAND_CLIENT_LIBRARY}
@@ -63,19 +102,18 @@ macro(fennec_check_wayland)
set(WAYLAND_EGL_FOUND 1)
set(FENNEC_GRAPHICS_WANT_EGL 1)
list(APPEND FENNEC_EXTRA_SOURCES
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/wayland-client.h
include/fennec/platform/linux/wayland/lib/wayland-util.h
include/fennec/platform/linux/wayland/lib/wayland.h
include/fennec/platform/linux/wayland/lib/loader.h source/platform/linux/wayland/lib/loader.cpp
# Fennec Files
include/fennec/platform/linux/wayland/display.h source/platform/linux/wayland/display.cpp
include/fennec/platform/linux/wayland/window.h source/platform/linux/wayland/window.cpp
)
list(APPEND FENNEC_COMPILE_DEFINITIONS
fennec_add_definitions(
FENNEC_HAS_WAYLAND=1
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
FENNEC_LIB_WAYLAND_EGL="${WAYLAND_EGL_LIBRARY}"

View File

@@ -41,17 +41,16 @@ macro(fennec_check_xkb)
set(XKB_FOUND 1)
list(APPEND FENNEC_EXTRA_SOURCES
fennec_add_sources(
# Dynamic Library Files
include/fennec/platform/linux/xkb/lib/fwd.h
include/fennec/platform/linux/xkb/lib/sym.h
include/fennec/platform/linux/xkb/lib/xkbcommon.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
)
list(APPEND FENNEC_COMPILE_DEFINITIONS
fennec_add_definitions(
FENNEC_HAS_XKB=1
FENNEC_LIB_XKB="${XKB_LIBRARY}"
)

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,71 @@
# script to download the emoticons from GitHub and to produce a table for
# inclusion in Doxygen. Works with python 2.7+ and python 3.x
import json
import os
import argparse
import re
try:
import urllib.request as urlrequest
except ImportError:
import urllib as urlrequest
unicode_re = re.compile(r'.*?/unicode/(.*?).png\?.*')
def get_emojis():
response = urlrequest.urlopen('https://api.github.com/emojis')
raw_data = response.read()
return json.loads(raw_data)
def download_images(dir_name, silent):
if not os.path.exists(dir_name):
os.makedirs(dir_name)
json_data = get_emojis()
num_items = len(json_data)
cur_item=0
for image,url in sorted(json_data.items()):
image_name = image+'.png'
cur_item=cur_item+1
if url.find('/unicode/')==-1 or not os.path.isfile(dir_name+'/'+image_name):
success = True
with open(dir_name+'/'+image_name,'wb') as file:
if not silent:
print('%s/%s: fetching %s' % (cur_item,num_items,image_name))
try:
file.write(urlrequest.urlopen(url).read())
except:
print('Unable to fetch %s' % (image_name))
success = False
if not success:
os.remove(dir_name+'/'+image_name)
else:
if not silent:
print('%s/%s: skipping %s' % (cur_item,num_items,image_name))
def produce_table():
json_data = get_emojis()
lines = []
for image,url in sorted(json_data.items()):
match = unicode_re.match(url)
if match:
unicodes = match.group(1).split('-')
unicodes_html = ''.join(["&#x"+x+";" for x in unicodes])
image_str = "\":"+image+":\","
unicode_str = "\""+unicodes_html+"\""
lines.append(' { %-42s %-38s }' % (image_str,unicode_str))
out_str = ',\n'.join(lines)
print("{")
print(out_str)
print("};")
if __name__=="__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-d','--dir',help='directory to place images in')
parser.add_argument('-t','--table',help='generate code fragment',action='store_true')
parser.add_argument('-s','--silent',help='silent mode',action='store_true')
args = parser.parse_args()
if args.table:
produce_table()
if args.dir:
download_images(args.dir, args.silent)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 60 KiB

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

@@ -0,0 +1,36 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
# GDB CODE =============================================================================================================
import gdb
from . import containers
from . import strings
from . import memory
from . import utility
from . import filesystem
from . import math
def register_printers(obj):
gdb.printing.register_pretty_printer(obj, containers.printer)
gdb.printing.register_pretty_printer(obj, strings.printer)
gdb.printing.register_pretty_printer(obj, memory.printer)
gdb.printing.register_pretty_printer(obj, filesystem.printer)
gdb.printing.register_pretty_printer(obj, math.printer)

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

@@ -0,0 +1,481 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import gdb
from collections import deque
# OPTIONAL =============================================================================================================
class OptionalPrinter:
"""Print a fennec::optional"""
def __init__(self, val):
self.val = val
def to_string(self):
is_initialized = self.val['_set']
if is_initialized:
return "{{ value = {} }}".format(self.val['_val'])
else:
return "{ empty }"
# PAIR =================================================================================================================
class PairPrinter:
"""Print a fennec::optional"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ first = " + str(self.val['first']) + ", second = " + str(self.val['second']) + " }"
def children(self):
return ("first", self.val['first']), ("second", self.val['second'])
# TUPLE ================================================================================================================
class TuplePrinter:
"""Print a fennec::tuple"""
def __init__(self, val):
self.fields = val.type.fields()[0].type.fields()
self.elems = val
self.count = len(self.fields)
def to_string(self):
return " { size = " + str(len(self.fields)) + " }"
def children(self):
return (('[{}]'.format(i), self.elems.cast(self.fields[i].type)['value']) for i in range(self.count))
# ARRAY ================================================================================================================
class ArrayPrinter:
"""Print a fennec::array"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length = " + str(self.val['elements'].type.range()[1] + 1) + " }"
def display_hint(self):
return 'array'
def children(self):
start = self.val['elements']
return (('[{}]'.format(i), start[i]) for i in range(0, self.val['elements'].type.range()[1] + 1))
# DYNARRAY =============================================================================================================
class DynArrayPrinter:
"""Print a fennec::dynarray"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_alloc']['_capacity']) + " }"
def children(self):
size = int(self.val['_size'])
start = self.val['_alloc']['_data']
return (('[{}]'.format(i), start[i]) for i in range(size))
# LIST =================================================================================================================
class ListPrinter:
"""Print a fennec::list"""
class Iterator:
def __init__(self, val):
self.list = val
self.node = self.list['_root']
self.index = 0
self.table = self.list['_table']['_data']
def __iter__(self):
return self
def __next__(self):
if self.node == 18446744073709551615:
raise StopIteration
i = self.index
self.index = self.index + 1
value = self.table[self.node]['value']['_val']
self.node = self.table[self.node]['next']
return '[{}]'.format(i), value
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_capacity']) + " }"
def children(self):
return self.Iterator(self.val)
def display_hint(self):
return 'array'
# DEQUE ================================================================================================================
class DequePrinter:
"""Print a fennec::deque"""
class Iterator:
def __init__(self, start):
self.node = start
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.node is None:
raise StopIteration
i = self.index
value = self.node.dereference()['value']
self.node = self.node.dereference()['next']
self.index = self.index + 1
return '[{}]'.format(i), value
def __init__(self, val):
self.first = val['_first']
self.last = val['_last']
self.size = val['_size']
def to_string(self):
if self.first is None:
return "{ empty }"
return "{ length " + str(self.size) + " }"
def children(self):
return self.Iterator(self.first)
# SET ==================================================================================================================
class SetPrinter:
"""Print a fennec::set"""
class Iterator:
def __init__(self, table, capacity):
self.table = table
self.capacity = capacity
self.node = 0
self.index = 0
def __iter__(self):
return self
def __next__(self):
while self.node < self.capacity:
if self.table[self.node]['value']['_set']:
break
self.node = self.node + 1
if self.node >= self.capacity:
raise StopIteration
i = self.index
value = self.table[self.node]['value']['_val']
self.node = self.node + 1
self.index = self.index + 1
return '[{}]'.format(i), value
def __init__(self, val):
self.table = val['_alloc']['_data']
self.capacity = val['_alloc']['_capacity']
self.size = val['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.table, self.capacity)
# MAP ==================================================================================================================
class MapPrinter:
"""Print a fennec::map"""
class Iterator:
def __init__(self, table, capacity):
self.table = table
self.capacity = capacity
self.node = 0
self.index = 0
self.move = False
def __iter__(self):
return self
def __next__(self):
while self.node < self.capacity:
if self.table[self.node]['value']['_set']:
break
self.node = self.node + 1
if self.node >= self.capacity:
raise StopIteration
i = self.index
pair = self.table[self.node]['value']['_val']
key = pair['first']
val = pair['second']
if not self.move:
self.move = True
return 'key' + str(i), key
else:
self.move = False
self.index = self.index + 1
self.node = self.node + 1
return 'value' + str(i), val
def __init__(self, val):
self.table = val['_set']['_alloc']['_data']
self.capacity = val['_set']['_alloc']['_capacity']
self.size = val['_set']['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.table, self.capacity)
def display_hint(self):
return 'map'
# OBJECT_POOL ==========================================================================================================
class ObjectPoolPrinter:
"""Print a fennec::object_pool"""
class Iterator:
def __init__(self, val):
self.list = val
self.index = 0
self.capacity = self.list['_table']['_alloc']['_capacity']
self.table = self.list['_table']['_alloc']['_data']
def __iter__(self):
return self
def __next__(self):
i = self.index
while True:
i = self.index
self.index = self.index + 1
if self.index >= self.capacity:
raise StopIteration
if bool(self.table[i]['_set']):
value = self.table[i]['_val']
break
return '[{}]'.format(i), value
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_alloc']['_capacity']) + " }"
def children(self):
return self.Iterator(self.val)
def display_hint(self):
return 'array'
# RDTREE ===============================================================================================================
class RDTreePrinter:
"""Print a fennec::rdtree"""
class Iterator:
def __init__(self, tree, node, capacity):
self.tree = tree
self.capacity = capacity
self.visit = deque()
self.visit.append((node, 0, 0))
def __iter__(self):
return self
def __next__(self):
if len(self.visit) == 0:
raise StopIteration
node = self.visit[0][0]
i = self.visit[0][1]
depth = self.visit[0][2]
self.visit.popleft()
value = self.tree[node]['value']
nnext = self.tree[node]['next']
nprev = self.tree[node]['prev']
nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
child = self.tree[node]['child']
n_chld = self.tree[node]['num_children']
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
if nnext < self.capacity:
self.visit.appendleft((nnext, i + 1, depth))
if child < self.capacity:
self.visit.appendleft((child, 0, depth + 1))
# ┌ ─ ├ └
if nnext != 18446744073709551615:
index += ''
else:
index += ''
index += ''
index += '[{}, {}]'.format(node, i)
return index, value
def __init__(self, val):
self.tree = val['_table']['_data']
self.size = val['_size']
self.capacity = val['_table']['_capacity']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.tree, 0, self.capacity)
# Graph ================================================================================================================
class GraphPrinter:
"""Print a fennec::graph"""
class Iterator:
def __init__(self, val):
self.node_pool = val['_vertex_pool']['_table']['_alloc']['_data']
self.max_nodes = val['_vertex_pool']['_table']['_alloc']['_capacity']
self.conn_map = val['_edge_map']['_alloc']['_data']
self.max_conn = val['_edge_map']['_size']
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= self.max_nodes:
raise StopIteration
i = self.index
self.index = self.index + 1
while not bool(self.node_pool[i]['_set']):
i = self.index
self.index = self.index + 1
conns = self.get_conns(i)
value = self.node_pool[i]['_val']
return '[{} -> {{{}}}]'.format(i, str(conns)), value
def get_conns(self, index):
indices = []
if index >= self.max_conn:
return indices
map = self.conn_map[index]['_set']
max_conns = map['_alloc']['_capacity']
conns = map['_alloc']['_data']
print(max_conns)
if max_conns == 0:
return indices
for i in range(0, max_conns):
if bool(conns[i]['value']['_set']):
conn = conns[i]['value']['_val']
indices.append(str(int(conn['first'])))
return indices
def __init__(self, val):
self.val = val
self.size = val['_vertex_pool']['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.val)
# GDB Code =============================================================================================================
def register_printers():
print("registering containers")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
pp.add_printer('fennec::deque', '^fennec::deque<.*>$', DequePrinter)
pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp
printer = register_printers()

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

@@ -0,0 +1,60 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import gdb
# PATH =================================================================================================================
class PathPrinter:
def __init__(self, val):
self.val = val['_str']['_str']
def to_string(self):
value = "\"" + self.val['_data'].string('', 'replace', self.val['_capacity'] - 1) + "\""
return value
def display_hint(self):
return 'string'
# PATH =================================================================================================================
class FilePrinter:
def __init__(self, val):
self.val = val
self.path = val['_path']['_str']['_str']
def to_string(self):
if self.val['_handle']:
value = "{ path = \"" + self.path['_data'].string('', 'replace', self.path['_capacity'] - 1) + "\" }"
return value
return "{ closed }"
def display_hint(self):
return 'string'
# GDB Code =============================================================================================================
def register_printers():
print("registering filesystem")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::filesystem")
pp.add_printer('fennec::path', '^fennec::path$', PathPrinter)
pp.add_printer('fennec::file', '^fennec::file$', FilePrinter)
return pp
printer = register_printers()

99
gdb/fennec/math.py Normal file
View File

@@ -0,0 +1,99 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import os
import gdb
from . import utility
# VECTOR =================================================================================================================
class VectorPrinter:
def __init__(self, val):
self.val = val
self.base = val['data']['elements']
self.len = self.base.type.range()[1] + 1
def to_string(self):
res = "< "
if self.len > 0:
res += "x = " + str(self.val['x'])
if self.len > 1:
res += ", y = " + str(self.val['y'])
if self.len > 2:
res += ", z = " + str(self.val['z'])
if self.len > 3:
res += ", w = " + str(self.val['w'])
res += " >"
return res
def children(self):
return (('[{}]'.format(i), self.base[i]) for i in range(self.len))
def display_hint(self):
return 'array'
# PATH =================================================================================================================
class QuaternionPrinter:
def __init__(self, val):
self.val = val
self.base = val['data']['elements']
def to_string(self):
res = ("< "
+ str(self.val['x']) + " i + "
+ str(self.val['y']) + " j + "
+ str(self.val['z']) + " k + "
+ str(self.val['w']))
res += " >"
return res
def children(self):
return (('[{}]'.format(i), self.base[i]) for i in range(4))
def display_hint(self):
return 'array'
# VECTOR =================================================================================================================
class MatrixPrinter:
def __init__(self, val):
self.columns = val['data']['elements']
self.num_columns = self.columns.type.range()[1] + 1
self.num_rows = val.type.template_argument(1)
def to_string(self):
return "{ rows = " + str(self.num_rows) + ", columns = " + str(self.num_columns) + " }"
def children(self):
return (('[{}]'.format(i), self.columns[i]) for i in range(self.num_columns))
def display_hint(self):
return 'array'
# GDB Code =============================================================================================================
def register_printers():
print("registering filesystem")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::math")
pp.add_printer('fennec::vector', '^fennec::vector<.*>$', VectorPrinter)
pp.add_printer('fennec::quaternion', '^fennec::quaternion<.*>$', QuaternionPrinter)
pp.add_printer('fennec::matrix', '^fennec::matrix<.*>$', MatrixPrinter)
return pp
printer = register_printers()

43
gdb/fennec/memory.py Normal file
View File

@@ -0,0 +1,43 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import gdb
# ALLOCATION ===========================================================================================================
class AllocationPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return "{ capacity = " + str(self.val['_capacity']) + " }"
def children(self):
size = int(self.val['_capacity'])
start = self.val['_data']
return (('[{}]'.format(i), start[i]) for i in range(size))
# GDB Code =============================================================================================================
def register_printers():
print("registering memory")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::memory")
pp.add_printer('fennec::allocation', '^fennec::allocation<.*>$', AllocationPrinter)
return pp
printer = register_printers()

61
gdb/fennec/strings.py Normal file
View File

@@ -0,0 +1,61 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import gdb
# CSTRING ==============================================================================================================
class CStringPrinter:
def __init__(self, val):
self.val = val
def display_hint(self):
return 'string'
def to_string(self):
value = "\"" + self.val['_str'].string('', 'replace', self.val['_size']) + "\""
return value
def display_hint(self):
return 'string'
# STRING ===============================================================================================================
class StringPrinter:
def __init__(self, val):
self.val = val['_str']
def to_string(self):
value = "\"" + self.val['_data'].string('', 'replace', self.val['_capacity'] - 1) + "\""
return value
def display_hint(self):
return 'string'
# GDB Code =============================================================================================================
def register_printers():
print("registering strings")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::strings")
pp.add_printer('fennec::cstring', '^fennec::cstring$', CStringPrinter)
pp.add_printer('fennec::wcstring', '^fennec::wcstring$', CStringPrinter)
pp.add_printer('fennec::string', '^fennec::_string<.*>$', StringPrinter)
pp.add_printer('fennec::wstring', '^fennec::_wstring<.*>$', StringPrinter)
return pp
printer = register_printers()

20
gdb/fennec/utility.py Normal file
View File

@@ -0,0 +1,20 @@
# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
def printMembers(obj):
print(str(dir(obj)))

View File

@@ -18,7 +18,7 @@
///
/// \file array.h
/// \brief statically allocated array wrapper
/// \brief A header containing the definition for a static/stack allocated array
///
///
/// \details
@@ -40,61 +40,48 @@ namespace fennec
///
///
/// \brief wrapper for fixed size arrays
/// \brief Data Structure that defines a compile-time allocated array
///
/// \details
/// | Property | Value |
/// |----------|-------------------------|
/// | stable | \emoji heavy_check_mark |
/// | access | \f$O(1)\f$ |
/// | space | \f$O(N)\f$ |
/// | 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];
}
constexpr ValueT* begin() { return elements; }
constexpr ValueT* end() { return elements + ElemV; }
constexpr const ValueT* begin() const { return elements; }
constexpr const ValueT* end() const { return elements + ElemV; }
/// \brief backing c-style array handle
value_t data[ElemV];
/// @}
/// \name Capacity
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
@@ -111,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>{});
}
///
/// \brief Checks if any element in the arrays is not equal
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) {
return not array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
}
/// @}
// Iteration ===========================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns A pointer to the first element of the array
constexpr value_t* begin() {
return data;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns A pointer to one after the end of the array
constexpr value_t* end() {
return data + ElemV;
}
///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const-qualified pointer to the first element of the array
constexpr const value_t* begin() const {
return data;
}
///
/// \brief Const C++ Iterator Specification `end()`
/// \returns A const-qualified pointer to one after the end of the array
constexpr const value_t* end() const {
return data + ElemV;
}
/// @}
private:
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]) && ...);
}
};

View File

@@ -0,0 +1,94 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file containers.h
/// \brief fennec containers library main header
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_CONTAINERS_H
#define FENNEC_CONTAINERS_CONTAINERS_H
///
/// \page fennec_containers Containers Library
///
/// \brief The fennec Containers Library.
/// Includes various data structures, those specified in the C++ Standard Library, and custom data structures
/// that fennec uses.
///
/// \code #include <fennec/containers/containers.h> \endcode
///
/// \section fennec_containers_cppstdlib C++ Standard Template Library
///
/// | Symbol | Implemented | Passed |
/// |:----------------------------------------------------------------------------|:-----------:|:------:|
/// | \ref fennec::any "fennec::any" | ⛔ | ⛔ |
/// | \ref fennec::array "fennec::array" | ✅ | ✅ |
/// | \ref fennec::bitset "fennec::bitset" | ⛔ | ⛔ |
/// | \ref fennec::deque "fennec::deque" | ⛔ | ⛔ |
/// | \ref fennec::dynarray "fennec::dynarray" `std::vector` | 🚧 | 🚧 |
/// | \ref fennec::list "fennec::list" | ✅ | ✅ |
/// | \ref fennec::map "fennec::map" `std::unordered_map` | ✅ | ✅ |
/// | \ref fennec::map_sequence "fennec::map_sequence" `std::map` | ⛔ | ⛔ |
/// | \ref fennec::multiset "fennec::multiset" `std::unordered_multiset` | ⛔ | ⛔ |
/// | \ref fennec::multisequence "fennec::multisequence" `std::multiset` | ⛔ | ⛔ |
/// | \ref fennec::multimap "fennec::multimap" `std::unordered_multimap` | ⛔ | ⛔ |
/// | \ref fennec::multimap_sequence "fennec::multimap_sequence" `std::multimap` | ⛔ | ⛔ |
/// | \ref fennec::optional "fennec::optional" | ✅ | ✅ |
/// | \ref fennec::pair "fennec::pair" | ✅ | ✅ |
/// | \ref fennec::sequence "fennec::sequence" `std::set` | ⛔ | ⛔ |
/// | \ref fennec::set "fennec::set" `std::unordered_set` | ✅ | ✅ |
/// | \ref fennec::tuple "fennec::tuple" | 🚧 | 🚧 |
/// | \ref fennec::variant "fennec::variant" | ⛔ | ⛔ |
///
///
/// \section fennec_containers_fennec fennec
///
/// | Symbol | Implemented | Passed |
/// |:-------------------------|:-----------:|:------:|
/// | \ref fennec::graph | 🚧 | 🚧 |
/// | \ref fennec::object_pool | 🚧 | 🚧 |
/// | \ref fennec::rdtree | ✅ | ✅ |
///
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
#include <fennec/containers/traversal.h>
#include <fennec/containers/array.h>
#include <fennec/containers/deque.h>
#include <fennec/containers/dynarray.h>
#include <fennec/containers/graph.h>
#include <fennec/containers/list.h>
#include <fennec/containers/map.h>
#include <fennec/containers/object_pool.h>
#include <fennec/containers/optional.h>
#include <fennec/containers/pair.h>
#include <fennec/containers/rdtree.h>
#include <fennec/containers/set.h>
#include <fennec/containers/tuple.h>
#endif // FENNEC_CONTAINERS_CONTAINERS_H

View File

@@ -0,0 +1,345 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file deque.h
/// \brief A header containing the definition for a double-ended queue
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_DEQUE_H
#define FENNEC_CONTAINERS_DEQUE_H
#include <fennec/memory/allocator.h>
// TODO: Document
namespace fennec
{
///
///
/// \brief Data structure defining a double-ended queue.
///
/// \details
/// This behaves the similar to fennec::list, however it does not allow arbitrary access, insertion, or deletion.
/// It is one of the few data structures in this library that is stable, i.e. pointers to elements do not change.
///
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ✅ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ⛔ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(N)\f$ |
/// | insertion | \f$O(1)\f$ |
/// | deletion | \f$O(1)\f$ |
///
/// \tparam TypeT value type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct deque {
// Definitions =========================================================================================================
public:
using value_t = TypeT; ///< Alias for the value type
class iterator;
private:
struct node {
value_t value;
node *prev, *next;
template<typename...ArgsT>
node(node* prev, node* next, ArgsT&&...args)
: value(fennec::forward<ArgsT>(args)...)
, prev(prev), next(next) {
}
~node() = default;
};
public:
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
using elem_t = node*;
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty deque
deque()
: _alloc()
, _first(nullptr)
, _last(nullptr)
, _size(0) {
}
///
/// \brief Alloc Constructor, initializes an empty deque with the specified allocator
/// \param alloc the allocator to copy
deque(const alloc_t& alloc)
: _alloc(alloc)
, _first(nullptr)
, _last(nullptr)
, _size(0) {
}
///
/// \brief Copy Constructor
/// \param deque the deque to copy
deque(const deque& deque)
: _alloc(deque._alloc)
, _first(nullptr)
, _last(nullptr)
, _size(0) {
const elem_t node = deque._first;
while (node) {
this->push_back(node->value);
node = node->next;
}
}
///
/// \brief Deque Move Constructor
/// \param deque the deque to move
deque(deque&& deque) noexcept
: _alloc(deque._alloc)
, _first(deque._first)
, _last(deque._last)
, _size(deque._size) {
deque._first = nullptr;
deque._last = nullptr;
}
///
/// \brief Destructor, calls deque::clear
~deque() {
clear();
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns `true` when the deque is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns the number of elements in size()
constexpr size_t size() const {
return _size;
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \returns a reference to the first element in the deque
value_t& front() {
assert(not empty(), "Attempted to access an empty deque.");
return _first->value;
}
///
/// \returns a const-qualified reference to the first element in the deque
const value_t& front() const {
assert(not empty(), "Attempted to access an empty deque.");
return _first->value;
}
///
/// \returns a reference to the last element in the deque
value_t& back() {
assert(not empty(), "Attempted to access an empty deque.");
return _last->value;
}
///
/// \returns a const-qualified reference to the last element in the deque
const value_t& back() const {
assert(not empty(), "Attempted to access an empty deque.");
return _last->value;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Push Front Move, moves a value to the front of the deque
/// \param elem the value to move
void push_front(value_t&& elem) {
this->_push_front(elem);
}
///
/// \brief Push Front Copy, copies a value to the front of the deque
/// \param elem the value to copy
void push_front(const value_t& elem) {
this->_push_front(elem);
}
///
/// \brief Emplace Front, constructs a new value at the front of the deque
/// \tparam ArgsT Argument types
/// \param args Arguments used to construct the value
template<typename...ArgsT>
void emplace_front(ArgsT&&...args) {
this->_push_front(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Push Back Move, moves a value to the back of the deque
/// \param elem the value to move
void push_back(value_t&& elem) {
this->_push_back(elem);
}
///
/// \brief Push Back Copy, copies a value to the back of the deque
/// \param elem the value to copy
void push_back(const value_t& elem) {
this->_push_back(elem);
}
///
/// \brief Emplace Back, constructs a new value at the back of the deque
/// \tparam ArgsT Argument types
/// \param args Arguments used to construct the value
template<typename...ArgsT>
void emplace_back(ArgsT&&...args) {
this->_push_back(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Clears the contents of the deque
void clear() {
elem_t it = _first;
while (it) {
elem_t next = it->next;
fennec::destruct(it);
_alloc.deallocate(it);
it = next;
}
_first = nullptr;
_last = nullptr;
_size = 0;
}
///
/// \brief Erase the First Element
void pop_front() {
if (_first == nullptr) {
return;
}
elem_t next = _first->next;
fennec::destruct(_first);
_alloc.deallocate(_first);
_first = next;
_last = next ? _last : nullptr;
--_size;
}
///
/// \brief Erase the Last Element
void pop_back() {
if (_last == nullptr) {
return;
}
elem_t prev = _last->prev;
fennec::destruct(_last);
_alloc.deallocate(_last);
_last = prev;
_first = prev ? _first : nullptr;
--_size;
}
/// @}
// Iteration ===========================================================================================================
/*
* TODO: Decide whether you should be able to iterate over a deque
*/
private:
alloc_t _alloc;
node *_first, *_last;
size_t _size;
template<typename...ArgsT>
void _push_front(ArgsT&&...args) {
elem_t next = _first;
_first = _alloc.allocate(1);
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
if (next) {
next->prev = _first;
} else {
_last = _first;
}
++_size;
}
template<typename...ArgsT>
void _push_back(ArgsT&&...args) {
elem_t prev = _last;
_last = _alloc.allocate(1);
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
if (prev) {
prev->next = _last;
} else {
_first = _last;
}
++_size;
}
};
}
#endif // FENNEC_CONTAINERS_DEQUE_H

View File

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

View File

@@ -18,7 +18,7 @@
///
/// \file dynarray.h
/// \brief dynamically allocated array wrapper
/// \brief A header containing the definition for a dynamically allocated array
///
///
/// \details
@@ -40,126 +40,287 @@ namespace fennec
///
///
/// \brief wrapper for dynamically sized arrays
/// \brief Wrapper for dynamically sized and allocated arrays
/// \details
/// | Property | Value |
/// |-----------|--------------|
/// | stable | \emoji anger |
/// | access | \f$O(1)\f$ |
/// | insertion | \f$O(N)\f$ |
/// | deletion | \f$O(N)\f$ |
/// | space | \f$O(N)\f$ |
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ✅ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(N)\f$ |
/// | insertion | \f$O(N)\f$ |
/// | deletion | \f$O(N)\f$ |
///
/// This structure prefers shallow moves and deep copies.
///
/// \tparam TypeT value type
template<class TypeT, class Alloc = allocator<TypeT>>
class dynarray {
public:
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.
constexpr 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.
constexpr 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.
constexpr 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
/// \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
/// \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>
constexpr 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)...);
}
}
///
/// \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() {
element_t* addr = _alloc.data();
value_t* addr = _alloc.data();
if (addr == nullptr) return;
for(int n = _size; n > 0; --n, ++addr) {
fennec::destruct(addr);
}
}
/// @}
// Assignment ==========================================================================================================
/// \name Properties
/// @{
///
/// \brief Copy Assignment Operator
/// \param arr the array to copy
/// \returns A dynarray after having copied each element of `arr`
constexpr dynarray& operator=(const dynarray& arr) {
this->clear();
_alloc.creallocate(_size = arr._size);
for (size_t i = 0; i < _size; ++i) {
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
}
return *this;
}
///
/// \brief Move Assignment Operator
/// \param arr the array to move
/// \returns A dynarray after having taken ownership of the contents of `arr`
constexpr dynarray& operator=(dynarray&& arr) noexcept {
this->clear();
_alloc = fennec::move(arr._alloc);
_size = arr._size;
arr._size = 0;
return *this;
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The size of the dynarray in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns The current capacity, in elements, of the underlying allocation
constexpr size_t capacity() const {
return _alloc.capacity();
}
///
/// \returns True when there are no elements active, otherwise false
constexpr bool empty() const {
return _size == 0;
}
constexpr TypeT& operator[](int i) {
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
/// @}
// 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];
}
constexpr const TypeT& operator[](int i) const {
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
///
/// \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];
}
constexpr TypeT* begin() { return _alloc.data(); }
constexpr TypeT* end() { return begin() + _size; }
///
/// \returns Reference to the first element in the dynarray
constexpr TypeT& front() {
return this->operator[](0);
}
constexpr const TypeT* begin() const { return _alloc; }
constexpr const TypeT* end() const { return begin() + _size; }
///
/// \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
@@ -180,6 +341,10 @@ public:
++_size;
}
///
/// \brief Copy Insertion
/// \param i index to insert at
/// \param val the value to initialize with
constexpr void insert(size_t i, const TypeT& val) {
// Grow if the size has reached the capacity of the allocation
@@ -190,9 +355,10 @@ public:
// 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));
(void*)(_alloc.data() + i),
(void*)(_alloc.data() + i + 1),
(_size - i) * sizeof(TypeT)
);
}
// Insert the element
@@ -200,6 +366,11 @@ public:
++_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) {
@@ -221,42 +392,91 @@ public:
++_size;
}
///
/// \brief Push Back Copy
/// \param val Value to initialize with
constexpr void push_back(const TypeT& val) {
dynarray::insert(_size, val);
}
///
/// \brief Push Back Move
/// \param val Value to initialize with
constexpr void push_back(TypeT&& val) {
dynarray::insert(_size, fennec::forward<TypeT>(val));
}
constexpr void pop_back() {
fennec::destruct(&_alloc[_size--]);
}
///
/// \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)...);
}
constexpr void resize(size_t n) {
_alloc.reallocate(n);
///
/// \brief Erase last element
constexpr void pop_back() {
fennec::destruct(&_alloc[--_size]);
}
if (_size < n) {
_size = n;
return;
}
///
/// \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();
}
}
private:
constexpr void _grow() {
_alloc.reallocate(_alloc.capacity() * 2);
///
/// \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();
}
allocation<element_t, alloc_t> _alloc;
/// @}
// 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:
constexpr void _grow() {
_alloc.creallocate(_alloc.capacity() * 2);
}
allocation<value_t, alloc_t> _alloc;
size_t _size;
};

View File

@@ -0,0 +1,518 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file graph.h
/// \brief A header containing the definition for a graph of vertices connected by edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_GRAPH_H
#define FENNEC_CONTAINERS_GRAPH_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h>
#include <fennec/containers/map.h>
#include <fennec/containers/object_pool.h>
#include <fennec/containers/set.h>
/*
* With the directed tree we were able to cheat a little, the structure has more rules to it which allows
* tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
* properties or rules to edges which can simply be an extension to this graph.
*
* The most effective way to do this is to have a dynarray of lists, however this results in double the
* memory being used. This can also result in two edge objects being created.
*
* There is no nice way to avoid the problem of mapping vertices to edges
*/
namespace fennec
{
///
/// \brief Graph Data Structure, describes sets of arbitrarily connected vertices
///
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ✅ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(1)\f$ |
/// | insertion | \f$O(1)\f$ |
/// | deletion | \f$O(N)\f$ |
///
/// Graphs contain vertices and edges. Graphs are either directed
/// or undirected. This structure allows the creation of both directed and undirected edges. As
/// far as what that means; a directed graph means that edges have direction, where there are edges
/// that are "to" and "from," rather than "between" which is used in undirected graphs.
///
/// An undirected graph is connected if there is a path between every pair of vertices in the graph.
///
/// A directed graph is weakly connected if replacing all of its directed edges with undirected edges would
/// produce a connected graph. We will call this "disjointed"
///
/// A directed graph is semi-connected if there is a directed path p for `u` &rarr; `v` *or* `v` &rarr; `u` for every
/// pair of vertices `u, v`. We will call this "unilateral"
///
/// A directed graph is strongly-connected if there is a directed path p for `u` &rarr; `v` *and* `v` &rarr; `u` for every pair
/// of vertices `u, v`. We will call this "connected"
///
/// \tparam VertexT The type associated with each vertex
/// \tparam EdgeT The type associated with each edge
template<typename VertexT, typename EdgeT = empty_t>
struct graph {
public:
// Definitions =========================================================================================================
using edge_t = EdgeT; ///< Alias for the edge type
using vertex_t = VertexT; ///< Alias for the vertex type
using vertex_pool_t = object_pool<vertex_t>; ///< Alias for a pool of vertices
using edge_map_t = dynarray<map<size_t, size_t>>; ///< Alias for edge mapping
using edge_pool_t = object_pool<edge_t>; ///< Alias for a pool of edges
static constexpr size_t npos = -1; ///< Constant for a non-existent vertex
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty graph
constexpr graph() = default;
///
/// \brief Destructor
constexpr ~graph() = default;
/// @}
/// \name Assignment Operators
/// @{
///
/// \brief Copy Assignment Operator
/// \param g The graph to copy
/// \returns A reference to this after assigning g
constexpr graph& operator=(const graph& g) = default;
///
/// \brief Move Assignment Operator
/// \param g The graph to copy
/// \returns A reference to this after assigning g
constexpr graph& operator=(graph&& g) = default;
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The number of vertices in the graph
constexpr size_t num_vertices() const {
return _vertex_pool.size();
}
///
/// \returns The number of edges in the graph
constexpr size_t num_edges() const {
return _edge_pool.size();
}
///
/// \returns The capacity of the vertex pool
constexpr size_t capacity() const {
return _vertex_pool.capacity();
}
///
/// \returns `true` when there are no vertices in the graph, `false` otherwise
constexpr bool empty() const {
return num_vertices() == 0;
}
///
/// \brief Checks if there exists an edge `e` that starts from `a` and ends at `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if the edge exists, `false` otherwise
constexpr bool exists(size_t a, size_t b) const {
return _edge_map[a][b] != nullptr;
}
///
/// \brief Checks if there exists an edge `e0` that starts from `a` and ends at `b` and `e1` that starts from `b`
/// and ends at `a`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
constexpr bool is_symmetric(size_t a, size_t b) const {
return exists(a, b) and exists(b, a);
}
///
/// \brief Checks if there exists an edge `e` between `a` and `b`
/// \param a The first vertex
/// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise
constexpr bool is_undirected(size_t a, size_t b) const {
const auto* e0 = _edge_map[a][b];
const auto* e1 = _edge_map[b][a];
if (not (e0 != nullptr && e1 != nullptr)) {
return false;
}
return *e0 == *e1;
}
// TODO: connected, disjoint, unilateral, get_component
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief vertex Access Operator
/// \param vertex The id of the vertex
/// \returns A reference to the value stored in the vertex
constexpr vertex_t& operator[](size_t vertex) {
return _vertex_pool[vertex];
}
///
/// \brief vertex Const Access Operator
/// \param vertex The id of the vertex
/// \returns A reference to the value stored in the vertex
constexpr const vertex_t& operator[](size_t vertex) const {
return _vertex_pool[vertex];
}
///
/// \brief edge Access Operator
/// \param a The id of the first vertex
/// \param b The id of the second vertex
/// \returns A pointer to the value stored in the edge, `nullptr` if not found
constexpr edge_t* operator[](size_t a, size_t b) {
if (empty()) {
return nullptr;
}
edge_t* it = _edge_map[a][b];
if (it) {
return _edge_pool[*it];
}
return nullptr;
}
///
/// \brief edge Const Access Operator
/// \param a The id of the first vertex
/// \param b The id of the second vertex
/// \returns A const-qualified pointer to the value stored in the edge, `nullptr` if not found
constexpr const edge_t* operator[](size_t a, size_t b) const {
if (empty()) {
return nullptr;
}
const edge_t* it = _edge_map[a][b];
if (it) {
return _edge_pool[*it];
}
return nullptr;
}
///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `vertex` to `x...`
list<size_t> outgoing(size_t vertex) {
list<size_t> res;
if (empty() || vertex >= _edge_map.size()) {
return res;
}
for (const auto& it : _edge_map[vertex]) {
res.push_back(it.first);
}
return res;
}
///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge from `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `x...` to `vertex`
list<size_t> incoming(size_t vertex) {
list<size_t> res;
if (empty() || vertex >= _edge_map.size()) {
return res;
}
for (size_t n = 0; n < _edge_map.size(); ++n) {
if (_edge_map[n][vertex]) {
res.push_back(n);
}
}
return res;
}
///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...`
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` that have symmetric edges with `vertex`
list<size_t> symmetric(size_t vertex) {
list<size_t> res;
if (empty() || vertex >= _edge_map.size()) {
return res;
}
for (const auto& it : _edge_map[vertex]) {
if (_edge_map[it.first][vertex]) {
res.push_back(it.first);
}
}
return res;
}
///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...` and share the same value
/// \details
/// "Joined" edges may also be referred to as "undirected." A joined, or undirected, edge may be
/// turned into a directed edge by changing the weight object associated with the edge, or by
/// removing one of the sub-edges.
/// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` that have symmetric edges with `vertex`
list<size_t> undirected(size_t vertex) {
list<size_t> res;
if (empty() || vertex >= _edge_map.size()) {
return res;
}
for (const auto& it : _edge_map[vertex]) {
const auto* at = _edge_map[it.first][vertex];
if (at != nullptr && *at == it.second) {
res.push_back(it.first);
}
}
return res;
}
///
/// \brief Getter for the internal storage of mapped edges from this vertex.
/// Use this when you want to iterate over edges that start from this vertex.
/// \param vertex The id of the vertex
/// \returns A pointer to a map containing edges mapped from this vertex
const auto* edges(size_t vertex) {
if (empty() || vertex >= _edge_map.size()) {
return nullptr;
}
return &_edge_map[vertex];
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move a new vertex into the graph
/// \param vertex The vertex to move into the graph
/// \returns The id of the new vertex
constexpr size_t insert(vertex_t&& vertex) {
return this->_insert(fennec::forward<vertex_t>(vertex));
}
///
/// \brief Copy a new vertex into the graph
/// \param vertex The vertex to copy into the graph
/// \returns The id of the new vertex
constexpr size_t insert(const vertex_t& vertex) {
return this->_insert(vertex);
}
///
/// \brief Construct a new vertex in the graph
/// \tparam ArgsT The types of the arguments
/// \param args The arguments to construct the vertex with
/// \returns The id of the new vertex
template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase a vertex from the graph
/// \param vertex The id of the vertex to erase
constexpr void erase(size_t vertex) {
cut(vertex);
_vertex_pool.erase(vertex);
}
///
/// \brief Form an edge from vertex `a` to vertex `b`
/// \tparam ArgsT The argument types
/// \param a The first vertex id
/// \param b The second vertex id
/// \param args The arguments to construct the edge with
template<typename...ArgsT>
constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
if (a == b) {
return;
}
if (_edge_map.size() < _vertex_pool.capacity()) {
_edge_map.resize(_vertex_pool.capacity());
}
auto it = _edge_map[a][b];
size_t conn;
if (it != nullptr) {
conn = *it;
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
} else {
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
}
_edge_map[a].emplace(b, conn);
}
///
/// \brief Form an undirected edge between vertex `a` and vertex `b`
/// \tparam ArgsT The argument types
/// \param a The first vertex id
/// \param b The second vertex id
/// \param args The arguments to construct the edge with
template<typename...ArgsT>
constexpr void make_edge2(size_t a, size_t b, ArgsT&&...args) {
if (a == b) {
return;
}
if (_edge_map.size() < _vertex_pool.capacity()) {
_edge_map.resize(_vertex_pool.capacity());
}
auto it = _edge_map[a][b];
size_t conn;
if (it != nullptr) {
conn = *it;
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
} else {
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
}
_edge_map[a].emplace(b, conn);
_edge_map[b].emplace(a, conn);
}
///
/// \brief Disconnect an edge from vertex `a` to vertex `b`
/// \param a The first vertex id
/// \param b The second vertex id
constexpr void cut_edge(size_t a, size_t b) {
// Find the edge object
const auto* it = _edge_map[a][b];
if (not it) {
return;
}
size_t c = *it;
// Check if undirected
const auto* at = _edge_map[b][a];
if (not at || *at != c) {
_edge_pool.erase(c);
}
// Erase the edge mapping
_edge_map[a].erase(b);
}
///
/// \brief Disconnect both directed edges between vertices `a` and `b`
/// \param a The first vertex id
/// \param b The second vertex id
constexpr void cut_edge2(size_t a, size_t b) {
const auto* ita = _edge_map[a][b];
const auto* itb = _edge_map[a][b];
if (not (ita || itb)) {
return;
}
if (ita) _edge_pool.erase(*ita);
if (itb) _edge_pool.erase(*itb);
_edge_map[a].erase(b);
_edge_map[b].erase(a);
}
///
/// \brief Break *all* edges to and from `n`
/// \param n The vertex id
void cut(size_t n) {
for (const auto it : outgoing(n)) {
cut_edge(n, it);
}
for (const auto it : incoming(n)) {
cut_edge(it, n);
}
}
///
/// \brief Clear the graph, destructing all vertices and edges.
void clear() {
_vertex_pool.clear();
_edge_pool.clear();
_edge_map.clear();
}
/// @}
// edges =========================================================================================================
private:
vertex_pool_t _vertex_pool;
edge_pool_t _edge_pool;
edge_map_t _edge_map;
template<typename...ArgsT>
size_t _insert(ArgsT&&...args) {
return _vertex_pool.emplace(fennec::forward<ArgsT>(args)...);
}
};
}
#endif // FENNEC_CONTAINERS_GRAPH_H

View File

@@ -18,7 +18,7 @@
///
/// \file list.h
/// \brief List of elements
/// \brief A header containing the definition for a linked list of values
///
///
/// \details
@@ -43,267 +43,392 @@ namespace fennec
///
///
/// \brief wrapper for lists of elements
/// \brief Data Structure defining lists of elements
/// \details
/// | Property | Value |
/// |-----------|--------------|
/// | stable | \emoji anger |
/// | access | \f$O(1)\f$ |
/// | insertion | \f$O(1)\f$ |
/// | deletion | \f$O(1)\f$ |
/// | space | \f$O(N)\f$ |
/// 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 {
public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
using value_t = TypeT;
static constexpr size_t npos = -1;
class iterator;
class const_iterator;
// Definitions =========================================================================================================
private:
struct node;
public:
using elem_t = node;
/// \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) {
}
constexpr ~list() = default;
///
/// \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);
}
}
constexpr bool size() const { return _size; }
constexpr bool capacity() const { return _table.capacity(); }
constexpr bool empty() const { return _root == npos; }
///
/// \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].data;
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].data;
}
void insert(const iterator& it, const value_t& x) {
if (size() == capacity()) {
_expand();
}
size_t n = it._n;
size_t p = _next_free();
fennec::construct(&_table[p].data, x);
if (n == npos) {
if (empty()) {
_root = p;
_table[p].next = npos;
_table[p].prev = npos;
} else {
_table[p].prev = n;
_table[p].next = npos;
_last = n;
}
return;
}
_table[p].next = n;
_table[p].prev = _prev(n);
_table[n].prev = p;
++_size;
}
void insert(const iterator& it, value_t&& x) {
if (size() == capacity()) {
_expand();
}
size_t n = it._n;
size_t p = _next_free();
fennec::construct(&_table[p].data, fennec::forward<value_t>(x));
if (n == npos) {
if (empty()) {
_root = p;
_table[p].next = npos;
_table[p].prev = npos;
} else {
_table[p].prev = n;
_table[p].next = npos;
_last = n;
}
return;
}
_table[p].next = n;
_table[p].prev = _prev(n);
_table[n].prev = p;
++_size;
}
void insert(size_t i, const value_t& x) {
assert(i <= size(), "Index out of Bounds");
if (size() == capacity()) {
_expand();
}
size_t n = _walk(min(i, size_t(size() - 1)));
size_t p = _next_free();
fennec::construct(&_table[p].data, x);
if (n == npos) {
if (empty()) {
_root = p;
_table[p].next = npos;
_table[p].prev = npos;
} else {
_table[p].prev = n;
_table[p].next = npos;
_last = n;
}
return;
}
_table[p].next = n;
_table[p].prev = _prev(n);
_table[n].prev = p;
++_size;
}
void insert(size_t i, value_t&& x) {
assert(i <= size(), "Index out of Bounds");
if (size() == capacity()) {
_expand();
}
size_t n = _walk(min(i, size_t(size() - 1)));
size_t p = _next_free();
fennec::construct(&_table[p].data, fennec::forward<value_t>(x));
if (n == npos) {
if (empty()) {
_root = p;
_table[p].next = npos;
_table[p].prev = npos;
} else {
_table[p].prev = n;
_table[p].next = npos;
_last = n;
}
return;
}
_table[p].next = n;
_table[p].prev = _prev(n);
_table[n].prev = p;
++_size;
}
template<typename...ArgsT>
void emplace(size_t i, ArgsT&&...args) {
assert(i <= size(), "Index out of Bounds");
if (size() == capacity()) {
_expand();
}
size_t n = _walk(min(i, size_t(size() - 1)));
size_t p = _next_free();
fennec::construct(&_table[p].data, fennec::forward<ArgsT>(args)...);
if (n == npos) {
if (empty()) {
_root = p;
_table[p].next = npos;
_table[p].prev = npos;
} else {
_table[p].prev = n;
_table[p].next = npos;
_last = n;
}
return;
}
_table[p].next = n;
_table[p].prev = _prev(n);
_table[n].prev = p;
++_size;
}
void push_front(const value_t& x) {
this->insert(0, x);
}
void push_front(value_t&& x) {
this->insert(0, fennec::forward<value_t>(x));
}
template<typename...ArgsT>
void emplace_front(ArgsT...args) {
this->emplace(0, fennec::forward<ArgsT>(args)...);
}
void push_back(const value_t& x) {
this->insert(_size, x);
}
void push_back(value_t&& x) {
this->insert(_size, fennec::forward<value_t>(x));
}
template<typename...ArgsT>
void emplace_back(ArgsT...args) {
this->emplace(_size, fennec::forward<ArgsT>(args)...);
}
void erase(size_t i) {
size_t j = _walk(i);
if (j == npos) return;
if (not _table[j].data) return;
// Get the prev and next indices
size_t p = _prev(j);
size_t n = _next(j);
// clear the node
_table[j].data = nullopt;
_table[j].prev = npos;
_table[j].next = npos;
// Fix prev and next nodes
if (p != npos) _table[p].next = n;
else _root = n;
if (n != npos) _table[n].prev = p;
else _last = p;
// Mark node as freed
_freed.push_back(j);
}
void pop_front() {
erase(0);
}
void pop_back() {
erase(_size - 1);
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].data;
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].data;
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].data;
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].data;
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() {
@@ -326,11 +451,11 @@ public:
}
constexpr value_t& operator*() {
return *(_list->_table[_n].data);
return *(_list->_table[_n].value);
}
constexpr value_t* operator->() {
return &*(_list->_table[_n].data);
return &*(_list->_table[_n].value);
}
constexpr bool operator==(const iterator& it) {
@@ -352,6 +477,8 @@ public:
}
};
///
/// \brief Iterator Class for Const Access
class const_iterator {
public:
~const_iterator() {
@@ -374,11 +501,11 @@ public:
}
constexpr const value_t& operator*() {
return *(_list->_table[_n].data);
return *(_list->_table[_n].value);
}
constexpr const value_t* operator->() {
return &*(_list->_table[_n].data);
return &*(_list->_table[_n].value);
}
constexpr bool operator==(const const_iterator& it) {
@@ -400,24 +527,8 @@ public:
}
};
constexpr iterator begin() {
return iterator(this, _root);
}
constexpr iterator end() {
return iterator(this, npos);
}
constexpr const_iterator begin() const {
return const_iterator(this, _root);
}
constexpr const_iterator end() const {
return const_iterator(this, npos);
}
private:
allocation<elem_t, alloc_t> _table;
allocation<node, alloc_t> _table;
dynarray<size_t> _freed;
size_t _root, _last, _size;
@@ -428,23 +539,23 @@ private:
}
struct node {
optional<value_t> data;
optional<value_t> value;
size_t prev, next;
constexpr node()
: data()
: value()
, prev(npos)
, next(npos) {
}
constexpr node(size_t p, size_t n)
: data()
: value()
, prev(p)
, next(n) {
}
constexpr node(size_t p, size_t n, value_t&& val)
: data(fennec::forward<value_t>(val))
: value(fennec::forward<value_t>(val))
, prev(p)
, next(n) {
}
@@ -452,21 +563,21 @@ private:
constexpr ~node() = default;
constexpr void clear() {
data = nullopt;
value = nullopt;
prev = npos;
next = npos;
}
};
size_t _next(size_t n) const {
constexpr size_t _next(size_t n) const {
return _table[n].next;
}
size_t _prev(size_t n) const {
constexpr size_t _prev(size_t n) const {
return _table[n].prev;
}
size_t _walk(size_t i) const {
constexpr size_t _walk(size_t i) const {
size_t n = _root;
if (n == npos) return n;
while (i > 0 && n != npos) {
@@ -475,15 +586,68 @@ private:
return n;
}
size_t _next_free() {
constexpr size_t _next_free() {
if (not _freed.empty()) {
size_t n = _freed.back();
_freed.pop_back();
return n;
}
_table[_size];
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;
}
};
}

View File

@@ -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,114 +99,191 @@ 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() {
fennec::destruct(&root);
}
} 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() {
fennec::destruct(&root);
}
} 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() {
fennec::destruct(&root);
}
} 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() {
fennec::destruct(&root);
}
} 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() {
fennec::destruct(&root);
}
} 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) {
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
}
///
/// \brief Clears the map destructing all elements
void clear() {
_set.clear();
}
/// @}
// Iteration ===========================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the start of the map
constexpr iterator begin() {
return _set.begin();
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns an iterator at the end of the map
constexpr iterator end() {
return _set.end();
}
/// @}
private:
set_t _set;
template<typename...ArgsT>
set_t::iterator _find(ArgsT&&...args) const {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
@@ -178,12 +292,19 @@ public:
fennec::destruct(&root);
}
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
_set.erase(trick.val);
return _set.find(trick.val);
}
private:
set<elem_t, key_hash, node_equals, alloc_t> _set;
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));
}
}
};
}

View File

@@ -0,0 +1,586 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file multiset.h
/// \brief A header containing the definition for a set of repeating values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_MULTISET_H
#define FENNEC_CONTAINERS_MULTISET_H
// https://programming.guide/robin-hood-hashing.html
#include <fennec/containers/optional.h>
#include <fennec/containers/multiset.h>
#include <fennec/lang/compare.h>
#include <fennec/math/ext/primes.h>
#include <fennec/memory/allocator.h>
#include <fennec/lang/hashing.h>
namespace fennec
{
///
///
/// \brief A Data Structure that defines a set of elements that may repeat
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | distinct | ⛔ |
/// | ordered | ⛔ |
/// | space | \f$O(N)\f$ |
/// | linear | ✅ |
/// | access | \f$O(1)\f$ |
/// | find | \f$O(1)\f$ |
/// | insertion | \f$O(1)\f$ |
/// | deletion | \f$O(1)\f$ |
///
/// \tparam TypeT The type to contain
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
struct multiset {
// Definitions =========================================================================================================
public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
using hash_t = Hash;
using equal_t = Equals;
using elem_t = TypeT;
class iterator;
class value_iterator;
static constexpr size_t npos = -1;
static constexpr double default_load = 0.8;
private:
struct node {
optional<elem_t> value;
int psl;
constexpr node() = default;
constexpr ~node() = default;
};
// Constructors ========================================================================================================
public:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty multiset
constexpr multiset()
: _alloc()
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
};
///
/// \brief Hash Copy Constructor, initializes empty multiset with a hash
constexpr multiset(const hash_t& hash)
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Constructor, initializes empty multiset with a hash
constexpr multiset(hash_t&& hash) noexcept
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Copy Constructor, initializes empty multiset with an allocator
constexpr multiset(const alloc_t& alloc)
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Move Constructor, initializes empty multiset with an allocator
constexpr multiset(alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Alloc Copy Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(const hash_t& hash, const alloc_t& alloc)
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Copy Alloc Move Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(const hash_t& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Move Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(hash_t&& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Alloc Copy Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(hash_t&& hash, const alloc_t& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Set Copy Constructor
/// \param multiset Set to copy
constexpr multiset(const multiset& multiset)
: _alloc( multiset._alloc)
, _hash( multiset._hash)
, _size( multiset._size)
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Set Move Constructor
/// \param multiset Set to move
constexpr multiset(multiset&& multiset) noexcept
: _alloc(fennec::move( multiset._alloc))
, _hash(fennec::move( multiset._hash))
, _size(fennec::move( multiset._size))
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Destructor, destructs all elements and releases the allocation
constexpr ~multiset() {
for (size_t i = 0; i < capacity(); ++i) {
_alloc[i].value = nullopt;
}
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns Size of the multiset in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns `true` when the set is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns Capacity of the multiset in elements
constexpr size_t capacity() const {
return _alloc.capacity();
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Find an Element
/// \param val Value to find
/// \returns An iterator at the location of the first instance of `value`
constexpr iterator find(const elem_t& val, size_t c = 0) const {
if (capacity() == 0) {
return end();
}
size_t s = _hash(val) % capacity(); // Initial search index
int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
size_t i = (s + psl) % capacity(); // Median search
size_t n = 0;
// Check the first element;
if (_alloc[i].psl >= psl && _alloc[i].value) {
if (*_alloc[i].value == val) {
return iterator(this, i);
}
}
// Loop while there is a value and its psl is greater than our probe
while (c > 0) {
++n;
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
size_t i1 = (i + n) % capacity();
int p0 = psl - n, p1 = psl + n;
bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
if (c0 && _alloc[i0].value) {
if (*_alloc[i0].value == val) {
if (c-- == 0) {
return iterator(this, i0);
}
}
}
if (c1 && _alloc[i1].value) {
if (*_alloc[i1].value == val) {
if (c-- == 0) {
return iterator(this, i1);
}
}
}
if (not(c0 or c1)) {
break;
}
}
return iterator(this, npos);
}
///
/// \brief Check if a multiset contains a value
/// \param val Value to check
/// \returns `true` if `val` can be found, `false` otherwise
constexpr bool contains(const elem_t& val) const {
return this->find(val) != end();
}
///
/// \brief Iterator Access
/// \param it Location to access
/// \returns A pointer to the element, `nullptr` if not found.
/// The value should not be changed in a manner that will change the hash of the element.
constexpr elem_t* at(const iterator& it) {
if (it == end()) {
return nullptr;
}
if (not _alloc[it._i].value) {
return nullptr;
}
return &*_alloc[it._i].value;
}
///
/// \brief Iterator Const Access
/// \param it Location to access
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
constexpr const elem_t* at(const iterator& it) const {
if (not _alloc[it._i].value) return nullptr;
return &*_alloc[it._i].value;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion
/// \param val Value to insert
constexpr void insert(elem_t&& val) {
this->_insert(fennec::forward<elem_t>(val));
}
///
/// \brief Copy Insertion
/// \param val Value to insert
constexpr void insert(const elem_t& val) {
this->_insert(val);
}
///
/// \brief Emplace Insertion
/// \tparam ArgsT Argument types
/// \param args Arguments to construct with
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Element Erase
/// \param it Location to erase
constexpr void erase(iterator it) {
size_t i = it._i;
if (i >= capacity()) {
return;
} // These are separated due to compilers being inconsistent
if (not _alloc[i].value) {
return;
}
_alloc[i].value = nullopt;
_sumpsl -= _alloc[i].psl;
--_size;
size_t p = i;
while (_alloc[i = (i + 1) % capacity()].value) {
if (_alloc[i].psl == 0) break;
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
--_alloc[p].psl, --_sumpsl;
p = i;
}
}
///
/// \brief Element Erase
/// \param val Value to erase
constexpr void erase(const elem_t& val) {
this->erase(this->find(val));
}
/// @}
// ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
///
/// \brief Class for Iterating the Set
class iterator {
public:
constexpr ~iterator() {
_set = nullptr;
}
// prefix operator
constexpr iterator& operator++() {
while (++_i < _set->capacity()) {
if (_set->_alloc[_i].value) {
return *this;
}
}
_i = npos;
return *this;
}
constexpr iterator operator++(int) {
iterator prev = *this;
this->operator++();
return prev;
}
constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value;
}
constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
private:
const multiset* _set;
size_t _i;
friend multiset;
constexpr iterator(const multiset* multiset, size_t i)
: _set(multiset)
, _i(i) {
}
};
class value_iterator {
public:
constexpr ~value_iterator() {
_set = nullptr;
}
// prefix operator
constexpr value_iterator& operator++() {
while (_psl <= _set->_alloc[_i].psl) {
if (not _set->_alloc[_i].value) {
break;
}
}
_i = npos;
return *this;
}
constexpr value_iterator operator++(int) {
value_iterator prev = *this;
this->operator++();
return prev;
}
constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value;
}
constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
private:
const multiset* _set;
size_t _i;
int _psl;
elem_t _value;
friend multiset;
constexpr value_iterator(const multiset* multiset, size_t i, int psl, const elem_t& value)
: _set(multiset)
, _i(i)
, _value(value) {
}
};
// PRIVATE =============================================================================================================
private:
constexpr void _expand() {
multiset cpy; // Create a new multiset
cpy._alloc.callocate(
fennec::next_prime2(_alloc.capacity())
);
// rehash
for (size_t i = 0; i < capacity(); ++i) {
if (_alloc[i].value) {
cpy.insert(fennec::move(*_alloc[i].value));
}
}
// Swap buffers
fennec::swap(_alloc, cpy._alloc);
}
template<typename...ArgsT>
constexpr void _insert(ArgsT&&...args) {
if (_size == 0 or static_cast<float>(_size) / capacity() >= _load) { // expand when full
_expand();
}
elem_t value(fennec::forward<ArgsT>(args)...);
size_t i = _hash(value) % capacity(); // Initial search index
int psl = 0;
while (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
return;
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
}
allocation<node, alloc_t> _alloc;
hash_t _hash;
equal_t _equal;
size_t _size;
size_t _sumpsl;
float _load;
};
}
#endif // FENNEC_CONTAINERS_SET_H

View File

@@ -0,0 +1,209 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file object_pool.h
/// \brief A header containing the definition for a pool of objects associated by ids
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
#define FENNEC_CONTAINERS_OBJECT_POOL_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h>
#include <fennec/containers/optional.h>
namespace fennec
{
///
/// \brief Struct which holds a pool of objects associated with ids
/// \tparam TypeT The value type
/// \tparam AllocT The allocator type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct object_pool {
// Definitions =========================================================================================================
public:
using value_t = TypeT;
using elem_t = optional<TypeT>;
using table_t = dynarray<elem_t, AllocT>;
// Constructors & Destructor ===========================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty object pool
constexpr object_pool()
: _size(0) {
}
///
/// \brief Default Destructor, destructs objects then releases the allocation.
constexpr ~object_pool() = default;
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns The number of active objects in the pool
constexpr size_t size() const {
return _size;
}
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const {
return _table.capacity();
}
///
/// \returns `true` when there are no objects in the pool, `false` otherwise
constexpr bool empty() const {
return size() == 0;
}
///
/// \brief Retrieve the next id `i` that would be assigned to an object `o` were it added to the object pool
///
/// \details This can be useful if there are constant members that need to be assigned at construction.
/// \returns The id of the next inserted node
constexpr size_t next_id() const {
size_t next = _size;
if (not _freed.empty()) {
next = _freed.front();
}
return next;
}
/// @}
// Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Array Access Operator
/// \param i id of the object
/// \returns a reference to the object with id `i`
constexpr value_t& operator[](size_t i) {
assert(i < capacity(), "Index out of Bounds!");
assert(_table[i], "Attempted to access Null Object.");
return *_table[i];
}
///
/// \brief Array Const Access Operator
/// \param i id of the object
/// \returns a const-qualified reference to the object with id `i`
constexpr const value_t& operator[](size_t i) const {
assert(i < capacity(), "Index out of Bounds!");
assert(_table[i], "Attempted to access Null Object.");
return *_table[i];
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Insertion, inserts `x` into the pool
/// \param x the object to move
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(value_t&& x) {
return this->_insert(fennec::forward<value_t>(x));
}
///
/// \brief Move Insertion, inserts a copy of `x` into the pool
/// \param x the object to copy
/// \returns An integer corresponding to the id of the node
constexpr size_t insert(const value_t& x) {
return this->_insert(x);
}
///
/// \brief Emplacement, constructs a new object using `args...`
/// \param args The arguments to construct the new object with
/// \returns An integer corresponding to the id of the node
template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase an object from the pool
/// \param i The id of the object
constexpr void erase(size_t i) {
_table[i] = nullopt;
_freed.push_back(i);
--_size;
}
/// @}
private:
dynarray<elem_t, AllocT> _table;
list<size_t> _freed;
size_t _size;
size_t _next_free() {
size_t next = _size;
if (not _freed.empty()) {
next = _freed.front();
_freed.pop_front();
}
++_size;
return next;
}
template<typename...ArgsT>
size_t _insert(ArgsT&&...args) {
size_t i = _next_free();
if (i >= _table.size()) {
_table.emplace_back();
}
_table[i].emplace(fennec::forward<ArgsT>(args)...);
return i;
}
};
}
#endif // FENNEC_CONTAINERS_OBJECT_POOL_H

View File

@@ -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,13 +49,20 @@ constexpr nullopt_t nullopt_v = {};
/// \tparam T
template<typename T>
struct optional {
// Definitions =========================================================================================================
public:
// Constructors ========================================================================================================
using reference_t = T&;
using pointer_t = T*;
using const_reference_t = T&;
using const_pointer_t = const T*;
// Constructors ========================================================================================================
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor
constexpr optional()
@@ -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> or is_pointer_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,6 +116,12 @@ 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;
@@ -106,12 +131,37 @@ public:
}
}
/// @}
// 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) {
@@ -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,7 +201,7 @@ 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;
@@ -169,7 +219,7 @@ 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;
@@ -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;
}
/// @}

View File

@@ -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;
///
/// \brief Copy Assignment, copies both elements
constexpr pair& operator=(const pair&) = default;
///
/// \brief Move Assignment, moves both elements
constexpr pair& operator=(pair&&) noexcept = default;
/// @}
// Comparison ==========================================================================================================
/// \name Comparison
/// @{
///
/// \brief Equality Operator
/// \param p Pair to compare with
/// \returns `true` when both elements of each pair are equal
constexpr bool operator==(const pair& p) const {
return first == p.first and second == p.second;
}
///
/// \brief Inequality Operator
/// \param p Pair to compare with
/// \returns `true` when either element of each pair are equal
constexpr bool operator!=(const pair& p) const {
return first != p.first or second != p.second;
}
///
/// \brief Less Than Operator
/// \param p Pair to compare with
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
/// equal and the second element is less
constexpr bool operator<(const pair& p) const {
return first < p.first or (first == p.first and second < p.second);
}
///
/// \brief Less Equal Operator
/// \param p Pair to compare with
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
/// equal and the second element is less or equal
constexpr bool operator<=(const pair& p) const {
return first < p.first or (first == p.first and second <= p.second);
}
///
/// \brief Greater Than Operator
/// \param p Pair to compare with
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
/// equal and the second element is greater
constexpr bool operator>(const pair& p) const {
return first > p.first or (first == p.first and second > p.second);
}
///
/// \brief Greater Equal Operator
/// \param p Pair to compare with
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
/// equal and the second element is greater or equal
constexpr bool operator>=(const pair& p) const {
return first > p.first or (first == p.first and second >= p.second);
}
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)
);
}
};

View File

@@ -16,19 +16,38 @@
// 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 {
private:
// Definitions =========================================================================================================
protected:
struct node;
public:
@@ -41,18 +60,29 @@ 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, ArgsT&&...args)
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) {
, parent(p), child(c), prev(v), next(n)
, depth(d), num_children(0) {
}
constexpr ~node() {
parent = npos;
child = npos;
prev = npos;
next = npos;
parent = npos;
child = npos;
prev = npos;
next = npos;
depth = 0;
num_children = 0;
}
};
@@ -60,69 +90,250 @@ 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)
: _data(), _freed(), _size(1) {
_data.callocate(10);
fennec::construct(&_data[0], npos, npos, npos, npos, fennec::forward<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)
: _data(tree._data), _freed(tree._freed), _size(tree._size) {
: _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
: _data(fennec::move(tree._data)), _freed(fennec::move(tree._freed)), _size(tree._size) {
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
}
/// @}
// Assignment ==========================================================================================================
friend constexpr rdtree& operator=(rdtree& lhs, const rdtree& rhs) {
for (value_t* it : lhs._data) {
/// \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);
}
lhs._data = rhs._data;
lhs._freed = rhs._freed;
lhs._size = rhs._size;
return lhs;
_table = rhs._table;
_freed = rhs._freed;
_size = rhs._size;
return *this;
}
friend constexpr rdtree& operator=(rdtree& lhs, rdtree&& rhs) noexcept {
for (value_t* it : lhs._data) {
///
/// \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);
}
lhs._data = fennec::move(rhs._data);
lhs._freed = fennec::move(rhs._freed);
lhs._size = rhs._size;
return lhs;
_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 {
return _data[i].parent;
if (i >= _table.capacity()) return npos;
return i == npos ? npos : _table[i].parent;
}
constexpr size_t child(size_t i) const {
return _data[i].child;
///
/// \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;
}
constexpr size_t next(size_t i) const {
return _data[i].next;
///
/// \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;
}
constexpr size_t prev(size_t i) const {
return _data[i].prev;
///
/// \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;
}
constexpr optional<value_t>& operator[](size_t i) {
return *_data[i].value;
///
/// \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;
}
}
}
constexpr const optional<value_t>& operator[](size_t i) const {
return *_data[i].value;
///
/// \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;
}
}
@@ -131,88 +342,283 @@ public:
///
/// \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, const value_t& val) {
if (parent == npos || _size == 0) {
if (_size == 0) {
fennec::construct(&_data[root], npos, npos, npos, npos, val);
_size = 1;
} else {
_data[root].value = val;
}
return root;
}
size_t i = _next_free();
size_t n = child(parent);
_data[parent].child = i;
fennec::construct(&_data[i], parent, npos, n, npos);
return i;
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, value_t&& val) {
if (parent == npos || _size == 0) {
if (_size == 0) {
fennec::construct(&_data[root], npos, npos, npos, npos, fennec::forward<value_t>(val));
_size = 1;
} else {
_data[root].value = fennec::forward<value_t>(val);
}
return root;
}
size_t i = _next_free();
size_t n = child(parent);
_data[parent].child = i;
fennec::construct(&_data[i], parent, npos, n, npos);
return i;
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) {
list<size_t> queue;
if (child(i) != npos) {
queue.push_back(i);
}
while (not queue.empty()) {
size_t n = queue.front(); queue.pop_front();
if (next(n) != npos) queue.push_back(next(n));
if (child(n) != npos) queue.push_back(child(n));
fennec::destruct(&_data[n]);
_freed.push_back(n);
}
fennec::destruct(&_data[i]);
_freed.push_back(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> _data;
allocation<node, alloc_t> _table;
list<size_t> _freed;
size_t _size;
void _expand() {
_data.creallocate(_data.capacity() * 2);
_table.creallocate(_table.capacity() * 2);
}
size_t _next_free() {
size_t next = _size + 1;
size_t next = _size;
if (not _freed.empty()) {
next = _freed.back();
_freed.pop_back();
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;
}
};
}

View File

@@ -16,6 +16,18 @@
// 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
@@ -31,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;
@@ -54,7 +87,14 @@ private:
constexpr ~node() = default;
};
// Constructors ========================================================================================================
public:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty set
constexpr set()
: _alloc()
, _hash()
@@ -63,6 +103,9 @@ public:
, _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)
@@ -71,14 +114,9 @@ public:
, _load(default_load) {
}
constexpr set(hash_t&& hash) noexcept
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
/// \param alloc the allocator object
constexpr set(const alloc_t& alloc)
: _alloc(alloc)
, _hash()
@@ -87,14 +125,10 @@ public:
, _load(default_load) {
}
constexpr set(alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
/// \param hash the hash object
/// \param alloc the allocator object
constexpr set(const hash_t& hash, const alloc_t& alloc)
: _alloc(alloc)
, _hash(hash)
@@ -103,30 +137,9 @@ public:
, _load(default_load) {
}
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Set Copy Constructor
/// \param set Set to copy
constexpr set(const set& set)
: _alloc(set._alloc)
, _hash(set._hash)
@@ -135,6 +148,9 @@ public:
, _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))
@@ -143,52 +159,56 @@ public:
, _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
_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;
}
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 {
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
@@ -196,7 +216,7 @@ public:
// Check the first element;
if (_alloc[i].psl >= psl && _alloc[i].value) {
if (*_alloc[i].value == val) {
if (_equal(*_alloc[i].value, val)) {
return iterator(this, i);
}
}
@@ -207,17 +227,17 @@ public:
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 = false, c1 = false;
bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
if ((c0 = (p0 > 0 && _alloc[i0].psl >= p0)) && _alloc[i0].value) {
if (*_alloc[i0].value == val) {
return iterator(this, i);
if (c0 && _alloc[i0].value) {
if (_equal(*_alloc[i0].value, val)) {
return iterator(this, i0);
}
}
if ((c1 = (_alloc[i1].psl >= p1)) && _alloc[i1].value) {
if (*_alloc[i1].value == val) {
return iterator(this, i);
if (c1 && _alloc[i1].value) {
if (_equal(*_alloc[i1].value, val)) {
return iterator(this, i1);
}
}
@@ -229,24 +249,71 @@ public:
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()) {
@@ -269,15 +336,56 @@ public:
}
}
///
/// \brief Element Erase
/// \param val Value to erase
constexpr void erase(const elem_t& val) {
this->erase(this->find(val));
}
///
/// \brief
constexpr void clear() {
for (size_t i = 0; i < _alloc.capacity(); ++i) {
}
}
/// @}
// ITERATOR ============================================================================================================
/// \name Iteration
/// @{
///
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
///
/// \brief Class for Iterating the Set
class iterator {
public:
constexpr iterator(const set* set, size_t i)
: _set(set)
, _i(i) {
}
constexpr ~iterator() {
_set = nullptr;
}
@@ -303,44 +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(const set* set, size_t i)
: _set(set)
, _i(i) {
}
};
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())
);
@@ -355,12 +453,38 @@ private:
fennec::swap(_alloc, cpy._alloc);
}
allocation<node, alloc_t> _alloc;
template<typename...ArgsT>
constexpr iterator _insert(ArgsT&&...args) {
if (_size == 0 or static_cast<float>(_size) / capacity() >= _load) { // expand when full
_expand();
}
elem_t value(fennec::forward<ArgsT>(args)...);
size_t i = _hash(value) % capacity(); // Initial search index
int psl = 0;
while (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
return iterator(this, i);
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
return iterator(this, npos);
}
dynarray<node, alloc_t> _alloc;
hash_t _hash;
equal_t _equal;
size_t _size;
size_t _sumpsl;
double _load;
float _load;
};
}

View File

@@ -0,0 +1,47 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file traversal.h
/// \brief a header containing constants and utilities related to traversal
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_TRAVERSAL_H
#define FENNEC_CONTAINERS_TRAVERSAL_H
namespace fennec
{
///
/// \brief A set of constants used in the traverser-visitor pattern
enum traversal_control_ {
traversal_control_continue = 0,
traversal_control_break = 1,
traversal_control_jump_over = 2,
};
}
#endif // FENNEC_CONTAINERS_TRAVERSAL_H

View File

@@ -16,6 +16,18 @@
// 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
@@ -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>
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>
constexpr 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>
constexpr 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

View File

@@ -16,21 +16,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_RENDERERS_OPENGL_MODERN_H
#define FENNEC_RENDERERS_OPENGL_MODERN_H
///
/// \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))
///
///
/*
*
* Requires the following:
* OpenGL 4.6 OR
* - ARB_shader_draw_parameters
* - ARB_multi_draw_indirect
* - EXT_texture_array
* - ARB_compute_shader
* - ARB_shader_image_load_store
* - ARB_texture_cube_map_array
*
* This will support >91.31% of devices on Steam, including all Desktop GPUs and iGPUs since 2012
*/
#ifndef FENNEC_CONTAINERS_VARIANT_H
#define FENNEC_CONTAINERS_VARIANT_H
#endif // FENNEC_RENDERERS_OPENGL_MODERN_H
#endif // FENNEC_CONTAINERS_VARIANT_H

View File

@@ -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,11 +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. |
/// | \subpage fennec_memory | Implementation of functions related to memory management. |
/// | 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

View File

@@ -84,7 +84,7 @@ void _assert_impl(const char* expression, const char* file, int line, const char
}
#if FENNEC_RELEASE
#define assertd(expression, description) (0)
#define assertd(expression, description)
#else
#define assertd(expression, description) assert(expression, description)
#endif

View File

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

View File

@@ -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)...>{};

View File

@@ -26,6 +26,8 @@
#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>

View File

@@ -32,8 +32,8 @@ namespace fennec::detail
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...>
n == i, HeadT,
typename _nth_element<n, i + 1, RestT...>::type
> {};
}

View File

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

View File

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

View File

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

View File

@@ -42,6 +42,22 @@ FENNEC_NO_INLINE uint64_t typeuuid() {
return id = detail::_typeuuid<RootT>();
}
template<typename RootT = void>
struct typed {
public:
const uint64_t type;
template<typename TypeT>
bool is_type() const {
return type == typeuuid<TypeT, RootT>();
}
template<typename TypeT>
typed(TypeT*)
: type(typeuuid<TypeT, RootT>()) {
}
};
}
#endif // FENNEC_LANG_TYPEUUID_H

View File

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

View File

@@ -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,6 +155,7 @@ public:
}
const string& str() const { return _str; }
const char* cstr() const { return _str.cstr(); }
bool empty() {
size_t size = _str.size();
@@ -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
}
@@ -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] == '.') {
// ".."

View File

@@ -171,6 +171,10 @@ 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;
}
@@ -198,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;
}
@@ -330,8 +341,8 @@ 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()));
}
};

View File

@@ -59,81 +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
/// \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 Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \tparam n the number of characters in the buffer including the null-terminator, if present
template<size_t n>
explicit constexpr _string(const char (&str)[n])
: _str(str[n - 1] != '\0' ? n + 1 : n) {
fennec::memcpy(_str, str, n);
if (str[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief Buffer 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];
}
@@ -141,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 =========================================================================================================
@@ -179,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);
}
///
@@ -193,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 {
@@ -209,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
}
///
@@ -299,63 +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) {
if (str.empty()) {
_str.deallocate();
return *this;
}
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
@@ -365,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;
};
@@ -426,7 +427,7 @@ 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()));
}
};

View File

@@ -66,9 +66,7 @@ public:
///
/// \brief Default Constructor, initializes with nullptr
constexpr wcstring()
: _str(nullptr)
, _size(0)
, _const(true) {
: _str(nullptr), _size(0), _const(true) {
}
///
@@ -93,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) {
@@ -116,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) {
@@ -148,7 +146,7 @@ public:
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;
}
@@ -175,6 +173,10 @@ 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;
}
@@ -196,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;
}
@@ -220,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 {
@@ -237,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
@@ -278,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;
}
@@ -300,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) {
@@ -321,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_LANGPROC_STRINGS_wcstring_H

View File

@@ -16,8 +16,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_LANGPROC_wstringS_wstring_H
#define FENNEC_LANGPROC_wstringS_wstring_H
#ifndef FENNEC_LANGPROC_wstringS_WSTRING_H
#define FENNEC_LANGPROC_wstringS_WSTRING_H
#include <fennec/langproc/strings/detail/_ctype.h>
#include <fennec/langproc/strings/wcstring.h>
@@ -59,110 +59,126 @@ public:
}
///
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null characters
/// \param n the number of characters
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with `'c'...`
/// \param n the number of wchar_tacters
/// \param c the wchar_tacter to fill with
///
/// \details adds additional character 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 character `c`
/// \param c the characters to fill with
/// \param n the number of characters
///
/// \details adds additional character for null termination.
constexpr _wstring(wchar_t c, size_t n)
/// \details adds additional wchar_tacter for null termination.
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 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.
constexpr _wstring(const wchar_t* str, size_t n)
: _str(str, n + 1) {
::wcsncpy(_str.data(), str, n);
_str[n] = '\0';
/// \brief Buffer Constructor, wraps the provided C-Style string, appending a null-terminator if not present
/// \param str the buffer to wrap
/// \tparam n the number of wchar_tacters in the buffer including the null-terminator, if present
template<size_t n>
explicit constexpr _wstring(const wchar_t (&str)[n])
: _str(str[n - 1] != '\0' ? n + 1 : n) {
fennec::wmemcpy(_str, str, n);
if (str[n - 1] != '\0') {
_str[n] = '\0';
}
}
///
/// \brief Buffer Copy Constructor
/// \param str the buffer to copy
/// \param len 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.
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 character
constexpr wchar_t& operator[](int i) {
/// \returns a reference to the wchar_tacter
constexpr wchar_t& operator[](size_t i) {
return _str[i];
}
///
/// \brief Const-Array Access Operator
/// \param i the index to access
/// \returns a copy of the character
constexpr wchar_t operator[](int i) const {
assertd(i >= 0 && i < size(), "Array Out of Bounds");
/// \returns a copy of the wchar_tacter
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 =========================================================================================================
@@ -183,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);
}
///
@@ -191,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 {
@@ -206,22 +222,22 @@ public:
///
/// \brief Finds the index of the first occurrence of `c` in the string
/// \param c the character to find
/// \param c the wchar_tacter to find
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
constexpr size_t find(wchar_t c, size_t i = 0) const {
if (i >= size()) { // bounds check
return size();
}
const wchar_t* loc = ::wcschr(_str.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();
}
@@ -287,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();
}
@@ -303,116 +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.empty()) {
_str.deallocate();
return *this;
}
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 characters
/// \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 character 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_LANGPROC_wstringS_wstring_H
#endif // FENNEC_LANGPROC_wstringS_WSTRING_H

View File

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

View File

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

View File

@@ -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> {};
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,9 +65,10 @@
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;
}

View File

@@ -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);
}
};

View File

@@ -334,7 +334,7 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
/// \param args arguments
template<typename... ArgsT> requires(total_component_count_v<ArgsT...> == N)
explicit constexpr vector(ArgsT&&... args) {
vector::__construct<0>(args...);
vector::_construct<0>(args...);
}
/// @}
@@ -1078,30 +1078,30 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
private:
template<size_t IndexV = 0, typename HeadT, typename... TailT>
constexpr void __construct(HeadT&& head, TailT&&... rest) {
vector::__insert<IndexV>(fennec::forward<HeadT>(head));
constexpr void _construct(HeadT&& head, TailT&&... rest) {
vector::_insert<IndexV>(fennec::forward<HeadT>(head));
if constexpr (sizeof...(TailT) > 0)
vector::__construct<IndexV + component_count_v<HeadT>>(fennec::forward<TailT>(rest)...);
vector::_construct<IndexV + component_count_v<HeadT>>(fennec::forward<TailT>(rest)...);
}
template<size_t OffsetV>
constexpr void __insert(ScalarT& x) {
constexpr void _insert(ScalarT& x) {
data[OffsetV] = x;
}
template<size_t OffsetV, typename OScalarT>
constexpr void __insert(OScalarT& x) {
constexpr void _insert(OScalarT& x) {
data[OffsetV] = ScalarT(x);
}
template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV>
constexpr void __insert(vector<OScalarT, OIndicesV...>& vec) {
constexpr void _insert(vector<OScalarT, OIndicesV...>& vec) {
((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...);
}
template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV>
constexpr void __insert(swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) {
constexpr void _insert(swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) {
size_t i = 0;
((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...);
}

View File

@@ -71,7 +71,7 @@ namespace fennec
///
/// \brief check if \p T is a fennec::vector type
/// \tparam T type to check
template<typename T> struct is_vector : detail::__is_vector_helper<remove_cvr_t<T>>{};
template<typename T> struct is_vector : detail::_is_vector_helper<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_vector<T>::value```
@@ -81,7 +81,7 @@ template<typename T> constexpr bool is_vector_v = is_vector<T>::value;
///
/// \brief Get the number of Components in \p T, returns 1 for types that pass ```is_arithmetic<T>```, returns \ref vector::N for \ref vector "Vector" Types, and returns 0 for all other cases
/// \tparam T type to check
template<typename T> struct component_count : detail::__component_count_helper<remove_cvr_t<T>>{};
template<typename T> struct component_count : detail::_component_count_helper<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```component_count<T>::value```

View File

@@ -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
@@ -32,7 +32,7 @@
#ifndef FENNEC_MEMORY_ALLOCATOR_H
#define FENNEC_MEMORY_ALLOCATOR_H
#include <fennec/memory/ptr_traits.h>
#include <fennec/memory/pointer_traits.h>
#include <fennec/memory/new.h>
#include <fennec/lang/conditional_types.h>
@@ -61,40 +61,40 @@ struct allocator_traits
{
private:
// These help with using concepts in `detect_t`
template<typename ClassT> using __pointer = typename ClassT::pointer_t;
template<typename ClassT> using __const_pointer = typename ClassT::const_pointer_t;
template<typename ClassT> using __void_pointer = typename ClassT::void_pointer_t;
template<typename ClassT> using __void_const_pointer = typename ClassT::void_const_pointer_t;
template<typename ClassT> using _pointer = typename ClassT::pointer_t;
template<typename ClassT> using _const_pointer = typename ClassT::const_pointer_t;
template<typename ClassT> using _void_pointer = typename ClassT::void_pointer_t;
template<typename ClassT> using _void_const_pointer = typename ClassT::void_const_pointer_t;
// Propagation Patterns
template<typename ClassT> using __propagate_on_containter_copy_assignment = typename ClassT::propagate_on_containter_copy_assignment;
template<typename ClassT> using __propagate_on_containter_move_assignment = typename ClassT::propagate_on_containter_move_assignment;
template<typename ClassT> using __propagate_on_containter_swap = typename ClassT::propagate_on_containter_swap;
template<typename ClassT> using _propagate_on_containter_copy_assignment = typename ClassT::propagate_on_containter_copy_assignment;
template<typename ClassT> using _propagate_on_containter_move_assignment = typename ClassT::propagate_on_containter_move_assignment;
template<typename ClassT> using _propagate_on_containter_swap = typename ClassT::propagate_on_containter_swap;
template<typename ClassT> using __is_always_equal = typename ClassT::is_always_equal;
template<typename ClassT> using _is_always_equal = typename ClassT::is_always_equal;
template<typename AllocT, typename TypeT>
struct __rebind : replace_first_element<AllocT, TypeT> {};
struct _rebind : replace_first_element<AllocT, TypeT> {};
template<typename AllocT, typename TypeT>
requires requires { typename AllocT::template rebind<TypeT>::other; }
struct __rebind<AllocT, TypeT> { using type = typename AllocT::template rebind<TypeT>::other; };
struct _rebind<AllocT, TypeT> { using type = typename AllocT::template rebind<TypeT>::other; };
// This detects AllocT::diff_t if present, otherwise uses the diff_t associated with PtrT
// It works using SFINAE, 'typename = void' forces the second __diff to be evaluated first when
// __diff is substituted using only two template arguments. That one uses 'type = typename AllocT::diff_t'
// however, if it fails, the compiler moves on to the original definition. __size works in the same manner.
// It works using SFINAE, 'typename = void' forces the second _diff to be evaluated first when
// _diff is substituted using only two template arguments. That one uses 'type = typename AllocT::diff_t'
// however, if it fails, the compiler moves on to the original definition. _size works in the same manner.
template<typename AllocT, typename PtrT, typename = void>
struct __diff { using type = typename ptr_traits<PtrT>::diff_t; };
struct _diff { using type = typename pointer_traits<PtrT>::diff_t; };
template<typename AllocT, typename PtrT>
struct __diff<AllocT, PtrT, void_t<typename AllocT::diff_t>> { using type = typename AllocT::diff_t; };
struct _diff<AllocT, PtrT, void_t<typename AllocT::diff_t>> { using type = typename AllocT::diff_t; };
template<typename AllocT, typename DiffT, typename = void>
struct __size : make_unsigned<DiffT> {};
struct _size : make_unsigned<DiffT> {};
template<typename AllocT, typename DiffT>
struct __size<AllocT, DiffT, void_t<typename AllocT::size_t>> { using type = typename AllocT::size_t; };
struct _size<AllocT, DiffT, void_t<typename AllocT::size_t>> { using type = typename AllocT::size_t; };
public:
@@ -105,33 +105,33 @@ public:
using value_t = typename Alloc::value_t;
/// \brief Alias for a pointer to the value type. Will use `Alloc::pointer_t` if present
using pointer_t = detect_t<value_t*, __pointer, Alloc>;
using pointer_t = detect_t<value_t*, _pointer, Alloc>;
/// \brief Alias for a const pointer to the value type. Will use `Alloc::const_pointer_t` if present
using const_pointer_t = detect_t<const value_t*, __const_pointer, Alloc>;
using const_pointer_t = detect_t<const value_t*, _const_pointer, Alloc>;
/// \brief Alias for a pointer to void. Will use `Alloc::void_pointer_t` if present
using void_pointer_t = detect_t<void*, __void_pointer, Alloc>;
using void_pointer_t = detect_t<void*, _void_pointer, Alloc>;
/// \brief Alias for a const pointer to void. Will use `Alloc::const_void_pointer_t` if present
using const_void_pointer_t = detect_t<const void*, __void_const_pointer, Alloc>;
using const_void_pointer_t = detect_t<const void*, _void_const_pointer, Alloc>;
/// \brief Alias for differences between pointers. Will use `Alloc::diff_t` if present
using diff_t = typename __diff<Alloc, pointer_t>::type;
using diff_t = typename _diff<Alloc, pointer_t>::type;
/// \brief Alias for the size of allocations. Will use `Alloc::size_t` if present
using size_t = typename __size<Alloc, pointer_t>::type;
using size_t = typename _size<Alloc, pointer_t>::type;
// TODO: Document propagation
using propagate_on_container_copy_assignment = detect_t<false_type, __propagate_on_containter_copy_assignment, Alloc>;
using propagate_on_container_move_assignment = detect_t<false_type, __propagate_on_containter_move_assignment, Alloc>;
using propagate_on_container_swap = detect_t<false_type, __propagate_on_containter_swap, Alloc>;
using propagate_on_container_copy_assignment = detect_t<false_type, _propagate_on_containter_copy_assignment, Alloc>;
using propagate_on_container_move_assignment = detect_t<false_type, _propagate_on_containter_move_assignment, Alloc>;
using propagate_on_container_swap = detect_t<false_type, _propagate_on_containter_swap, Alloc>;
/// \brief Checks if this allocator type is always equal to another allocator of similar type
using is_always_equal = detect_t<false_type, __is_always_equal, Alloc>;
using is_always_equal = detect_t<false_type, _is_always_equal, Alloc>;
/// \brief Rebinds the allocator type to produce an element type of type `TypeT`
template<typename TypeT> using rebind = typename __rebind<Alloc, TypeT>::type;
template<typename TypeT> using rebind = typename _rebind<Alloc, TypeT>::type;
// TODO: allocator_traits static functions
};
@@ -310,7 +310,7 @@ public:
/// \param n The number of elements of type `T` to allocate for
explicit constexpr allocation(size_t n) noexcept
: _data(nullptr), _capacity(0), _alignment(zero<align_t>()) {
allocate(n);
callocate(n);
}
///
@@ -320,7 +320,7 @@ public:
/// \param n the number of elements
constexpr allocation(const T* data, size_t n)
: allocation(n) {
fennec::memcpy(_data, data, n);
fennec::memmove(_data, data, n);
}
///
@@ -331,7 +331,7 @@ public:
: _data(nullptr)
, _capacity(0)
, _alignment(align) {
allocate(n, align);
callocate(n, align);
}
///
@@ -342,7 +342,7 @@ public:
/// \param align The alignment of the allocation
constexpr allocation(const T* data, size_t n, align_t align)
: allocation(n, align) {
fennec::memcpy(_data, data, n);
fennec::memmove(static_cast<void*>(_data), data, n);
}
///
@@ -367,7 +367,7 @@ public:
: _alloc(alloc)
, _data(nullptr)
, _capacity(0) {
allocate(n);
callocate(n);
}
///
@@ -380,7 +380,7 @@ public:
/// \details This constructor should be used when the type `AllocT` needs internal data.
constexpr allocation(const T* data, size_t n, const alloc_t& alloc)
: allocation(n, alloc) {
fennec::memcpy(_data, data, n);
fennec::memmove(static_cast<void*>(_data), data, n);
}
///
@@ -395,7 +395,7 @@ public:
, _data(nullptr)
, _capacity(0)
, _alignment(zero<align_t>()) {
allocate(n, align);
callocate(n, align);
}
///
@@ -409,7 +409,7 @@ public:
/// \details This constructor should be used when the type `AllocT` needs internal data.
constexpr allocation(const T* data, size_t n, align_t align, const alloc_t& alloc)
: allocation(n, align, alloc) {
fennec::memcpy(_data, data, n);
fennec::memmove(_data, data, n);
}
///
@@ -420,7 +420,7 @@ public:
, _data(_alloc.allocate(alloc._capacity))
, _capacity(alloc._capacity)
, _alignment(alloc._alignment) {
fennec::memcpy(_data, alloc._data, alloc._capacity * sizeof(T));
fennec::memmove(static_cast<void*>(_data), alloc._data, alloc._capacity * sizeof(T));
}
///
@@ -443,7 +443,7 @@ public:
}
// Assignment ==========================================================================================================
// Assignment ==========================================================================================================
///
/// \brief Copy Assignment Operator
@@ -451,7 +451,7 @@ public:
/// \returns a reference to `this`
constexpr allocation& operator=(const allocation& alloc) {
allocation::allocate(alloc.capacity());
fennec::memcpy(_data, alloc, size());
fennec::memmove(_data, alloc, size());
return *this;
}
@@ -491,13 +491,7 @@ public:
/// If there is already an allocated block of memory, the previous allocation is released.
/// \param n The number of elements of type `T` to allocate for
constexpr void callocate(size_t n, align_t align = zero<align_t>()) noexcept {
deallocate();
if (align != zero<align_t>()) {
_data = _alloc.allocate(_capacity = n, _alignment = align);
} else {
_data = _alloc.allocate(_capacity = n);
}
allocate(n, align);
fennec::memset(static_cast<void*>(_data), 0, _capacity * sizeof(T));
}
@@ -523,11 +517,15 @@ public:
constexpr void reallocate(size_t n, align_t align = zero<align_t>()) noexcept {
if (_data == nullptr) {
allocate(n, align);
return;
}
value_t* old = _data;
_data = _alloc.allocate(n);
fennec::memcpy(static_cast<void*>(_data), old, min(_capacity, n) * sizeof(T));
value_t* old = _data; size_t old_cap = _capacity;
_data = nullptr;
allocate(n, align);
fennec::memmove(static_cast<void*>(_data), old, min(_capacity, old_cap) * sizeof(T));
_alloc.deallocate(old);
_capacity = n;
}
@@ -538,14 +536,19 @@ public:
constexpr void creallocate(size_t n, align_t align = zero<align_t>()) noexcept {
if (_data == nullptr) {
callocate(n, align);
return;
}
value_t* old = _data;
_data = _alloc.allocate(n);
fennec::memcpy(static_cast<void*>(_data), old, min(_capacity, n) * sizeof(T));
if (n > _capacity) {
fennec::memset(static_cast<void*>(_data + _capacity), 0, n - _capacity);
value_t* old = _data; size_t old_cap = _capacity;
_data = nullptr;
allocate(n, align);
fennec::memmove(static_cast<void*>(_data), old, min(_capacity, old_cap) * sizeof(T));
if (_capacity > old_cap) {
fennec::memset(static_cast<void*>(_data + old_cap), 0, _capacity - old_cap);
}
_alloc.deallocate(old);
_capacity = n;
}
@@ -558,14 +561,17 @@ public:
return _data[i];
}
constexpr value_t operator[](size_t i) const requires is_fundamental_v<value_t> {
constexpr const value_t& operator[](size_t i) const {
assertd(i < capacity(), "Array Out of Bounds");
return _data[i];
}
constexpr const value_t& operator[](size_t i) const {
assertd(i < capacity(), "Array Out of Bounds");
return _data[i];
constexpr operator value_t*() {
return _data;
}
constexpr operator const value_t*() const {
return _data;
}
value_t* begin() {

View File

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

View File

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

View File

@@ -24,14 +24,20 @@
// implementation
#if FENNEC_COMPILER_GCC
#define __OPTIMIZE__
#ifndef __OPTIMIZE__
# define __OPTIMIZE__
#else
# define FENNEC_OPTIMIZE_FOUND
#endif
#endif
#include <string.h>
#include <wchar.h>
#if FENNEC_COMPILER_GCC
#ifndef FENNEC_OPTIMIZE_FOUND
#undef __OPTIMIZE__
#endif
#endif
#endif // FENNEC_MEMORY_DETAIL_MEMORY_H

View File

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

View File

@@ -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
@@ -29,12 +29,12 @@ namespace fennec
/// \brief Class for retrieving the traits of Pointer-like types
/// \tparam ClassT The Pointer class type
template<typename ClassT>
struct ptr_traits
struct pointer_traits
: detail::_ptr_traits_impl<ClassT, detail::_ptr_get_element<ClassT>> {};
// overload for C-Style Pointers
template<typename ElemT>
struct ptr_traits<ElemT*> : detail::_ptr_traits_ptr_to<ElemT*, ElemT>
struct pointer_traits<ElemT*> : detail::_ptr_traits_ptr_to<ElemT*, ElemT>
{
using pointer_t = ElemT*;
using element_t = ElemT;

View File

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

View File

@@ -27,7 +27,7 @@
namespace fennec
{
class display
class display : public typed<display>
{
public:
struct pixel_format {
@@ -42,7 +42,7 @@ public:
virtual bool connected() const = 0;
virtual ~display();
virtual window* create_window() = 0;
virtual window* create_window(window* parent) = 0;
const pixel_format& get_color_format() const {
return _config.format;
@@ -55,7 +55,6 @@ public:
gfxcontext* get_context() { return _context; }
const string name;
const uint64_t uuid;
protected:
platform* _platform;
@@ -63,9 +62,9 @@ protected:
config _config;
template<typename DisplayT>
explicit display(platform* platform, const cstring& name, DisplayT*)
: name(name)
, uuid(typeuuid<DisplayT>())
explicit display(platform* platform, const cstring& name, DisplayT* type)
: typed(type)
, name(name)
, _platform(platform)
, _context(nullptr)
, _config {

View File

@@ -22,33 +22,39 @@
#include <fennec/langproc/strings/string.h>
#include <fennec/lang/typeuuid.h>
#include <fennec/platform/interface/fwd.h>
#include <fennec/platform/interface/window.h>
namespace fennec
{
class gfxcontext {
class gfxcontext : public typed<gfxcontext> {
public:
const string name;
const uint64_t uuid;
virtual bool connected() = 0;
virtual const cstring& get_name() = 0;
virtual int32_t get_version_major() = 0;
virtual int32_t get_version_minor() = 0;
virtual int32_t get_context_version() = 0;
virtual const cstring& get_context_name() = 0;
virtual int32_t get_version() = 0;
virtual bool check_extension(const cstring& ext) = 0;
virtual void make_current(gfxsurface* surface) = 0;
virtual gfxsurface* create_surface(window* window) = 0;
virtual ~gfxcontext() = default;
display* get_display() {
return _display;
}
protected:
display* _display;
template<typename ContextT>
gfxcontext(display* display, const cstring& name, ContextT*)
: name(name)
, uuid(typeuuid<ContextT>())
gfxcontext(display* display, const cstring& name, ContextT* type)
: typed(type)
, name(name)
, _display(display) {
}
};

View File

@@ -0,0 +1,76 @@
// =====================================================================================================================
// 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 gfxsurface.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_PLATFORM_INTERFACE_GFXSURFACE_H
#define FENNEC_PLATFORM_INTERFACE_GFXSURFACE_H
#include <fennec/lang/types.h>
#include <fennec/lang/typeuuid.h>
#include <fennec/platform/interface/fwd.h>
#include <fennec/platform/interface/window.h>
namespace fennec
{
class gfxsurface : public typed<gfxsurface> {
public:
virtual ~gfxsurface() = default;
virtual void resize(size_t width, size_t height) {
_width = width;
_height = height;
}
gfxcontext* get_context() {
return _context;
}
window* get_window() {
return _window;
}
protected:
gfxcontext* _context;
window* _window;
size_t _width, _height;
template<typename SurfaceT>
gfxsurface(gfxcontext* ctx, window* window, SurfaceT* type)
: typed(type)
, _context(ctx)
, _window(window)
, _width(window->get_width()), _height(window->get_height()) {
}
};
}
#endif // FENNEC_PLATFORM_INTERFACE_GFXSURFACE_H

View File

@@ -59,13 +59,12 @@
namespace fennec
{
class platform {
class platform : public typed<platform> {
public:
using shared_object = struct shared_object;
using symbol = void*;
const string name;
const uint64_t uuid;
virtual ~platform() = default;
@@ -81,9 +80,9 @@ public:
protected:
template<typename PlatformT>
explicit platform(const cstring& name, PlatformT*)
: name(name)
, uuid(typeuuid<PlatformT>()) {
explicit platform(const cstring& name, PlatformT* type)
: typed(type)
, name(name) {
auto& globals = _get_globals();
assertf(globals.singleton == nullptr, "Conflicting Platform Definitions.");
globals.singleton = this;

View File

@@ -22,6 +22,7 @@
#include <fennec/containers/optional.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/platform/interface/fwd.h>
#include <fennec/platform/linux/wayland/display.h>
namespace fennec
{
@@ -29,7 +30,7 @@ namespace fennec
///
/// \brief interface for handling windows
/// \details the interface makes no guarantees about the bit-depth and is completely dependent on the implementation.
class window {
class window : public typed<window> {
public:
enum class fullscreen_mode {
windowed = 0,
@@ -53,6 +54,8 @@ public:
fullscreen_mode fullscreen;
};
virtual ~window() = default;
virtual bool running() = 0;
virtual void configure(const config& config) = 0;
virtual bool initialize(bool modal) = 0;
@@ -63,7 +66,7 @@ public:
virtual bool set_width(size_t w) = 0;
virtual bool set_height(size_t h) = 0;
virtual bool set_size(size_t w, size_t h) = 0;
virtual bool resize(size_t w, size_t h) = 0;
virtual bool set_fullscreen_mode(fullscreen_mode mode) = 0;
@@ -73,6 +76,8 @@ public:
virtual bool grab_mouse(bool e) = 0;
virtual bool block_screensaver(bool e) = 0;
virtual struct wl_surface* get_native_handle() = 0;
bool is_child() const {
if (not _config) return false;
return _config->flags & flags_child;
@@ -91,9 +96,13 @@ public:
return _display;
}
const config& get_config() const {
return *_config;
}
const string& get_title() const {
static const string _null = { "null" };
static const string _null{"null"};
if (not _config) return _null;
return _config->title;
}
@@ -137,13 +146,20 @@ public:
}
protected:
virtual ~window() = default;
window(display* display, window* parent);
template<typename TypeT>
window(display* display, window* parent, TypeT* type)
: typed(type)
, _display(display)
, _parent(parent)
, _surface(nullptr) {
}
display* _display;
window* _parent;
optional<config> _config;
gfxsurface* _context;
gfxsurface* _surface;
private:
private:
};

View File

@@ -20,7 +20,7 @@
#define FENNEC_PLATFORM_LINUX_WAYLAND_DISPLAY_H
#include <fennec/platform/interface/display.h>
#include <fennec/platform/linux/wayland/lib/fwd.h>
#include <fennec/platform/linux/wayland/lib/wayland.h>
namespace fennec
{
@@ -33,13 +33,28 @@ public:
bool connected() const override;
void* get_native_handle() override { return _handle; }
window* create_window() override;
window* create_window(window* parent) override;
wl_registry* get_registry() { return _registry; }
const wl_registry* get_registry() const { return _registry; }
wl_compositor* get_compositor() { return _compositor; }
const wl_compositor* get_compositor() const { return _compositor; }
// xdg_wm_base* get_shell() { return _shell; }
//const xdg_wm_base* get_shell() const { return _shell; }
wl_seat* get_seat() { return _seat; }
const wl_seat* get_seat() const { return _seat; }
wl_shm* get_shm() { return _shm; }
const wl_shm* get_shm() const { return _shm; }
private:
wl_display* _handle;
wl_registry* _registry;
wl_compositor* _compositor;
wl_shell* _shell;
//xdg_wm_base* _shell;
wl_seat* _seat;
wl_shm* _shm;
bool _fifo;

View File

@@ -1,86 +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_PLATFORM_LINUX_WAYLAND_LIB_FWD_H
#define FENNEC_PLATFORM_LINUX_WAYLAND_LIB_FWD_H
/*
* Copyright © 2008 Kristian Høgsberg
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <fennec/lang/types.h>
#include <stdarg.h>
struct wl_object;
struct wl_proxy;
struct wl_display;
struct wl_event_queue;
struct wl_buffer;
struct wl_callback;
struct wl_compositor;
struct wl_data_device;
struct wl_data_device_manager;
struct wl_data_offer;
struct wl_data_source;
struct wl_keyboard;
struct wl_output;
struct wl_pointer;
struct wl_region;
struct wl_registry;
struct wl_seat;
struct wl_shell;
struct wl_shell_surface;
struct wl_shm;
struct wl_shm_pool;
struct wl_subcompositor;
struct wl_subsurface;
struct wl_surface;
struct wl_touch;
struct wl_egl_window;
struct wl_surface;
typedef int32_t wl_fixed_t;
#define WL_PRINTF(x, y) __attribute__((__format__(__printf__, x, y)))
typedef void (*wl_log_func_t)(const char *fmt, va_list args) WL_PRINTF(1, 0);
#define WL_MARSHAL_FLAG_DESTROY (1 << 0)
#endif // FENNEC_PLATFORM_LINUX_WAYLAND_LIB_FWD_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,526 @@
/* Generated by wayland-scanner 1.23.1 */
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2012-2013 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_buffer_interface;
extern const struct wl_interface wl_callback_interface;
extern const struct wl_interface wl_data_device_interface;
extern const struct wl_interface wl_data_offer_interface;
extern const struct wl_interface wl_data_source_interface;
extern const struct wl_interface wl_keyboard_interface;
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_pointer_interface;
extern const struct wl_interface wl_region_interface;
extern const struct wl_interface wl_registry_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_shell_surface_interface;
extern const struct wl_interface wl_shm_pool_interface;
extern const struct wl_interface wl_subsurface_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface wl_touch_interface;
static const struct wl_interface *wayland_types[] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_callback_interface,
&wl_registry_interface,
&wl_surface_interface,
&wl_region_interface,
&wl_buffer_interface,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_shm_pool_interface,
NULL,
NULL,
&wl_data_source_interface,
&wl_surface_interface,
&wl_surface_interface,
NULL,
&wl_data_source_interface,
NULL,
&wl_data_offer_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_data_offer_interface,
&wl_data_offer_interface,
&wl_data_source_interface,
&wl_data_device_interface,
&wl_seat_interface,
&wl_shell_surface_interface,
&wl_surface_interface,
&wl_seat_interface,
NULL,
&wl_seat_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_output_interface,
&wl_seat_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_output_interface,
&wl_buffer_interface,
NULL,
NULL,
&wl_callback_interface,
&wl_region_interface,
&wl_region_interface,
&wl_output_interface,
&wl_output_interface,
&wl_pointer_interface,
&wl_keyboard_interface,
&wl_touch_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_surface_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_subsurface_interface,
&wl_surface_interface,
&wl_surface_interface,
&wl_surface_interface,
&wl_surface_interface,
};
static const struct wl_message wl_display_requests[] = {
{ "sync", "n", wayland_types + 8 },
{ "get_registry", "n", wayland_types + 9 },
};
static const struct wl_message wl_display_events[] = {
{ "error", "ous", wayland_types + 0 },
{ "delete_id", "u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_display_interface = {
"wl_display", 1,
2, wl_display_requests,
2, wl_display_events,
};
static const struct wl_message wl_registry_requests[] = {
{ "bind", "usun", wayland_types + 0 },
};
static const struct wl_message wl_registry_events[] = {
{ "global", "usu", wayland_types + 0 },
{ "global_remove", "u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_registry_interface = {
"wl_registry", 1,
1, wl_registry_requests,
2, wl_registry_events,
};
static const struct wl_message wl_callback_events[] = {
{ "done", "u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_callback_interface = {
"wl_callback", 1,
0, NULL,
1, wl_callback_events,
};
static const struct wl_message wl_compositor_requests[] = {
{ "create_surface", "n", wayland_types + 10 },
{ "create_region", "n", wayland_types + 11 },
};
WL_PRIVATE const struct wl_interface wl_compositor_interface = {
"wl_compositor", 6,
2, wl_compositor_requests,
0, NULL,
};
static const struct wl_message wl_shm_pool_requests[] = {
{ "create_buffer", "niiiiu", wayland_types + 12 },
{ "destroy", "", wayland_types + 0 },
{ "resize", "i", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_shm_pool_interface = {
"wl_shm_pool", 2,
3, wl_shm_pool_requests,
0, NULL,
};
static const struct wl_message wl_shm_requests[] = {
{ "create_pool", "nhi", wayland_types + 18 },
{ "release", "2", wayland_types + 0 },
};
static const struct wl_message wl_shm_events[] = {
{ "format", "u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_shm_interface = {
"wl_shm", 2,
2, wl_shm_requests,
1, wl_shm_events,
};
static const struct wl_message wl_buffer_requests[] = {
{ "destroy", "", wayland_types + 0 },
};
static const struct wl_message wl_buffer_events[] = {
{ "release", "", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_buffer_interface = {
"wl_buffer", 1,
1, wl_buffer_requests,
1, wl_buffer_events,
};
static const struct wl_message wl_data_offer_requests[] = {
{ "accept", "u?s", wayland_types + 0 },
{ "receive", "sh", wayland_types + 0 },
{ "destroy", "", wayland_types + 0 },
{ "finish", "3", wayland_types + 0 },
{ "set_actions", "3uu", wayland_types + 0 },
};
static const struct wl_message wl_data_offer_events[] = {
{ "offer", "s", wayland_types + 0 },
{ "source_actions", "3u", wayland_types + 0 },
{ "action", "3u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_data_offer_interface = {
"wl_data_offer", 3,
5, wl_data_offer_requests,
3, wl_data_offer_events,
};
static const struct wl_message wl_data_source_requests[] = {
{ "offer", "s", wayland_types + 0 },
{ "destroy", "", wayland_types + 0 },
{ "set_actions", "3u", wayland_types + 0 },
};
static const struct wl_message wl_data_source_events[] = {
{ "target", "?s", wayland_types + 0 },
{ "send", "sh", wayland_types + 0 },
{ "cancelled", "", wayland_types + 0 },
{ "dnd_drop_performed", "3", wayland_types + 0 },
{ "dnd_finished", "3", wayland_types + 0 },
{ "action", "3u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_data_source_interface = {
"wl_data_source", 3,
3, wl_data_source_requests,
6, wl_data_source_events,
};
static const struct wl_message wl_data_device_requests[] = {
{ "start_drag", "?oo?ou", wayland_types + 21 },
{ "set_selection", "?ou", wayland_types + 25 },
{ "release", "2", wayland_types + 0 },
};
static const struct wl_message wl_data_device_events[] = {
{ "data_offer", "n", wayland_types + 27 },
{ "enter", "uoff?o", wayland_types + 28 },
{ "leave", "", wayland_types + 0 },
{ "motion", "uff", wayland_types + 0 },
{ "drop", "", wayland_types + 0 },
{ "selection", "?o", wayland_types + 33 },
};
WL_PRIVATE const struct wl_interface wl_data_device_interface = {
"wl_data_device", 3,
3, wl_data_device_requests,
6, wl_data_device_events,
};
static const struct wl_message wl_data_device_manager_requests[] = {
{ "create_data_source", "n", wayland_types + 34 },
{ "get_data_device", "no", wayland_types + 35 },
};
WL_PRIVATE const struct wl_interface wl_data_device_manager_interface = {
"wl_data_device_manager", 3,
2, wl_data_device_manager_requests,
0, NULL,
};
static const struct wl_message wl_shell_requests[] = {
{ "get_shell_surface", "no", wayland_types + 37 },
};
WL_PRIVATE const struct wl_interface wl_shell_interface = {
"wl_shell", 1,
1, wl_shell_requests,
0, NULL,
};
static const struct wl_message wl_shell_surface_requests[] = {
{ "pong", "u", wayland_types + 0 },
{ "move", "ou", wayland_types + 39 },
{ "resize", "ouu", wayland_types + 41 },
{ "set_toplevel", "", wayland_types + 0 },
{ "set_transient", "oiiu", wayland_types + 44 },
{ "set_fullscreen", "uu?o", wayland_types + 48 },
{ "set_popup", "ouoiiu", wayland_types + 51 },
{ "set_maximized", "?o", wayland_types + 57 },
{ "set_title", "s", wayland_types + 0 },
{ "set_class", "s", wayland_types + 0 },
};
static const struct wl_message wl_shell_surface_events[] = {
{ "ping", "u", wayland_types + 0 },
{ "configure", "uii", wayland_types + 0 },
{ "popup_done", "", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_shell_surface_interface = {
"wl_shell_surface", 1,
10, wl_shell_surface_requests,
3, wl_shell_surface_events,
};
static const struct wl_message wl_surface_requests[] = {
{ "destroy", "", wayland_types + 0 },
{ "attach", "?oii", wayland_types + 58 },
{ "damage", "iiii", wayland_types + 0 },
{ "frame", "n", wayland_types + 61 },
{ "set_opaque_region", "?o", wayland_types + 62 },
{ "set_input_region", "?o", wayland_types + 63 },
{ "commit", "", wayland_types + 0 },
{ "set_buffer_transform", "2i", wayland_types + 0 },
{ "set_buffer_scale", "3i", wayland_types + 0 },
{ "damage_buffer", "4iiii", wayland_types + 0 },
{ "offset", "5ii", wayland_types + 0 },
};
static const struct wl_message wl_surface_events[] = {
{ "enter", "o", wayland_types + 64 },
{ "leave", "o", wayland_types + 65 },
{ "preferred_buffer_scale", "6i", wayland_types + 0 },
{ "preferred_buffer_transform", "6u", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_surface_interface = {
"wl_surface", 6,
11, wl_surface_requests,
4, wl_surface_events,
};
static const struct wl_message wl_seat_requests[] = {
{ "get_pointer", "n", wayland_types + 66 },
{ "get_keyboard", "n", wayland_types + 67 },
{ "get_touch", "n", wayland_types + 68 },
{ "release", "5", wayland_types + 0 },
};
static const struct wl_message wl_seat_events[] = {
{ "capabilities", "u", wayland_types + 0 },
{ "name", "2s", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_seat_interface = {
"wl_seat", 9,
4, wl_seat_requests,
2, wl_seat_events,
};
static const struct wl_message wl_pointer_requests[] = {
{ "set_cursor", "u?oii", wayland_types + 69 },
{ "release", "3", wayland_types + 0 },
};
static const struct wl_message wl_pointer_events[] = {
{ "enter", "uoff", wayland_types + 73 },
{ "leave", "uo", wayland_types + 77 },
{ "motion", "uff", wayland_types + 0 },
{ "button", "uuuu", wayland_types + 0 },
{ "axis", "uuf", wayland_types + 0 },
{ "frame", "5", wayland_types + 0 },
{ "axis_source", "5u", wayland_types + 0 },
{ "axis_stop", "5uu", wayland_types + 0 },
{ "axis_discrete", "5ui", wayland_types + 0 },
{ "axis_value120", "8ui", wayland_types + 0 },
{ "axis_relative_direction", "9uu", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_pointer_interface = {
"wl_pointer", 9,
2, wl_pointer_requests,
11, wl_pointer_events,
};
static const struct wl_message wl_keyboard_requests[] = {
{ "release", "3", wayland_types + 0 },
};
static const struct wl_message wl_keyboard_events[] = {
{ "keymap", "uhu", wayland_types + 0 },
{ "enter", "uoa", wayland_types + 79 },
{ "leave", "uo", wayland_types + 82 },
{ "key", "uuuu", wayland_types + 0 },
{ "modifiers", "uuuuu", wayland_types + 0 },
{ "repeat_info", "4ii", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_keyboard_interface = {
"wl_keyboard", 9,
1, wl_keyboard_requests,
6, wl_keyboard_events,
};
static const struct wl_message wl_touch_requests[] = {
{ "release", "3", wayland_types + 0 },
};
static const struct wl_message wl_touch_events[] = {
{ "down", "uuoiff", wayland_types + 84 },
{ "up", "uui", wayland_types + 0 },
{ "motion", "uiff", wayland_types + 0 },
{ "frame", "", wayland_types + 0 },
{ "cancel", "", wayland_types + 0 },
{ "shape", "6iff", wayland_types + 0 },
{ "orientation", "6if", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_touch_interface = {
"wl_touch", 9,
1, wl_touch_requests,
7, wl_touch_events,
};
static const struct wl_message wl_output_requests[] = {
{ "release", "3", wayland_types + 0 },
};
static const struct wl_message wl_output_events[] = {
{ "geometry", "iiiiissi", wayland_types + 0 },
{ "mode", "uiii", wayland_types + 0 },
{ "done", "2", wayland_types + 0 },
{ "scale", "2i", wayland_types + 0 },
{ "name", "4s", wayland_types + 0 },
{ "description", "4s", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_output_interface = {
"wl_output", 4,
1, wl_output_requests,
6, wl_output_events,
};
static const struct wl_message wl_region_requests[] = {
{ "destroy", "", wayland_types + 0 },
{ "add", "iiii", wayland_types + 0 },
{ "subtract", "iiii", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_region_interface = {
"wl_region", 1,
3, wl_region_requests,
0, NULL,
};
static const struct wl_message wl_subcompositor_requests[] = {
{ "destroy", "", wayland_types + 0 },
{ "get_subsurface", "noo", wayland_types + 90 },
};
WL_PRIVATE const struct wl_interface wl_subcompositor_interface = {
"wl_subcompositor", 1,
2, wl_subcompositor_requests,
0, NULL,
};
static const struct wl_message wl_subsurface_requests[] = {
{ "destroy", "", wayland_types + 0 },
{ "set_position", "ii", wayland_types + 0 },
{ "place_above", "o", wayland_types + 93 },
{ "place_below", "o", wayland_types + 94 },
{ "set_sync", "", wayland_types + 0 },
{ "set_desync", "", wayland_types + 0 },
};
WL_PRIVATE const struct wl_interface wl_subsurface_interface = {
"wl_subsurface", 1,
6, wl_subsurface_requests,
0, NULL,
};

View File

@@ -0,0 +1,184 @@
/* Generated by wayland-scanner 1.23.1 */
/*
* Copyright © 2008-2013 Kristian Høgsberg
* Copyright © 2013 Rafael Antognolli
* Copyright © 2013 Jasper St. Pierre
* Copyright © 2010-2013 Intel Corporation
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
* Copyright © 2015-2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface xdg_popup_interface;
extern const struct wl_interface xdg_positioner_interface;
extern const struct wl_interface xdg_surface_interface;
extern const struct wl_interface xdg_toplevel_interface;
static const struct wl_interface *xdg_shell_types[] = {
NULL,
NULL,
NULL,
NULL,
&xdg_positioner_interface,
&xdg_surface_interface,
&wl_surface_interface,
&xdg_toplevel_interface,
&xdg_popup_interface,
&xdg_surface_interface,
&xdg_positioner_interface,
&xdg_toplevel_interface,
&wl_seat_interface,
NULL,
NULL,
NULL,
&wl_seat_interface,
NULL,
&wl_seat_interface,
NULL,
NULL,
&wl_output_interface,
&wl_seat_interface,
NULL,
&xdg_positioner_interface,
NULL,
};
static const struct wl_message xdg_wm_base_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "create_positioner", "n", xdg_shell_types + 4 },
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
{ "pong", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_wm_base_events[] = {
{ "ping", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
"xdg_wm_base", 6,
4, xdg_wm_base_requests,
1, xdg_wm_base_events,
};
static const struct wl_message xdg_positioner_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "set_size", "ii", xdg_shell_types + 0 },
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
{ "set_anchor", "u", xdg_shell_types + 0 },
{ "set_gravity", "u", xdg_shell_types + 0 },
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
{ "set_offset", "ii", xdg_shell_types + 0 },
{ "set_reactive", "3", xdg_shell_types + 0 },
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
"xdg_positioner", 6,
10, xdg_positioner_requests,
0, NULL,
};
static const struct wl_message xdg_surface_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "get_toplevel", "n", xdg_shell_types + 7 },
{ "get_popup", "n?oo", xdg_shell_types + 8 },
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
{ "ack_configure", "u", xdg_shell_types + 0 },
};
static const struct wl_message xdg_surface_events[] = {
{ "configure", "u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
"xdg_surface", 6,
5, xdg_surface_requests,
1, xdg_surface_events,
};
static const struct wl_message xdg_toplevel_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "set_parent", "?o", xdg_shell_types + 11 },
{ "set_title", "s", xdg_shell_types + 0 },
{ "set_app_id", "s", xdg_shell_types + 0 },
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
{ "move", "ou", xdg_shell_types + 16 },
{ "resize", "ouu", xdg_shell_types + 18 },
{ "set_max_size", "ii", xdg_shell_types + 0 },
{ "set_min_size", "ii", xdg_shell_types + 0 },
{ "set_maximized", "", xdg_shell_types + 0 },
{ "unset_maximized", "", xdg_shell_types + 0 },
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
{ "unset_fullscreen", "", xdg_shell_types + 0 },
{ "set_minimized", "", xdg_shell_types + 0 },
};
static const struct wl_message xdg_toplevel_events[] = {
{ "configure", "iia", xdg_shell_types + 0 },
{ "close", "", xdg_shell_types + 0 },
{ "configure_bounds", "4ii", xdg_shell_types + 0 },
{ "wm_capabilities", "5a", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
"xdg_toplevel", 6,
14, xdg_toplevel_requests,
4, xdg_toplevel_events,
};
static const struct wl_message xdg_popup_requests[] = {
{ "destroy", "", xdg_shell_types + 0 },
{ "grab", "ou", xdg_shell_types + 22 },
{ "reposition", "3ou", xdg_shell_types + 24 },
};
static const struct wl_message xdg_popup_events[] = {
{ "configure", "iiii", xdg_shell_types + 0 },
{ "popup_done", "", xdg_shell_types + 0 },
{ "repositioned", "3u", xdg_shell_types + 0 },
};
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
"xdg_popup", 6,
3, xdg_popup_requests,
3, xdg_popup_events,
};

View File

@@ -17,32 +17,6 @@
// =====================================================================================================================
#include <fennec/lang/types.h>
#include <fennec/platform/linux/wayland/lib/fwd.h>
/*
* Copyright © 2008 Kristian Høgsberg
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef FENNEC_LIB
#define FENNEC_LIB(...)
@@ -60,7 +34,7 @@
FENNEC_LIB(WAYLAND);
FENNEC_SYMBOL(void, wl_proxy_marshal, struct wl_proxy*, uint32_t, ...);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_flags, struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_flags, struct wl_proxy*, uint32_t, const struct wl_interface*, uint32_t, uint32_t, ...);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_create, struct wl_proxy*, const struct wl_interface*);
FENNEC_SYMBOL(void, wl_proxy_destroy, struct wl_proxy*);
FENNEC_SYMBOL(int, wl_proxy_add_listener, struct wl_proxy*, void (**)(void), void*);
@@ -72,6 +46,11 @@ FENNEC_SYMBOL(const char*, wl_proxy_get_class, st
FENNEC_SYMBOL(void, wl_proxy_set_queue, struct wl_proxy*, struct wl_event_queue*);
FENNEC_SYMBOL(void*, wl_proxy_create_wrapper, void*);
FENNEC_SYMBOL(void, wl_proxy_wrapper_destroy, void*);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_constructor, struct wl_proxy*, uint32_t, const struct wl_interface*, ...);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_constructor_versioned, struct wl_proxy*, uint32_t, const struct wl_interface*, uint32_t, ...);
FENNEC_SYMBOL(void, wl_proxy_set_tag, struct wl_proxy*, const char* const*);
FENNEC_SYMBOL(const char* const*, wl_proxy_get_tag, struct wl_proxy*);
FENNEC_SYMBOL(struct wl_display*, wl_display_connect, const char*);
FENNEC_SYMBOL(struct wl_display*, wl_display_connect_to_fd, int);
FENNEC_SYMBOL(void, wl_display_disconnect, struct wl_display*);
@@ -88,6 +67,7 @@ FENNEC_SYMBOL(int, wl_display_get_error, st
FENNEC_SYMBOL(int, wl_display_flush, struct wl_display*);
FENNEC_SYMBOL(int, wl_display_roundtrip, struct wl_display*);
FENNEC_SYMBOL(struct wl_event_queue*, wl_display_create_queue, struct wl_display*);
FENNEC_SYMBOL(void, wl_event_queue_destroy, struct wl_event_queue*);
FENNEC_SYMBOL(void, wl_log_set_handler_client, wl_log_func_t);
FENNEC_SYMBOL(void, wl_list_init, struct wl_list*);
@@ -96,10 +76,6 @@ FENNEC_SYMBOL(void, wl_list_remove, st
FENNEC_SYMBOL(int, wl_list_length, const struct wl_list*);
FENNEC_SYMBOL(int, wl_list_empty, const struct wl_list*);
FENNEC_SYMBOL(void, wl_list_insert_list, struct wl_list*, struct wl_list*);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_constructor, struct wl_proxy*, uint32_t opcode, const struct wl_interface*interface, ...);
FENNEC_SYMBOL(struct wl_proxy*, wl_proxy_marshal_constructor_versioned, struct wl_proxy*proxy, uint32_t opcode, const struct wl_interface*interface, uint32_t version, ...);
FENNEC_SYMBOL(void, wl_proxy_set_tag, struct wl_proxy*, const char* const*);
FENNEC_SYMBOL(const char* const*, wl_proxy_get_tag, struct wl_proxy*);
FENNEC_GLOBAL(const struct wl_interface, wl_display_interface);
FENNEC_GLOBAL(const struct wl_interface, wl_registry_interface);

File diff suppressed because it is too large Load Diff

View File

@@ -1,198 +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_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_UTIL_H
#define FENNEC_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_UTIL_H
/*
* Copyright © 2008 Kristian Høgsberg
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stddef.h>
#include <inttypes.h>
#include <stdarg.h>
#if defined(__GNUC__) && __GNUC__ >= 4
#define WL_EXPORT __attribute__ ((visibility("default")))
#else
#define WL_EXPORT
#endif
#if __STDC_VERSION__ >= 202311L
#define WL_DEPRECATED [[deprecated]]
#elif defined(__GNUC__) && __GNUC__ >= 4
#define WL_DEPRECATED __attribute__ ((deprecated))
#else
#define WL_DEPRECATED
#endif
#if defined(__GNUC__) && __GNUC__ >= 4
#define WL_PRINTF(x, y) __attribute__((__format__(__printf__, x, y)))
#else
#define WL_PRINTF(x, y)
#endif
#if __STDC_VERSION__ >= 202311L
#define WL_TYPEOF(expr) typeof(expr)
#else
#define WL_TYPEOF(expr) __typeof__(expr)
#endif
struct wl_message {
const char *name;
const char *signature;
const struct wl_interface **types;
};
struct wl_interface {
const char *name;
int version;
int method_count;
const struct wl_message *methods;
int event_count;
const struct wl_message *events;
};
struct wl_list {
struct wl_list *prev;
struct wl_list *next;
};
#define wl_container_of(ptr, sample, member) \
(WL_TYPEOF(sample))((char *)(ptr) - \
offsetof(WL_TYPEOF(*sample), member))
#define wl_list_for_each(pos, head, member) \
for (pos = wl_container_of((head)->next, pos, member); \
&pos->member != (head); \
pos = wl_container_of(pos->member.next, pos, member))
#define wl_list_for_each_safe(pos, tmp, head, member) \
for (pos = wl_container_of((head)->next, pos, member), \
tmp = wl_container_of((pos)->member.next, tmp, member); \
&pos->member != (head); \
pos = tmp, \
tmp = wl_container_of(pos->member.next, tmp, member))
#define wl_list_for_each_reverse(pos, head, member) \
for (pos = wl_container_of((head)->prev, pos, member); \
&pos->member != (head); \
pos = wl_container_of(pos->member.prev, pos, member))
#define wl_list_for_each_reverse_safe(pos, tmp, head, member) \
for (pos = wl_container_of((head)->prev, pos, member), \
tmp = wl_container_of((pos)->member.prev, tmp, member); \
&pos->member != (head); \
pos = tmp, \
tmp = wl_container_of(pos->member.prev, tmp, member))
struct wl_array {
size_t size;
size_t alloc;
void *data;
};
void
wl_array_init(struct wl_array *array);
void
wl_array_release(struct wl_array *array);
void *
wl_array_add(struct wl_array *array, size_t size);
int
wl_array_copy(struct wl_array *array, struct wl_array *source);
#define wl_array_for_each(pos, array) \
for (pos = (array)->data; \
(array)->size != 0 && \
(const char *) pos < ((const char *) (array)->data + (array)->size); \
(pos)++)
typedef int32_t wl_fixed_t;
static inline double
wl_fixed_to_double(wl_fixed_t f)
{
return f / 256.0;
}
static inline wl_fixed_t
wl_fixed_from_double(double d)
{
return (wl_fixed_t) (d * 256.0);
}
static inline int
wl_fixed_to_int(wl_fixed_t f)
{
return f / 256;
}
static inline wl_fixed_t
wl_fixed_from_int(int i)
{
return i * 256;
}
union wl_argument {
int32_t i;
uint32_t u;
wl_fixed_t f;
const char *s;
struct wl_object *o;
uint32_t n;
struct wl_array *a;
int32_t h;
};
typedef int (*wl_dispatcher_func_t)(const void *user_data, void *target,
uint32_t opcode, const struct wl_message *msg,
union wl_argument *args);
typedef void (*wl_log_func_t)(const char *fmt, va_list args) WL_PRINTF(1, 0);
enum wl_iterator_result {
WL_ITERATOR_STOP,
WL_ITERATOR_CONTINUE
};
#endif // FENNEC_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_UTIL_H

View File

@@ -0,0 +1,98 @@
// =====================================================================================================================
// 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_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_CLIENT_H
#define FENNEC_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_CLIENT_H
#include <wayland-client-core.h>
#define FENNEC_LIB(name) extern "C" bool FENNEC_HAS_LIB_##name;
#define FENNEC_SYMBOL(ret, fn, ...) using WAYLAND_sym_##fn = ret(*)(__VA_ARGS__); \
extern "C" WAYLAND_sym_##fn WAYLAND_##fn;
#define FENNEC_GLOBAL(type, name) extern "C" type* WAYLAND_##name;
#include <fennec/platform/linux/wayland/lib/sym.h>
#define wl_proxy_marshal WAYLAND_wl_proxy_marshal
#define wl_proxy_marshal_flags WAYLAND_wl_proxy_marshal_flags
#define wl_proxy_create WAYLAND_wl_proxy_create
#define wl_proxy_destroy WAYLAND_wl_proxy_destroy
#define wl_proxy_add_listener WAYLAND_wl_proxy_add_listener
#define wl_proxy_set_user_data WAYLAND_wl_proxy_set_user_data
#define wl_proxy_get_user_data WAYLAND_wl_proxy_get_user_data
#define wl_proxy_get_version WAYLAND_wl_proxy_get_version
#define wl_proxy_get_id WAYLAND_wl_proxy_get_id
#define wl_proxy_get_class WAYLAND_wl_proxy_get_class
#define wl_proxy_set_queue WAYLAND_wl_proxy_set_queue
#define wl_proxy_create_wrapper WAYLAND_wl_proxy_create_wrapper
#define wl_proxy_wrapper_destroy WAYLAND_wl_proxy_wrapper_destroy
#define wl_proxy_marshal_constructor WAYLAND_wl_proxy_marshal_constructor
#define wl_proxy_marshal_constructor_versioned WAYLAND_wl_proxy_marshal_constructor_versioned
#define wl_proxy_set_tag WAYLAND_wl_proxy_set_tag
#define wl_proxy_get_tag WAYLAND_wl_proxy_get_tag
#define wl_event_queue_destroy WAYLAND_wl_event_queue_destroy
#define wl_log_set_handler_client WAYLAND_wl_log_set_handler_client
#define wl_list_init WAYLAND_wl_list_init
#define wl_list_insert WAYLAND_wl_list_insert
#define wl_list_remove WAYLAND_wl_list_remove
#define wl_list_length WAYLAND_wl_list_length
#define wl_list_empty WAYLAND_wl_list_empty
#define wl_list_insert_list WAYLAND_wl_list_insert_list
#define wl_display_connect WAYLAND_wl_display_connect
#define wl_display_connect_to_fd WAYLAND_wl_display_connect_to_fd
#define wl_display_reconnect WAYLAND_wl_display_reconnect
#define wl_display_disconnect WAYLAND_wl_display_disconnect
#define wl_display_get_fd WAYLAND_wl_display_get_fd
#define wl_display_dispatch WAYLAND_wl_display_dispatch
#define wl_display_dispatch_queue WAYLAND_wl_display_dispatch_queue
#define wl_display_dispatch_queue_pending WAYLAND_wl_display_dispatch_queue_pending
#define wl_display_dispatch_pending WAYLAND_wl_display_dispatch_pending
#define wl_display_prepare_read WAYLAND_wl_display_prepare_read
#define wl_display_prepare_read_queue WAYLAND_wl_display_prepare_read_queue
#define wl_display_read_events WAYLAND_wl_display_read_events
#define wl_display_cancel_read WAYLAND_wl_display_cancel_read
#define wl_display_get_error WAYLAND_wl_display_get_error
#define wl_display_flush WAYLAND_wl_display_flush
#define wl_display_roundtrip WAYLAND_wl_display_roundtrip
#define wl_display_create_queue WAYLAND_wl_display_create_queue
#define wl_seat_interface *WAYLAND_wl_seat_interface
#define wl_surface_interface *WAYLAND_wl_surface_interface
#define wl_shm_pool_interface *WAYLAND_wl_shm_pool_interface
#define wl_buffer_interface *WAYLAND_wl_buffer_interface
#define wl_registry_interface *WAYLAND_wl_registry_interface
#define wl_region_interface *WAYLAND_wl_region_interface
#define wl_pointer_interface *WAYLAND_wl_pointer_interface
#define wl_keyboard_interface *WAYLAND_wl_keyboard_interface
#define wl_compositor_interface *WAYLAND_wl_compositor_interface
#define wl_output_interface *WAYLAND_wl_output_interface
#define wl_shm_interface *WAYLAND_wl_shm_interface
#define wl_data_device_interface *WAYLAND_wl_data_device_interface
#define wl_data_offer_interface *WAYLAND_wl_data_offer_interface
#define wl_data_source_interface *WAYLAND_wl_data_source_interface
#define wl_data_device_manager_interface *WAYLAND_wl_data_device_manager_interface
#define wl_egl_window_create WAYLAND_wl_egl_window_create
#define wl_egl_window_destroy WAYLAND_wl_egl_window_destroy
#define wl_egl_window_resize WAYLAND_wl_egl_window_resize
#define wl_egl_window_get_attached_size WAYLAND_wl_egl_window_get_attached_size
#include <fennec/platform/linux/wayland/lib/headers/wayland-client-protocols.h>
#endif // FENNEC_PLATFORM_LINUX_WAYLAND_LIB_WAYLAND_CLIENT_H

View File

@@ -0,0 +1,83 @@
// =====================================================================================================================
// 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 window.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H
#define FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H
#include <fennec/platform/interface/window.h>
#include <fennec/platform/linux/wayland/display.h>
namespace fennec
{
class wayland_window : public window {
public:
bool running() override;
void configure(const config& config) override;
bool initialize(bool modal) override;
bool shutdown() override;
bool set_title(const cstring& title) override;
bool set_title(const string& title) override;
bool set_width(size_t w) override;
bool set_height(size_t h) override;
bool resize(size_t w, size_t h) override;
bool set_resizable(bool e) override;
bool set_fullscreen_mode(fullscreen_mode mode) override;
bool grab_keyboard(bool e) override;
bool grab_mouse(bool e) override;
bool block_screensaver(bool e) override;
wayland_window(wayland_display* display, wayland_window* parent);
~wayland_window() override;
struct wl_surface* get_native_handle() override {
return _handle;
}
private:
wl_surface* _handle;
wl_shell_surface* _shell;
size_t _nfs_width, _nfs_height;
static void listen_ping(void*, wl_shell_surface*, uint32_t);
static void listen_configure(void*, wl_shell_surface*, uint32_t, int32_t, int32_t);
static void listen_popup_done(void*, wl_shell_surface*);
};
}
#endif // FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H

View File

@@ -1,215 +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_PLATFORM_LINUX_XKB_LIB_FWD_H
#define FENNEC_PLATFORM_LINUX_XKB_LIB_FWD_H
#include <fennec/lang/types.h>
/*
* Copyright © 2009-2012 Daniel Stone
* Copyright © 2012 Intel Corporation
* Copyright © 2012 Ran Benita
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Author: Daniel Stone <daniel@fooishbar.org>
*/
struct xkb_context;
struct xkb_keymap;
struct xkb_state;
struct xkb_rule_names;
typedef uint32_t xkb_keycode_t;
typedef uint32_t xkb_keysym_t;
typedef uint32_t xkb_layout_index_t;
typedef uint32_t xkb_layout_mask_t;
typedef uint32_t xkb_level_index_t;
typedef uint32_t xkb_mod_index_t;
typedef uint32_t xkb_mod_mask_t;
typedef uint32_t xkb_led_index_t;
typedef uint32_t xkb_led_mask_t;
/**
* The iterator used by xkb_keymap_key_for_each().
*
* @sa xkb_keymap_key_for_each
* @memberof xkb_keymap
* @since 0.3.1
*/
typedef void
(*xkb_keymap_key_iter_t)(struct xkb_keymap *keymap, xkb_keycode_t key,
void *data);
/** Flags for xkb_keysym_from_name(). */
enum xkb_keysym_flags {
/** Do not apply any flags. */
XKB_KEYSYM_NO_FLAGS = 0,
/** Find keysym by case-insensitive search. */
XKB_KEYSYM_CASE_INSENSITIVE = (1 << 0)
};
/** Flags for context creation. */
enum xkb_context_flags {
/** Do not apply any context flags. */
XKB_CONTEXT_NO_FLAGS = 0,
/** Create this context with an empty include path. */
XKB_CONTEXT_NO_DEFAULT_INCLUDES = (1 << 0),
/**
* Don't take RMLVO names from the environment.
*
* @since 0.3.0
*/
XKB_CONTEXT_NO_ENVIRONMENT_NAMES = (1 << 1),
/**
* Disable the use of secure_getenv for this context, so that privileged
* processes can use environment variables. Client uses at their own risk.
*
* @since 1.5.0
*/
XKB_CONTEXT_NO_SECURE_GETENV = (1 << 2)
};
/** Specifies a logging level. */
enum xkb_log_level {
XKB_LOG_LEVEL_CRITICAL = 10, /**< Log critical internal errors only. */
XKB_LOG_LEVEL_ERROR = 20, /**< Log all errors. */
XKB_LOG_LEVEL_WARNING = 30, /**< Log warnings and errors. */
XKB_LOG_LEVEL_INFO = 40, /**< Log information, warnings, and errors. */
XKB_LOG_LEVEL_DEBUG = 50 /**< Log everything. */
};
/** Flags for keymap compilation. */
enum xkb_keymap_compile_flags {
/** Do not apply any flags. */
XKB_KEYMAP_COMPILE_NO_FLAGS = 0
};
/** The possible keymap formats. */
enum xkb_keymap_format {
/** The current/classic XKB text format, as generated by xkbcomp -xkb. */
XKB_KEYMAP_FORMAT_TEXT_V1 = 1
};
/** Specifies the direction of the key (press / release). */
enum xkb_key_direction {
XKB_KEY_UP, /**< The key was released. */
XKB_KEY_DOWN /**< The key was pressed. */
};
/**
* Modifier and layout types for state objects. This enum is bitmaskable,
* e.g. (XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED) is valid to
* exclude locked modifiers.
*
* In XKB, the DEPRESSED components are also known as 'base'.
*/
enum xkb_state_component {
/** Depressed modifiers, i.e. a key is physically holding them. */
XKB_STATE_MODS_DEPRESSED = (1 << 0),
/** Latched modifiers, i.e. will be unset after the next non-modifier
* key press. */
XKB_STATE_MODS_LATCHED = (1 << 1),
/** Locked modifiers, i.e. will be unset after the key provoking the
* lock has been pressed again. */
XKB_STATE_MODS_LOCKED = (1 << 2),
/** Effective modifiers, i.e. currently active and affect key
* processing (derived from the other state components).
* Use this unless you explicitly care how the state came about. */
XKB_STATE_MODS_EFFECTIVE = (1 << 3),
/** Depressed layout, i.e. a key is physically holding it. */
XKB_STATE_LAYOUT_DEPRESSED = (1 << 4),
/** Latched layout, i.e. will be unset after the next non-modifier
* key press. */
XKB_STATE_LAYOUT_LATCHED = (1 << 5),
/** Locked layout, i.e. will be unset after the key provoking the lock
* has been pressed again. */
XKB_STATE_LAYOUT_LOCKED = (1 << 6),
/** Effective layout, i.e. currently active and affects key processing
* (derived from the other state components).
* Use this unless you explicitly care how the state came about. */
XKB_STATE_LAYOUT_EFFECTIVE = (1 << 7),
/** LEDs (derived from the other state components). */
XKB_STATE_LEDS = (1 << 8)
};
/**
* Match flags for xkb_state_mod_indices_are_active() and
* xkb_state_mod_names_are_active(), specifying the conditions for a
* successful match. XKB_STATE_MATCH_NON_EXCLUSIVE is bitmaskable with
* the other modes.
*/
enum xkb_state_match {
/** Returns true if any of the modifiers are active. */
XKB_STATE_MATCH_ANY = (1 << 0),
/** Returns true if all of the modifiers are active. */
XKB_STATE_MATCH_ALL = (1 << 1),
/** Makes matching non-exclusive, i.e. will not return false if a
* modifier not specified in the arguments is active. */
XKB_STATE_MATCH_NON_EXCLUSIVE = (1 << 16)
};
enum xkb_consumed_mode {
/**
* This is the mode defined in the XKB specification and used by libX11.
*
* A modifier is consumed if and only if it *may affect* key translation.
*
* For example, if `Control+Alt+<Backspace>` produces some assigned keysym,
* then when pressing just `<Backspace>`, `Control` and `Alt` are consumed,
* even though they are not active, since if they *were* active they would
* have affected key translation.
*/
XKB_CONSUMED_MODE_XKB,
/**
* This is the mode used by the GTK+ toolkit.
*
* The mode consists of the following two independent heuristics:
*
* - The currently active set of modifiers, excluding modifiers which do
* not affect the key (as described for @ref XKB_CONSUMED_MODE_XKB), are
* considered consumed, if the keysyms produced when all of them are
* active are different from the keysyms produced when no modifiers are
* active.
*
* - A single modifier is considered consumed if the keysyms produced for
* the key when it is the only active modifier are different from the
* keysyms produced when no modifiers are active.
*/
XKB_CONSUMED_MODE_GTK
};
#endif // FENNEC_PLATFORM_LINUX_XKB_LIB_FWD_H

View File

@@ -44,7 +44,6 @@
*/
#include <stdio.h>
#include <fennec/platform/linux/xkb/lib/fwd.h>
#ifndef FENNEC_LIB
#define FENNEC_LIB(...)
@@ -60,82 +59,85 @@
FENNEC_LIB(XKB);
FENNEC_SYMBOL(int, xkb_keysym_get_name, xkb_keysym_t keysym, char *buffer, size_t size);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_from_name, const char *name, enum xkb_keysym_flags flags);
FENNEC_SYMBOL(int, xkb_keysym_to_utf8, xkb_keysym_t keysym, char *buffer, size_t size);
FENNEC_SYMBOL(uint32_t, xkb_keysym_to_utf32, xkb_keysym_t keysym);
FENNEC_SYMBOL(xkb_keysym_t, xkb_utf32_to_keysym, uint32_t ucs);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_to_upper, xkb_keysym_t ks);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_to_lower, xkb_keysym_t ks);
FENNEC_SYMBOL(struct xkb_context*, xkb_context_new, enum xkb_context_flags flags);
FENNEC_SYMBOL(struct xkb_context*, xkb_context_ref, struct xkb_context *context);
FENNEC_SYMBOL(void, xkb_context_unref, struct xkb_context *context);
FENNEC_SYMBOL(void, xkb_context_set_user_data, struct xkb_context *context, void *user_data);
FENNEC_SYMBOL(void*, xkb_context_get_user_data, struct xkb_context *context);
FENNEC_SYMBOL(int, xkb_context_include_path_append, struct xkb_context *context, const char *path);
FENNEC_SYMBOL(int, xkb_context_include_path_append_default, struct xkb_context *context);
FENNEC_SYMBOL(int, xkb_context_include_path_reset_defaults, struct xkb_context *context);
FENNEC_SYMBOL(void, xkb_context_include_path_clear, struct xkb_context *context);
FENNEC_SYMBOL(unsigned int, xkb_context_num_include_paths, struct xkb_context *context);
FENNEC_SYMBOL(const char*, xkb_context_include_path_get, struct xkb_context *context, unsigned int index);
FENNEC_SYMBOL(void, xkb_context_set_log_level, struct xkb_context *context, enum xkb_log_level level);
FENNEC_SYMBOL(enum xkb_log_level, xkb_context_get_log_level, struct xkb_context *context);
FENNEC_SYMBOL(void, xkb_context_set_log_verbosity, struct xkb_context *context, int verbosity);
FENNEC_SYMBOL(int, xkb_context_get_log_verbosity, struct xkb_context *context);
FENNEC_SYMBOL(void, xkb_context_set_log_fn, struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args));
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_names, struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_file, struct xkb_context *context, FILE *file, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_string, struct xkb_context *context, const char *string, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_buffer, struct xkb_context *context, const char *buffer, size_t length, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_ref, struct xkb_keymap *keymap);
FENNEC_SYMBOL(void, xkb_keymap_unref, struct xkb_keymap *keymap);
FENNEC_SYMBOL(char*, xkb_keymap_get_as_string, struct xkb_keymap *keymap, enum xkb_keymap_format format);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_min_keycode, struct xkb_keymap *keymap);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_max_keycode, struct xkb_keymap *keymap);
FENNEC_SYMBOL(void, xkb_keymap_key_for_each, struct xkb_keymap *keymap, xkb_keymap_key_iter_t iter, void *data);
FENNEC_SYMBOL(const char*, xkb_keymap_key_get_name, struct xkb_keymap *keymap, xkb_keycode_t key);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_key_by_name, struct xkb_keymap *keymap, const char *name);
FENNEC_SYMBOL(xkb_mod_index_t, xkb_keymap_num_mods, struct xkb_keymap *keymap);
FENNEC_SYMBOL(const char*, xkb_keymap_mod_get_name, struct xkb_keymap *keymap, xkb_mod_index_t idx);
FENNEC_SYMBOL(xkb_mod_index_t, xkb_keymap_mod_get_index, struct xkb_keymap *keymap, const char *name);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_num_layouts, struct xkb_keymap *keymap);
FENNEC_SYMBOL(const char*, xkb_keymap_layout_get_name, struct xkb_keymap *keymap, xkb_layout_index_t idx);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_layout_get_index, struct xkb_keymap *keymap, const char *name);
FENNEC_SYMBOL(xkb_led_index_t, xkb_keymap_num_leds, struct xkb_keymap *keymap);
FENNEC_SYMBOL(const char*, xkb_keymap_led_get_name, struct xkb_keymap *keymap, xkb_led_index_t idx);
FENNEC_SYMBOL(xkb_led_index_t, xkb_keymap_led_get_index, struct xkb_keymap *keymap, const char *name);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_num_layouts_for_key, struct xkb_keymap *keymap, xkb_keycode_t key);
FENNEC_SYMBOL(xkb_level_index_t, xkb_keymap_num_levels_for_key, struct xkb_keymap *keymap, xkb_keycode_t key, xkb_layout_index_t layout);
FENNEC_SYMBOL(size_t, xkb_keymap_key_get_mods_for_level, struct xkb_keymap *keymap, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, xkb_mod_mask_t *masks_out, size_t masks_size);
FENNEC_SYMBOL(int, xkb_keymap_key_get_syms_by_level, struct xkb_keymap *keymap, xkb_keycode_t key, xkb_layout_index_t layout, xkb_level_index_t level, const xkb_keysym_t **syms_out);
FENNEC_SYMBOL(int, xkb_keymap_key_repeats, struct xkb_keymap *keymap, xkb_keycode_t key);
FENNEC_SYMBOL(struct xkb_state*, xkb_state_new, struct xkb_keymap *keymap);
FENNEC_SYMBOL(struct xkb_state*, xkb_state_ref, struct xkb_state *state);
FENNEC_SYMBOL(void, xkb_state_unref, struct xkb_state *state);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_state_get_keymap, struct xkb_state *state);
FENNEC_SYMBOL(enum xkb_state_component, xkb_state_update_key, struct xkb_state *state, xkb_keycode_t key, enum xkb_key_direction direction);
FENNEC_SYMBOL(enum xkb_state_component, xkb_state_update_mask, struct xkb_state *state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout);
FENNEC_SYMBOL(int, xkb_state_key_get_syms, struct xkb_state *state, xkb_keycode_t key, const xkb_keysym_t **syms_out);
FENNEC_SYMBOL(int, xkb_state_key_get_utf8, struct xkb_state *state, xkb_keycode_t key, char *buffer, size_t size);
FENNEC_SYMBOL(uint32_t, xkb_state_key_get_utf32, struct xkb_state *state, xkb_keycode_t key);
FENNEC_SYMBOL(xkb_keysym_t, xkb_state_key_get_one_sym, struct xkb_state *state, xkb_keycode_t key);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_state_key_get_layout, struct xkb_state *state, xkb_keycode_t key);
FENNEC_SYMBOL(xkb_level_index_t, xkb_state_key_get_level, struct xkb_state *state, xkb_keycode_t key, xkb_layout_index_t layout);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_serialize_mods, struct xkb_state *state, enum xkb_state_component components);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_state_serialize_layout, struct xkb_state *state, enum xkb_state_component components);
FENNEC_SYMBOL(int, xkb_state_mod_name_is_active, struct xkb_state *state, const char *name, enum xkb_state_component type);
FENNEC_SYMBOL(int, xkb_state_mod_names_are_active, struct xkb_state *state, enum xkb_state_component type, enum xkb_state_match match, ...);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_active, struct xkb_state *state, xkb_mod_index_t idx, enum xkb_state_component type);
FENNEC_SYMBOL(int, xkb_state_mod_indices_are_active, struct xkb_state *state, enum xkb_state_component type, enum xkb_state_match match, ...);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_key_get_consumed_mods2, struct xkb_state *state, xkb_keycode_t key, enum xkb_consumed_mode mode);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_key_get_consumed_mods, struct xkb_state *state, xkb_keycode_t key);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_consumed2, struct xkb_state *state, xkb_keycode_t key, xkb_mod_index_t idx, enum xkb_consumed_mode mode);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_consumed, struct xkb_state *state, xkb_keycode_t key, xkb_mod_index_t idx);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_mod_mask_remove_consumed, struct xkb_state *state, xkb_keycode_t key, xkb_mod_mask_t mask);
FENNEC_SYMBOL(int, xkb_state_layout_name_is_active, struct xkb_state *state, const char *name, enum xkb_state_component type);
FENNEC_SYMBOL(int, xkb_state_layout_index_is_active, struct xkb_state *state, xkb_layout_index_t idx, enum xkb_state_component type);
FENNEC_SYMBOL(int, xkb_state_led_name_is_active, struct xkb_state *state, const char *name);
FENNEC_SYMBOL(int, xkb_state_led_index_is_active, struct xkb_state *state, xkb_led_index_t idx);
FENNEC_SYMBOL(int, xkb_keysym_get_name, xkb_keysym_t, char*, size_t);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_from_name, const char*, enum xkb_keysym_flags);
FENNEC_SYMBOL(int, xkb_keysym_to_utf8, xkb_keysym_t, char*, size_t);
FENNEC_SYMBOL(uint32_t, xkb_keysym_to_utf32, xkb_keysym_t);
FENNEC_SYMBOL(xkb_keysym_t, xkb_utf32_to_keysym, uint32_t);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_to_upper, xkb_keysym_t);
FENNEC_SYMBOL(xkb_keysym_t, xkb_keysym_to_lower, xkb_keysym_t);
FENNEC_SYMBOL(struct xkb_context*, xkb_context_new, enum xkb_context_flags);
FENNEC_SYMBOL(struct xkb_context*, xkb_context_ref, struct xkb_context*);
FENNEC_SYMBOL(void, xkb_context_unref, struct xkb_context*);
FENNEC_SYMBOL(void, xkb_context_set_user_data, struct xkb_context*, void*);
FENNEC_SYMBOL(void*, xkb_context_get_user_data, struct xkb_context*);
FENNEC_SYMBOL(int, xkb_context_include_path_append, struct xkb_context*, const char*);
FENNEC_SYMBOL(int, xkb_context_include_path_append_default, struct xkb_context*);
FENNEC_SYMBOL(int, xkb_context_include_path_reset_defaults, struct xkb_context*);
FENNEC_SYMBOL(void, xkb_context_include_path_clear, struct xkb_context*);
FENNEC_SYMBOL(unsigned int, xkb_context_num_include_paths, struct xkb_context*);
FENNEC_SYMBOL(const char*, xkb_context_include_path_get, struct xkb_context*, unsigned int);
FENNEC_SYMBOL(void, xkb_context_set_log_level, struct xkb_context*, enum xkb_log_level);
FENNEC_SYMBOL(enum xkb_log_level, xkb_context_get_log_level, struct xkb_context*);
FENNEC_SYMBOL(void, xkb_context_set_log_verbosity, struct xkb_context*, int);
FENNEC_SYMBOL(int, xkb_context_get_log_verbosity, struct xkb_context*);
FENNEC_SYMBOL(void, xkb_context_set_log_fn, struct xkb_context*, void (*)(struct xkb_context*, enum xkb_log_level, const char*, va_list));
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_names, struct xkb_context*, const struct xkb_rule_names*, enum xkb_keymap_compile_flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_file, struct xkb_context*, FILE*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_string, struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_new_from_buffer, struct xkb_context*, const char*, size_t, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_keymap_ref, struct xkb_keymap*);
FENNEC_SYMBOL(void, xkb_keymap_unref, struct xkb_keymap*);
FENNEC_SYMBOL(char*, xkb_keymap_get_as_string, struct xkb_keymap*, enum xkb_keymap_format);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_min_keycode, struct xkb_keymap*);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_max_keycode, struct xkb_keymap*);
FENNEC_SYMBOL(void, xkb_keymap_key_for_each, struct xkb_keymap*, xkb_keymap_key_iter_t, void*);
FENNEC_SYMBOL(const char*, xkb_keymap_key_get_name, struct xkb_keymap*, xkb_keycode_t);
FENNEC_SYMBOL(xkb_keycode_t, xkb_keymap_key_by_name, struct xkb_keymap*, const char*);
FENNEC_SYMBOL(xkb_mod_index_t, xkb_keymap_num_mods, struct xkb_keymap*);
FENNEC_SYMBOL(const char*, xkb_keymap_mod_get_name, struct xkb_keymap*, xkb_mod_index_t);
FENNEC_SYMBOL(xkb_mod_index_t, xkb_keymap_mod_get_index, struct xkb_keymap*, const char*);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_num_layouts, struct xkb_keymap*);
FENNEC_SYMBOL(const char*, xkb_keymap_layout_get_name, struct xkb_keymap*, xkb_layout_index_t);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_layout_get_index, struct xkb_keymap*, const char*);
FENNEC_SYMBOL(xkb_led_index_t, xkb_keymap_num_leds, struct xkb_keymap*);
FENNEC_SYMBOL(const char*, xkb_keymap_led_get_name, struct xkb_keymap*, xkb_led_index_t);
FENNEC_SYMBOL(xkb_led_index_t, xkb_keymap_led_get_index, struct xkb_keymap*, const char*);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_keymap_num_layouts_for_key, struct xkb_keymap*, xkb_keycode_t);
FENNEC_SYMBOL(xkb_level_index_t, xkb_keymap_num_levels_for_key, struct xkb_keymap*, xkb_keycode_t, xkb_layout_index_t);
FENNEC_SYMBOL(size_t, xkb_keymap_key_get_mods_for_level, struct xkb_keymap*, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, xkb_mod_mask_t*, size_t);
FENNEC_SYMBOL(int, xkb_keymap_key_get_syms_by_level, struct xkb_keymap*, xkb_keycode_t, xkb_layout_index_t, xkb_level_index_t, const xkb_keysym_t**);
FENNEC_SYMBOL(int, xkb_keymap_key_repeats, struct xkb_keymap*, xkb_keycode_t);
FENNEC_SYMBOL(struct xkb_state*, xkb_state_new, struct xkb_keymap*);
FENNEC_SYMBOL(struct xkb_state*, xkb_state_ref, struct xkb_state*);
FENNEC_SYMBOL(void, xkb_state_unref, struct xkb_state*);
FENNEC_SYMBOL(struct xkb_keymap*, xkb_state_get_keymap, struct xkb_state*);
FENNEC_SYMBOL(enum xkb_state_component, xkb_state_update_key, struct xkb_state*, xkb_keycode_t, enum xkb_key_direction);
FENNEC_SYMBOL(enum xkb_state_component, xkb_state_update_mask, struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
FENNEC_SYMBOL(int, xkb_state_key_get_syms, struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
FENNEC_SYMBOL(int, xkb_state_key_get_utf8, struct xkb_state*, xkb_keycode_t, char*, size_t);
FENNEC_SYMBOL(uint32_t, xkb_state_key_get_utf32, struct xkb_state*, xkb_keycode_t);
FENNEC_SYMBOL(xkb_keysym_t, xkb_state_key_get_one_sym, struct xkb_state*, xkb_keycode_t);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_state_key_get_layout, struct xkb_state*, xkb_keycode_t);
FENNEC_SYMBOL(xkb_level_index_t, xkb_state_key_get_level, struct xkb_state*, xkb_keycode_t, xkb_layout_index_t);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_serialize_mods, struct xkb_state*, enum xkb_state_component);
FENNEC_SYMBOL(xkb_layout_index_t, xkb_state_serialize_layout, struct xkb_state*, enum xkb_state_component);
FENNEC_SYMBOL(int, xkb_state_mod_name_is_active, struct xkb_state*, const char*, enum xkb_state_component);
FENNEC_SYMBOL(int, xkb_state_mod_names_are_active, struct xkb_state*, enum xkb_state_component, enum xkb_state_match, ...);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_active, struct xkb_state*, xkb_mod_index_t, enum xkb_state_component);
FENNEC_SYMBOL(int, xkb_state_mod_indices_are_active, struct xkb_state*, enum xkb_state_component, enum xkb_state_match, ...);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_key_get_consumed_mods2, struct xkb_state*, xkb_keycode_t, enum xkb_consumed_mode);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_key_get_consumed_mods, struct xkb_state*, xkb_keycode_t);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_consumed2, struct xkb_state*, xkb_keycode_t, xkb_mod_index_t, enum xkb_consumed_mode);
FENNEC_SYMBOL(int, xkb_state_mod_index_is_consumed, struct xkb_state*, xkb_keycode_t, xkb_mod_index_t);
FENNEC_SYMBOL(xkb_mod_mask_t, xkb_state_mod_mask_remove_consumed, struct xkb_state*, xkb_keycode_t, xkb_mod_mask_t);
FENNEC_SYMBOL(int, xkb_state_layout_name_is_active, struct xkb_state*, const char*, enum xkb_state_component);
FENNEC_SYMBOL(int, xkb_state_layout_index_is_active, struct xkb_state*, xkb_layout_index_t, enum xkb_state_component);
FENNEC_SYMBOL(int, xkb_state_led_name_is_active, struct xkb_state*, const char*);
FENNEC_SYMBOL(int, xkb_state_led_index_is_active, struct xkb_state*, xkb_led_index_t);
#undef FENNEC_LIB
#undef FENNEC_SYMBOL

View File

@@ -0,0 +1,110 @@
// =====================================================================================================================
// 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_PLATFORM_LINUX_XKB_LIB_XKB_H
#define FENNEC_PLATFORM_LINUX_XKB_LIB_XKB_H
#include <xkbcommon/xkbcommon.h>
#define FENNEC_LIB(name) extern bool FENNEC_HAS_LIB_##name;
#define FENNEC_SYMBOL(ret, fn, ...) using XKB_sym_##fn = ret(*)(__VA_ARGS__); \
extern XKB_sym_##fn XKB_##fn;
#define FENNEC_GLOBAL(type, name) extern type* XKB_##name;
#include <fennec/platform/linux/xkb/lib/sym.h>
#define xkb_keysym_get_name XKB_xkb_keysym_get_name
#define xkb_keysym_from_name XKB_xkb_keysym_from_name
#define xkb_keysym_to_utf8 XKB_xkb_keysym_to_utf8
#define xkb_keysym_to_utf32 XKB_xkb_keysym_to_utf32
#define xkb_utf32_to_keysym XKB_xkb_utf32_to_keysym
#define xkb_keysym_to_upper XKB_xkb_keysym_to_upper
#define xkb_keysym_to_lower XKB_xkb_keysym_to_lower
#define xkb_context_new XKB_xkb_context_new
#define xkb_context_ref XKB_xkb_context_ref
#define xkb_context_unref XKB_xkb_context_unref
#define xkb_context_set_user_data XKB_xkb_context_set_user_data
#define xkb_context_get_user_data XKB_xkb_context_get_user_data
#define xkb_context_include_path_append XKB_xkb_context_include_path_append
#define xkb_context_include_path_append_default XKB_xkb_context_include_path_append_default
#define xkb_context_include_path_reset_defaults XKB_xkb_context_include_path_reset_defaults
#define xkb_context_include_path_clear XKB_xkb_context_include_path_clear
#define xkb_context_num_include_paths XKB_xkb_context_num_include_paths
#define xkb_context_include_path_get XKB_xkb_context_include_path_get
#define xkb_context_set_log_level XKB_xkb_context_set_log_level
#define xkb_context_get_log_level XKB_xkb_context_get_log_level
#define xkb_context_set_log_verbosity XKB_xkb_context_set_log_verbosity
#define xkb_context_get_log_verbosity XKB_xkb_context_get_log_verbosity
#define xkb_context_set_log_fn XKB_xkb_context_set_log_fn
#define xkb_keymap_new_from_names XKB_xkb_keymap_new_from_names
#define xkb_keymap_new_from_file XKB_xkb_keymap_new_from_file
#define xkb_keymap_new_from_string XKB_xkb_keymap_new_from_string
#define xkb_keymap_new_from_buffer XKB_xkb_keymap_new_from_buffer
#define xkb_keymap_ref XKB_xkb_keymap_ref
#define xkb_keymap_unref XKB_xkb_keymap_unref
#define xkb_keymap_get_as_string XKB_xkb_keymap_get_as_string
#define xkb_keymap_min_keycode XKB_xkb_keymap_min_keycode
#define xkb_keymap_max_keycode XKB_xkb_keymap_max_keycode
#define xkb_keymap_key_for_each XKB_xkb_keymap_key_for_each
#define xkb_keymap_key_get_name XKB_xkb_keymap_key_get_name
#define xkb_keymap_key_by_name XKB_xkb_keymap_key_by_name
#define xkb_keymap_num_mods XKB_xkb_keymap_num_mods
#define xkb_keymap_mod_get_name XKB_xkb_keymap_mod_get_name
#define xkb_keymap_mod_get_index XKB_xkb_keymap_mod_get_index
#define xkb_keymap_num_layouts XKB_xkb_keymap_num_layouts
#define xkb_keymap_layout_get_name XKB_xkb_keymap_layout_get_name
#define xkb_keymap_layout_get_index XKB_xkb_keymap_layout_get_index
#define xkb_keymap_num_leds XKB_xkb_keymap_num_leds
#define xkb_keymap_led_get_name XKB_xkb_keymap_led_get_name
#define xkb_keymap_led_get_index XKB_xkb_keymap_led_get_index
#define xkb_keymap_num_layouts_for_key XKB_xkb_keymap_num_layouts_for_key
#define xkb_keymap_num_levels_for_key XKB_xkb_keymap_num_levels_for_key
#define xkb_keymap_key_get_mods_for_level XKB_xkb_keymap_key_get_mods_for_level
#define xkb_keymap_key_get_syms_by_level XKB_xkb_keymap_key_get_syms_by_level
#define xkb_keymap_key_repeats XKB_xkb_keymap_key_repeats
#define xkb_state_new XKB_xkb_state_new
#define xkb_state_ref XKB_xkb_state_ref
#define xkb_state_unref XKB_xkb_state_unref
#define xkb_state_get_keymap XKB_xkb_state_get_keymap
#define xkb_state_update_key XKB_xkb_state_update_key
#define xkb_state_update_mask XKB_xkb_state_update_mask
#define xkb_state_key_get_syms XKB_xkb_state_key_get_syms
#define xkb_state_key_get_utf8 XKB_xkb_state_key_get_utf8
#define xkb_state_key_get_utf32 XKB_xkb_state_key_get_utf32
#define xkb_state_key_get_one_sym XKB_xkb_state_key_get_one_sym
#define xkb_state_key_get_layout XKB_xkb_state_key_get_layout
#define xkb_state_key_get_level XKB_xkb_state_key_get_level
#define xkb_state_serialize_mods XKB_xkb_state_serialize_mods
#define xkb_state_serialize_layout XKB_xkb_state_serialize_layout
#define xkb_state_mod_name_is_active XKB_xkb_state_mod_name_is_active
#define xkb_state_mod_names_are_active XKB_xkb_state_mod_names_are_active
#define xkb_state_mod_index_is_active XKB_xkb_state_mod_index_is_active
#define xkb_state_mod_indices_are_active XKB_xkb_state_mod_indices_are_active
#define xkb_state_key_get_consumed_mods2 XKB_xkb_state_key_get_consumed_mods2
#define xkb_state_key_get_consumed_mods XKB_xkb_state_key_get_consumed_mods
#define xkb_state_mod_index_is_consumed2 XKB_xkb_state_mod_index_is_consumed2
#define xkb_state_mod_index_is_consumed XKB_xkb_state_mod_index_is_consumed
#define xkb_state_mod_mask_remove_consumed XKB_xkb_state_mod_mask_remove_consumed
#define xkb_state_layout_name_is_active XKB_xkb_state_layout_name_is_active
#define xkb_state_layout_index_is_active XKB_xkb_state_layout_index_is_active
#define xkb_state_led_name_is_active XKB_xkb_state_led_name_is_active
#define xkb_state_led_index_is_active XKB_xkb_state_led_index_is_active
#endif // FENNEC_PLATFORM_LINUX_XKB_LIB_XKB_H

File diff suppressed because it is too large Load Diff

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