├── .gitignore
├── Linux.sh
├── README.md
├── Visual Studio 2010.bat
├── Visual Studio 2015.bat
├── Visual Studio 2017.bat
├── Visual Studio 2019.bat
├── Visual Studio 2022.bat
├── Xcode.sh
├── appveyor.yml
├── crstl.lua
├── include
├── crstl.module
├── crstl.natvis
└── crstl
│ ├── allocator.h
│ ├── array.h
│ ├── atomic.h
│ ├── bit.h
│ ├── bitset.h
│ ├── compressed_pair.h
│ ├── config.h
│ ├── config_fwd.h
│ ├── critical_section.h
│ ├── crstldef.h
│ ├── debugging.h
│ ├── deque.h
│ ├── filesystem.h
│ ├── fixed_deque.h
│ ├── fixed_function.h
│ ├── fixed_open_hashmap.h
│ ├── fixed_path.h
│ ├── fixed_string.h
│ ├── fixed_vector.h
│ ├── forward_declarations.h
│ ├── function.h
│ ├── function_common.h
│ ├── hash.h
│ ├── intrusive_ptr.h
│ ├── move_forward.h
│ ├── open_hashmap.h
│ ├── open_hashtable_base.h
│ ├── pair.h
│ ├── path.h
│ ├── platform
│ ├── atomic_arm.h
│ ├── atomic_clang_gcc.h
│ ├── atomic_win32.h
│ ├── common_win32.h
│ ├── critical_section_posix.h
│ ├── critical_section_win32.h
│ ├── filesystem_posix.h
│ ├── filesystem_win32.h
│ ├── process_posix.h
│ ├── process_win32.h
│ ├── thread_posix.h
│ ├── thread_win32.h
│ └── timer_platform.h
│ ├── process.h
│ ├── sort.h
│ ├── span.h
│ ├── stack_vector.h
│ ├── string.h
│ ├── string_view.h
│ ├── thread.h
│ ├── timer.h
│ ├── tuple.h
│ ├── type_array.h
│ ├── type_builtins.h
│ ├── type_utils.h
│ ├── unique_ptr.h
│ ├── utility
│ ├── constructor_utils.h
│ ├── fixed_common.h
│ ├── hashmap_common.h
│ ├── memory_ops.h
│ ├── path_common.h
│ ├── placement_new.h
│ ├── sequence.h
│ ├── string_common.h
│ ├── string_length.h
│ └── string_utf.h
│ ├── vector.h
│ └── vector_base.h
├── module
└── crstl.ixx
├── notes
└── hash_map_optimizations.txt
├── premake
├── linux
│ └── premake5
├── osx
│ └── premake5
└── win
│ └── premake5.exe
└── unit_tests
├── android
├── AndroidManifest.xml
├── android_native_app_glue.c
├── android_native_app_glue.h
├── build.xml
├── project.properties
└── res
│ └── values
│ └── strings.xml
├── unit_tests.cpp
├── unit_tests.h
├── unit_tests_array.cpp
├── unit_tests_associative.cpp
├── unit_tests_atomic.cpp
├── unit_tests_bit.cpp
├── unit_tests_bitset.cpp
├── unit_tests_deque.cpp
├── unit_tests_filesystem.cpp
├── unit_tests_function.cpp
├── unit_tests_pair.cpp
├── unit_tests_path.cpp
├── unit_tests_process.cpp
├── unit_tests_smartptr.cpp
├── unit_tests_string.cpp
├── unit_tests_thread.cpp
├── unit_tests_timer.cpp
└── unit_tests_vector.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # User files
2 | *.suo
3 | *.user
4 | *.sln.docstates
5 |
6 | # Build folders
7 |
8 | workspace/
9 |
10 | # Visual Studio Intermediate files
11 | *.opendb
12 | *.db
13 |
14 | # Temporary
15 | *.tmp
--------------------------------------------------------------------------------
/Linux.sh:
--------------------------------------------------------------------------------
1 | ./premake/linux/premake5 --file=crstl.lua gmake
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://ci.appveyor.com/project/redorav/crstl)
2 |
3 |
4 | # CRSTL
5 | An alternative or complementary STL that focuses on fast compile times and runtime, focusing on fixed size alternatives for containers and functionality. **This is a work in progress and will be for the time being**. Some of the guiding goals are:
6 |
7 | - No RTTI
8 | - No exceptions
9 | - Fast to compile: minimal dependencies on external headers, a forward declarations file and C++20 module provided when available
10 | - Fast debug code. Avoid deep function nesting so that unoptimized code runs fast
11 | - Containers that don't do heap allocations (**fixed_**)
12 | - Containers that only use the stack (**stack_**)
13 | - Simple C++ code as clear and unobfuscated as possible
14 | - Will add functionality from newer standards, compatible with older versions of C++
15 | - Decoupled and granular. A single include has a small impact
16 |
17 | ## Rationale
18 |
19 | - Exceptions are rarely used in constrained environments because they introduce a lot of complexity, are restrictive, and ultimately not good for performance. Since this library has performance-oriented goals, they are not allowed
20 | - Codebases get more bloated every day, and compilation in some popular codebases can take hours. This codebase tries as much as possible to avoid external dependencies
21 | - Debug performance is important. Some codebases and the STL itself have deep, nested callstacks that make debug performance much worse than desirable. To that end this codebase will manually expand code or use macros as necessary
22 | - Fixed containers should be used in many circumstances if the requirements allow it. They will not access the heap at any point and can take advantage of the knowledge of the length to save memory. They impose an upper bound on certain operations so make sure requirements are clear from the beginning
23 | - The STL or Boost aim to be as generic as possible, the codebases large and complicated. CRSTL does not have that aim. Instead, it uses as little template code as necessary, and tries to use simple constructs to achieve it
24 | - The STL is tied to C++ revisions, which makes upgrading the language and accesing STL features the same operation. CRSTL aims to offer functionality available in later C++ revisions to older compilers. This means codebases can upgrade without upgrading their C++ version
25 | - Modern language features such as constexpr, inline variables or modules can be very useful in certain scenarios. CRSTL doesn't shy away from using them as appropriate while providing fallbacks
26 |
27 | ## Additional Design Goals
28 |
29 | - The include philosophy differs. Where possible, we forward declare dependencies. It is the user's responsibility to include everything necessary for their usage. For example, vector can be implicitly cast to span, but we don't include span in vector. If this automatic behavior is desired, one can create a new header where both are included and use that.
30 |
31 | - Template metaprogramming is avoided as much as possible, while acknowledging the fact that there are many instances where it is necessary. For example, unique_ptr for arrays can be done with a single class and enable_if, or with two classes that share functionality. The second approach is preferred in this library.
32 |
33 | - The STL interface is well known, as is Boost or EASTL. We maintain an interface that is familiar where it makes sense (e.g. function naming such as push_back() or size()), but very often we'll include additional performance oriented functions such as push_back_uninitialized() or resize_uninitialized()
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Visual Studio 2010.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start /B /W premake/win/premake5 --file=crstl.lua vs2010
--------------------------------------------------------------------------------
/Visual Studio 2015.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start /B /W premake/win/premake5 --file=crstl.lua vs2015
--------------------------------------------------------------------------------
/Visual Studio 2017.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start /B /W premake/win/premake5 --file=crstl.lua vs2017
--------------------------------------------------------------------------------
/Visual Studio 2019.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start /B /W premake/win/premake5 --file=crstl.lua vs2019
--------------------------------------------------------------------------------
/Visual Studio 2022.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start /B /W premake/win/premake5 --file=crstl.lua vs2022
--------------------------------------------------------------------------------
/Xcode.sh:
--------------------------------------------------------------------------------
1 | ./premake/osx/premake5 --file=crstl.lua xcode4
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # version format
2 | version: 1.0.{build}
3 |
4 | # Do not build feature branch with open Pull Requests
5 | skip_branch_with_pr: true
6 |
7 | environment:
8 | matrix:
9 |
10 | - platform: OSX64 Clang C++11
11 | configuration: Release
12 | APPVEYOR_BUILD_WORKER_IMAGE: macOS
13 | build: off
14 |
15 | - platform: OSX64 Clang C++14
16 | configuration: Release
17 | APPVEYOR_BUILD_WORKER_IMAGE: macOS
18 | build: off
19 |
20 | - platform: Linux64 GCC C++11
21 | configuration: Release
22 | APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
23 | build: off
24 |
25 | - platform: Linux64 Clang C++11
26 | configuration: Release
27 | APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
28 | build: off
29 |
30 | - platform: Linux64 GCC C++14
31 | configuration: Release
32 | APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
33 | build: off
34 |
35 | - platform: Linux64 Clang C++14
36 | configuration: Release
37 | APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2204
38 | build: off
39 |
40 | - vsversion: 2022
41 | platform: MSVC ARM64
42 | configuration: Release
43 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
44 |
45 | - vsversion: 2022
46 | platform: MSVC 64
47 | configuration: Release
48 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
49 |
50 | - vsversion: 2019
51 | platform: MSVC 64
52 | configuration: Release
53 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
54 |
55 | - vsversion: 2017
56 | platform: MSVC 64
57 | configuration: Release
58 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
59 |
60 | - vsversion: 2022
61 | platform: LLVM 64
62 | configuration: Release
63 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
64 |
65 | - vsversion: 2019
66 | platform: LLVM 64
67 | configuration: Release
68 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
69 |
70 | install:
71 | - cmd: if %vsversion%==2015
72 | "Visual Studio 2015.bat"
73 | - cmd: if %vsversion%==2017
74 | "Visual Studio 2017.bat"
75 | - cmd: if %vsversion%==2019
76 | "Visual Studio 2019.bat"
77 | - cmd: if %vsversion%==2022
78 | "Visual Studio 2022.bat"
79 | - sh: if [[ $APPVEYOR_BUILD_WORKER_IMAGE = Ubuntu2204 ]]; then gcc -v; clang -v; ./Linux.sh; fi;
80 | - sh: if [[ $APPVEYOR_BUILD_WORKER_IMAGE = macOS ]]; then ./Xcode.sh; fi;
81 |
82 | build_script:
83 | - cmd: if %vsversion%==2015
84 | msbuild "C:\projects\crstl\workspace\vs2015\crstl.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
85 | - cmd: if %vsversion%==2017
86 | msbuild "C:\projects\crstl\workspace\vs2017\crstl.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
87 | - cmd: if %vsversion%==2019
88 | msbuild "C:\projects\crstl\workspace\vs2019\crstl.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
89 | - cmd: if %vsversion%==2022
90 | msbuild "C:\projects\crstl\workspace\vs2022\crstl.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
91 | - sh: if [[ $platform = "Linux64 GCC C++11" ]]; then
92 | set -e; make -C workspace/gmake all config=release_linux64_gcc_c++11; fi;
93 | if [[ $platform = "Linux64 Clang C++11" ]]; then
94 | set -e; make -C workspace/gmake all config=release_linux64_clang_c++11; fi;
95 | if [[ $platform = "Linux64 GCC C++14" ]]; then
96 | set -e; make -C workspace/gmake all config=release_linux64_gcc_c++14; fi;
97 | if [[ $platform = "Linux64 Clang C++14" ]]; then
98 | set -e; make -C workspace/gmake all config=release_linux64_clang_c++14; fi;
99 | if [[ $platform = "OSX64 Clang C++11" ]] || [[ $platform = "OSX64 Clang C++14" ]]; then
100 | pushd workspace/xcode4;
101 | set -e;
102 | xcodebuild build -project unit_tests.xcodeproj -configuration Release;
103 | popd; fi;
--------------------------------------------------------------------------------
/crstl.lua:
--------------------------------------------------------------------------------
1 | Workspace = 'workspace/'.._ACTION
2 |
3 | -- Compilers
4 |
5 | -- Windows x86/x64
6 | PlatformMSVC64 = 'MSVC 64'
7 | PlatformMSVC64Modules = 'MSVC 64 Modules'
8 | PlatformLLVM64 = 'LLVM 64'
9 | PlatformLLVM64Modules = 'LLVM 64 Modules'
10 | PlatformIntel64 = 'ICC 64'
11 | PlatformWSL64GCC = 'Linux 64 GCC'
12 | PlatformWSL64Clang = 'Linux 64 Clang'
13 |
14 | -- MacOS x86/64
15 | PlatformOSX64Cpp11 = 'OSX 64 C++11'
16 | PlatformOSX64Cpp14 = 'OSX 64 C++14'
17 |
18 | -- Linux x86/64
19 | PlatformLinux64_GCC_Cpp11 = 'Linux64_GCC_C++11'
20 | PlatformLinux64_Clang_Cpp11 = 'Linux64_Clang_C++11'
21 | PlatformLinux64_GCC_Cpp14 = 'Linux64_GCC_C++14'
22 | PlatformLinux64_Clang_Cpp14 = 'Linux64_Clang_C++14'
23 |
24 | -- NEON
25 | PlatformARM = 'MSVC ARM'
26 | PlatformARM64 = 'MSVC ARM64'
27 | PlatformAndroidARM = 'Android ARM'
28 | PlatformAndroidARM64 = 'Android ARM64'
29 |
30 | UnitTestProject = 'unit_tests'
31 | AndroidProject = 'crstl_android'
32 |
33 | isMacBuild = _ACTION == 'xcode4'
34 | isLinuxBuild = _ACTION == 'gmake'
35 | isWindowsBuild = not isMacBuild and not isLinuxBuild
36 |
37 | supportsARMBuild = _ACTION >= 'vs2017'
38 | supportsModules = _ACTION >= 'vs2022'
39 |
40 | cppDefaultDialect = 'C++11'
41 |
42 | -- Directories
43 | srcDir = 'unit_tests'
44 | includeDir = 'include'
45 | moduleDir = 'module'
46 |
47 | workspace('crstl')
48 | configurations { 'Debug', 'Release' }
49 | location (Workspace)
50 | warnings('extra')
51 |
52 | filter('toolset:msc*')
53 | flags
54 | {
55 | 'multiprocessorcompile',
56 | }
57 |
58 | filter {}
59 |
60 | includedirs
61 | {
62 | includeDir,
63 | }
64 |
65 | vectorextensions ('sse4.1')
66 | cppdialect(cppDefaultDialect)
67 | fatalwarnings { 'all' }
68 |
69 | if(isMacBuild) then
70 |
71 | platforms { PlatformOSX64Cpp11, PlatformOSX64Cpp14 }
72 | toolset('clang')
73 | architecture('x64')
74 |
75 | linkoptions { '-stdlib=libc++' }
76 |
77 | filter { 'platforms:'..PlatformOSX64Cpp11 }
78 | buildoptions { '-std=c++11' }
79 |
80 | filter { 'platforms:'..PlatformOSX64Cpp14 }
81 | buildoptions { '-std=c++14' }
82 |
83 | elseif(isLinuxBuild) then
84 |
85 | platforms { PlatformLinux64_GCC_Cpp11, PlatformLinux64_Clang_Cpp11, PlatformLinux64_GCC_Cpp14, PlatformLinux64_Clang_Cpp14 }
86 | architecture('x64')
87 |
88 | links('pthread')
89 |
90 | filter { 'platforms:'..PlatformLinux64_GCC_Cpp11 }
91 | toolset('gcc')
92 | buildoptions { '-std=c++11' }
93 |
94 | filter { 'platforms:'..PlatformLinux64_Clang_Cpp11 }
95 | toolset('clang')
96 | buildoptions { '-std=c++11' }
97 |
98 | filter { 'platforms:'..PlatformLinux64_GCC_Cpp14 }
99 | toolset('gcc')
100 | buildoptions { '-std=c++14' }
101 |
102 | filter { 'platforms:'..PlatformLinux64_Clang_Cpp14 }
103 | toolset('clang')
104 | buildoptions { '-std=c++14' }
105 |
106 | else
107 |
108 | platforms
109 | {
110 | PlatformMSVC64,
111 | PlatformLLVM64,
112 | PlatformIntel64,
113 | PlatformWSL64GCC,
114 | PlatformWSL64Clang
115 | }
116 |
117 | -- Add modules platform if environment supports them
118 | if(supportsModules) then
119 |
120 | platforms
121 | {
122 | PlatformMSVC64Modules,
123 | PlatformLLVM64Modules
124 | }
125 |
126 | end
127 |
128 | -- Add ARM platform if environment supports them
129 | if(supportsARMBuild) then
130 |
131 | platforms
132 | {
133 | PlatformARM,
134 | PlatformARM64,
135 | PlatformAndroidARM,
136 | PlatformAndroidARM64
137 | }
138 |
139 | end
140 |
141 | local llvmToolset;
142 |
143 | if (_ACTION == 'vs2015') then
144 | llvmToolset = 'msc-llvm-vs2014';
145 | elseif(_ACTION == 'vs2017') then
146 | llvmToolset = 'msc-llvm';
147 | else
148 | llvmToolset = 'msc-clangcl';
149 | end
150 |
151 | startproject(UnitTestProject)
152 |
153 | filter { 'platforms:'..PlatformMSVC64 }
154 | toolset('msc')
155 | architecture('x64')
156 | vectorextensions('sse4.1')
157 | defines { '__SSE4_1__' }
158 | buildoptions { '/permissive-' }
159 |
160 | filter { 'platforms:'..PlatformMSVC64Modules }
161 | cppdialect('C++20')
162 | toolset('msc')
163 | architecture('x64')
164 | vectorextensions('sse4.1')
165 | defines { '__SSE4_1__', 'CRSTL_USE_CPP_MODULE' }
166 | buildoptions { '/permissive-' }
167 |
168 | filter { 'platforms:'..PlatformLLVM64 }
169 | toolset(llvmToolset)
170 | architecture('x64')
171 | vectorextensions('avx')
172 | buildoptions { '-Wno-unused-variable -mavx' }
173 |
174 | filter { 'platforms:'..PlatformLLVM64Modules }
175 | cppdialect('C++20')
176 | toolset(llvmToolset)
177 | architecture('x64')
178 | vectorextensions('sse4.1')
179 | defines { '__SSE4_1__', 'CRSTL_USE_CPP_MODULE' }
180 |
181 | filter { 'platforms:'..PlatformIntel64 }
182 | toolset('msc-Intel C++ Compiler 2024')
183 | architecture('x64')
184 | vectorextensions('sse4.1')
185 | defines { '__SSE4_1__' }
186 |
187 | filter { 'platforms:'..PlatformARM }
188 | architecture('arm')
189 | vectorextensions ('neon')
190 |
191 | filter { 'platforms:'..PlatformARM64 }
192 | architecture('arm64')
193 | vectorextensions ('neon')
194 |
195 | filter { 'platforms:'..PlatformAndroidARM }
196 | system('android')
197 | architecture('arm')
198 | vectorextensions('neon')
199 | buildoptions { '-Wno-unused-variable' }
200 | linkoptions { '-lm' } -- Link against the standard math library
201 |
202 | filter { 'platforms:'..PlatformAndroidARM64 }
203 | system('android')
204 | architecture('arm64')
205 | vectorextensions('neon')
206 | buildoptions { '-Wno-unused-variable' }
207 | linkoptions { '-lm' } -- Link against the standard math library
208 |
209 | filter { 'platforms:'..PlatformWSL64GCC..' or '..PlatformWSL64Clang}
210 | system('linux')
211 | architecture('x64')
212 | toolchainversion('wsl2')
213 |
214 | -- Make sure all files are copied to the same folder, without splitting by project
215 | -- This works for both remote and WSL projects
216 | remoterootdir("~/projects/crstl")
217 | remoteprojectrelativedir("")
218 | remoteprojectdir("$(RemoteRootDir)")
219 | remotedeploydir("$(RemoteRootDir)")
220 |
221 | filter { 'platforms:'..PlatformWSL64GCC}
222 | toolset('gcc')
223 |
224 | filter { 'platforms:'..PlatformWSL64Clang}
225 | toolset('clang')
226 |
227 | filter{}
228 | end
229 |
230 | filter { 'configurations:Debug' }
231 | defines { 'DEBUG' }
232 | symbols ('full')
233 | optimize('debug')
234 |
235 | filter { 'configurations:Release' }
236 | defines { 'NDEBUG' }
237 | inlining('auto')
238 | optimize('speed')
239 |
240 | project ('crstl')
241 | kind('utility')
242 | language('c++')
243 | files
244 | {
245 | includeDir..'/**.h',
246 | includeDir..'/*.natvis'
247 | }
248 |
249 | project (UnitTestProject)
250 | kind('consoleapp')
251 |
252 | files
253 | {
254 | srcDir..'/*.cpp',
255 | srcDir..'/*.h',
256 | }
257 |
258 | -- Add module as compilation target for any platform that supports modules
259 | filter { 'platforms:'..PlatformMSVC64Modules }
260 |
261 | files
262 | {
263 | moduleDir..'/*.ixx'
264 | }
265 |
266 | filter { 'platforms:'..PlatformAndroidARM..' or '.. PlatformAndroidARM64 }
267 | kind('sharedlib')
268 | files
269 | {
270 | srcDir..'/android/android_native_app_glue.h',
271 | srcDir..'/android/android_native_app_glue.c',
272 | }
273 |
274 | filter{}
275 |
276 | includedirs
277 | {
278 | srcDir..'/**.h'
279 | }
280 |
281 | if (supportsARMBuild) then
282 |
283 | project (AndroidProject)
284 | removeplatforms('*')
285 | platforms { PlatformAndroidARM, PlatformAndroidARM64 }
286 | kind('Packaging') -- This type of project builds the apk
287 | system('android')
288 | links (UnitTestProject) -- Android needs to link to the main project which was built as a dynamic library
289 | androidapplibname(UnitTestProject)
290 | files
291 | {
292 | srcDir..'/android/AndroidManifest.xml',
293 | srcDir..'/android/build.xml',
294 | srcDir..'/android/project.properties',
295 | srcDir..'/android/res/values/strings.xml',
296 | }
297 | end
298 |
--------------------------------------------------------------------------------
/include/crstl.module:
--------------------------------------------------------------------------------
1 | import crstl;
2 |
3 | // Add macros and other utilities that modules cannot export
--------------------------------------------------------------------------------
/include/crstl/allocator.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | // crstl::allocator
8 | //
9 | // Replacement for std::allocator
10 | //
11 |
12 | crstl_module_export namespace crstl
13 | {
14 | class allocator
15 | {
16 | public:
17 |
18 | typedef size_t size_type;
19 |
20 | crstl_nodiscard void* allocate(size_type size_bytes) const crstl_noexcept
21 | {
22 | return ::operator new(size_bytes);
23 | }
24 |
25 | crstl_nodiscard void* allocate_at_least(size_type size_bytes) const crstl_noexcept
26 | {
27 | return ::operator new(size_bytes);
28 | }
29 |
30 | void deallocate(void* p, size_type /*size_bytes*/ ) const crstl_noexcept
31 | {
32 | ::operator delete(p);
33 | }
34 | };
35 | };
--------------------------------------------------------------------------------
/include/crstl/array.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | // crstl::array
8 | //
9 | // Replacement for std::array
10 | //
11 |
12 | crstl_module_export namespace crstl
13 | {
14 | template
15 | class array
16 | {
17 | public:
18 |
19 | typedef array this_type;
20 | typedef T& reference;
21 | typedef const T& const_reference;
22 | typedef T* pointer;
23 | typedef const T* const_pointer;
24 | typedef T* iterator;
25 | typedef const T* const_iterator;
26 | typedef uint32_t length_type;
27 |
28 | crstl_constexpr14 iterator begin() { return m_data; }
29 | crstl_constexpr const_iterator begin() const { return m_data; }
30 | crstl_constexpr const_iterator cbegin() const { return m_data; }
31 |
32 | crstl_constexpr14 iterator end() { return m_data + N; }
33 | crstl_constexpr const_iterator end() const { return m_data + N; }
34 | crstl_constexpr const_iterator cend() const { return m_data + N; }
35 |
36 | crstl_nodiscard
37 | crstl_constexpr bool empty() const { return N == 0; }
38 |
39 | crstl_constexpr size_t size() const { return N; }
40 | crstl_constexpr size_t max_size() const { return N; }
41 |
42 | crstl_constexpr14 T* data() { return m_data; }
43 | crstl_constexpr const T* data() const { return m_data; }
44 |
45 | crstl_constexpr14 T& operator[](size_t i) { return crstl_assert(i < N), m_data[i]; }
46 | crstl_constexpr const T& operator[](size_t i) const { return crstl_assert(i < N), m_data[i]; }
47 |
48 | crstl_constexpr14 T& at(size_t i) { return crstl_assert(i < N), m_data[i]; }
49 | crstl_constexpr const T& at(size_t i) const { return crstl_assert(i < N), m_data[i]; }
50 |
51 | // Public to allow braced initialization
52 | T m_data[N ? N : 1];
53 | };
54 | }
--------------------------------------------------------------------------------
/include/crstl/atomic.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | #if defined(CRSTL_OS_WINDOWS)
8 | #include "crstl/platform/atomic_win32.h"
9 | #elif defined(CRSTL_ARCH_ARM)
10 | #include "crstl/platform/atomic_arm.h"
11 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_OSX)
12 | #include "crstl/platform/atomic_clang_gcc.h"
13 | #endif
14 |
15 | // crstl::atomic
16 | //
17 | // Replacement for std::atomic
18 | //
19 |
20 | crstl_module_export namespace crstl
21 | {
22 | // atomic_store: store value to variable pointed by target, and return its previous value
23 |
24 | inline int8_t atomic_store(int8_t volatile* target, int8_t value) crstl_noexcept
25 | {
26 | return (int8_t)crstl_atomic_store8(target, value);
27 | }
28 |
29 | inline int16_t atomic_store(int16_t volatile* target, int16_t value) crstl_noexcept
30 | {
31 | return (int16_t)crstl_atomic_store16(target, value);
32 | }
33 |
34 | inline int32_t atomic_store(int32_t volatile* target, int32_t value) crstl_noexcept
35 | {
36 | return (int32_t)crstl_atomic_store32(target, value);
37 | }
38 |
39 | inline int64_t atomic_store(int64_t volatile* target, int64_t value) crstl_noexcept
40 | {
41 | return (int64_t)crstl_atomic_store64(target, value);
42 | }
43 |
44 | // atomic_add: add value to variable pointed by target, and return the previous value of target
45 |
46 | inline int8_t atomic_add(int8_t volatile* target, int8_t value) crstl_noexcept
47 | {
48 | return (int8_t)crstl_atomic_add8(target, value);
49 | }
50 |
51 | inline int16_t atomic_add(int16_t volatile* target, int16_t value) crstl_noexcept
52 | {
53 | return (int16_t)crstl_atomic_add16(target, value);
54 | }
55 |
56 | inline int32_t atomic_add(int32_t volatile* target, int32_t value) crstl_noexcept
57 | {
58 | return (int32_t)crstl_atomic_add32(target, value);
59 | }
60 |
61 | inline int64_t atomic_add(int64_t volatile* target, int64_t value) crstl_noexcept
62 | {
63 | return (int64_t)crstl_atomic_add64(target, value);
64 | }
65 |
66 | // atomic_sub: subtract value from variable pointed by target, and return the previous value of target
67 |
68 | inline int8_t atomic_sub(int8_t volatile* target, int8_t value) crstl_noexcept
69 | {
70 | return (int8_t)crstl_atomic_sub8(target, value);
71 | }
72 |
73 | inline int16_t atomic_sub(int16_t volatile* target, int16_t value) crstl_noexcept
74 | {
75 | return (int16_t)crstl_atomic_sub16(target, value);
76 | }
77 |
78 | inline int32_t atomic_sub(int32_t volatile* target, int32_t value) crstl_noexcept
79 | {
80 | return (int32_t)crstl_atomic_sub32(target, value);
81 | }
82 |
83 | inline int64_t atomic_sub(int64_t volatile* target, int64_t value) crstl_noexcept
84 | {
85 | return (int64_t)crstl_atomic_sub64(target, value);
86 | }
87 |
88 | // atomic_and: AND value with variable pointed by target, and return the previous value of target
89 |
90 | inline int8_t atomic_and(int8_t volatile* target, int8_t value) crstl_noexcept
91 | {
92 | return (int8_t)crstl_atomic_and8(target, value);
93 | }
94 |
95 | inline int16_t atomic_and(int16_t volatile* target, int16_t value) crstl_noexcept
96 | {
97 | return (int16_t)crstl_atomic_and16(target, value);
98 | }
99 |
100 | inline int32_t atomic_and(int32_t volatile* target, int32_t value) crstl_noexcept
101 | {
102 | return (int32_t)crstl_atomic_and32(target, value);
103 | }
104 |
105 | inline int64_t atomic_and(int64_t volatile* target, int64_t value) crstl_noexcept
106 | {
107 | return (int64_t)crstl_atomic_and64(target, value);
108 | }
109 |
110 | // atomic_or: OR value with variable pointed by target, and return the previous value of target
111 |
112 | inline int8_t atomic_or(int8_t volatile* target, int8_t value) crstl_noexcept
113 | {
114 | return (int8_t)crstl_atomic_or8(target, value);
115 | }
116 |
117 | inline int16_t atomic_or(int16_t volatile* target, int16_t value) crstl_noexcept
118 | {
119 | return (int16_t)crstl_atomic_or16(target, value);
120 | }
121 |
122 | inline int32_t atomic_or(int32_t volatile* target, int32_t value) crstl_noexcept
123 | {
124 | return (int32_t)crstl_atomic_or32(target, value);
125 | }
126 |
127 | inline int64_t atomic_or(int64_t volatile* target, int64_t value) crstl_noexcept
128 | {
129 | return (int64_t)crstl_atomic_or64(target, value);
130 | }
131 |
132 | // atomic_xor: XOR value with variable pointed by target, and return the previous value of target
133 |
134 | inline int8_t atomic_xor(int8_t volatile* target, int8_t value) crstl_noexcept
135 | {
136 | return (int8_t)crstl_atomic_xor8(target, value);
137 | }
138 |
139 | inline int16_t atomic_xor(int16_t volatile* target, int16_t value) crstl_noexcept
140 | {
141 | return (int16_t)crstl_atomic_xor16(target, value);
142 | }
143 |
144 | inline int32_t atomic_xor(int32_t volatile* target, int32_t value) crstl_noexcept
145 | {
146 | return (int32_t)crstl_atomic_xor32(target, value);
147 | }
148 |
149 | inline int64_t atomic_xor(int64_t volatile* target, int64_t value) crstl_noexcept
150 | {
151 | return (int64_t)crstl_atomic_xor64(target, value);
152 | }
153 |
154 | template struct atomic_type {};
155 |
156 | template<> struct atomic_type<1> { typedef int8_t type; };
157 | template<> struct atomic_type<2> { typedef int16_t type; };
158 | template<> struct atomic_type<4> { typedef int32_t type; };
159 | template<> struct atomic_type<8> { typedef int64_t type; };
160 |
161 | template
162 | struct atomic
163 | {
164 | public:
165 |
166 | typedef T value_type;
167 |
168 | typedef typename atomic_type::type operation_type;
169 |
170 | atomic() : m_value(0) {}
171 |
172 | atomic(const value_type& value)
173 | {
174 | atomic_store((operation_type*)&m_value, value);
175 | }
176 |
177 | atomic& operator += (const value_type& value)
178 | {
179 | atomic_add((operation_type*)&m_value, value); return *this;
180 | }
181 |
182 | atomic& operator -= (const value_type& value)
183 | {
184 | atomic_sub((operation_type*)&m_value, value); return *this;
185 | }
186 |
187 | atomic& operator &= (const value_type& value)
188 | {
189 | atomic_and((operation_type*)&m_value, value); return *this;
190 | }
191 |
192 | atomic& operator |= (const value_type& value)
193 | {
194 | atomic_or((operation_type*)&m_value, value); return *this;
195 | }
196 |
197 | atomic& operator ^= (const value_type& value)
198 | {
199 | atomic_xor((operation_type*)& m_value, value); return *this;
200 | }
201 |
202 | // Pre-increment
203 | value_type operator ++ ()
204 | {
205 | return (value_type)atomic_add((operation_type*)&m_value, operation_type(1)) + 1;
206 | }
207 |
208 | // Post-increment
209 | value_type operator ++ (int)
210 | {
211 | return (value_type)atomic_add((operation_type*)&m_value, 1);
212 | }
213 |
214 | // Pre-decrement
215 | value_type operator -- ()
216 | {
217 | return (value_type)atomic_sub((operation_type*)&m_value, 1) - 1;
218 | }
219 |
220 | // Post-decrement
221 | value_type operator -- (int)
222 | {
223 | return (value_type)atomic_sub((operation_type*)&m_value, 1);
224 | }
225 |
226 | operator value_type() const
227 | {
228 | return m_value;
229 | }
230 |
231 | private:
232 |
233 | value_type m_value;
234 | };
235 | };
236 |
--------------------------------------------------------------------------------
/include/crstl/compressed_pair.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/type_builtins.h"
6 |
7 | // crstl::compressed_pair
8 | //
9 | // Implements empty base class optimization for a pair of types
10 |
11 | crstl_module_export namespace crstl
12 | {
13 | template
14 | struct compressed_pair_base {};
15 |
16 | template
17 | struct compressed_pair_base : private T1
18 | {
19 | T1& first() { return *this; }
20 | const T1& first() const { return *this; }
21 | T2& second() { return m_second; }
22 | const T2& second() const { return m_second; }
23 |
24 | T2 m_second;
25 | };
26 |
27 | template
28 | struct compressed_pair_base : private T2
29 | {
30 | T1& first() { return m_first; }
31 | const T1& first() const { return m_first; }
32 | T2& second() { return *this; }
33 | const T2& second() const { return *this; }
34 |
35 | T1 m_first;
36 | };
37 |
38 | template
39 | struct compressed_pair_base
40 | {
41 | T1& first() { return m_first; }
42 | const T1& first() const { return m_first; }
43 | T2& second() { return m_second; }
44 | const T2& second() const { return m_second; }
45 |
46 | T1 m_first;
47 | T2 m_second;
48 | };
49 |
50 | template
51 | struct compressed_pair_base : private T1, private T2
52 | {
53 | T1& first() { return *this; }
54 | const T1& first() const { return *this; }
55 | T2& second() { return *this; }
56 | const T2& second() const { return *this; }
57 | };
58 |
59 | template
60 | struct compressed_pair : compressed_pair_base
61 | {
62 | typedef compressed_pair_base base;
63 |
64 | T1& first() { return base::first(); }
65 | const T1& first() const { return base::first(); }
66 | T2& second() { return base::second(); }
67 | const T2& second() const { return base::second(); }
68 | };
69 | };
--------------------------------------------------------------------------------
/include/crstl/config_fwd.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #if defined(CRSTL_COMPILER_MSVC)
6 | #define crstl_dllimport __declspec(dllimport)
7 |
8 | #if CRSTL_MSVC_VERSION <= CRSTL_MSVC_2015
9 | #define crstl_2015_dllimport __declspec(dllimport)
10 | #endif
11 | #else
12 | #define crstl_dllimport
13 | #endif
14 |
15 | #if !defined(crstl_dllimport)
16 | #define crstl_dllimport
17 | #endif
18 |
19 | #if !defined(crstl_2015_dllimport)
20 | #define crstl_2015_dllimport
21 | #endif
22 |
23 | #if defined(CRSTL_OS_LINUX)
24 | #define crstl_linux_wthrow throw()
25 | #else
26 | #define crstl_linux_wthrow
27 | #endif
--------------------------------------------------------------------------------
/include/crstl/critical_section.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | // crstl::critical_section
8 | //
9 | // Implementation of critical section
10 | //
11 | // enter: request ownership of the critical section
12 | // try_enter: attempt to enter critical section without blocking
13 | // leave: release the critical section
14 |
15 | crstl_module_export namespace crstl
16 | {
17 | class critical_section_base
18 | {
19 | public:
20 |
21 | critical_section_base() {}
22 |
23 | private:
24 |
25 | critical_section_base(const critical_section_base& other) crstl_constructor_delete;
26 |
27 | critical_section_base& operator = (const critical_section_base& other) crstl_constructor_delete;
28 | };
29 | };
30 |
31 | #if defined(CRSTL_OS_WINDOWS)
32 | #include "crstl/platform/critical_section_win32.h"
33 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_ANDROID) || defined(CRSTL_OS_OSX)
34 | #include "crstl/platform/critical_section_posix.h"
35 | #endif
36 |
37 | crstl_module_export namespace crstl
38 | {
39 | class scoped_critical_section_lock
40 | {
41 | public:
42 |
43 | crstl_nodiscard
44 | scoped_critical_section_lock(critical_section& cs)
45 | : m_critical_section(cs)
46 | {
47 | m_critical_section.lock();
48 | }
49 |
50 | ~scoped_critical_section_lock()
51 | {
52 | m_critical_section.unlock();
53 | }
54 |
55 | private:
56 |
57 | critical_section& m_critical_section;
58 | };
59 | };
--------------------------------------------------------------------------------
/include/crstl/crstldef.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/type_builtins.h"
6 |
7 | // These headers are so basic that essentially every C++ header ends up including them in one form or the other
8 | // We can make our own and they can work but it can cause conflicts in certain scenarios and it's probably better
9 | // for each platform to take care of them
10 | #if defined(CRSTL_MODULE_DECLARATION)
11 | import ;
12 | import ;
13 | #else
14 | #include
15 | #include
16 | #endif
17 |
18 | // Helps with some compilers that don't define e.g. nullptr_t in the global namespace. For classes inside the crstl
19 | // namespace, these declarations help out and they aren't a big issue
20 | namespace crstl
21 | {
22 | typedef decltype(nullptr) nullptr_t;
23 | typedef decltype(sizeof(1)) size_t;
24 | };
25 |
26 | #if !defined(CRSTL_CHAR8_TYPE)
27 |
28 | crstl_warning_char8_t_keyword_begin
29 | typedef unsigned char char8_t;
30 | crstl_warning_char8_t_keyword_end
31 |
32 | #endif
--------------------------------------------------------------------------------
/include/crstl/debugging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #if defined(CRSTL_COMPILER_MSVC)
6 | #include "crstl/platform/common_win32.h"
7 |
8 | extern "C"
9 | {
10 | void __debugbreak();
11 |
12 | __declspec(dllimport) BOOL IsDebuggerPresent();
13 | };
14 | #elif defined(CRSTL_OS_OSX)
15 | #include
16 | #include
17 | #include
18 | #endif
19 |
20 | #if defined(CRSTL_COMPILER_MSVC)
21 | #define crstl_breakpoint() __debugbreak()
22 | #elif crstl_has_builtin(__builtin_debugtrap) // Mostly the Clang compiler
23 | #define crstl_breakpoint() __builtin_debugtrap()
24 | #elif defined(CRSTL_ARCH_X86)
25 | #define crstl_breakpoint() __asm__ volatile("int $0x03")
26 | #elif defined(CRSTL_ARCH_ARM32)
27 | #define crstl_breakpoint() __asm__ volatile("udf #0xfe")
28 | #elif defined(CRSTL_ARCH_ARM64)
29 | #define crstl_breakpoint() __asm__ volatile("brk #0xf000")
30 | #elif defined(CRSTL_ARCH_POWERPC)
31 | #define crstl_breakpoint() __asm__ volatile(".4byte 0x7d821008")
32 | #else
33 | #error not implemented
34 | #endif
35 |
36 | crstl_module_export namespace crstl
37 | {
38 | inline void breakpoint() crstl_noexcept
39 | {
40 | crstl_breakpoint();
41 | }
42 |
43 | inline bool is_debugger_present() crstl_noexcept
44 | {
45 | #if defined(CRSTL_COMPILER_MSVC)
46 | return IsDebuggerPresent();
47 | #elif defined(CRSTL_OS_OSX)
48 | // https://developer.apple.com/library/archive/qa/qa1361/_index.html
49 |
50 | // Initialize the flags so that, if sysctl fails for some reason, we get a predictable result
51 | struct kinfo_proc info;
52 | info.kp_proc.p_flag = 0;
53 |
54 | // Initialize mib, which tells sysctl the info we want, in this case we're looking for information about a specific process ID
55 | int mib[4];
56 | mib[0] = CTL_KERN;
57 | mib[1] = KERN_PROC;
58 | mib[2] = KERN_PROC_PID;
59 | mib[3] = getpid();
60 |
61 | size_t size = sizeof(info);
62 | int sysctl_return = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
63 | crstl_assert(sysctl_return == 0);
64 |
65 | // We're in the debugger if the P_TRACED flag is set
66 | return ((info.kp_proc.p_flag & P_TRACED) != 0);
67 | #else
68 | return false;
69 | #endif
70 | }
71 |
72 | inline void breakpoint_if_debugger()
73 | {
74 | if (is_debugger_present())
75 | {
76 | breakpoint();
77 | }
78 | }
79 | };
--------------------------------------------------------------------------------
/include/crstl/filesystem.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 | #include "crstl/crstldef.h"
5 | #include "crstl/path.h"
6 | #include "crstl/fixed_path.h"
7 | #include "crstl/move_forward.h"
8 |
9 | crstl_module_export namespace crstl
10 | {
11 | // Set a reasonable maximum across all possible OSs
12 | static const uint32_t kMaxPathLength = 1024;
13 |
14 | namespace file_flags
15 | {
16 | enum t : uint32_t
17 | {
18 | none = 0,
19 | read = 1 << 0, // Read from the file
20 | write = 1 << 1, // Write to the file
21 | append = 1 << 2, // Append to the file
22 | create = 1 << 3, // Create file if it does not exist
23 | force_create = 1 << 4, // Always create file, overwriting previous contents
24 | };
25 |
26 | inline file_flags::t operator | (file_flags::t a, file_flags::t b)
27 | {
28 | return static_cast((uint32_t)a | (uint32_t)b);
29 | }
30 | };
31 |
32 | namespace file_copy_options
33 | {
34 | enum t
35 | {
36 | none = 0 << 0,
37 | overwrite = 1 << 0
38 | };
39 | };
40 |
41 | enum class file_seek_origin
42 | {
43 | begin,
44 | current,
45 | end,
46 | };
47 |
48 | enum class filesystem_result
49 | {
50 | success,
51 | error,
52 | error_not_found,
53 | error_access_denied
54 | };
55 |
56 | struct directory_entry
57 | {
58 | const char* directory;
59 | const char* filename;
60 | bool is_directory;
61 | };
62 |
63 | class file_base
64 | {
65 | public:
66 |
67 | const path& get_path() const
68 | {
69 | return m_path;
70 | }
71 |
72 | file_flags::t get_flags() const
73 | {
74 | return m_flags;
75 | }
76 |
77 | protected:
78 |
79 | // Don't try to instantiate a file_base directly
80 |
81 | file_base() : m_flags(file_flags::none) {}
82 |
83 | file_base(file_flags::t flags) : m_flags(flags) {}
84 |
85 | file_base(file_base&& other) crstl_noexcept
86 | {
87 | m_path = crstl_move(other.m_path);
88 |
89 | m_flags = other.m_flags;
90 | other.m_flags = file_flags::none;
91 | }
92 |
93 | file_base& operator = (file_base&& other) crstl_noexcept
94 | {
95 | m_path = crstl_move(other.m_path);
96 |
97 | m_flags = other.m_flags;
98 | other.m_flags = file_flags::none;
99 |
100 | return *this;
101 | }
102 |
103 | file_flags::t m_flags;
104 |
105 | path m_path;
106 | };
107 | };
108 |
109 | #if defined(CRSTL_OS_WINDOWS)
110 | #include "crstl/platform/filesystem_win32.h"
111 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_ANDROID) || defined(CRSTL_OS_OSX)
112 | #include "crstl/platform/filesystem_posix.h"
113 | #endif
114 |
115 | namespace crstl
116 | {
117 | // These only get initialized once
118 |
119 | template
120 | struct filesystem_globals
121 | {
122 | // Path to a directory suitable for temporary files
123 | static const path temp_path;
124 |
125 | // Path to the executable, including filename
126 | static const path executable_path;
127 | };
128 |
129 | template
130 | const path filesystem_globals::temp_path = detail::compute_temp_path();
131 |
132 | // https://stackoverflow.com/questions/1528298/get-path-of-executable
133 | // https://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
134 | template
135 | const path filesystem_globals::executable_path = detail::compute_executable_path();
136 | }
137 |
138 | crstl_module_export namespace crstl
139 | {
140 | inline const path& temp_directory_path()
141 | {
142 | return filesystem_globals::temp_path;
143 | }
144 |
145 | inline const path& executable_path()
146 | {
147 | return filesystem_globals::executable_path;
148 | }
149 |
150 | // Create directory, including the tree that leads up to it
151 | // If folder was created successfully or folder already exists, return true
152 | inline bool create_directories(const char* directory_path)
153 | {
154 | if (!directory_path)
155 | {
156 | return false;
157 | }
158 |
159 | filesystem_result result = create_directory(directory_path);
160 |
161 | if (result == filesystem_result::success)
162 | {
163 | return true;
164 | }
165 | else
166 | {
167 | // We guarantee path separators are only ever represented with forward slashes in the path class
168 | const char* Separator = "/";
169 |
170 | // Start working our way backwards. Chances are most of the path exists
171 | crstl::path temp_path = directory_path;
172 | size_t separator_pos = temp_path.find_last_of(Separator);
173 |
174 | // Skip trailing slash
175 | if (separator_pos == temp_path.length() - 1)
176 | {
177 | separator_pos = temp_path.find_last_of(Separator, separator_pos - 1);
178 | }
179 |
180 | while (separator_pos != crstl::path::npos)
181 | {
182 | temp_path[separator_pos] = 0;
183 | bool subdirectory_exists = exists(temp_path.c_str());
184 | temp_path[separator_pos] = '/';
185 |
186 | // If the subdirectory exists, we now need to move forward creating the rest
187 | if (subdirectory_exists)
188 | {
189 | separator_pos = temp_path.find_first_of(Separator, separator_pos + 1);
190 | break;
191 | }
192 | else
193 | {
194 | separator_pos = temp_path.find_last_of(Separator, separator_pos - 1);
195 | }
196 | }
197 |
198 | bool all_paths_created = false;
199 |
200 | // All the paths from here should be correctly created. If any one path fails,
201 | // we need to assume there is an error
202 | while (separator_pos != crstl::path::npos)
203 | {
204 | temp_path[separator_pos] = 0;
205 | filesystem_result subdirectory_created = create_directory(temp_path.c_str());
206 | temp_path[separator_pos] = '/';
207 |
208 | if (subdirectory_created == filesystem_result::success)
209 | {
210 | separator_pos = temp_path.find_first_of(Separator, separator_pos + 1);
211 | all_paths_created |= true;
212 | }
213 | else
214 | {
215 | all_paths_created = false;
216 | break;
217 | }
218 | }
219 |
220 | return all_paths_created;
221 | }
222 | }
223 | }
--------------------------------------------------------------------------------
/include/crstl/fixed_function.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/function_common.h"
4 |
5 | // crstl::fixed_function
6 | //
7 | // Fixed replacement for std::function
8 | //
9 | // - The user supplies a size to the template object, which creates storage
10 | // - The storage is used to locally copy or instantiate functors
11 | // - fixed_function will static assert if storage is not enough to hold functor
12 |
13 | crstl_module_export namespace crstl
14 | {
15 | template
16 | class fixed_function;
17 |
18 | template struct is_not_fixed_function { typedef const char* type; };
19 |
20 | template
21 | struct is_not_fixed_function> {};
22 |
23 | template
24 | class fixed_function
25 | {
26 | public:
27 |
28 | template
29 | friend class fixed_function;
30 |
31 | typedef Result result_type;
32 |
33 | fixed_function() crstl_noexcept : m_invoker(nullptr), m_manager(nullptr) {}
34 |
35 | template
36 | fixed_function
37 | (
38 | FunctorT&& functor,
39 | // Do not accept as Functor a fixed_function, we'll deal with it in a specialized copy/move constructor but due to overload
40 | // resolution rules, this one takes precedence. Remove reference to be able to compare the underlying type
41 | typename crstl::is_not_fixed_function::type>::type = nullptr
42 | ) crstl_noexcept
43 | {
44 | if (!handler::empty_function(functor))
45 | {
46 | handler::template create(m_functor_storage, crstl_forward(FunctorT, functor));
47 | m_invoker = &handler::invoke;
48 | m_manager = &handler::manage;
49 | }
50 | else
51 | {
52 | m_invoker = nullptr;
53 | m_manager = nullptr;
54 | }
55 | }
56 |
57 | fixed_function(fixed_function&& other) crstl_noexcept
58 | {
59 | m_functor_storage = other.m_functor_storage;
60 | m_invoker = other.m_invoker;
61 | m_manager = other.m_manager;
62 | other.m_invoker = nullptr;
63 | other.m_manager = nullptr;
64 | }
65 |
66 | fixed_function(const fixed_function& other) crstl_noexcept
67 | {
68 | copy(other);
69 | }
70 |
71 | template
72 | fixed_function(const fixed_function& other) crstl_noexcept
73 | {
74 | static_assert(NewSizeBytes <= SizeBytes, "Not enough size to hold new function");
75 |
76 | if (other.m_manager)
77 | {
78 | other.m_manager(&m_functor_storage, &other.m_functor_storage, manager_operation::copy);
79 | }
80 |
81 | m_manager = other.m_manager;
82 | m_invoker = other.m_invoker;
83 | }
84 |
85 | ~fixed_function() crstl_noexcept
86 | {
87 | if (m_manager)
88 | {
89 | m_manager(&m_functor_storage, &m_functor_storage, manager_operation::destroy);
90 | }
91 | }
92 |
93 | fixed_function& operator = (fixed_function&& other)
94 | {
95 | crstl_assert(this != &other);
96 |
97 | m_functor_storage = other.m_functor_storage;
98 | m_invoker = other.m_invoker;
99 | m_manager = other.m_manager;
100 | other.m_invoker = nullptr;
101 | other.m_manager = nullptr;
102 |
103 | return *this;
104 | }
105 |
106 | fixed_function& operator = (const fixed_function& other)
107 | {
108 | crstl_assert(this != &other);
109 |
110 | copy(other);
111 |
112 | return *this;
113 | }
114 |
115 | Result operator()(Args... args) const crstl_noexcept
116 | {
117 | crstl_assert(m_invoker != nullptr);
118 | return m_invoker(&m_functor_storage, crstl_forward(Args, args)...);
119 | }
120 |
121 | explicit operator bool() const crstl_noexcept
122 | {
123 | return m_invoker != nullptr;
124 | }
125 |
126 | private:
127 |
128 | template
129 | using handler = functor_handler;
130 |
131 | // The actual signature is void* because we need to be able to copy function pointers between fixed_functions that have different
132 | // buffer sizes, which unfortunately makes this a bit harder to read than necessary. We cannot just cast on use because we copy the
133 | // function pointer over when copy-assigning
134 |
135 | // The invoker_type is a function pointer that returns Result and takes functor_storage plus a variable number of arguments
136 | using invoker_type = Result(*)(const void*, Args&&...);
137 |
138 | // The manager_type is a function pointer that returns void and takes a destination (non-const) and source (const) functor_storage
139 | using manager_type = void(*)(void*, const void*, manager_operation::t);
140 |
141 | void copy(const fixed_function& other) crstl_noexcept
142 | {
143 | if (other.m_manager)
144 | {
145 | other.m_manager(&m_functor_storage, &other.m_functor_storage, manager_operation::copy);
146 | }
147 |
148 | m_manager = other.m_manager;
149 | m_invoker = other.m_invoker;
150 | }
151 |
152 | // The invoker is a pointer to a function that reinterprets the data we pass in and calls the function
153 | // It returns the same value as the fixed_function
154 | invoker_type m_invoker;
155 |
156 | // The manager can perform operations such as copy, move, create and destroy by reinterpreting the data
157 | manager_type m_manager;
158 |
159 | // Storage for the function object
160 | functor_storage m_functor_storage;
161 | };
162 | };
--------------------------------------------------------------------------------
/include/crstl/fixed_open_hashmap.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/open_hashtable_base.h"
4 |
5 | #include "crstl/forward_declarations.h"
6 |
7 | #if defined(CRSTL_MODULE_DECLARATION)
8 | import ;
9 | #elif defined(CRSTL_FEATURE_INITIALIZER_LISTS)
10 | #include
11 | #endif
12 |
13 | crstl_module_export namespace crstl
14 | {
15 | template
16 | class fixed_open_hashtable_storage
17 | {
18 | public:
19 |
20 | typedef Key key_type;
21 | typedef T value_type;
22 | typedef size_t size_type;
23 | typedef Hasher hasher;
24 | typedef open_iterator iterator;
25 | typedef open_iterator const_iterator;
26 | typedef open_node node_type;
27 | typedef decltype(open_node::key_value) key_value_type;
28 |
29 | fixed_open_hashtable_storage() : m_length(0) {}
30 |
31 | ~fixed_open_hashtable_storage() {}
32 |
33 | template
34 | crstl_constexpr14 void reallocate_rehash_if_length_above_load_factor(HashmapType)
35 | {
36 | crstl_assert(m_length <= NodeCount);
37 | }
38 |
39 | template
40 | crstl_constexpr14 void reallocate_rehash_if_length_above_capacity(size_t capacity, HashmapType)
41 | {
42 | crstl_unused(capacity);
43 | crstl_assert(capacity <= NodeCount);
44 | }
45 |
46 | crstl_constexpr14 size_t compute_bucket(size_t hash_value) const
47 | {
48 | return crstl::compute_bucket(hash_value);
49 | }
50 |
51 | crstl_constexpr14 size_t get_bucket_count() const { return NodeCount; }
52 |
53 | protected:
54 |
55 | crstl_warning_anonymous_struct_union_begin
56 | union
57 | {
58 | struct { node_type m_data[NodeCount]; };
59 | };
60 | crstl_warning_anonymous_struct_union_end
61 |
62 | size_t m_length;
63 | };
64 |
65 | // It is recommended to use a power of 2 for buckets as it is faster to find
66 | template
67 | class fixed_open_hashtable : public open_hashtable_base, IsMultipleValue>
68 | {
69 | public:
70 |
71 | static_assert(NodeCount >= 1, "Must have at least one node");
72 |
73 | typedef open_hashtable_base> base_type;
74 |
75 | typedef typename base_type::key_type key_type;
76 | typedef typename base_type::value_type value_type;
77 | typedef typename base_type::key_value_type key_value_type;
78 | typedef typename base_type::size_type size_type;
79 | typedef typename base_type::iterator iterator;
80 | typedef typename base_type::const_iterator const_iterator;
81 | typedef typename base_type::node_type node_type;
82 |
83 | using base_type::clear;
84 |
85 | crstl_constexpr14 fixed_open_hashtable() crstl_noexcept : base_type()
86 | {
87 | for (size_t i = 0; i < NodeCount; ++i)
88 | {
89 | m_data[i].set_empty();
90 | }
91 | }
92 |
93 | crstl_constexpr14 fixed_open_hashtable(const fixed_open_hashtable& other) crstl_noexcept : fixed_open_hashtable()
94 | {
95 | for (const key_value_type& iter : other)
96 | {
97 | insert_empty_impl(iter);
98 | }
99 | }
100 |
101 | #if defined(CRSTL_FEATURE_INITIALIZER_LISTS)
102 |
103 | crstl_constexpr14 fixed_open_hashtable(std::initializer_list ilist) crstl_noexcept : fixed_open_hashtable()
104 | {
105 | crstl_assert(ilist.size() <= NodeCount);
106 |
107 | for (const key_value_type& iter : ilist)
108 | {
109 | insert_empty_impl(iter);
110 | }
111 | }
112 |
113 | #endif
114 |
115 | ~fixed_open_hashtable() crstl_noexcept
116 | {
117 | // Only destroy the value, no need to destroy buckets or nodes
118 | crstl_constexpr_if(!crstl_is_trivially_destructible(key_value_type))
119 | {
120 | for (const key_value_type& iter : *this)
121 | {
122 | iter.~key_value_type();
123 | }
124 | }
125 | }
126 |
127 | crstl_constexpr14 fixed_open_hashtable& operator = (const fixed_open_hashtable& other)
128 | {
129 | clear();
130 |
131 | for (const key_value_type& iter : other)
132 | {
133 | insert_empty_impl(iter);
134 | }
135 |
136 | return *this;
137 | }
138 |
139 | private:
140 |
141 | using base_type::insert_empty_impl;
142 |
143 | using base_type::m_data;
144 | using base_type::m_length;
145 | };
146 |
147 | template
148 | class fixed_open_hashmap : public fixed_open_hashtable
149 | {
150 | using fixed_open_hashtable::fixed_open_hashtable;
151 | };
152 |
153 | template
154 | class fixed_open_hashset : public fixed_open_hashtable
155 | {
156 | using fixed_open_hashtable::fixed_open_hashtable;
157 |
158 | private:
159 |
160 | using fixed_open_hashtable::for_each;
161 | };
162 |
163 | template
164 | class fixed_open_multi_hashmap : public fixed_open_hashtable
165 | {
166 | using fixed_open_hashtable::fixed_open_hashtable;
167 | };
168 |
169 | template
170 | class fixed_open_multi_hashset : public fixed_open_hashtable
171 | {
172 | using fixed_open_hashtable::fixed_open_hashtable;
173 |
174 | private:
175 |
176 | using fixed_open_hashtable::for_each;
177 | };
178 | };
--------------------------------------------------------------------------------
/include/crstl/fixed_path.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/utility/path_common.h"
4 |
5 | #include "crstl/fixed_string.h"
6 |
7 | crstl_module_export namespace crstl
8 | {
9 | typedef path_base fixed_path16;
10 | typedef path_base fixed_path32;
11 | typedef path_base fixed_path64;
12 | typedef path_base fixed_path128;
13 | typedef path_base fixed_path256;
14 | typedef path_base fixed_path512;
15 | };
--------------------------------------------------------------------------------
/include/crstl/forward_declarations.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // Forward declarations for all types
4 | //
5 | // Use in your header files to gain access to all the declarations without worrying about the
6 | // details of declaring them. Forward declarations have a particularity, that they define the
7 | // default arguments of a type. The reason for this is that template defaults can only be
8 | // defined once, and we want to be able to also benefit from these in the forward declarations
9 | // as if we had included the file. The crstl files therefore have to include this file too and
10 | // the details are hidden away here. It's a minor inconvenience for a good benefit
11 |
12 | namespace crstl
13 | {
14 | // allocator.h
15 | class allocator;
16 |
17 | // hash.h
18 | template struct hash;
19 |
20 | // array.h
21 | template class array;
22 |
23 | // bitset.h
24 | template class bitset;
25 |
26 | // deque.h
27 | template class deque;
28 |
29 | // filesystem.h
30 | class file;
31 |
32 | // fixed_deque.h
33 | template class fixed_deque;
34 |
35 | // fixed_function.h
36 | template class fixed_function;
37 |
38 | // fixed_open_hashmap.h
39 | template> class fixed_open_hashmap;
40 | template> class fixed_open_hashset;
41 | template> class fixed_open_multi_hashmap;
42 | template> class fixed_open_multi_hashset;
43 |
44 | // fixed_string.h
45 | template class basic_fixed_string;
46 |
47 | typedef basic_fixed_string fixed_string8;
48 | typedef basic_fixed_string fixed_string16;
49 | typedef basic_fixed_string fixed_string32;
50 | typedef basic_fixed_string fixed_string64;
51 | typedef basic_fixed_string fixed_string128;
52 | typedef basic_fixed_string fixed_string256;
53 | typedef basic_fixed_string fixed_string512;
54 | typedef basic_fixed_string fixed_string1024;
55 | typedef basic_fixed_string fixed_string2048;
56 |
57 | typedef basic_fixed_string fixed_wstring8;
58 | typedef basic_fixed_string fixed_wstring16;
59 | typedef basic_fixed_string fixed_wstring32;
60 | typedef basic_fixed_string fixed_wstring64;
61 | typedef basic_fixed_string fixed_wstring128;
62 | typedef basic_fixed_string fixed_wstring256;
63 | typedef basic_fixed_string fixed_wstring512;
64 | typedef basic_fixed_string fixed_wstring1024;
65 | typedef basic_fixed_string fixed_wstring2048;
66 |
67 | // fixed_vector.h
68 | template class fixed_vector;
69 |
70 | // intrusive_ptr.h
71 | template class intrusive_ptr;
72 |
73 | // open_hashmap.h
74 | template, typename Allocator = crstl::allocator> class open_hashmap;
75 | template, typename Allocator = crstl::allocator> class open_hashset;
76 | template, typename Allocator = crstl::allocator> class open_multi_hashmap;
77 | template, typename Allocator = crstl::allocator> class open_multi_hashset;
78 |
79 | // pair.h
80 | template class pair;
81 |
82 | // process.h
83 | class process;
84 |
85 | // span.h
86 | static const size_t dynamic_extent = size_t(-1);
87 | template class span;
88 |
89 | // stack_vector.h
90 | template class stack_vector;
91 |
92 | // string.h
93 | template class basic_string;
94 | typedef basic_string string;
95 | typedef basic_string wstring;
96 |
97 | // string_view.h
98 | template class basic_string_view;
99 | typedef basic_string_view string_view;
100 | typedef basic_string_view wstring_view;
101 |
102 | // thread.h
103 | class thread;
104 |
105 | // timer.h
106 | class time;
107 | class timer;
108 |
109 | // unique_ptr.h
110 | template class unique_ptr;
111 |
112 | // vector.h
113 | template class vector;
114 |
115 | // path.h
116 | template class path_base;
117 | typedef path_base path;
118 | typedef path_base fixed_path32;
119 | typedef path_base fixed_path64;
120 | typedef path_base fixed_path128;
121 | typedef path_base fixed_path256;
122 | typedef path_base fixed_path512;
123 | }
124 |
125 | // This needs to appear right after all the forward declarations so that we know which symbols we have and wish to expose
126 | #if defined(CRSTL_USE_IN_GLOBAL_NAMESPACE)
127 |
128 | using crstl::array;
129 | using crstl::bitset;
130 | using crstl::deque;
131 | using crstl::file;
132 | using crstl::fixed_deque;
133 | using crstl::fixed_function;
134 | using crstl::fixed_open_hashmap;
135 | using crstl::fixed_open_hashset;
136 | using crstl::fixed_open_multi_hashmap;
137 | using crstl::fixed_open_multi_hashset;
138 |
139 | using crstl::fixed_string8;
140 | using crstl::fixed_string16;
141 | using crstl::fixed_string32;
142 | using crstl::fixed_string64;
143 | using crstl::fixed_string128;
144 | using crstl::fixed_string256;
145 | using crstl::fixed_string512;
146 | using crstl::fixed_string1024;
147 | using crstl::fixed_string2048;
148 |
149 | using crstl::fixed_wstring8;
150 | using crstl::fixed_wstring16;
151 | using crstl::fixed_wstring32;
152 | using crstl::fixed_wstring64;
153 | using crstl::fixed_wstring128;
154 | using crstl::fixed_wstring256;
155 | using crstl::fixed_wstring512;
156 | using crstl::fixed_wstring1024;
157 | using crstl::fixed_wstring2048;
158 |
159 | using crstl::fixed_vector;
160 | using crstl::intrusive_ptr;
161 |
162 | using crstl::open_hashmap;
163 | using crstl::open_hashset;
164 | using crstl::open_multi_hashmap;
165 | using crstl::open_multi_hashset;
166 |
167 | using crstl::pair;
168 |
169 | using crstl::process;
170 |
171 | using crstl::span;
172 |
173 | using crstl::stack_vector;
174 |
175 | using crstl::string;
176 | using crstl::wstring;
177 |
178 | using crstl::string_view;
179 | using crstl::wstring_view;
180 |
181 | using crstl::thread;
182 |
183 | using crstl::time;
184 | using crstl::timer;
185 |
186 | using crstl::unique_ptr;
187 |
188 | using crstl::vector;
189 |
190 | using crstl::path;
191 | using crstl::fixed_path32;
192 | using crstl::fixed_path64;
193 | using crstl::fixed_path128;
194 | using crstl::fixed_path256;
195 | using crstl::fixed_path512;
196 |
197 | #endif
--------------------------------------------------------------------------------
/include/crstl/function.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/function_common.h"
4 |
5 | // crstl::function
6 | //
7 | // Replacement for std::function
8 | //
9 | // - function has a small configurable buffer that can be increased. However, if a larger size is necessary and known,
10 | // consider using fixed_function
11 |
12 | // 0 has a special value in functor_storage that means the minimum to store a pointer, but it is customizable
13 | #if !defined(CRSTL_FUNCTION_INTERNAL_BUFFER_BYTES)
14 | #define CRSTL_FUNCTION_INTERNAL_BUFFER_BYTES 0
15 | #endif
16 |
17 | crstl_module_export namespace crstl
18 | {
19 | template
20 | class function;
21 |
22 | template struct is_not_function { typedef const char* type; };
23 |
24 | template
25 | struct is_not_function> {};
26 |
27 | template
28 | class function
29 | {
30 | typedef Result result_type;
31 |
32 | public:
33 |
34 | function() crstl_noexcept : m_invoker(nullptr), m_manager(nullptr) {}
35 |
36 | template
37 | function
38 | (
39 | FunctorT&& functor,
40 | // Do not accept as Functor a function, we'll deal with it in a specialized copy/move constructor but due to overload
41 | // resolution rules, this one takes precedence. Remove reference to be able to compare the underlying type
42 | typename crstl::is_not_function::type>::type = nullptr
43 | ) crstl_noexcept
44 | {
45 | if (!handler::empty_function(functor))
46 | {
47 | handler::template create(m_functor_storage, crstl_forward(FunctorT, functor));
48 | m_invoker = &handler::invoke;
49 | m_manager = &handler::manage;
50 | }
51 | else
52 | {
53 | m_invoker = nullptr;
54 | m_manager = nullptr;
55 | }
56 | }
57 |
58 | function(function&& other) crstl_noexcept
59 | {
60 | m_functor_storage = other.m_functor_storage;
61 | m_invoker = other.m_invoker;
62 | m_manager = other.m_manager;
63 | other.m_invoker = nullptr;
64 | other.m_manager = nullptr;
65 | }
66 |
67 | function(const function& other) crstl_noexcept
68 | {
69 | copy(other);
70 | }
71 |
72 | ~function() crstl_noexcept
73 | {
74 | if (m_manager)
75 | {
76 | m_manager(&m_functor_storage, &m_functor_storage, manager_operation::destroy);
77 | }
78 | }
79 |
80 | function& operator = (function&& other)
81 | {
82 | crstl_assert(this != &other);
83 |
84 | m_functor_storage = other.m_functor_storage;
85 | m_invoker = other.m_invoker;
86 | m_manager = other.m_manager;
87 | other.m_invoker = nullptr;
88 | other.m_manager = nullptr;
89 |
90 | return *this;
91 | }
92 |
93 | function& operator = (const function& other)
94 | {
95 | crstl_assert(this != &other);
96 |
97 | copy(other);
98 |
99 | return *this;
100 | }
101 |
102 | Result operator()(Args... args) const crstl_noexcept
103 | {
104 | crstl_assert(m_invoker != nullptr);
105 | return m_invoker(&m_functor_storage, crstl_forward(Args, args)...);
106 | }
107 |
108 | explicit operator bool() const crstl_noexcept
109 | {
110 | return m_invoker != nullptr;
111 | }
112 |
113 | private:
114 |
115 | template
116 | using handler = functor_handler;
117 |
118 | // The invoker_type is a function pointer that returns Result and takes functor_storage plus a variable number of arguments
119 | using invoker_type = Result(*)(const void*, Args&&...);
120 |
121 | // The manager_type is a function pointer that returns void and takes a destination (non-const) and source (const) functor_storage
122 | using manager_type = void(*)(void*, const void*, manager_operation::t);
123 |
124 | void copy(const function& other) crstl_noexcept
125 | {
126 | if (other.m_manager)
127 | {
128 | other.m_manager(&m_functor_storage, &other.m_functor_storage, manager_operation::copy);
129 | }
130 |
131 | m_manager = other.m_manager;
132 | m_invoker = other.m_invoker;
133 | }
134 |
135 | // The invoker is a pointer to a function that reinterprets the data we pass in and calls the function
136 | // It returns the same value as the fixed_function
137 | invoker_type m_invoker;
138 |
139 | // The manager can perform operations such as copy, move, create and destroy by reinterpreting the data
140 | manager_type m_manager;
141 |
142 | // Storage for the function object
143 | functor_storage m_functor_storage;
144 | };
145 | };
--------------------------------------------------------------------------------
/include/crstl/function_common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/move_forward.h"
6 |
7 | #include "crstl/utility/placement_new.h"
8 |
9 | namespace crstl
10 | {
11 | namespace manager_operation
12 | {
13 | enum t
14 | {
15 | destroy,
16 | copy
17 | };
18 | }
19 |
20 | class undefined_class;
21 |
22 | union callable_types
23 | {
24 | void* object;
25 | void(*function)(void);
26 | void(undefined_class::*member)(void);
27 | };
28 |
29 | // functor_storage works as either a pointer to a memory block in the heap or a memory
30 | // block itself, depending on whether the captures of the lambda fit in m_data
31 | template
32 | union functor_storage
33 | {
34 | static_assert(Size >= 0, "Cannot have a negative size");
35 |
36 | // Effectively returns this
37 | void* data() crstl_noexcept { return &m_data[0]; }
38 | const void* data() const crstl_noexcept { return &m_data[0]; }
39 |
40 | template
41 | T& data() crstl_noexcept { return *static_cast(data()); }
42 |
43 | template
44 | const T& data() const crstl_noexcept { return *static_cast(data()); }
45 |
46 | char m_data[Size == 0 ? sizeof(callable_types) : Size];
47 | };
48 |
49 | template
50 | class functor_handler;
51 |
52 | // FunctorT is anything from a lambda to a function pointer. It cannot represent fixed_function itself
53 | // it's too complicated to manage and we can do better via direct handling of differently sized fixed_function
54 |
55 | template
56 | class functor_handler
57 | {
58 | public:
59 |
60 | static_assert(!SupportsHeap ? Size > 0 : true, "Need Size > 0 if we don't support heap");
61 |
62 | // If we request a specific size, then we'll consider it local, without the option to make a heap allocation
63 | static const bool static_local = sizeof(FunctorT) <= sizeof(functor_storage);
64 |
65 | static FunctorT* get_pointer(const functor_storage& source)
66 | {
67 | crstl_constexpr_if(static_local)
68 | {
69 | const FunctorT& functor = source.template data();
70 | return const_cast(&(functor));
71 | }
72 | else
73 | {
74 | return source.template data();
75 | }
76 | }
77 |
78 | template
79 | static void create(functor_storage& destination, Fn&& fn)
80 | {
81 | static_assert(!SupportsHeap ? sizeof(FunctorT) <= sizeof(destination) : true, "Not enough space to store functor");
82 |
83 | crstl_constexpr_if(static_local)
84 | {
85 | crstl_placement_new(destination.data()) FunctorT(crstl_forward(Fn, fn));
86 | }
87 | else
88 | {
89 | destination.template data() = new FunctorT(crstl_forward(Fn, fn));
90 | }
91 | }
92 |
93 | static void destroy(functor_storage& destination)
94 | {
95 | crstl_constexpr_if(static_local) // If local, just call destructor
96 | {
97 | destination.template data().~FunctorT();
98 | }
99 | else
100 | {
101 | delete destination.template data();
102 | }
103 | }
104 |
105 | template
106 | static void init_functor(functor_storage& functor, Fn&& f) crstl_noexcept
107 | {
108 | create(functor, crstl_forward(Fn, f));
109 | }
110 |
111 | static Result invoke(const void* functor, Args&&... args)
112 | {
113 | return (*get_pointer(*((const functor_storage*)functor)))(crstl_forward(Args, args)...);
114 | }
115 |
116 | static void manage(void* destination, const void* source, manager_operation::t operation)
117 | {
118 | switch (operation)
119 | {
120 | case manager_operation::destroy:
121 | {
122 | destroy(*((functor_storage*)destination));
123 | break;
124 | }
125 | case manager_operation::copy:
126 | {
127 | init_functor(*((functor_storage*)destination), *static_cast(get_pointer(*((const functor_storage*)source))));
128 | break;
129 | }
130 | }
131 | }
132 |
133 | // A null function pointer is considered empty
134 | template
135 | static bool empty_function(T* function_pointer) noexcept
136 | {
137 | return function_pointer == nullptr;
138 | }
139 |
140 | // A null member function is empty
141 | template
142 | static bool empty_function(T Class::* member_pointer) noexcept
143 | {
144 | return member_pointer == nullptr;
145 | }
146 |
147 | // A reference is never empty
148 | template
149 | static bool empty_function(const T&) noexcept
150 | {
151 | return false;
152 | }
153 | };
154 | };
--------------------------------------------------------------------------------
/include/crstl/hash.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | namespace crstl
8 | {
9 | template struct hash;
10 |
11 | // Pointers tend to be correlated by being aligned so they'll typically end up in the same buckets and cause many collisions.
12 | // This is a relatively fast way to mix them up a bit without being too expensive. Other approaches such as removing the bottom
13 | // bits are very cheap but cause many collisions, and better hashes like Wang are more expensive
14 | // https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
15 | template struct hash
16 | {
17 | size_t operator()(T* ptr) const
18 | {
19 | uintptr_t uptr = (uintptr_t)ptr;
20 |
21 | crstl_constexpr_if(sizeof(T*) == 8)
22 | {
23 | uptr = (uptr ^ (uptr >> 30)) * 0xbf58476d1ce4e5b9;
24 | uptr = (uptr ^ (uptr >> 27)) * 0x94d049bb133111eb;
25 | uptr = uptr ^ (uptr >> 31);
26 | }
27 | else
28 | {
29 | uptr = ((uptr >> 16) ^ uptr) * 0x45d9f3b;
30 | uptr = ((uptr >> 16) ^ uptr) * 0x45d9f3b;
31 | uptr = (uptr >> 16) ^ uptr;
32 | }
33 |
34 | return static_cast(uptr);
35 | }
36 | };
37 |
38 | template<> struct hash { size_t operator()(bool value) const { return static_cast(value); } };
39 |
40 | template<> struct hash { size_t operator()(char value) const { return static_cast(value); } };
41 |
42 | template<> struct hash { size_t operator()(unsigned char value) const { return static_cast(value); } };
43 |
44 | template<> struct hash { size_t operator()(short value) const { return static_cast(value); } };
45 |
46 | template<> struct hash { size_t operator()(unsigned short value) const { return static_cast(value); } };
47 |
48 | template<> struct hash { size_t operator()(int value) const { return static_cast(value); } };
49 |
50 | template<> struct hash { size_t operator()(unsigned int value) const { return static_cast(value); } };
51 |
52 | template<> struct hash { size_t operator()(long value) const { return static_cast(value); } };
53 |
54 | template<> struct hash { size_t operator()(unsigned long value) const { return static_cast(value); } };
55 |
56 | template<> struct hash { size_t operator()(long long value) const { return static_cast(value); } };
57 |
58 | template<> struct hash { size_t operator()(unsigned long long value) const { return static_cast(value); } };
59 |
60 | template<> struct hash
61 | {
62 | size_t operator()(float value)
63 | {
64 | union
65 | {
66 | float f;
67 | uint32_t u;
68 | } u;
69 |
70 | u.f = value;
71 |
72 | return static_cast(u.u);
73 | }
74 | };
75 |
76 | template<> struct hash
77 | {
78 | size_t operator()(double value)
79 | {
80 | union
81 | {
82 | double d;
83 | size_t u;
84 | } u;
85 |
86 | u.d = value;
87 |
88 | return static_cast(u.u);
89 | }
90 | };
91 | };
--------------------------------------------------------------------------------
/include/crstl/intrusive_ptr.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/crstldef.h"
6 |
7 | #include "crstl/atomic.h"
8 |
9 | // crstl::intrusive_ptr
10 | //
11 | // Alternative to std::shared_ptr
12 | //
13 |
14 | crstl_module_export namespace crstl
15 | {
16 | template
17 | void intrusive_ptr_add_ref(T* ptr)
18 | {
19 | ptr->add_ref();
20 | }
21 |
22 | template
23 | void intrusive_ptr_release(T* ptr)
24 | {
25 | int32_t currentRef = ptr->release_ref();
26 |
27 | if (currentRef == 0)
28 | {
29 | ptr->template intrusive_ptr_delete_callback();
30 | }
31 | }
32 |
33 | template
34 | class intrusive_ptr
35 | {
36 | public:
37 |
38 | intrusive_ptr() crstl_noexcept : m_ptr(nullptr) {}
39 |
40 | intrusive_ptr(nullptr_t) crstl_noexcept : m_ptr(nullptr) {}
41 |
42 | intrusive_ptr(T* ptr) crstl_noexcept : m_ptr(ptr)
43 | {
44 | if (ptr)
45 | {
46 | intrusive_ptr_add_ref(ptr);
47 | }
48 | }
49 |
50 | intrusive_ptr(const intrusive_ptr& other) crstl_noexcept : m_ptr(other.m_ptr)
51 | {
52 | if (m_ptr != nullptr)
53 | {
54 | intrusive_ptr_add_ref(m_ptr);
55 | }
56 | }
57 |
58 | intrusive_ptr(intrusive_ptr&& other) crstl_noexcept : m_ptr(other.m_ptr)
59 | {
60 | other.m_ptr = nullptr;
61 | }
62 |
63 | ~intrusive_ptr() crstl_noexcept
64 | {
65 | if (m_ptr)
66 | {
67 | intrusive_ptr_release(m_ptr);
68 | }
69 | }
70 |
71 | intrusive_ptr& operator = (nullptr_t) crstl_noexcept
72 | {
73 | set_pointer(nullptr);
74 | return *this;
75 | }
76 |
77 | intrusive_ptr& operator = (T* ptr) crstl_noexcept
78 | {
79 | set_pointer(ptr);
80 | return *this;
81 | }
82 |
83 | intrusive_ptr& operator = (const intrusive_ptr& ptr) crstl_noexcept
84 | {
85 | set_pointer(ptr.m_ptr);
86 | return *this;
87 | }
88 |
89 | intrusive_ptr& operator = (intrusive_ptr&& ptr) crstl_noexcept
90 | {
91 | if (m_ptr != ptr.m_ptr)
92 | {
93 | T* const ptr_temp = m_ptr;
94 |
95 | // Assign to member pointer
96 | m_ptr = ptr.m_ptr;
97 |
98 | // Release reference from the old pointer we used to hold on to
99 | if (ptr_temp)
100 | {
101 | intrusive_ptr_release(ptr_temp);
102 | }
103 |
104 | ptr.m_ptr = nullptr;
105 | }
106 |
107 | return *this;
108 | }
109 |
110 | T* get() const crstl_noexcept { return m_ptr; }
111 |
112 | T* operator ->() const crstl_noexcept
113 | {
114 | return m_ptr;
115 | }
116 |
117 | typedef T* (intrusive_ptr::*boolean)() const;
118 |
119 | operator boolean() const crstl_noexcept
120 | {
121 | // Return anything that isn't easily castable but is guaranteed to be non-null, such as the get function pointer
122 | return m_ptr ? &intrusive_ptr::get : nullptr;
123 | }
124 |
125 | bool operator!() const crstl_noexcept
126 | {
127 | return (m_ptr == nullptr);
128 | }
129 |
130 | private:
131 |
132 | void set_pointer(T* ptr)
133 | {
134 | if (m_ptr != ptr)
135 | {
136 | T* const ptr_temp = m_ptr;
137 |
138 | // Add a reference to the new pointer
139 | if (ptr)
140 | {
141 | intrusive_ptr_add_ref(ptr);
142 | }
143 |
144 | // Assign to member pointer
145 | m_ptr = ptr;
146 |
147 | // Release reference from the old pointer we used to hold on to
148 | if (ptr_temp)
149 | {
150 | intrusive_ptr_release(ptr_temp);
151 | }
152 | }
153 | }
154 |
155 | T* m_ptr;
156 | };
157 |
158 | // Simple interface for intrusive_ptr. If there is need for more advanced behavior,
159 | // create a new class that conforms to this interface. Intentionally removing virtual
160 | // to ensure there is no virtual dispatching
161 | class intrusive_ptr_interface_base
162 | {
163 | public:
164 |
165 | int32_t add_ref()
166 | {
167 | m_refcount++;
168 | return m_refcount;
169 | }
170 |
171 | int32_t release_ref()
172 | {
173 | m_refcount--;
174 | return m_refcount;
175 | }
176 |
177 | int32_t get_ref() const
178 | {
179 | return m_refcount;
180 | }
181 |
182 | crstl::atomic m_refcount;
183 | };
184 |
185 | class intrusive_ptr_interface_delete : public intrusive_ptr_interface_base
186 | {
187 | public:
188 |
189 | template
190 | void intrusive_ptr_delete_callback()
191 | {
192 | delete (Q*)this;
193 | }
194 | };
195 | };
--------------------------------------------------------------------------------
/include/crstl/move_forward.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // These are so commonly used without anything else that they warrant their own header
4 |
5 | crstl_module_export namespace crstl
6 | {
7 | template struct remove_reference { typedef T type; };
8 |
9 | template struct remove_reference { typedef T type; };
10 |
11 | template struct remove_reference { typedef T type; };
12 |
13 | template struct is_lvalue_reference { static const bool value = false; };
14 |
15 | template struct is_lvalue_reference { static const bool value = true; };
16 |
17 | template
18 | crstl_constexpr T&& forward(typename crstl::remove_reference::type& x) crstl_noexcept
19 | {
20 | return static_cast(x);
21 | }
22 |
23 | template
24 | crstl_constexpr T&& forward(typename crstl::remove_reference::type&& x) crstl_noexcept
25 | {
26 | static_assert(!crstl::is_lvalue_reference::value, "T is not an lvalue reference");
27 | return static_cast(x);
28 | }
29 |
30 | template
31 | crstl_constexpr typename crstl::remove_reference::type&& move(T&& x) crstl_noexcept
32 | {
33 | return static_cast::type&&>(x);
34 | }
35 | };
36 |
37 | // https://www.foonathan.net/2020/09/move-forward/
38 |
39 | // Use inside the library for faster code in debug builds
40 | #define crstl_move(x) static_cast::type&&>(x)
41 |
42 | #define crstl_forward(T, x) static_cast(x)
--------------------------------------------------------------------------------
/include/crstl/pair.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "crstl/config.h"
4 |
5 | #include "crstl/move_forward.h"
6 |
7 | // crstl::pair
8 | //
9 | // Replacement for std::pair
10 | //
11 |
12 | crstl_module_export namespace crstl
13 | {
14 | template
15 | class pair
16 | {
17 | public:
18 |
19 | typedef T1 first_type;
20 | typedef T2 second_type;
21 |
22 | crstl_constexpr pair() crstl_noexcept : first(T1()), second(T2()) {}
23 |
24 | template
25 | crstl_constexpr pair(const U1& first, const U2& second) crstl_noexcept : first(first), second(second) {}
26 |
27 | template
28 | crstl_constexpr pair(U1&& first, U2&& second) crstl_noexcept : first(crstl_forward(U1, first)), second(crstl_forward(U2, second)) {}
29 |
30 | template
31 | crstl_constexpr pair(const pair& other) crstl_noexcept : first(other.first), second(other.second) {}
32 |
33 | template
34 | crstl_constexpr pair(pair&& other) crstl_noexcept : first(crstl_forward(U1, other.first)), second(crstl_forward(U2, other.second)) {}
35 |
36 | template
37 | pair& operator = (const pair