├── .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 | [![AppVeyor](https://ci.appveyor.com/api/projects/status/7ig4jp76wia7xn1k?svg=true)](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& other) 38 | { 39 | first = other.first; 40 | second = other.second; 41 | return *this; 42 | } 43 | 44 | template 45 | pair& operator = (pair&& other) 46 | { 47 | first = crstl_forward(U1, other.first); 48 | second = crstl_forward(U2, other.second); 49 | return *this; 50 | } 51 | 52 | crstl_constexpr bool operator == (const pair& other) const { return first == other.first && second == other.second; } 53 | crstl_constexpr bool operator != (const pair& other) const { return !(*this == other); } 54 | crstl_constexpr bool operator < (const pair& other) const { return first < other.first || (!(first < other.first) && second < other.second); } 55 | crstl_constexpr bool operator <= (const pair& other) const { return !(other < *this); } 56 | crstl_constexpr bool operator > (const pair& other) const { return other < *this; } 57 | crstl_constexpr bool operator >= (const pair& other) const { return !(*this < other); } 58 | 59 | T1 first; 60 | T2 second; 61 | }; 62 | 63 | template 64 | crstl_constexpr pair make_pair(T1&& first, T2&& second) 65 | { 66 | return pair(first, second); 67 | } 68 | }; -------------------------------------------------------------------------------- /include/crstl/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/utility/path_common.h" 4 | 5 | #include "crstl/string.h" 6 | 7 | crstl_module_export namespace crstl 8 | { 9 | typedef path_base path; 10 | }; -------------------------------------------------------------------------------- /include/crstl/platform/atomic_arm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Storage 4 | 5 | #define crstl_atomic_store8(target, value) __sync_lock_test_and_set((char*)(target), (value)) 6 | 7 | #define crstl_atomic_store16(target, value) __sync_lock_test_and_set((short*)(target), (value)) 8 | 9 | #define crstl_atomic_store32(target, value) __sync_lock_test_and_set((long*)(target), (value)) 10 | 11 | #define crstl_atomic_store64(target, value) __sync_lock_test_and_set((long long*)(target), (value)) 12 | 13 | // Addition 14 | 15 | #define crstl_atomic_add8(target, value) __sync_fetch_and_add((char*)(target), (value)) 16 | 17 | #define crstl_atomic_add16(target, value) __sync_fetch_and_add((short*)(target), (value)) 18 | 19 | #define crstl_atomic_add32(target, value) __sync_fetch_and_add((long*)(target), (value)) 20 | 21 | #define crstl_atomic_add64(target, value) __sync_fetch_and_add((long long*)(target), (value)) 22 | 23 | // Subtraction 24 | 25 | #define crstl_atomic_sub8(target, value) __sync_fetch_and_sub((char*)(target), (value)) 26 | 27 | #define crstl_atomic_sub16(target, value) __sync_fetch_and_sub((short*)(target), (value)) 28 | 29 | #define crstl_atomic_sub32(target, value) __sync_fetch_and_sub((long*)(target), (value)) 30 | 31 | #define crstl_atomic_sub64(target, value) __sync_fetch_and_sub((long long*)(target), (value)) 32 | 33 | // And 34 | 35 | #define crstl_atomic_and8(target, value) __sync_fetch_and_and((char*)(target), (value)) 36 | 37 | #define crstl_atomic_and16(target, value) __sync_fetch_and_and((short*)(target), (value)) 38 | 39 | #define crstl_atomic_and32(target, value) __sync_fetch_and_and((long*)(target), (value)) 40 | 41 | #define crstl_atomic_and64(target, value) __sync_fetch_and_and((long long*)(target), (value)) 42 | 43 | // Or 44 | 45 | #define crstl_atomic_or8(target, value) __sync_fetch_and_or((char*)(target), (value)) 46 | 47 | #define crstl_atomic_or16(target, value) __sync_fetch_and_or((short*)(target), (value)) 48 | 49 | #define crstl_atomic_or32(target, value) __sync_fetch_and_or((long*)(target), (value)) 50 | 51 | #define crstl_atomic_or64(target, value) __sync_fetch_and_or((long long*)(target), (value)) 52 | 53 | // Xor 54 | 55 | #define crstl_atomic_xor8(target, value) __sync_fetch_and_xor((char*)(target), (value)) 56 | 57 | #define crstl_atomic_xor16(target, value) __sync_fetch_and_xor((short*)(target), (value)) 58 | 59 | #define crstl_atomic_xor32(target, value) __sync_fetch_and_xor((long*)(target), (value)) 60 | 61 | #define crstl_atomic_xor64(target, value) __sync_fetch_and_xor((long long*)(target), (value)) -------------------------------------------------------------------------------- /include/crstl/platform/atomic_clang_gcc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html 4 | 5 | // Storage 6 | 7 | #define crstl_atomic_store8(target, value) __atomic_exchange_n((target), (value), __ATOMIC_SEQ_CST) 8 | 9 | #define crstl_atomic_store16(target, value) __atomic_exchange_n((target), (value), __ATOMIC_SEQ_CST) 10 | 11 | #define crstl_atomic_store32(target, value) __atomic_exchange_n((target), (value), __ATOMIC_SEQ_CST) 12 | 13 | #define crstl_atomic_store64(target, value) __atomic_exchange_n((target), (value), __ATOMIC_SEQ_CST) 14 | 15 | // Addition 16 | 17 | #define crstl_atomic_add8(target, value) __atomic_fetch_add((target), (value), __ATOMIC_SEQ_CST) 18 | 19 | #define crstl_atomic_add16(target, value) __atomic_fetch_add((target), (value), __ATOMIC_SEQ_CST) 20 | 21 | #define crstl_atomic_add32(target, value) __atomic_fetch_add((target), (value), __ATOMIC_SEQ_CST) 22 | 23 | #define crstl_atomic_add64(target, value) __atomic_fetch_add((target), (value), __ATOMIC_SEQ_CST) 24 | 25 | // Subtraction 26 | 27 | #define crstl_atomic_sub8(target, value) __atomic_fetch_sub((target), (value), __ATOMIC_SEQ_CST) 28 | 29 | #define crstl_atomic_sub16(target, value) __atomic_fetch_sub((target), (value), __ATOMIC_SEQ_CST) 30 | 31 | #define crstl_atomic_sub32(target, value) __atomic_fetch_sub((target), (value), __ATOMIC_SEQ_CST) 32 | 33 | #define crstl_atomic_sub64(target, value) __atomic_fetch_sub((target), (value), __ATOMIC_SEQ_CST) 34 | 35 | // And 36 | 37 | #define crstl_atomic_and8(target, value) __atomic_fetch_and((target), (value), __ATOMIC_SEQ_CST) 38 | 39 | #define crstl_atomic_and16(target, value) __atomic_fetch_and((target), (value), __ATOMIC_SEQ_CST) 40 | 41 | #define crstl_atomic_and32(target, value) __atomic_fetch_and((target), (value), __ATOMIC_SEQ_CST) 42 | 43 | #define crstl_atomic_and64(target, value) __atomic_fetch_and((target), (value), __ATOMIC_SEQ_CST) 44 | 45 | // Or 46 | 47 | #define crstl_atomic_or8(target, value) __atomic_fetch_or((target), (value), __ATOMIC_SEQ_CST) 48 | 49 | #define crstl_atomic_or16(target, value) __atomic_fetch_or((target), (value), __ATOMIC_SEQ_CST) 50 | 51 | #define crstl_atomic_or32(target, value) __atomic_fetch_or((target), (value), __ATOMIC_SEQ_CST) 52 | 53 | #define crstl_atomic_or64(target, value) __atomic_fetch_or((target), (value), __ATOMIC_SEQ_CST) 54 | 55 | // Xor 56 | 57 | #define crstl_atomic_xor8(target, value) __atomic_fetch_xor((target), (value), __ATOMIC_SEQ_CST) 58 | 59 | #define crstl_atomic_xor16(target, value) __atomic_fetch_xor((target), (value), __ATOMIC_SEQ_CST) 60 | 61 | #define crstl_atomic_xor32(target, value) __atomic_fetch_xor((target), (value), __ATOMIC_SEQ_CST) 62 | 63 | #define crstl_atomic_xor64(target, value) __atomic_fetch_xor((target), (value), __ATOMIC_SEQ_CST) 64 | -------------------------------------------------------------------------------- /include/crstl/platform/atomic_win32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace crstl 4 | { 5 | // We declare these to avoid including windows.h 6 | extern "C" 7 | { 8 | // Storage 9 | 10 | char _InterlockedExchange8(char volatile* target, char value); 11 | 12 | short _InterlockedExchange16(short volatile* target, short value); 13 | 14 | long _InterlockedExchange(long volatile* target, long value); 15 | 16 | long long _InterlockedExchange64(long long volatile* target, long long value); 17 | 18 | // Addition 19 | 20 | char _InterlockedExchangeAdd8(char volatile* target, char value); 21 | 22 | short _InterlockedExchangeAdd16(short volatile* target, short value); 23 | 24 | long _InterlockedExchangeAdd(long volatile* target, long value); 25 | 26 | long long _InterlockedExchangeAdd64(long long volatile* target, long long value); 27 | 28 | // And 29 | 30 | char _InterlockedAnd8(char volatile* target, char mask); 31 | 32 | short _InterlockedAnd16(short volatile* target, short mask); 33 | 34 | long _InterlockedAnd(long volatile* target, long mask); 35 | 36 | long long _InterlockedAnd64(long long volatile* target, long long mask); 37 | 38 | // Or 39 | 40 | char _InterlockedOr8(char volatile* target, char mask); 41 | 42 | short _InterlockedOr16(short volatile* target, short mask); 43 | 44 | long _InterlockedOr(long volatile* target, long mask); 45 | 46 | long long _InterlockedOr64(long long volatile* target, long long mask); 47 | 48 | // Xor 49 | 50 | char _InterlockedXor8(char volatile* target, char mask); 51 | 52 | short _InterlockedXor16(short volatile* target, short mask); 53 | 54 | long _InterlockedXor(long volatile* target, long mask); 55 | 56 | long long _InterlockedXor64(long long volatile* target, long long mask); 57 | 58 | // Compare and Exchange 59 | 60 | char _InterlockedCompareExchange8(char volatile* target, char exchange, char comparand); 61 | 62 | short _InterlockedCompareExchange16(short volatile* target, short exchange, short comparand); 63 | 64 | long _InterlockedCompareExchange(long volatile* target, long exchange, long comparand); 65 | 66 | long long _InterlockedCompareExchange64(long long volatile* target, long long exchange, long long comparand); 67 | } 68 | }; 69 | 70 | // Storage 71 | 72 | #define crstl_atomic_store8(target, value) _InterlockedExchange8((char*)(target), (value)) 73 | 74 | #define crstl_atomic_store16(target, value) _InterlockedExchange16((short*)(target), (value)) 75 | 76 | #define crstl_atomic_store32(target, value) _InterlockedExchange((long*)(target), (value)) 77 | 78 | #define crstl_atomic_store64(target, value) _InterlockedExchange64((long long*)(target), (value)) 79 | 80 | // Addition 81 | 82 | #define crstl_atomic_add8(target, value) _InterlockedExchangeAdd8((char*)(target), (value)) 83 | 84 | #define crstl_atomic_add16(target, value) _InterlockedExchangeAdd16((short*)(target), (value)) 85 | 86 | #define crstl_atomic_add32(target, value) _InterlockedExchangeAdd((long*)(target), (value)) 87 | 88 | #define crstl_atomic_add64(target, value) _InterlockedExchangeAdd64((long long*)(target), (value)) 89 | 90 | // Subtraction 91 | 92 | #define crstl_atomic_sub8(target, value) _InterlockedExchangeAdd8((char*)(target), -(char)(value)) 93 | 94 | #define crstl_atomic_sub16(target, value) _InterlockedExchangeAdd16((short*)(target), -(short)(value)) 95 | 96 | #define crstl_atomic_sub32(target, value) _InterlockedExchangeAdd((long*)(target), -(long)(value)) 97 | 98 | #define crstl_atomic_sub64(target, value) _InterlockedExchangeAdd64((long long*)(target), -(long long)(value)) 99 | 100 | // And 101 | 102 | #define crstl_atomic_and8(target, value) _InterlockedAnd8((char*)(target), (value)) 103 | 104 | #define crstl_atomic_and16(target, value) _InterlockedAnd16((short*)(target), (value)) 105 | 106 | #define crstl_atomic_and32(target, value) _InterlockedAnd((long*)(target), (value)) 107 | 108 | #define crstl_atomic_and64(target, value) _InterlockedAnd64((long long*)(target), (value)) 109 | 110 | // Or 111 | 112 | #define crstl_atomic_or8(target, value) _InterlockedOr8((char*)(target), (value)) 113 | 114 | #define crstl_atomic_or16(target, value) _InterlockedOr16((short*)(target), (value)) 115 | 116 | #define crstl_atomic_or32(target, value) _InterlockedOr((long*)(target), (value)) 117 | 118 | #define crstl_atomic_or64(target, value) _InterlockedOr64((long long*)(target), (value)) 119 | 120 | // Xor 121 | 122 | #define crstl_atomic_xor8(target, value) _InterlockedXor8((char*)(target), (value)) 123 | 124 | #define crstl_atomic_xor16(target, value) _InterlockedXor16((short*)(target), (value)) 125 | 126 | #define crstl_atomic_xor32(target, value) _InterlockedXor((long*)(target), (value)) 127 | 128 | #define crstl_atomic_xor64(target, value) _InterlockedXor64((long long*)(target), (value)) -------------------------------------------------------------------------------- /include/crstl/platform/critical_section_posix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | crstl_module_export namespace crstl 6 | { 7 | class critical_section final : public critical_section_base 8 | { 9 | public: 10 | 11 | critical_section() 12 | { 13 | pthread_mutexattr_t attr; 14 | pthread_mutexattr_init(&attr); 15 | 16 | // A recursive mutex maintains a lock counter, just like the Windows critical sections 17 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 18 | pthread_mutex_init(&m_mutex, &attr); 19 | pthread_mutexattr_destroy(&attr); 20 | } 21 | 22 | void lock() 23 | { 24 | pthread_mutex_lock(&m_mutex); 25 | } 26 | 27 | crstl_nodiscard bool try_lock() 28 | { 29 | return pthread_mutex_trylock(&m_mutex) == 0; 30 | } 31 | 32 | void unlock() 33 | { 34 | pthread_mutex_unlock(&m_mutex); 35 | } 36 | 37 | ~critical_section() 38 | { 39 | pthread_mutex_destroy(&m_mutex); 40 | } 41 | 42 | protected: 43 | 44 | pthread_mutex_t m_mutex; 45 | }; 46 | }; -------------------------------------------------------------------------------- /include/crstl/platform/critical_section_win32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common_win32.h" 4 | 5 | // https://learn.microsoft.com/en-us/windows/win32/sync/critical-section-objects 6 | extern "C" 7 | { 8 | typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; 9 | typedef struct _RTL_CRITICAL_SECTION* PRTL_CRITICAL_SECTION; 10 | typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; 11 | typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION; 12 | typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION; 13 | 14 | __declspec(dllimport) void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 15 | __declspec(dllimport) void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 16 | 17 | __declspec(dllimport) void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 18 | __declspec(dllimport) BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 19 | __declspec(dllimport) void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 20 | }; 21 | 22 | crstl_module_export namespace crstl 23 | { 24 | namespace detail 25 | { 26 | typedef struct _LIST_ENTRY { 27 | struct _LIST_ENTRY* Flink; 28 | struct _LIST_ENTRY* Blink; 29 | } LIST_ENTRY, *PLIST_ENTRY; 30 | 31 | typedef struct _RTL_CRITICAL_SECTION_DEBUG { 32 | WORD Type; 33 | WORD CreatorBackTraceIndex; 34 | struct _RTL_CRITICAL_SECTION* CriticalSection; 35 | LIST_ENTRY ProcessLocksList; 36 | DWORD EntryCount; 37 | DWORD ContentionCount; 38 | DWORD Flags; 39 | WORD CreatorBackTraceIndexHigh; 40 | WORD Identifier; 41 | } RTL_CRITICAL_SECTION_DEBUG, * PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, * PRTL_RESOURCE_DEBUG; 42 | 43 | #pragma pack(push, 8) 44 | 45 | typedef struct _RTL_CRITICAL_SECTION { 46 | PRTL_CRITICAL_SECTION_DEBUG DebugInfo; 47 | 48 | // 49 | // The following three fields control entering and exiting the critical 50 | // section for the resource 51 | // 52 | 53 | LONG LockCount; 54 | LONG RecursionCount; 55 | HANDLE OwningThread; // from the thread's ClientId->UniqueThread 56 | HANDLE LockSemaphore; 57 | ULONG_PTR SpinCount; // force size on 64-bit systems when packed 58 | } RTL_CRITICAL_SECTION, * PRTL_CRITICAL_SECTION; 59 | 60 | #pragma pack(pop) 61 | 62 | typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; 63 | typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION; 64 | typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION; 65 | } 66 | 67 | class critical_section final : public critical_section_base 68 | { 69 | public: 70 | 71 | critical_section() 72 | { 73 | InitializeCriticalSection((LPCRITICAL_SECTION)&m_critical_section); 74 | } 75 | 76 | void lock() 77 | { 78 | EnterCriticalSection((LPCRITICAL_SECTION)&m_critical_section); 79 | } 80 | 81 | crstl_nodiscard bool try_lock() 82 | { 83 | return TryEnterCriticalSection((LPCRITICAL_SECTION)&m_critical_section) != 0; 84 | } 85 | 86 | void unlock() 87 | { 88 | LeaveCriticalSection((LPCRITICAL_SECTION)&m_critical_section); 89 | } 90 | 91 | ~critical_section() 92 | { 93 | DeleteCriticalSection((LPCRITICAL_SECTION)&m_critical_section); 94 | } 95 | 96 | protected: 97 | 98 | detail::CRITICAL_SECTION m_critical_section; 99 | }; 100 | }; -------------------------------------------------------------------------------- /include/crstl/platform/process_posix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/utility/string_length.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // https://www.gnu.org/software/gnulib/manual/html_node/environ.html 12 | #if defined(CRSTL_OS_OSX) 13 | #include 14 | #define environ (*_NSGetEnviron()) 15 | #endif 16 | 17 | crstl_module_export namespace crstl 18 | { 19 | class process : public process_base 20 | { 21 | public: 22 | 23 | process() 24 | : process_base() 25 | , m_child_pid(0) 26 | , m_stdout_read_file(nullptr) 27 | {} 28 | 29 | process(const char* executable, const char* args) 30 | : process_base() 31 | , m_child_pid(0) 32 | , m_stdout_read_file(nullptr) 33 | { 34 | int stdoutfd[2]; 35 | if (pipe(stdoutfd) != 0) 36 | { 37 | m_state = process_state::error_failed_to_launch; 38 | return; 39 | } 40 | 41 | posix_spawn_file_actions_t actions; 42 | if (posix_spawn_file_actions_init(&actions) != 0) 43 | { 44 | m_state = process_state::error_failed_to_launch; 45 | return; 46 | } 47 | 48 | // Close the stdout read end and map the write end to stdout 49 | if (posix_spawn_file_actions_addclose(&actions, stdoutfd[0]) != 0 || 50 | posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO) != 0) 51 | { 52 | m_state = process_state::error_failed_to_launch; 53 | } 54 | 55 | if (m_state == process_state::undefined) 56 | { 57 | char* argv[] = { (char*)args, nullptr }; 58 | int spawn_return = posix_spawn(&m_child_pid, executable, &actions, nullptr/*attrp*/, argv, environ/*envp*/); 59 | 60 | if (spawn_return == 0) 61 | { 62 | m_state = process_state::launched; 63 | } 64 | else 65 | { 66 | m_state = process_state::error_failed_to_launch; 67 | } 68 | } 69 | 70 | // Close the write end of stdout as we'll only read it 71 | close(stdoutfd[1]); 72 | 73 | // Open the stdout as a readonly binary file 74 | m_stdout_read_file = fdopen(stdoutfd[0], "rb"); 75 | 76 | posix_spawn_file_actions_destroy(&actions); 77 | } 78 | 79 | ~process() 80 | { 81 | if (m_stdout_read_file) 82 | { 83 | fclose(m_stdout_read_file); 84 | } 85 | } 86 | 87 | process(process&& other) 88 | { 89 | m_child_pid = other.m_child_pid; 90 | m_state = other.m_state; 91 | 92 | other.m_child_pid = 0; 93 | other.m_state = process_state::undefined; 94 | } 95 | 96 | process& operator = (process&& other) 97 | { 98 | terminate(); 99 | 100 | m_child_pid = other.m_child_pid; 101 | m_state = other.m_state; 102 | 103 | other.m_child_pid = 0; 104 | other.m_state = process_state::undefined; 105 | 106 | return *this; 107 | } 108 | 109 | bool is_running() const 110 | { 111 | // https://linux.die.net/man/2/waitpid 112 | // On success, returns the process ID of the child whose state has changed; 113 | // if WNOHANG was specified and one or more child(ren) specified by pid exist, 114 | // but have not yet changed state, then 0 is returned. On error, -1 is returned. 115 | int status; 116 | int wpid = waitpid(m_child_pid, &status, WNOHANG); 117 | return wpid == 0; 118 | } 119 | 120 | process_exit_status wait() 121 | { 122 | if (m_state == process_state::launched) 123 | { 124 | int status; 125 | int wpid = waitpid(m_child_pid, &status, 0); 126 | 127 | if (m_child_pid == wpid) 128 | { 129 | if (WIFEXITED(status)) 130 | { 131 | int return_value = WEXITSTATUS(status); 132 | m_state = process_state::waited; 133 | m_exit_status = process_exit_status(return_value); 134 | } 135 | else 136 | { 137 | m_state = process_state::error_wait; 138 | } 139 | } 140 | else 141 | { 142 | m_state = process_state::error_wait; 143 | } 144 | 145 | m_child_pid = 0; 146 | } 147 | 148 | return m_exit_status; 149 | } 150 | 151 | process_size read_stdout(char* buffer, size_t buffer_size) 152 | { 153 | crstl_assert_msg(buffer != nullptr, "Buffer is null"); 154 | crstl_assert_msg(buffer_size > 0, "Invalid size"); 155 | 156 | ssize_t bytes_read = 0; 157 | 158 | // We need to check whether the process is alive because it might have been killed by someone else 159 | // before we're trying to access the stdout handle. There's not a lot we can do 160 | if ((m_state == process_state::launched || m_state == process_state::waited) && is_running()) 161 | { 162 | int fd = fileno(m_stdout_read_file); 163 | bytes_read = read(fd, buffer, buffer_size); 164 | 165 | if (bytes_read >= 0) 166 | { 167 | return process_size(bytes_read); 168 | } 169 | } 170 | 171 | return process_size(); 172 | } 173 | 174 | bool terminate() 175 | { 176 | if (m_state == process_state::launched) 177 | { 178 | int result = kill(m_child_pid, 9); 179 | m_state = process_state::terminated; 180 | return result; 181 | } 182 | 183 | return false; 184 | } 185 | 186 | private: 187 | 188 | pid_t m_child_pid; 189 | 190 | FILE* m_stdout_read_file; 191 | }; 192 | }; -------------------------------------------------------------------------------- /include/crstl/platform/thread_posix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern "C" 6 | { 7 | int usleep(uint32_t __useconds); 8 | } 9 | 10 | crstl_module_export namespace crstl 11 | { 12 | template 13 | void* MainThreadFn(void* parameter) 14 | { 15 | crstl::thread_data* thread_data = (crstl::thread_data*)parameter; 16 | 17 | // If we want to delay the start, we need to signal a condition that this function has run 18 | if (thread_data->parameters.delay_start) 19 | { 20 | int result; 21 | result = pthread_mutex_lock(&thread_data->thread_ptr->m_mainthread_mutex); 22 | crstl_assert(result == 0); 23 | result = pthread_cond_wait(&thread_data->thread_ptr->m_mainthread_condition, &thread_data->thread_ptr->m_mainthread_mutex); 24 | crstl_assert(result == 0); 25 | result = pthread_mutex_unlock(&thread_data->thread_ptr->m_mainthread_mutex); 26 | crstl_assert(result == 0); 27 | } 28 | 29 | // TODO Do we need the result of the function 30 | call(thread_data->function, thread_data->function_args, crstl::make_index_sequence()); 31 | 32 | delete thread_data; 33 | 34 | pthread_exit(nullptr); 35 | 36 | return nullptr; 37 | } 38 | 39 | class thread final : thread_base 40 | { 41 | public: 42 | 43 | #define crstl_thread_invalid_handle 0 44 | 45 | thread() crstl_nodiscard : m_delayed_start(false), m_pthread(crstl_thread_invalid_handle) {} 46 | 47 | thread(thread&& other) crstl_nodiscard 48 | { 49 | m_pthread = other.m_pthread; 50 | m_delayed_start = other.m_delayed_start; 51 | 52 | other.m_pthread = crstl_thread_invalid_handle; 53 | } 54 | 55 | thread& operator = (thread&& other) 56 | { 57 | join(); 58 | 59 | m_pthread = other.m_pthread; 60 | m_delayed_start = other.m_delayed_start; 61 | 62 | other.m_pthread = crstl_thread_invalid_handle; 63 | 64 | return *this; 65 | } 66 | 67 | template 68 | thread(const thread_parameters& parameters, Function&& func, Args&& ... args) crstl_nodiscard 69 | { 70 | pthread_attr_t attr; 71 | pthread_attr_init(&attr); 72 | 73 | crstl::thread_data* thread_data = new crstl::thread_data 74 | { 75 | this, 76 | parameters, 77 | func, 78 | crstl::tuple(crstl_forward(Args, args)...) 79 | }; 80 | 81 | m_delayed_start = parameters.delay_start; 82 | 83 | if (m_delayed_start) 84 | { 85 | int mutex_result = pthread_mutex_init(&m_mainthread_mutex, nullptr); 86 | crstl_assert(mutex_result == 0); 87 | int cond_result = pthread_cond_init(&m_mainthread_condition, nullptr); 88 | crstl_assert(cond_result == 0); 89 | } 90 | 91 | uint32_t stackSize = parameters.stack_size ? parameters.stack_size : DefaultStackSize; 92 | pthread_attr_setstacksize(&attr, stackSize); 93 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 94 | pthread_create(&m_pthread, &attr, MainThreadFn, thread_data); 95 | pthread_attr_destroy(&attr); 96 | 97 | m_started = !parameters.delay_start; 98 | 99 | set_priority(parameters.priority); 100 | } 101 | 102 | void set_priority(thread_priority::t priority) 103 | { 104 | if (m_priority != priority) 105 | { 106 | int highest_priority = sched_get_priority_max(SCHED_RR); 107 | int lowest_priority = sched_get_priority_min(SCHED_RR); 108 | int posix_priority = lowest_priority + ((highest_priority - lowest_priority) * static_cast(priority)) / (thread_priority::count - 1); 109 | 110 | sched_param sched_parameter; 111 | sched_parameter.sched_priority = posix_priority; 112 | int result = pthread_setschedparam(m_pthread, SCHED_RR, &sched_parameter); 113 | 114 | if (result != 0) 115 | { 116 | m_priority = priority; 117 | } 118 | } 119 | } 120 | 121 | void start() 122 | { 123 | if (!m_started) 124 | { 125 | int result; 126 | result = pthread_mutex_lock(&m_mainthread_mutex); 127 | crstl_assert(result == 0); 128 | result = pthread_cond_signal(&m_mainthread_condition); 129 | crstl_assert(result == 0); 130 | result = pthread_mutex_unlock(&m_mainthread_mutex); 131 | crstl_assert(result == 0); 132 | 133 | m_started = true; 134 | } 135 | } 136 | 137 | void join() 138 | { 139 | if (m_pthread != crstl_thread_invalid_handle) 140 | { 141 | void* value_pointer = nullptr; 142 | int result = pthread_join(m_pthread, &value_pointer); 143 | crstl_assert(result == 0); 144 | m_pthread = crstl_thread_invalid_handle; 145 | } 146 | } 147 | 148 | ~thread() 149 | { 150 | join(); 151 | 152 | if (m_delayed_start) 153 | { 154 | pthread_mutex_destroy(&m_mainthread_mutex); 155 | pthread_cond_destroy(&m_mainthread_condition); 156 | } 157 | } 158 | 159 | bool m_delayed_start; 160 | 161 | pthread_mutex_t m_mainthread_mutex; 162 | 163 | pthread_cond_t m_mainthread_condition; 164 | 165 | pthread_t m_pthread; 166 | }; 167 | 168 | namespace this_thread 169 | { 170 | // Yields timeslice back to the OS 171 | inline void yield() 172 | { 173 | sched_yield(); // pthread_yield is deprecated 174 | } 175 | 176 | inline void sleep_for(uint32_t milliseconds) 177 | { 178 | usleep(milliseconds * 1000); 179 | } 180 | }; 181 | }; -------------------------------------------------------------------------------- /include/crstl/platform/timer_platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #if defined(CRSTL_OS_OSX) 8 | #include 9 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_ANDROID) 10 | #include 11 | #endif 12 | 13 | #if defined(CRSTL_COMPILER_MSVC) 14 | 15 | #include "crstl/platform/common_win32.h" 16 | 17 | extern "C" 18 | { 19 | __declspec(dllimport) int QueryPerformanceFrequency(_LARGE_INTEGER* lpFrequency); 20 | __declspec(dllimport) int QueryPerformanceCounter(_LARGE_INTEGER* lpPerformanceCount); 21 | 22 | #if defined(CRSTL_ARCH_X86) 23 | unsigned __int64 __rdtsc(); 24 | #pragma intrinsic(__rdtsc) 25 | #elif defined(CRSTL_ARCH_ARM) 26 | 27 | #define CRSTL_ARM64_SYSREG(op0, op1, crn, crm, op2) \ 28 | ( ((op0 & 1) << 14) | \ 29 | ((op1 & 7) << 11) | \ 30 | ((crn & 15) << 7) | \ 31 | ((crm & 15) << 3) | \ 32 | ((op2 & 7) << 0) ) 33 | 34 | #define CRSTL_ARM64_PMCCNTR_EL0 CRSTL_ARM64_SYSREG(3,3, 9,13,0) // Cycle Count Register [CP15_PMCCNTR] 35 | #define CRSTL_ARM64_CNTVCT CRSTL_ARM64_SYSREG(3,3,14, 0,2) // Generic Timer counter register 36 | 37 | #if defined(CRSTL_ARCH_ARM64) 38 | 39 | __int64 _ReadStatusReg(int); 40 | 41 | #elif defined(CRSTL_ARCH_ARM32) 42 | 43 | int _ReadStatusReg(int); 44 | 45 | #endif 46 | #endif 47 | }; 48 | 49 | #endif 50 | 51 | #if defined(CRSTL_ARCH_X86) 52 | 53 | extern "C" 54 | { 55 | void _mm_lfence(); 56 | }; 57 | 58 | #elif defined(CRSTL_ARCH_ARM) 59 | 60 | namespace crstl 61 | { 62 | namespace detail 63 | { 64 | typedef enum 65 | { 66 | _ARM64_BARRIER_SY = 0xF, 67 | _ARM64_BARRIER_ST = 0xE, 68 | _ARM64_BARRIER_LD = 0xD, 69 | _ARM64_BARRIER_ISH = 0xB, 70 | _ARM64_BARRIER_ISHST = 0xA, 71 | _ARM64_BARRIER_ISHLD = 0x9, 72 | _ARM64_BARRIER_NSH = 0x7, 73 | _ARM64_BARRIER_NSHST = 0x6, 74 | _ARM64_BARRIER_NSHLD = 0x5, 75 | _ARM64_BARRIER_OSH = 0x3, 76 | _ARM64_BARRIER_OSHST = 0x2, 77 | _ARM64_BARRIER_OSHLD = 0x1 78 | } 79 | _ARM64INTR_BARRIER_TYPE; 80 | 81 | typedef enum 82 | { 83 | _ARM_BARRIER_SY = 0xF, 84 | _ARM_BARRIER_ST = 0xE, 85 | _ARM_BARRIER_ISH = 0xB, 86 | _ARM_BARRIER_ISHST = 0xA, 87 | _ARM_BARRIER_NSH = 0x7, 88 | _ARM_BARRIER_NSHST = 0x6, 89 | _ARM_BARRIER_OSH = 0x3, 90 | _ARM_BARRIER_OSHST = 0x2 91 | } 92 | _ARMINTR_BARRIER_TYPE; 93 | }; 94 | }; 95 | 96 | extern "C" 97 | { 98 | void __dmb(unsigned int _Type); 99 | }; 100 | 101 | #endif 102 | 103 | crstl_module_export namespace crstl 104 | { 105 | inline uint64_t get_cycle_count() 106 | { 107 | #if defined(CRSTL_OS_OSX) 108 | return mach_absolute_time(); 109 | #elif defined(CRSTL_COMPILER_MSVC) 110 | 111 | // Inline assembly is not supported in MSVC so do our best to use the available intrinsics here 112 | #if defined(CRSTL_ARCH_X86) 113 | return __rdtsc(); 114 | #elif defined(CRSTL_ARCH_ARM64) 115 | return _ReadStatusReg(CRSTL_ARM64_CNTVCT); 116 | #elif defined(CRSTL_ARCH_ARM32) 117 | return 0; // _ReadStatusReg(CRSTL_ARM64_PMCCNTR_EL0); 118 | #endif 119 | 120 | #elif defined(CRSTL_ARCH_X86_32) 121 | uint64_t ret; 122 | __asm__ volatile("rdtsc" : "=A"(ret)); 123 | return ret; 124 | #elif defined(CRSTL_ARCH_X86_64) 125 | uint64_t low, high; 126 | __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); 127 | return (high << 32) | low; 128 | #elif defined(CRSTL_ARCH_ARM64) 129 | uint64_t cntvct; 130 | asm volatile("mrs %0, cntvct_el0" : "=r"(cntvct)); 131 | return cntvct; 132 | #elif defined(CRSTL_ARCH_ARM32) 133 | uint64_t pmccntr; 134 | asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); 135 | return pmccntr; 136 | #else 137 | return 0; 138 | #endif 139 | } 140 | 141 | inline void serializing_instruction() 142 | { 143 | #if defined(CRSTL_ARCH_X86) 144 | _mm_lfence(); 145 | #elif defined(CRSTL_ARCH_ARM64) 146 | __dmb(detail::_ARM64_BARRIER_ISHLD); 147 | #elif defined(CRSTL_ARCH_ARM32) 148 | __dmb(detail::_ARM_BARRIER_ISH); 149 | #endif 150 | } 151 | 152 | #if defined(CRSTL_OS_WINDOWS) 153 | 154 | inline double ticks_to_seconds() 155 | { 156 | long long ticksPerSecond; 157 | QueryPerformanceFrequency((_LARGE_INTEGER*)&ticksPerSecond); 158 | return 1.0 / (double)ticksPerSecond; 159 | } 160 | 161 | inline uint64_t current_ticks() 162 | { 163 | long long ticks; 164 | QueryPerformanceCounter((_LARGE_INTEGER*)&ticks); 165 | return (uint64_t)ticks; 166 | } 167 | 168 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_ANDROID) 169 | 170 | crstl_constexpr double ticks_to_seconds() 171 | { 172 | return 1.0 / 1000000000.0; 173 | } 174 | 175 | inline uint64_t current_ticks() 176 | { 177 | timespec time; 178 | clock_gettime(CLOCK_REALTIME, &time); 179 | return (uint64_t)(time.tv_sec * 1000000000LL + time.tv_nsec); 180 | } 181 | 182 | #elif defined(CRSTL_OS_OSX) 183 | 184 | // https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x 185 | 186 | inline double ticks_to_seconds() 187 | { 188 | mach_timebase_info_data_t timebase; 189 | mach_timebase_info(&timebase); 190 | 191 | return (double)timebase.numer / ((double)timebase.denom); 192 | } 193 | 194 | inline uint64_t current_ticks() 195 | { 196 | return mach_absolute_time(); 197 | } 198 | 199 | #endif 200 | 201 | inline uint64_t begin_cycle_count() 202 | { 203 | return get_cycle_count(); 204 | } 205 | 206 | inline uint64_t end_cycle_count() 207 | { 208 | return get_cycle_count(); 209 | } 210 | }; -------------------------------------------------------------------------------- /include/crstl/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | crstl_module_export namespace crstl 6 | { 7 | // State of the process. Errors in the execution of the process will be 8 | // notified through the return value when calling wait() 9 | namespace process_state 10 | { 11 | enum t 12 | { 13 | undefined, // Process launch has not been attempted yet 14 | launched, // Process launched successfully 15 | terminated, // Process was forcibly terminated 16 | waited, // Process was waited to completion. Check the return code 17 | error_failed_to_launch, // There was an error launching the process 18 | error_wait, // There was an error during wait 19 | }; 20 | } 21 | 22 | struct process_exit_status 23 | { 24 | enum class status 25 | { 26 | success, 27 | error 28 | }; 29 | 30 | process_exit_status() : status(status::error), exit_code(2147483647) {} 31 | 32 | process_exit_status(int exit_code) : status(status::success), exit_code(exit_code) {} 33 | 34 | explicit operator bool() const { return status == status::success; } 35 | 36 | // This return code is process-specific, and should be handled by the application 37 | int get_exit_code() 38 | { 39 | return exit_code; 40 | } 41 | 42 | private: 43 | 44 | status status; 45 | 46 | int exit_code; 47 | }; 48 | 49 | // Handles the state of a read or write operation. For example, a read on a process returns 50 | // how many bytes were read, but also whether there was an error during the operation 51 | struct process_size 52 | { 53 | enum t 54 | { 55 | success, 56 | error 57 | }; 58 | 59 | process_size() : m_result(error), m_size(0) {} 60 | process_size(size_t size) : m_result(success), m_size(size) {} 61 | 62 | explicit operator bool() const { return m_result != error; } 63 | 64 | size_t size() const { return m_size; } 65 | 66 | private: 67 | 68 | t m_result; 69 | 70 | size_t m_size; 71 | }; 72 | 73 | class process_base 74 | { 75 | public: 76 | 77 | process_base() 78 | : m_state(process_state::undefined) 79 | {} 80 | 81 | process_exit_status get_exit_status() const 82 | { 83 | return m_exit_status; 84 | } 85 | 86 | bool is_launched() const 87 | { 88 | return m_state == process_state::launched; 89 | } 90 | 91 | protected: 92 | 93 | process_state::t m_state; 94 | 95 | process_exit_status m_exit_status; 96 | 97 | private: 98 | 99 | process_base(const process_base& other) crstl_constructor_delete; 100 | 101 | process_base& operator = (const process_base& other) crstl_constructor_delete; 102 | }; 103 | } 104 | 105 | #if defined(CRSTL_OS_WINDOWS) 106 | #include "crstl/platform/process_win32.h" 107 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_OSX) 108 | #include "crstl/platform/process_posix.h" 109 | #endif -------------------------------------------------------------------------------- /include/crstl/sort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | #include "crstl/crstldef.h" 5 | #include "crstl/move_forward.h" 6 | #include "crstl/bit.h" 7 | #include "crstl/utility/memory_ops.h" 8 | 9 | crstl_module_export namespace crstl 10 | { 11 | #if defined(CRSTL_QUICK_SORT_INSERTION_SIZE) 12 | static const size_t kQuicksortInsertionLimit = CRSTL_QUICK_SORT_INSERTION_SIZE; 13 | #else 14 | static const size_t kQuicksortInsertionLimit = 32; 15 | #endif 16 | 17 | template 18 | struct less 19 | { 20 | crstl_constexpr14 bool operator()(const T& a, const T& b) const 21 | { 22 | return a < b; 23 | } 24 | }; 25 | 26 | template<> 27 | struct less 28 | { 29 | template 30 | crstl_constexpr14 bool operator()(A&& a, B&& b) const 31 | { 32 | return crstl_forward(A, a) < crstl_forward(B, b); 33 | } 34 | }; 35 | 36 | template 37 | void swap(T& left, T& right) 38 | { 39 | T temp = crstl_move(left); 40 | left = crstl_move(right); 41 | right = crstl_move(temp); 42 | } 43 | 44 | // bubble_sort is the simplest possible sort. It starts from the beginning and checks the data set multiple times, 45 | // swapping as necessary. If there are no swap in one of the runs, the array is sorted. It is very slow on large 46 | // sets and insertion_sort is usually faster 47 | template 48 | void bubble_sort(T* begin, T* end, Compare compare) 49 | { 50 | crstl_assert(begin < end); 51 | 52 | const size_t size = end - begin; 53 | 54 | bool swapped = true; 55 | 56 | while (swapped) 57 | { 58 | swapped = false; 59 | 60 | for (size_t i = 0; i < size - 1; ++i) 61 | { 62 | if (compare(begin[i + 1], begin[i])) 63 | { 64 | swap(begin[i], begin[i + 1]); 65 | swapped = true; 66 | } 67 | } 68 | } 69 | } 70 | 71 | template 72 | void bubble_sort(T* begin, T* end) 73 | { 74 | bubble_sort(begin, end, less<>()); 75 | } 76 | 77 | // insertion_sort takes an element and inserts it in the sorted position of an array. To accomplish that it checks 78 | // for all elements until one is found that does not meet the criteria. On its way all elements are moved until we 79 | // find a place for the current element 80 | template 81 | void insertion_sort(T* begin, T* end, Compare compare) 82 | { 83 | const size_t size = end - begin; 84 | 85 | for (size_t i = 1; i < size; ++i) 86 | { 87 | // Access the current element 88 | T current_element = crstl_move(begin[i]); 89 | 90 | size_t j = i; 91 | 92 | // Compare it walking backward 93 | for (; j > 0; --j) 94 | { 95 | T sort_element = crstl_move(begin[j - 1]); 96 | 97 | if (compare(current_element, sort_element)) 98 | { 99 | begin[j] = crstl_move(sort_element); 100 | } 101 | else 102 | { 103 | break; 104 | } 105 | } 106 | 107 | // Write the last element 108 | begin[j] = crstl_move(current_element); 109 | } 110 | } 111 | 112 | template 113 | void insertion_sort(T* begin, T* end) 114 | { 115 | insertion_sort(begin, end, less<>()); 116 | } 117 | 118 | template 119 | inline const T& median(const T& a, const T& b, const T& c, Compare compare) 120 | { 121 | if (compare(a, b)) 122 | { 123 | if (compare(b, c)) 124 | return b; 125 | else if (compare(a, c)) 126 | return c; 127 | else 128 | return a; 129 | } 130 | else if (compare(a, c)) 131 | return a; 132 | else if (compare(b, c)) 133 | return c; 134 | return b; 135 | } 136 | 137 | template 138 | void quick_sort(T* begin, T* end, Compare compare) 139 | { 140 | crstl_assert(begin <= end); 141 | 142 | const size_t size = end - begin; 143 | 144 | if (size > kQuicksortInsertionLimit) 145 | { 146 | // Select pivot from the middle of the array. Median of 3 has some overhead, might be better in some cases 147 | // if we can find a really fast way of selecting it (e.g. branchless for integers) 148 | T pivot = begin[size >> 1]; 149 | 150 | // Distribute items for this range. Start from the left and start swapping with the elements at the end 151 | // when items are on the wrong side of the pivot 152 | int64_t left_index = -1; 153 | int64_t right_index = size; 154 | 155 | while (true) 156 | { 157 | // We don't range compare here because whether the pivot is the maximum or minimum element, 158 | // this is guaranteed to compare with the pivot itself at some point, by construction 159 | while (compare(begin[++left_index], pivot)); 160 | while (compare(pivot, begin[--right_index])); 161 | 162 | if (left_index < right_index) 163 | { 164 | swap(begin[left_index], begin[right_index]); 165 | } 166 | else 167 | { 168 | break; 169 | } 170 | } 171 | 172 | // Call recursively for partitions 173 | quick_sort(begin, begin + left_index, compare); 174 | quick_sort(begin + left_index, end, compare); 175 | } 176 | else 177 | { 178 | insertion_sort(begin, end, compare); 179 | } 180 | } 181 | 182 | template 183 | void quick_sort(T* begin, T* end) 184 | { 185 | quick_sort(begin, end, less<>{}); 186 | } 187 | 188 | // Map sort to quick_sort 189 | template 190 | void sort(T* begin, T* end) 191 | { 192 | quick_sort(begin, end, less<>{}); 193 | } 194 | }; -------------------------------------------------------------------------------- /include/crstl/span.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | #include "crstl/type_utils.h" 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::span 14 | // 15 | // Replacement for std::span 16 | // 17 | 18 | crstl_module_export namespace crstl 19 | { 20 | template 21 | class span_base 22 | { 23 | protected: 24 | 25 | // Use this to allow child class to use m_length as though it was the member variable 26 | enum t : size_t 27 | { 28 | m_length = Size 29 | }; 30 | 31 | span_base(T* data) 32 | : m_data(data) 33 | {} 34 | 35 | T* m_data; 36 | }; 37 | 38 | template 39 | class span_base 40 | { 41 | protected: 42 | 43 | span_base(T* data, size_t length) 44 | : m_data(data) 45 | , m_length(length) 46 | {} 47 | 48 | T* m_data; 49 | size_t m_length; 50 | }; 51 | 52 | template 53 | class span : public span_base 54 | { 55 | public: 56 | 57 | typedef span_base base_type; 58 | typedef T element_type; 59 | typedef typename remove_cv:: type value_type; 60 | typedef size_t size_type; 61 | typedef T& reference; 62 | typedef const T& const_reference; 63 | typedef T* pointer; 64 | typedef const T* const_pointer; 65 | typedef T* iterator; 66 | typedef const T* const_iterator; 67 | 68 | using base_type::m_data; 69 | using base_type::m_length; 70 | 71 | template ::type> 72 | span(T* data, size_t length) crstl_noexcept : base_type(data, length) {} 73 | 74 | template ::type> 75 | span(T* data) crstl_noexcept : base_type(data) {} 76 | 77 | #if defined(CRSTL_FEATURE_INITIALIZER_LISTS) 78 | 79 | template ::type> 80 | crstl_constexpr14 span(std::initializer_list ilist) crstl_noexcept : base_type(ilist.begin(), ilist.size()) {} 81 | 82 | #endif 83 | 84 | T& at(size_t i) { crstl_assert(i < m_length); return m_data[i]; } 85 | const T& at(size_t i) const { crstl_assert(i < m_length); return m_data[i]; } 86 | 87 | T& back() { crstl_assert(m_length > 0); return m_data[m_length - 1]; } 88 | const T& back() const { crstl_assert(m_length > 0); return m_data[m_length - 1]; } 89 | 90 | iterator begin() { return &m_data[0]; } 91 | const_iterator begin() const { return &m_data[0]; } 92 | const_iterator cbegin() const { return &m_data[0]; } 93 | 94 | pointer data() { return &m_data[0]; } 95 | const_pointer data() const { return &m_data[0]; } 96 | 97 | crstl_nodiscard 98 | crstl_constexpr bool empty() const { return m_length == 0; } 99 | 100 | iterator end() { return &m_data[0] + m_length; } 101 | const_iterator end() const { return &m_data[0] + m_length; } 102 | const_iterator cend() const { return &m_data[0] + m_length; } 103 | 104 | span first(size_t length) const { crstl_assert(length < m_length); return subspan(m_data + length, m_length - length); } 105 | 106 | T& front() { return m_data[0]; } 107 | const T& front() const { return m_data[0]; } 108 | 109 | span last(size_t length) const { crstl_assert(length < m_length); return subspan(m_data + (m_length - length), length); } 110 | 111 | size_t size() const { return m_length; } 112 | 113 | size_t size_bytes() const { return m_length * sizeof(T); } 114 | 115 | span subspan(size_t offset, size_t length) const 116 | { 117 | crstl_assert(offset < m_length); 118 | return span(m_data + offset, length); 119 | } 120 | 121 | T& operator [] (size_t i) { crstl_assert(i < m_length); return m_data[i]; } 122 | 123 | const T& operator [] (size_t i) const { crstl_assert(i < m_length); return m_data[i]; } 124 | }; 125 | }; -------------------------------------------------------------------------------- /include/crstl/stack_vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | #include "crstl/crstldef.h" 5 | #include "crstl/vector_base.h" 6 | 7 | // crstl::stack_vector 8 | // 9 | // Replacement for std::vector that doesn't manage its own memory. Useful for using dynamically-size stack-allocated memory via 10 | // the alloca macro, but any other source of externally managed memory works too. stack_vector does not deallocate or free 11 | // memory, but it does call the destructor of objects it created 12 | // 13 | // Remember that alloca has function-scope lifetime, not braced. Also, reserving too much stack memory can cause a stack overflow, 14 | // so use with care. 15 | // 16 | // - Example usage with stack memory 17 | // crstl::stack_vector stackvector(crstl_alloca_t(MyClass, 64)); 18 | // 19 | // - default initialization, copy, move and assignment are disallowed 20 | // to prevent lifetime issues 21 | // 22 | // - Cannot resize, reserve or otherwise alter capacity once created 23 | // 24 | // - Consider using a fixed_vector first, and if requirements require dynamic 25 | // size for temporary usage, use stack_vector instead 26 | 27 | crstl_module_export namespace crstl 28 | { 29 | template 30 | class stack_vector_storage 31 | { 32 | public: 33 | 34 | typedef size_t length_type; 35 | 36 | stack_vector_storage() : m_data(nullptr), m_length(0), m_capacity(0) {} 37 | 38 | size_t get_capacity() const { return m_capacity; } 39 | 40 | void reallocate_if_length_equals_capacity() 41 | { 42 | crstl_assert(m_length < m_capacity); 43 | } 44 | 45 | void reallocate_if_length_greater_than_capacity(size_t length) 46 | { 47 | crstl_unused(length); 48 | crstl_assert(length <= m_capacity); 49 | } 50 | 51 | protected: 52 | 53 | T* m_data; 54 | 55 | length_type m_length; 56 | 57 | size_t m_capacity; 58 | }; 59 | 60 | template 61 | class stack_vector : public vector_base> 62 | { 63 | public: 64 | 65 | typedef vector_base> base_type; 66 | typedef stack_vector this_type; 67 | 68 | typedef typename base_type::length_type length_type; 69 | typedef typename base_type::reference reference; 70 | typedef typename base_type::const_reference const_reference; 71 | typedef typename base_type::iterator iterator; 72 | typedef typename base_type::const_iterator const_iterator; 73 | typedef typename base_type::pointer pointer; 74 | typedef typename base_type::const_pointer const_pointer; 75 | 76 | using base_type::clear; 77 | 78 | crstl_constexpr14 stack_vector(transient_memory_t init) 79 | { 80 | m_data = (T*)init.memory; 81 | m_length = 0; 82 | m_capacity = init.capacity; 83 | } 84 | 85 | ~stack_vector() 86 | { 87 | clear(); 88 | } 89 | 90 | private: 91 | 92 | using base_type::reserve; 93 | 94 | using base_type::m_length; 95 | using base_type::m_data; 96 | using base_type::m_capacity; 97 | 98 | // Stack vectors are meant to be transient. They are allocated on the stack and given stack 99 | // memory for their operations. Because of that, we don't allow default construction or copy 100 | stack_vector() crstl_constructor_delete; 101 | 102 | stack_vector(const stack_vector& other) crstl_constructor_delete; 103 | 104 | stack_vector(const stack_vector&& other) crstl_constructor_delete; 105 | }; 106 | }; 107 | -------------------------------------------------------------------------------- /include/crstl/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #include "crstl/tuple.h" 8 | 9 | // crstl::thread 10 | // 11 | // Replacement for std::thread 12 | // 13 | // - Accepts a thread_parameters structure that configures the thread for initialization 14 | // This allows the implementation to set a thread name for debugging or an initial priority 15 | // - There are extra functions not present in the standard such as 16 | // - set_priority: sets thread priority 17 | // - start: combine with with the delay_start parameter to start the thread at some other 18 | // point after creation 19 | // 20 | 21 | crstl_module_export namespace crstl 22 | { 23 | const uint32_t DefaultStackSize = 32 * 1024; 24 | 25 | namespace thread_priority 26 | { 27 | enum t 28 | { 29 | idle = 0, 30 | lowest = 1, 31 | below_normal = 2, 32 | normal = 3, 33 | abovenormal = 4, 34 | highest = 5, 35 | count 36 | }; 37 | }; 38 | 39 | struct thread_parameters 40 | { 41 | thread_parameters() 42 | : stack_size(0) 43 | , priority(thread_priority::normal) 44 | , core(-1) 45 | , delay_start(false) 46 | , debug_name(nullptr) 47 | {} 48 | 49 | // Size of stack in bytes 50 | uint32_t stack_size; 51 | 52 | // Define initial priority 53 | thread_priority::t priority; 54 | 55 | // Peg thread to a core 56 | int32_t core; 57 | 58 | // Start thread execution with resume(), otherwise it runs as soon as the thread is created 59 | bool delay_start; 60 | 61 | // Name that appears in thread when debugging 62 | const char* debug_name; 63 | }; 64 | 65 | class thread; 66 | 67 | // Data we copy from the thread_parameters that needs to have the same lifetime as the thread 68 | template 69 | struct thread_data 70 | { 71 | thread* thread_ptr; 72 | 73 | thread_parameters parameters; 74 | 75 | Function function; 76 | 77 | crstl::tuple function_args; 78 | }; 79 | 80 | // Call the Function with the Tuple as arguments. These come from the thread_data we create on construction of the thread 81 | template 82 | void call(Function&& function, Tuple&& arguments, crstl::index_sequence) 83 | { 84 | crstl_unused(arguments); // For some reason some compilers seem to think arguments is unused when the tuple is empty 85 | function(crstl::get(arguments)...); 86 | } 87 | 88 | // Base class of thread. Ensures copy constructor is deleted 89 | class thread_base 90 | { 91 | public: 92 | 93 | thread_base() 94 | : m_started(false) 95 | , m_stack_bytes(0) 96 | , m_priority(thread_priority::count) 97 | {} 98 | 99 | protected: 100 | 101 | bool m_started; 102 | 103 | uint32_t m_stack_bytes; 104 | 105 | thread_priority::t m_priority; 106 | 107 | private: 108 | 109 | thread_base(const thread_base& other) crstl_constructor_delete; 110 | 111 | thread_base& operator = (const thread_base& other) crstl_constructor_delete; 112 | }; 113 | }; 114 | 115 | #if defined(CRSTL_OS_WINDOWS) 116 | #include "crstl/platform/thread_win32.h" 117 | #elif defined(CRSTL_OS_LINUX) || defined(CRSTL_OS_ANDROID) || defined(CRSTL_OS_OSX) 118 | #include "crstl/platform/thread_posix.h" 119 | #endif 120 | 121 | static_assert(crstl_is_base_of(crstl::thread_base, crstl::thread) && crstl_is_final(crstl::thread), ""); -------------------------------------------------------------------------------- /include/crstl/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #include "crstl/platform/timer_platform.h" 8 | 9 | // crstl::timer 10 | // 11 | // Replacement for std::chrono facilities 12 | // 13 | 14 | crstl_module_export namespace crstl 15 | { 16 | template 17 | struct timer_globals { static const double TicksToSeconds; }; 18 | 19 | // This only gets initialized once. See https://youtu.be/xVT1y0xWgww?t=1320 for the trick and 20 | // https://www.reddit.com/r/cpp/comments/e41t8r/stdnumeric_limits_members_functions_vs_constants/ 21 | // for the variation that works across compilers 22 | template 23 | const double timer_globals::TicksToSeconds = ticks_to_seconds(); 24 | 25 | class time 26 | { 27 | public: 28 | 29 | time() : m_ticks(0) {} 30 | 31 | explicit time(int64_t ticks) : m_ticks(ticks) {} 32 | 33 | // Return raw ticks value 34 | int64_t ticks() const 35 | { 36 | return m_ticks; 37 | } 38 | 39 | static time now() 40 | { 41 | return time(current_ticks()); 42 | } 43 | 44 | // Return ticks as seconds 45 | double seconds() const 46 | { 47 | return (double)m_ticks * timer_globals::TicksToSeconds; 48 | } 49 | 50 | // Return ticks as milliseconds 51 | double milliseconds() const 52 | { 53 | return (double)m_ticks * 1000.0 * timer_globals::TicksToSeconds; 54 | } 55 | 56 | // Return ticks as microseconds 57 | double microseconds() const 58 | { 59 | return (double)m_ticks * 1000000.0 * timer_globals::TicksToSeconds; 60 | } 61 | 62 | // Return number of ticks per second 63 | double ticks_per_second() const 64 | { 65 | return 1.0 / ((double)m_ticks * timer_globals::TicksToSeconds); 66 | } 67 | 68 | inline time operator + (const time& other) const 69 | { 70 | return time(m_ticks + other.m_ticks); 71 | } 72 | 73 | inline time operator - (const time& other) const 74 | { 75 | return time(m_ticks - other.m_ticks); 76 | } 77 | 78 | inline time& operator += (const time& other) 79 | { 80 | m_ticks += other.m_ticks; return *this; 81 | } 82 | 83 | inline time& operator -= (const time& other) 84 | { 85 | m_ticks -= other.m_ticks; return *this; 86 | } 87 | 88 | inline time operator * (const int64_t other) const 89 | { 90 | return time(m_ticks * other); 91 | } 92 | 93 | inline time& operator *= (const int64_t other) 94 | { 95 | m_ticks *= other; return *this; 96 | } 97 | 98 | inline time operator / (const int64_t other) const 99 | { 100 | return time(m_ticks / other); 101 | } 102 | 103 | inline time& operator /= (const int64_t other) 104 | { 105 | m_ticks /= other; return *this; 106 | } 107 | 108 | inline bool operator > (const time& other) const 109 | { 110 | return m_ticks > other.m_ticks; 111 | } 112 | 113 | inline bool operator < (const time& other) const 114 | { 115 | return m_ticks < other.m_ticks; 116 | } 117 | 118 | inline bool operator >= (const time& other) const 119 | { 120 | return m_ticks >= other.m_ticks; 121 | } 122 | 123 | inline bool operator <= (const time& other) const 124 | { 125 | return m_ticks <= other.m_ticks; 126 | } 127 | 128 | inline bool operator == (const time& other) const 129 | { 130 | return m_ticks == other.m_ticks; 131 | } 132 | 133 | inline bool operator != (const time& other) const 134 | { 135 | return m_ticks != other.m_ticks; 136 | } 137 | 138 | private: 139 | 140 | int64_t m_ticks; 141 | }; 142 | 143 | class timer 144 | { 145 | public: 146 | 147 | timer(bool immediate_start = true) 148 | { 149 | immediate_start ? start() : reset(); 150 | } 151 | 152 | void reset() 153 | { 154 | m_start = time(); 155 | } 156 | 157 | void start() 158 | { 159 | m_start = time::now(); 160 | } 161 | 162 | time elapsed() const 163 | { 164 | return time::now() - m_start; 165 | } 166 | 167 | private: 168 | 169 | time m_start; 170 | }; 171 | 172 | typedef uint64_t clock_cycle_t; 173 | 174 | class cycle_timer 175 | { 176 | public: 177 | 178 | cycle_timer() 179 | { 180 | reset(); 181 | start(); 182 | } 183 | 184 | static clock_cycle_t now() 185 | { 186 | return begin_cycle_count(); 187 | } 188 | 189 | void reset() 190 | { 191 | m_start = 0; 192 | } 193 | 194 | void start() 195 | { 196 | m_start = begin_cycle_count(); 197 | } 198 | 199 | clock_cycle_t elapsed() 200 | { 201 | return end_cycle_count() - m_start; 202 | } 203 | 204 | private: 205 | 206 | clock_cycle_t m_start; 207 | }; 208 | }; 209 | -------------------------------------------------------------------------------- /include/crstl/tuple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/move_forward.h" 6 | 7 | #include "crstl/type_builtins.h" 8 | 9 | #include "crstl/utility/sequence.h" 10 | 11 | // crstl::tuple 12 | // 13 | // Replacement for std::tuple 14 | // 15 | 16 | crstl_module_export namespace crstl 17 | { 18 | #if defined(CRSTL_COMPILER_CLANG) 19 | 20 | template 21 | struct tuple_element 22 | { 23 | typedef __type_pack_element type; 24 | }; 25 | 26 | #else 27 | 28 | namespace detail 29 | { 30 | // https://ldionne.com/2015/11/29/efficient-parameter-pack-indexing/ 31 | template struct parameter_pack_indexed { using type = T; }; 32 | 33 | template 34 | parameter_pack_indexed parameter_pack_select(parameter_pack_indexed) {} 35 | 36 | template struct parameter_pack_indexer; 37 | 38 | template 39 | struct parameter_pack_indexer, Ts...> : parameter_pack_indexed... {}; 40 | }; 41 | 42 | template 43 | struct tuple_element 44 | { 45 | typedef detail::parameter_pack_indexer, Ts...> indexer_type; 46 | typedef typename decltype(detail::parameter_pack_select(indexer_type {}))::type type; 47 | }; 48 | 49 | #endif 50 | 51 | // Base declaration of tuple_leaf. Holds the value for each element in the tuple 52 | // A tuple is a collection of tuple_leafs, with an associated index for each leaf 53 | template 54 | class tuple_leaf; 55 | 56 | // Generic tuple_leaf implementation 57 | template 58 | class tuple_leaf 59 | { 60 | public: 61 | 62 | tuple_leaf() : m_value() {} 63 | tuple_leaf(const ValueT& value) : m_value(value) {} 64 | tuple_leaf(ValueT&& value) : m_value(value) {} 65 | tuple_leaf(const tuple_leaf& other) : m_value(other.m_value) {} 66 | 67 | template 68 | explicit tuple_leaf(const tuple_leaf& other) : m_value(other.value) {} 69 | 70 | template 71 | tuple_leaf& operator = (OtherValueT&& t) 72 | { 73 | m_value = crstl_forward(OtherValueT, t); 74 | return *this; 75 | } 76 | 77 | ValueT& get() { return m_value; } 78 | const ValueT& get() const { return m_value; } 79 | 80 | private: 81 | 82 | tuple_leaf& operator = (const tuple_leaf& other) crstl_constructor_delete; 83 | 84 | ValueT m_value; 85 | }; 86 | 87 | // Empty base class specialization for tuple_leaf 88 | template 89 | class tuple_leaf : private ValueT 90 | { 91 | public: 92 | 93 | tuple_leaf() crstl_constructor_default; 94 | tuple_leaf(const ValueT& value) { crstl_unused(value); } 95 | tuple_leaf(ValueT&& value) { crstl_unused(value); } 96 | tuple_leaf(const tuple_leaf& other) : ValueT(other.get()) {} 97 | 98 | template 99 | explicit tuple_leaf(OtherValueT&& t) : ValueT(forward(t)) {} 100 | 101 | template 102 | explicit tuple_leaf(const tuple_leaf& t) : ValueT(t.get()) {} 103 | 104 | template 105 | tuple_leaf& operator = (OtherValueT&& t) 106 | { 107 | ValueT::operator = (forward(t)); 108 | return *this; 109 | } 110 | 111 | ValueT& get() { return static_cast(*this); } 112 | const ValueT& get() const { return static_cast(*this); } 113 | 114 | private: 115 | 116 | tuple_leaf& operator = (const tuple_leaf&) crstl_constructor_delete; 117 | }; 118 | 119 | // Handles indices and types 120 | template 121 | struct tuple_implementation; 122 | 123 | // e.g. tuple_implementation<0, 1, 2, int, float, char> 124 | // Multiple inheritance of tuple_leaf for every combination of Indices and Ts, so every member is laid out consecutively 125 | // This guarantees that the order in which we add the elements is the order in which they appear. Each constructor then 126 | // calls the parent constructors in sequence as well 127 | template 128 | struct tuple_implementation, Ts...> : public tuple_leaf... 129 | { 130 | crstl_constexpr tuple_implementation() crstl_constructor_default; 131 | 132 | template 133 | explicit tuple_implementation(integer_sequence, ValueTs&&... values) : tuple_leaf(crstl_forward(ValueTs, values))... {} 134 | 135 | template 136 | explicit tuple_implementation(integer_sequence, const ValueTs&... values) : tuple_leaf(values)... {} 137 | }; 138 | 139 | template class tuple; 140 | 141 | // tuple owns a tuple_implementation and passes all parameters for it to handle it appropriately 142 | // This is because we convert the variadic tuple parameters into an integer sequence plus a parameter count, 143 | // then forward the parameters across 144 | template 145 | class tuple 146 | { 147 | public: 148 | 149 | crstl_constexpr tuple() crstl_constructor_default; 150 | 151 | tuple(T&& arg1, Ts&& ... args) : m_implementation(make_integer_sequence{}, crstl_forward(T, arg1), crstl_forward(Ts, args)...) {} 152 | 153 | tuple(const T& arg1, const Ts& ... args) : m_implementation(make_integer_sequence{}, arg1, args...) {} 154 | 155 | // This does not conform to the standard but is a lot more convenient than the free get<>() function 156 | template 157 | const typename tuple_element::type& get() 158 | { 159 | return static_cast::type>&>(m_implementation).get(); 160 | } 161 | 162 | crstl_constexpr size_t size() const { return sizeof...(Ts) + 1; } 163 | 164 | tuple_implementation, T, Ts...> m_implementation; 165 | }; 166 | 167 | // Definition of empty tuple. This may seem like an odd class to have, but consider for example a tuple that stores arguments 168 | // to a function. It needs to store the arguments to f(void) as well, which the generic tuple cannot handle 169 | template<> 170 | class tuple<> 171 | { 172 | public: 173 | 174 | crstl_constexpr size_t size() const { return 0; } 175 | }; 176 | 177 | // We get the type from the index using the type_pack_element helper, and using that type we cast 178 | // m_implementation to the tuple_leaf with the corresponding index and type. Since m_implementation 179 | // derives from all the possible tuple_leafs, we get a valid object and just call get() on it 180 | template 181 | typename tuple_element::type& get(tuple& t) 182 | { 183 | return static_cast::type>&>(t.m_implementation).get(); 184 | } 185 | 186 | template 187 | const typename tuple_element::type& get(const tuple& t) 188 | { 189 | return static_cast::type>&>(t.m_implementation).get(); 190 | } 191 | 192 | template 193 | typename tuple_element::type&& get(tuple&& t) 194 | { 195 | return static_cast::type>&&>(crstl_move(t.m_implementation)).get(); 196 | } 197 | 198 | template 199 | const typename tuple_element::type&& get(const tuple&& t) 200 | { 201 | return static_cast::type>&&>(crstl_move(t.m_implementation)).get(); 202 | } 203 | }; -------------------------------------------------------------------------------- /include/crstl/type_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #include "crstl/type_builtins.h" 8 | 9 | crstl_module_export namespace crstl 10 | { 11 | // is_array 12 | 13 | template struct is_array { static const bool value = false; }; 14 | template struct is_array { static const bool value = true; }; 15 | template struct is_array { static const bool value = true; }; 16 | 17 | // remove_extent: removes array extents 18 | 19 | template struct remove_extent { typedef T type; }; 20 | template struct remove_extent { typedef T type; }; 21 | template struct remove_extent { typedef T type; }; 22 | 23 | template 24 | crstl_constexpr size_t array_size(const T(&)[N]) crstl_noexcept 25 | { 26 | return N; 27 | } 28 | }; -------------------------------------------------------------------------------- /include/crstl/type_builtins.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | //---------------- 6 | // Type Properties 7 | //---------------- 8 | 9 | // Whether type is empty, i.e. has no members. Mainly used for the 10 | // empty base class optimization 11 | #define crstl_is_empty(T) __is_empty(T) 12 | 13 | // Whether type is a union 14 | #define crstl_is_union(T) __is_union(T) 15 | 16 | // Whether type is a class 17 | #define crstl_is_class(T) __is_class(T) 18 | 19 | // Whether type is an enum 20 | #define crstl_is_enum(T) __is_enum(T) 21 | 22 | // Whether type has a pure virtual function 23 | #define crstl_is_abstract(T) __is_abstract(T) 24 | 25 | // Whether type declares or inherits at least one virtual function 26 | #define crstl_is_polymorphic(T) __is_polymorphic(T) 27 | 28 | // Whether Base is a base class of Derived 29 | #define crstl_is_base_of(Base, Derived) __is_base_of(Base, Derived) 30 | 31 | // Whether this object can be safely hashed, as it has no padding that could alter the result 32 | #define crstl_has_unique_object_representations(T) __has_unique_object_representations(T) 33 | 34 | // Whether class From can be implicitly converted to To 35 | #if defined(CRSTL_COMPILER_MSVC) 36 | 37 | #define crstl_is_convertible(From, To) __is_convertible_to(From, To) 38 | 39 | #else 40 | 41 | #define crstl_is_convertible(From, To) __is_convertible(From, To) 42 | 43 | #endif 44 | 45 | // Whether type is trivially constructible and destructible 46 | #if crstl_has_builtin(__is_trivially_destructible) || (defined(CRSTL_COMPILER_MSVC) && CRSTL_MSVC_VERSION >= CRSTL_MSVC_2015) 47 | 48 | #define crstl_is_trivially_constructible(T) __is_trivially_constructible(T) 49 | #define crstl_is_trivially_destructible(T) __is_trivially_destructible(T) 50 | 51 | #else 52 | 53 | #define crstl_is_trivially_constructible(T) __has_trivial_constructor(T) 54 | #define crstl_is_trivially_destructible(T) __has_trivial_destructor(T) 55 | 56 | #endif 57 | 58 | // Whether type is trivially copyable, i.e. whether we can use mempcy directly 59 | // to copy an object or stream it in 60 | #if defined(CRSTL_COMPILER_MSVC) && (CRSTL_MSVC_VERSION < CRSTL_MSVC_2015) 61 | 62 | #define crstl_is_trivially_copyable(T) __has_trivial_copy(T) 63 | #define crstl_is_final(T) false 64 | 65 | #else 66 | 67 | #define crstl_is_trivially_copyable(T) __is_trivially_copyable(T) 68 | #define crstl_is_final(T) __is_final(T) 69 | 70 | #endif 71 | 72 | // Whether type is signed 73 | #define crstl_is_signed(T) (T(-1) < T(0)) 74 | 75 | // Whether type is unsigned 76 | #define crstl_is_unsigned(T) (T(-1) > T(0)) -------------------------------------------------------------------------------- /include/crstl/type_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #include "crstl/type_builtins.h" 8 | 9 | crstl_module_export namespace crstl 10 | { 11 | // remove_cv 12 | 13 | template struct remove_const { typedef T type; }; 14 | template struct remove_const { typedef T type; }; 15 | template struct remove_const { typedef T type[]; }; 16 | template struct remove_const { typedef T type[N]; }; 17 | 18 | template struct remove_volatile { typedef T type; }; 19 | template struct remove_volatile { typedef T type; }; 20 | template struct remove_volatile { typedef T type[]; }; 21 | template struct remove_volatile { typedef T type[N]; }; 22 | 23 | template struct remove_cv { typedef typename crstl::remove_volatile::type>::type type; }; 24 | 25 | // enable_if 26 | 27 | template struct enable_if {}; 28 | template struct enable_if { typedef T type; }; 29 | 30 | // conditional 31 | 32 | template struct conditional { typedef T2 type; }; // Type is T2 when Test is false 33 | template struct conditional { typedef T1 type; }; // Type is T1 when Test is true 34 | 35 | // is_same 36 | 37 | template struct is_same { static const bool value = false; }; 38 | template struct is_same { static const bool value = true; }; 39 | 40 | // is_void 41 | 42 | template struct is_void { static const bool value = false; }; 43 | template<> struct is_void { static const bool value = true; }; 44 | }; -------------------------------------------------------------------------------- /include/crstl/unique_ptr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/crstldef.h" 6 | 7 | #include "crstl/move_forward.h" 8 | 9 | #include "crstl/type_array.h" 10 | 11 | // crstl::unique_ptr 12 | // 13 | // Replacement for std::unique_ptr, functionally the same 14 | // but with a very small compile time footprint 15 | // The move-only semantics remain the same 16 | // 17 | // - unique_ptr doesn't allocate memory, the pointer is supplied externally 18 | // but it does deallocate memory, so care has to be take to allocate and 19 | // deallocate in a consistent manner 20 | 21 | crstl_module_export namespace crstl 22 | { 23 | template 24 | class unique_ptr_base 25 | { 26 | public: 27 | 28 | typedef T element_type; 29 | typedef T* pointer; 30 | 31 | crstl_constexpr unique_ptr_base() crstl_noexcept : m_ptr(nullptr) {} 32 | 33 | crstl_constexpr explicit unique_ptr_base(T* ptr) crstl_noexcept : m_ptr(ptr) {} 34 | 35 | crstl_constexpr unique_ptr_base(nullptr_t) crstl_noexcept : m_ptr(nullptr) {} 36 | 37 | crstl_constexpr14 unique_ptr_base(unique_ptr_base&& uptr) crstl_noexcept 38 | { 39 | m_ptr = uptr.m_ptr; 40 | uptr.m_ptr = nullptr; 41 | } 42 | 43 | crstl_constexpr14 unique_ptr_base& operator = (nullptr_t) crstl_noexcept 44 | { 45 | static_cast(*this).reset(nullptr); 46 | return *this; 47 | } 48 | 49 | crstl_constexpr14 unique_ptr_base& operator = (unique_ptr_base&& uptr) crstl_noexcept 50 | { 51 | m_ptr = uptr.m_ptr; 52 | uptr.m_ptr = nullptr; 53 | return *this; 54 | } 55 | 56 | crstl_constexpr T* operator ->() const crstl_noexcept 57 | { 58 | return m_ptr; 59 | } 60 | 61 | bool operator!() const crstl_noexcept 62 | { 63 | return (m_ptr == nullptr); 64 | } 65 | 66 | ~unique_ptr_base() crstl_noexcept 67 | { 68 | static_cast(*this).reset(nullptr); 69 | } 70 | 71 | typedef T* (unique_ptr_base::* boolean)() const; 72 | 73 | operator boolean() const crstl_noexcept 74 | { 75 | // Return anything that isn't easily castable but is guaranteed to be non-null, such as the get function pointer 76 | return m_ptr ? &unique_ptr_base::get : nullptr; 77 | } 78 | 79 | T* get() const 80 | { 81 | return m_ptr; 82 | } 83 | 84 | bool operator == (const unique_ptr_base& other) const { return m_ptr == other.m_ptr; } 85 | bool operator != (const unique_ptr_base& other) const { return m_ptr != other.m_ptr; } 86 | bool operator < (const unique_ptr_base& other) const { return m_ptr < other.m_ptr; } 87 | bool operator <= (const unique_ptr_base& other) const { return m_ptr <= other.m_ptr; } 88 | bool operator > (const unique_ptr_base& other) const { return m_ptr > other.m_ptr; } 89 | bool operator >= (const unique_ptr_base& other) const { return m_ptr >= other.m_ptr; } 90 | 91 | bool operator == (pointer ptr) const { return m_ptr == ptr; } 92 | bool operator != (pointer ptr) const { return m_ptr != ptr; } 93 | bool operator < (pointer ptr) const { return m_ptr < ptr; } 94 | bool operator <= (pointer ptr) const { return m_ptr <= ptr; } 95 | bool operator > (pointer ptr) const { return m_ptr > ptr; } 96 | bool operator >= (pointer ptr) const { return m_ptr >= ptr; } 97 | 98 | bool operator == (nullptr_t) const { return m_ptr == nullptr; } 99 | bool operator != (nullptr_t) const { return m_ptr != nullptr; } 100 | bool operator < (nullptr_t) const { return m_ptr < nullptr; } 101 | bool operator <= (nullptr_t) const { return m_ptr <= nullptr; } 102 | bool operator > (nullptr_t) const { return m_ptr > nullptr; } 103 | bool operator >= (nullptr_t) const { return m_ptr >= nullptr; } 104 | 105 | protected: 106 | 107 | pointer m_ptr; 108 | 109 | private: 110 | 111 | unique_ptr_base(const unique_ptr_base& uptr) crstl_constructor_delete; 112 | unique_ptr_base& operator = (const unique_ptr_base& uptr) crstl_constructor_delete; 113 | unique_ptr_base& operator = (T* ptr) crstl_constructor_delete; 114 | }; 115 | 116 | // unique_ptr for scalar types 117 | 118 | template 119 | class unique_ptr : public unique_ptr_base, T> 120 | { 121 | public: 122 | 123 | typedef unique_ptr_base, T> base; 124 | 125 | crstl_constexpr unique_ptr() crstl_noexcept : base() {} 126 | 127 | crstl_constexpr explicit unique_ptr(T* ptr) crstl_noexcept : base(ptr) {} 128 | 129 | crstl_constexpr unique_ptr(nullptr_t) crstl_noexcept : base(nullptr) {} 130 | 131 | crstl_constexpr unique_ptr(unique_ptr&& uptr) crstl_noexcept : base(crstl_move(uptr)) {} 132 | 133 | crstl_constexpr14 unique_ptr& operator = (nullptr_t) crstl_noexcept { base::operator = (nullptr); return *this; } 134 | 135 | crstl_constexpr14 unique_ptr& operator = (unique_ptr&& uptr) crstl_noexcept { base::operator = (crstl_move(uptr)); return *this; } 136 | 137 | crstl_constexpr14 void reset(nullptr_t) crstl_noexcept 138 | { 139 | delete base::m_ptr; 140 | base::m_ptr = nullptr; 141 | } 142 | 143 | crstl_constexpr14 void reset(typename base::pointer ptr = base::pointer()) crstl_noexcept 144 | { 145 | delete base::m_ptr; 146 | base::m_ptr = ptr; 147 | } 148 | }; 149 | 150 | // unique_ptr for arrays 151 | 152 | template 153 | class unique_ptr : public unique_ptr_base, T> 154 | { 155 | public: 156 | 157 | typedef unique_ptr_base, T> base; 158 | 159 | crstl_constexpr unique_ptr() crstl_noexcept : base() {} 160 | 161 | crstl_constexpr explicit unique_ptr(T* ptr) crstl_noexcept : base(ptr) {} 162 | 163 | crstl_constexpr unique_ptr(nullptr_t) crstl_noexcept : base(nullptr) {} 164 | 165 | crstl_constexpr unique_ptr(unique_ptr&& uptr) crstl_noexcept : base(crstl_move(uptr)) {} 166 | 167 | crstl_constexpr14 unique_ptr& operator = (nullptr_t) crstl_noexcept { base::operator = (nullptr); return *this; } 168 | 169 | crstl_constexpr14 unique_ptr& operator = (unique_ptr&& uptr) crstl_noexcept { base::operator = (crstl_move(uptr)); return *this; } 170 | 171 | crstl_constexpr14 void reset(nullptr_t) crstl_noexcept 172 | { 173 | delete[] base::m_ptr; 174 | base::m_ptr = nullptr; 175 | } 176 | 177 | crstl_constexpr14 void reset(typename base::pointer ptr = base::pointer()) crstl_noexcept 178 | { 179 | delete[] base::m_ptr; 180 | base::m_ptr = ptr; 181 | } 182 | }; 183 | 184 | template struct unique_ptr_enable_if; 185 | template struct unique_ptr_enable_if { typedef T type; }; 186 | 187 | template ::value>::type = 0> 188 | crstl_nodiscard crstl_constexpr unique_ptr make_unique(Args&&... args) 189 | { 190 | return unique_ptr(new T(crstl_forward(Args, args)...)); 191 | } 192 | 193 | template ::value>::type = 0> 194 | crstl_nodiscard crstl_constexpr unique_ptr make_unique(const size_t size) 195 | { 196 | using TNoExtents = typename remove_extent::type; 197 | return unique_ptr(new TNoExtents[size]()); 198 | } 199 | 200 | template ::value>::type = 0> 201 | crstl_nodiscard crstl_constexpr unique_ptr make_unique_uninitialized() 202 | { 203 | return unique_ptr(new T); 204 | } 205 | 206 | template ::value>::type = 0> 207 | crstl_nodiscard crstl_constexpr unique_ptr make_unique_uninitialized(const size_t size) 208 | { 209 | using TNoExtents = typename remove_extent::type; 210 | return unique_ptr(new TNoExtents[size]()); 211 | } 212 | }; -------------------------------------------------------------------------------- /include/crstl/utility/constructor_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | crstl_module_export namespace crstl 4 | { 5 | enum ctor_concatenate_e 6 | { 7 | ctor_concatenate 8 | }; 9 | 10 | enum ctor_no_initialize_e 11 | { 12 | ctor_no_initialize 13 | }; 14 | }; -------------------------------------------------------------------------------- /include/crstl/utility/fixed_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace crstl 4 | { 5 | template struct fixed_length_type { typedef uint64_t type; }; 6 | template<> struct fixed_length_type<1> { typedef uint8_t type; }; 7 | template<> struct fixed_length_type<2> { typedef uint16_t type; }; 8 | template<> struct fixed_length_type<4> { typedef uint32_t type; }; 9 | template struct fixed_length_select_type { typedef typename fixed_length_type::type type; }; 10 | }; -------------------------------------------------------------------------------- /include/crstl/utility/hashmap_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/utility/placement_new.h" 6 | 7 | #define crstl_is_pow2(n) (!((n) & ((n) - 1))) 8 | 9 | crstl_module_export namespace crstl 10 | { 11 | // Compute which bucket a certain hash_value goes into, by taking into account whether it's a power of 2 or not 12 | 13 | template 14 | struct compute_bucket_function 15 | { 16 | static size_t compute_bucket(size_t hash_value, size_t bucket_count) { return hash_value % bucket_count; } 17 | }; 18 | 19 | template<> 20 | struct compute_bucket_function 21 | { 22 | static size_t compute_bucket(size_t hash_value, size_t bucket_count) { return hash_value & (bucket_count - 1); } 23 | }; 24 | 25 | template 26 | size_t compute_bucket(size_t hash_value) 27 | { 28 | size_t bucket_index = compute_bucket_function::compute_bucket(hash_value, BucketCount); 29 | crstl_assert(bucket_index < BucketCount); 30 | return bucket_index; 31 | } 32 | 33 | // To be able to select between a const and a non-const object for const and non-const iterators 34 | template 35 | struct hashmap_type_select { typedef IsTrueType type; }; 36 | 37 | template 38 | struct hashmap_type_select { typedef IsFalseType type; }; 39 | 40 | // Behavior to run when node exists already 41 | namespace exists_behavior 42 | { 43 | enum t 44 | { 45 | find, // Return the node if found, else insert 46 | assign, // Assign (or replace) the node when found, else insert 47 | multi, // Insert multiple values with the same key, even if the values are the same 48 | }; 49 | }; 50 | 51 | // Whether to insert or emplace 52 | namespace insert_emplace 53 | { 54 | enum t 55 | { 56 | insert, 57 | emplace 58 | }; 59 | }; 60 | 61 | // Use this selector class to determine whether to insert an already constructed object, or construct it in place given 62 | // the variadic arguments. This is so that emplace only constructs the object if it really needs to 63 | template 64 | struct node_create_selector; 65 | 66 | // TODO Rework this to not pass NodeType but pass a pointer to key_value instead 67 | template 68 | struct node_create_selector 69 | { 70 | // Use for when we have both a key and a value/values 71 | template 72 | crstl_forceinline static void create(NodeType* new_node, KeyType&& key, ValueType&& value) 73 | { 74 | crstl_placement_new((void*)&(new_node->key_value)) KeyValueType(crstl_forward(KeyType, key), crstl_forward(ValueType, value)); 75 | } 76 | 77 | // Use for when we only have the key (hashset) 78 | template 79 | crstl_forceinline static void create(NodeType* new_node, KeyType&& key) 80 | { 81 | crstl_placement_new((void*)&(new_node->key_value)) KeyValueType(crstl_forward(KeyType, key)); 82 | } 83 | }; 84 | 85 | template 86 | struct node_create_selector 87 | { 88 | // Use for when we have both a key and a value/values 89 | template 90 | crstl_forceinline static void create(NodeType* new_node, KeyType&& key, Args&&... args) 91 | { 92 | crstl_placement_new((void*)&(new_node->key_value)) KeyValueType(crstl_forward(KeyType, key), T(crstl_forward(Args, args)...)); 93 | } 94 | 95 | // Use for when we only have the key (hashset) 96 | template 97 | crstl_forceinline static void create(NodeType* new_node, KeyType&& key) 98 | { 99 | crstl_placement_new((void*)&(new_node->key_value)) KeyValueType(crstl_forward(KeyType, key)); 100 | } 101 | }; 102 | }; -------------------------------------------------------------------------------- /include/crstl/utility/placement_new.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | #include "crstl/crstldef.h" 5 | 6 | // Create our own placement new to avoid including header which is quite heavy 7 | // We pass in a dummy parameter to disambiguate from the global placement new. The 8 | // macro is there just for convenience 9 | crstl_module_export namespace crstl 10 | { 11 | namespace placement_new 12 | { 13 | enum t 14 | { 15 | dummy 16 | }; 17 | }; 18 | }; 19 | 20 | crstl_module_export inline void* operator new (crstl::size_t, crstl::placement_new::t, void* ptr) { return ptr; } 21 | crstl_module_export inline void operator delete (void*, crstl::placement_new::t, void*) crstl_noexcept {} 22 | 23 | #define crstl_placement_new(x) ::new(crstl::placement_new::dummy, x) -------------------------------------------------------------------------------- /include/crstl/utility/sequence.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/type_builtins.h" 6 | 7 | crstl_module_export namespace crstl 8 | { 9 | // integer_sequence 10 | template 11 | class integer_sequence 12 | { 13 | public: 14 | typedef T value_type; 15 | //static_assert(is_integral::value, "crstl::integer_sequence can only be instantiated with an integral type"); 16 | static crstl_constexpr size_t size() crstl_noexcept { return sizeof...(Integers); } 17 | }; 18 | 19 | // An index sequence is integer_sequence with T as size_t 20 | 21 | #if defined(CRSTL_COMPILER_CLANG) || defined(CRSTL_COMPILER_MSVC) 22 | template 23 | using make_integer_sequence = __make_integer_seq; 24 | #elif defined(CRSTL_COMPILER_GCC) 25 | template 26 | using make_integer_sequence = integer_sequence; 27 | #else 28 | 29 | template 30 | struct make_index_sequence_implementation; 31 | 32 | template 33 | struct make_index_sequence_implementation> 34 | { 35 | typedef typename make_index_sequence_implementation>::type type; 36 | }; 37 | 38 | template 39 | struct make_index_sequence_implementation<0, integer_sequence> 40 | { 41 | typedef integer_sequence type; 42 | }; 43 | 44 | template 45 | struct integer_sequence_convert_implementation; 46 | 47 | template 48 | struct integer_sequence_convert_implementation> 49 | { 50 | typedef integer_sequence type; 51 | }; 52 | 53 | template 54 | struct make_integer_sequence_impl 55 | { 56 | typedef typename integer_sequence_convert_implementation>::type>::type type; 57 | }; 58 | 59 | template 60 | using make_integer_sequence = typename make_integer_sequence_impl::type; 61 | 62 | #endif 63 | 64 | // An index sequence is an integer sequence with size_t 65 | 66 | template 67 | using index_sequence = integer_sequence; 68 | 69 | template 70 | using make_index_sequence = make_integer_sequence; 71 | 72 | template 73 | using index_sequence_for = make_index_sequence; 74 | }; -------------------------------------------------------------------------------- /include/crstl/utility/string_length.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/config_fwd.h" 6 | 7 | #include "crstl/crstldef.h" 8 | 9 | #if !defined(CRSTL_COMPILER_MSVC) && !defined(CRSTL_COMPILER_GCC) 10 | #define CRSTL_BUILTIN_WCSLEN 11 | #endif 12 | 13 | #if !defined(CRSTL_COMPILER_MSVC) 14 | #define CRSTL_BUILTIN_STRLEN 15 | #endif 16 | 17 | extern "C" 18 | { 19 | #if !defined(CRSTL_BUILTIN_STRLEN) 20 | size_t strlen(const char* str); 21 | #endif 22 | 23 | #if !defined(CRSTL_BUILTIN_WCSLEN) 24 | crstl_dllimport size_t wcslen(const wchar_t* str) crstl_linux_wthrow; 25 | #endif 26 | } 27 | 28 | crstl_module_export namespace crstl 29 | { 30 | inline size_t string_length(const char* str) 31 | { 32 | #if defined(CRSTL_BUILTIN_STRLEN) 33 | return __builtin_strlen(str); 34 | #else 35 | return strlen(str); 36 | #endif 37 | } 38 | 39 | inline size_t string_length(const char* str, size_t max_length) 40 | { 41 | size_t length = string_length(str); 42 | return length < max_length ? length : max_length; 43 | } 44 | 45 | inline size_t string_length(const wchar_t* str) 46 | { 47 | #if defined(CRSTL_BUILTIN_WCSLEN) 48 | return __builtin_wcslen(str); 49 | #else 50 | return wcslen(str); 51 | #endif 52 | } 53 | 54 | inline size_t string_length(const wchar_t* str, size_t max_length) 55 | { 56 | size_t length = string_length(str); 57 | return length < max_length ? length : max_length; 58 | } 59 | 60 | inline size_t string_length(const char8_t* str) 61 | { 62 | return string_length((const char*)str); 63 | } 64 | 65 | inline size_t string_length(const char8_t* str, size_t max_length) 66 | { 67 | size_t length = string_length((const char*)str); 68 | return length < max_length ? length : max_length; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /include/crstl/vector_base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crstl/config.h" 4 | 5 | #include "crstl/utility/placement_new.h" 6 | 7 | #include "crstl/move_forward.h" 8 | 9 | #include "crstl/utility/memory_ops.h" 10 | 11 | // Non-standard functions 12 | // 13 | // - push_back(): push back an empty default object 14 | // - push_back_uninitialized(): push back an uninitialized object 15 | // - resize_uninitialized(length): resizes vector to have specified length but does not initialize the contents of the objects. Use with care as 16 | // objects with assignment operators accessing member variables assumed to be initialized can crash 17 | // - size_bytes(): return size of vector in bytes 18 | 19 | namespace crstl 20 | { 21 | template 22 | class vector_base : public VectorStorage 23 | { 24 | public: 25 | 26 | typedef VectorStorage base_type; 27 | typedef vector_base this_type; 28 | typedef T* iterator; 29 | typedef const T* const_iterator; 30 | typedef T* pointer; 31 | typedef const T* const_pointer; 32 | typedef T& reference; 33 | typedef const T& const_reference; 34 | 35 | typedef typename base_type::length_type length_type; 36 | 37 | crstl_constexpr14 T& at(size_t i) { return crstl_assert(i < m_length), m_data[i]; } 38 | crstl_constexpr const T& at(size_t i) const { return crstl_assert(i < m_length), m_data[i]; } 39 | 40 | crstl_constexpr14 T& back() 41 | { 42 | crstl_assert(m_length > 0); 43 | crstl_assume(m_length > 0); 44 | return m_data[m_length - 1]; 45 | } 46 | 47 | crstl_constexpr14 const T& back() const 48 | { 49 | crstl_assert(m_length > 0); 50 | crstl_assume(m_length > 0); 51 | return m_data[m_length - 1]; 52 | } 53 | 54 | crstl_constexpr14 iterator begin() { return &m_data[0]; } 55 | crstl_constexpr const_iterator begin() const { return &m_data[0]; } 56 | crstl_constexpr const_iterator cbegin() const { return &m_data[0]; } 57 | 58 | crstl_constexpr size_t capacity() const { return get_capacity(); } 59 | 60 | crstl_constexpr14 void clear() 61 | { 62 | destruct_or_ignore(m_data, m_length); 63 | m_length = 0; 64 | } 65 | 66 | crstl_constexpr14 pointer data() { return &m_data[0]; } 67 | crstl_constexpr const_pointer data() const { return &m_data[0]; } 68 | 69 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 70 | template 71 | crstl_constexpr14 T& emplace_back(Args&&... args) 72 | { 73 | reallocate_if_length_equals_capacity(); 74 | 75 | crstl_assert(m_length < get_capacity()); 76 | crstl_placement_new((void*)&m_data[m_length]) T(crstl_forward(Args, args)...); 77 | m_length++; 78 | return back(); 79 | } 80 | #endif 81 | 82 | crstl_nodiscard 83 | crstl_constexpr bool empty() const { return m_length == 0; } 84 | 85 | crstl_constexpr14 iterator end() { return &m_data[0] + m_length; } 86 | crstl_constexpr const_iterator end() const { return &m_data[0] + m_length; } 87 | crstl_constexpr const_iterator cend() const { return &m_data[0] + m_length; } 88 | 89 | crstl_constexpr14 T& front() { return m_data[0]; } 90 | crstl_constexpr const T& front() const { return m_data[0]; } 91 | 92 | crstl_constexpr14 void pop_back() 93 | { 94 | crstl_assert(m_length > 0); 95 | destruct_or_ignore(back()); 96 | m_length--; 97 | } 98 | 99 | //---------- 100 | // push_back 101 | //---------- 102 | 103 | crstl_constexpr14 T& push_back() 104 | { 105 | reallocate_if_length_equals_capacity(); 106 | default_initialize_or_memset_zero(m_data[m_length]); 107 | m_length++; 108 | return back(); 109 | } 110 | 111 | crstl_constexpr14 void push_back(const T& v) 112 | { 113 | reallocate_if_length_equals_capacity(); 114 | set_initialize_or_memset(m_data[m_length], v); 115 | m_length++; 116 | } 117 | 118 | crstl_constexpr14 void push_back(T&& v) 119 | { 120 | reallocate_if_length_equals_capacity(); 121 | crstl_placement_new((void*)&m_data[m_length]) T(crstl_move(v)); 122 | m_length++; 123 | } 124 | 125 | crstl_constexpr14 T& push_back_uninitialized() 126 | { 127 | reallocate_if_length_equals_capacity(); 128 | m_length++; 129 | return back(); 130 | } 131 | 132 | crstl_constexpr14 void reserve(size_t capacity) 133 | { 134 | reallocate_if_length_greater_than_capacity(capacity); 135 | } 136 | 137 | //------- 138 | // resize 139 | //------- 140 | 141 | crstl_constexpr14 void resize(size_t length) 142 | { 143 | if ((size_t)m_length < length) 144 | { 145 | reallocate_if_length_greater_than_capacity(length); 146 | 147 | default_initialize_or_memset_zero(m_data, length); 148 | } 149 | else if ((size_t)m_length > length) 150 | { 151 | destruct_or_ignore(&m_data[length], m_length - length); 152 | } 153 | 154 | m_length = (length_type)length; 155 | } 156 | 157 | crstl_constexpr14 void resize(size_t length, const T& value) 158 | { 159 | if ((size_t)m_length < length) 160 | { 161 | reallocate_if_length_greater_than_capacity(length); 162 | 163 | set_initialize_or_memset(&m_data[m_length], value, length - m_length); 164 | } 165 | else if ((size_t)m_length > length) 166 | { 167 | destruct_or_ignore(&m_data[length], m_length - length); 168 | } 169 | 170 | m_length = (length_type)length; 171 | } 172 | 173 | crstl_constexpr14 void resize_uninitialized(size_t length) 174 | { 175 | if ((size_t)m_length < length) 176 | { 177 | reallocate_if_length_greater_than_capacity(length); 178 | } 179 | else if ((size_t)m_length > length) 180 | { 181 | destruct_or_ignore(&m_data[length], m_length - length); 182 | } 183 | 184 | m_length = (length_type)length; 185 | } 186 | 187 | crstl_constexpr size_t size() const { return m_length; } 188 | 189 | crstl_constexpr size_t size_bytes() const { return m_length * sizeof(T); } 190 | 191 | crstl_constexpr14 T& operator [] (size_t i) { return crstl_assert(i < m_length), m_data[i]; } 192 | 193 | crstl_constexpr const T& operator [] (size_t i) const { return crstl_assert(i < m_length), m_data[i]; } 194 | 195 | //--------------------- 196 | // Comparison Operators 197 | //--------------------- 198 | 199 | crstl_constexpr14 bool operator == (const this_type& other) const crstl_noexcept 200 | { 201 | if (m_length == other.m_length) 202 | { 203 | for (size_t i = 0; i < m_length; ++i) 204 | { 205 | if (!(m_data[i] == other.m_data[i])) { return false; } 206 | } 207 | 208 | return true; 209 | } 210 | else 211 | { 212 | return false; 213 | } 214 | } 215 | 216 | crstl_constexpr14 bool operator != (const this_type& other) crstl_noexcept 217 | { 218 | return !(*this == other); 219 | } 220 | 221 | protected: 222 | 223 | using base_type::get_capacity; 224 | using base_type::reallocate_if_length_equals_capacity; 225 | using base_type::reallocate_if_length_greater_than_capacity; 226 | 227 | using base_type::m_length; 228 | using base_type::m_data; 229 | }; 230 | }; -------------------------------------------------------------------------------- /module/crstl.ixx: -------------------------------------------------------------------------------- 1 | export module crstl; 2 | 3 | #define CRSTL_MODULE_DECLARATION 4 | #include "crstl/allocator.h" 5 | #include "crstl/array.h" 6 | #include "crstl/bit.h" 7 | #include "crstl/bitset.h" 8 | #include "crstl/critical_section.h" 9 | #include "crstl/debugging.h" 10 | #include "crstl/deque.h" 11 | #include "crstl/filesystem.h" 12 | #include "crstl/fixed_deque.h" 13 | #include "crstl/fixed_function.h" 14 | #include "crstl/fixed_open_hashmap.h" 15 | #include "crstl/fixed_path.h" 16 | #include "crstl/fixed_string.h" 17 | #include "crstl/fixed_vector.h" 18 | #include "crstl/function.h" 19 | #include "crstl/hash.h" 20 | #include "crstl/intrusive_ptr.h" 21 | #include "crstl/open_hashmap.h" 22 | #include "crstl/pair.h" 23 | #include "crstl/path.h" 24 | #include "crstl/process.h" 25 | #include "crstl/span.h" 26 | #include "crstl/stack_vector.h" 27 | #include "crstl/string.h" 28 | #include "crstl/string_view.h" 29 | #include "crstl/thread.h" 30 | #include "crstl/timer.h" 31 | #include "crstl/tuple.h" 32 | #include "crstl/type_array.h" 33 | #include "crstl/unique_ptr.h" 34 | #include "crstl/vector.h" 35 | -------------------------------------------------------------------------------- /notes/hash_map_optimizations.txt: -------------------------------------------------------------------------------- 1 | Optimization 1 - Avoid the bitfield 2 | We have only two values now, end and empty 3 | 4 | MSVC 5 | 6 | VERSION1 7 | std::unordered_map took 1398.798300 ms with 0 elements 8 | robin_hood::unordered_map took 169.284600 ms with 0 elements 9 | eastl::hash_map took 843.420000 ms with 0 elements 10 | ankerl::unordered_dense::map took 172.053700 ms with 0 elements 11 | crstl::fixed_bucket_map took 128.931300 ms with 0 elements 12 | 13 | VERSION2 14 | std::unordered_map took 1172.118700 ms with 0 elements 15 | robin_hood::unordered_map took 157.321900 ms with 0 elements 16 | eastl::hash_map took 1005.509400 ms with 0 elements 17 | ankerl::unordered_dense::map took 166.000600 ms with 0 elements 18 | crstl::fixed_bucket_map took 104.077600 ms with 0 elements 19 | 20 | LLVM 21 | 22 | VERSION1 23 | std::unordered_map took 1154.207400 ms with 0 elements 24 | robin_hood::unordered_map took 164.929600 ms with 0 elements 25 | eastl::hash_map took 897.046000 ms with 0 elements 26 | ankerl::unordered_dense::map took 89.928600 ms with 0 elements 27 | crstl::fixed_bucket_map took 137.407700 ms with 0 elements 28 | 29 | VERSION2 30 | 31 | LLVM 32 | std::unordered_map took 1107.045100 ms with 0 elements 33 | robin_hood::unordered_map took 137.653100 ms with 0 elements 34 | eastl::hash_map took 817.834800 ms with 0 elements 35 | ankerl::unordered_dense::map took 87.025800 ms with 0 elements 36 | crstl::fixed_bucket_map took 116.744400 ms with 0 elements 37 | 38 | Optimization 1 - Avoid the if in erase() 39 | We need to statically assert that end value of end and the invalid value for the bucket are the same 40 | 41 | MSVC 42 | 43 | VERSION1 44 | 45 | std::unordered_map took 1183.788800 ms with 0 elements 46 | robin_hood::unordered_map took 200.514600 ms with 0 elements 47 | eastl::hash_map took 1152.385000 ms with 0 elements 48 | ankerl::unordered_dense::map took 175.657400 ms with 0 elements 49 | crstl::fixed_bucket_map took 104.420400 ms with 0 elements 50 | 51 | VERSION2 52 | 53 | std::unordered_map took 1262.597900 ms with 0 elements 54 | robin_hood::unordered_map took 216.315100 ms with 0 elements 55 | eastl::hash_map took 954.657800 ms with 0 elements 56 | ankerl::unordered_dense::map took 161.239000 ms with 0 elements 57 | crstl::fixed_bucket_map took 89.889000 ms with 0 elements 58 | 59 | LLVM 60 | 61 | VERSION1 62 | 63 | std::unordered_map took 1046.381000 ms with 0 elements 64 | robin_hood::unordered_map took 122.325400 ms with 0 elements 65 | eastl::hash_map took 823.489400 ms with 0 elements 66 | ankerl::unordered_dense::map took 99.672700 ms with 0 elements 67 | crstl::fixed_bucket_map took 119.462000 ms with 0 elements 68 | 69 | VERSION2 70 | 71 | std::unordered_map took 1035.251400 ms with 0 elements 72 | robin_hood::unordered_map took 141.170100 ms with 0 elements 73 | eastl::hash_map took 925.701400 ms with 0 elements 74 | ankerl::unordered_dense::map took 102.253400 ms with 0 elements 75 | crstl::fixed_bucket_map took 87.121800 ms with 0 elements -------------------------------------------------------------------------------- /premake/linux/premake5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redorav/crstl/33efc2a911c1f734acbf579b7965e4f1e86b4515/premake/linux/premake5 -------------------------------------------------------------------------------- /premake/osx/premake5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redorav/crstl/33efc2a911c1f734acbf579b7965e4f1e86b4515/premake/osx/premake5 -------------------------------------------------------------------------------- /premake/win/premake5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redorav/crstl/33efc2a911c1f734acbf579b7965e4f1e86b4515/premake/win/premake5.exe -------------------------------------------------------------------------------- /unit_tests/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /unit_tests/android/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 46 | 47 | 59 | 60 | 61 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /unit_tests/android/project.properties: -------------------------------------------------------------------------------- 1 | # Project target 2 | target=$(androidapilevel) 3 | # Provide path to the directory where prebuilt external jar files are by setting jar.libs.dir= 4 | -------------------------------------------------------------------------------- /unit_tests/android/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | AndroidTestApp.Packaging 4 | 5 | -------------------------------------------------------------------------------- /unit_tests/unit_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/debugging.h" 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | Dummy::Dummy() : a(4), b(5.0f), ptr(nullptr) 14 | { 15 | 16 | } 17 | 18 | Dummy::Dummy(int a, float b) : a(a), b(b), ptr(nullptr) 19 | { 20 | 21 | } 22 | 23 | Dummy::Dummy(int a, float b, int* ptr) : a(a), b(b), ptr(ptr) 24 | { 25 | 26 | } 27 | 28 | Dummy::Dummy(const Dummy& dummy) 29 | { 30 | a = dummy.a; 31 | b = dummy.b; 32 | c = dummy.c; 33 | d = dummy.d; 34 | e = dummy.e; 35 | f = dummy.f; 36 | g = dummy.g; 37 | h = dummy.h; 38 | i = dummy.i; 39 | j = dummy.j; 40 | ptr = dummy.ptr; 41 | } 42 | 43 | Dummy::Dummy(Dummy&& d) crstl_noexcept : a(d.a), b(d.b), ptr(d.ptr) 44 | { 45 | d.ptr = (int*)(int)(0xDEADBEEF); 46 | } 47 | 48 | Dummy::Dummy(int a, float b, int c, int d, int e, int f, int g, int h, int i, int j) : a(a), b((float)b), c(c), d(d), e(e), f(f), g(g), h(h), i(i), j(j) 49 | { 50 | 51 | } 52 | 53 | Dummy& Dummy::operator=(const Dummy& dummy) 54 | { 55 | a = dummy.a; 56 | b = dummy.b; 57 | c = dummy.c; 58 | d = dummy.d; 59 | e = dummy.e; 60 | f = dummy.f; 61 | g = dummy.g; 62 | h = dummy.h; 63 | i = dummy.i; 64 | j = dummy.j; 65 | ptr = dummy.ptr; 66 | return *this; 67 | } 68 | 69 | Dummy::~Dummy() 70 | { 71 | // Set to values that look uninitialized, for debugging 72 | a = -858993460; 73 | b = -107374176.0f; 74 | } 75 | 76 | bool Dummy::operator == (const Dummy& other) const 77 | { 78 | bool isEqual = 79 | a == other.a && 80 | b == other.b; 81 | 82 | return isEqual; 83 | } 84 | 85 | void Dummy::PrintName() 86 | { 87 | printf("Dummy\n"); 88 | } 89 | 90 | void RefCountDummy::PrintName() 91 | { 92 | printf("RefCountDummy\n"); 93 | } 94 | 95 | RefCountDummy::~RefCountDummy() 96 | { 97 | printf("RefCountDummy destroyed\n"); 98 | } 99 | 100 | namespace crstl_unit 101 | { 102 | void begin_test_impl(const char* name, const char* filename) 103 | { 104 | printf("%s, %s\n", name, filename); 105 | } 106 | 107 | void end_test() 108 | { 109 | 110 | } 111 | 112 | 113 | void begin_test_case(const char* functionString, int lineNumber, const char* file) 114 | { 115 | (void)functionString; 116 | (void)lineNumber; 117 | (void)file; 118 | } 119 | 120 | void check_condition(bool condition) 121 | { 122 | if (condition) 123 | { 124 | 125 | } 126 | else 127 | { 128 | assert(false); 129 | } 130 | } 131 | 132 | void end_test_case() 133 | { 134 | 135 | } 136 | } 137 | 138 | void RunUnitTestsArray(); 139 | void RunUnitTestsAssociative(); 140 | void RunUnitTestsAtomic(); 141 | void RunUnitTestsBit(); 142 | void RunUnitTestsBitset(); 143 | void RunUnitTestsDeque(); 144 | void RunUnitTestsFilesystem(); 145 | void RunUnitTestsFunction(); 146 | void RunUnitTestsPair(); 147 | void RunUnitTestsPath(); 148 | void RunUnitTestsProcess(); 149 | void RunUnitTestsSmartPtr(); 150 | void RunUnitTestsString(); 151 | void RunUnitTestsThread(); 152 | void RunUnitTestsTimer(); 153 | void RunUnitTestsVector(); 154 | 155 | #if defined(__ANDROID__) 156 | 157 | #if defined(__cplusplus) 158 | extern "C" 159 | { 160 | #endif 161 | 162 | void android_main(struct android_app* app) 163 | 164 | #else 165 | 166 | int main() 167 | 168 | #endif 169 | { 170 | RunUnitTestsArray(); 171 | RunUnitTestsAssociative(); 172 | RunUnitTestsAtomic(); 173 | RunUnitTestsBit(); 174 | RunUnitTestsBitset(); 175 | RunUnitTestsDeque(); 176 | RunUnitTestsFilesystem(); 177 | RunUnitTestsFunction(); 178 | RunUnitTestsPair(); 179 | RunUnitTestsPath(); 180 | RunUnitTestsProcess(); 181 | RunUnitTestsSmartPtr(); 182 | RunUnitTestsString(); 183 | RunUnitTestsThread(); 184 | RunUnitTestsTimer(); 185 | RunUnitTestsVector(); 186 | } 187 | 188 | #if defined(__ANDROID__) 189 | } 190 | #endif -------------------------------------------------------------------------------- /unit_tests/unit_tests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/intrusive_ptr.h" 7 | #endif 8 | 9 | // We use this to disable some unit tests on old compilers just so they compile 10 | // and run the rest of the tests. Put here so we don't accidentally use it in 11 | // the actual library 12 | #if !(defined(_MSC_VER) && _MSC_VER < 1900) 13 | #define CRSTL_UNIT_RANGED_FOR 14 | #define CRSTL_UNIT_UNICODE_LITERALS 15 | 16 | // Using curly brackets for initializing vectors, etc 17 | #define CRSTL_UNIT_UNIFORM_INITIALIZATION 18 | #endif 19 | 20 | #if defined(CRSTL_USE_CPP_MODULE) 21 | 22 | #define CRSTL_UNIT_MODULES 23 | 24 | #endif 25 | 26 | #define crstl_unit_unused(x) (void)x 27 | 28 | #include "crstl/config.h" 29 | 30 | // Dummy structure with all the necessary constructors and operators to test different containers 31 | class Dummy : public crstl::intrusive_ptr_interface_delete 32 | { 33 | public: 34 | 35 | Dummy(); 36 | 37 | Dummy(int a, float b); 38 | 39 | Dummy(int a, float b, int* ptr); 40 | 41 | // Copy constructor 42 | Dummy(const Dummy& d); 43 | 44 | Dummy(Dummy&& d) crstl_noexcept; 45 | 46 | // For testing emplace() 47 | Dummy(int a, float b, int c, int d, int e, int f, int g, int h, int i, int j); 48 | 49 | Dummy& operator = (const Dummy& dummy); 50 | 51 | ~Dummy(); 52 | 53 | bool operator == (const Dummy& other) const; 54 | 55 | void PrintName(); 56 | 57 | int a; 58 | float b; 59 | int c; 60 | int d; 61 | int e; 62 | int f; 63 | int g; 64 | int h; 65 | int i; 66 | int j; 67 | 68 | int* ptr; 69 | }; 70 | 71 | class DummyChild : public Dummy 72 | { 73 | public: 74 | 75 | int k; 76 | }; 77 | 78 | class DummyOther 79 | { 80 | public: 81 | 82 | int k; 83 | }; 84 | 85 | class RefCountDummy : public crstl::intrusive_ptr_interface_delete 86 | { 87 | public: 88 | 89 | void PrintName(); 90 | 91 | ~RefCountDummy(); 92 | }; 93 | 94 | struct Example 95 | { 96 | int a; 97 | float b; 98 | 99 | Example() 100 | { 101 | a = 0; 102 | b = 1.0f; 103 | } 104 | 105 | Example(int _a, float _b) 106 | { 107 | a = _a; 108 | b = _b; 109 | } 110 | 111 | Example(const Example& other) 112 | { 113 | a = other.a; 114 | b = other.b; 115 | } 116 | 117 | Example(Example&& other) crstl_noexcept 118 | { 119 | a = other.a; 120 | b = other.b; 121 | other.a = 10000; 122 | other.b = 10000.0f; 123 | } 124 | 125 | Example& operator = (const Example& other) 126 | { 127 | a = other.a; 128 | b = other.b; 129 | return *this; 130 | } 131 | 132 | Example& operator = (Example&& other) crstl_noexcept 133 | { 134 | a = other.a; 135 | b = other.b; 136 | other.a = 10000; 137 | other.b = 10000.0f; 138 | return *this; 139 | } 140 | 141 | bool operator == (const Example& other) const 142 | { 143 | return a == other.a && b == other.b; 144 | } 145 | 146 | operator int () 147 | { 148 | return a; 149 | } 150 | 151 | ~Example() 152 | { 153 | a = -1; 154 | b = -1.0f; 155 | } 156 | }; 157 | 158 | namespace crstl_unit 159 | { 160 | void begin_test_impl(const char* name); 161 | 162 | void end_test(); 163 | 164 | #define crstl_check(x) \ 165 | crstl_unit::begin_test_case(#x, __LINE__, __FILE__); \ 166 | crstl_unit::check_condition(x); \ 167 | crstl_unit::end_test_case(); 168 | 169 | #define begin_test(x) 170 | 171 | void begin_test_case(const char* functionString, int lineNumber, const char* file); 172 | 173 | void check_condition(bool condition); 174 | 175 | void end_test_case(); 176 | }; -------------------------------------------------------------------------------- /unit_tests/unit_tests_array.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/array.h" 7 | #endif 8 | 9 | #include 10 | 11 | #if defined(CRSTL_FEATURE_CONSTEXPR) 12 | 13 | crstl_constexpr crstl::array MyFunction() 14 | { 15 | return { 1, 2, 3 }; 16 | } 17 | 18 | crstl_constexpr crstl::array constexprArray = MyFunction(); 19 | 20 | #endif 21 | 22 | #include 23 | 24 | void RunUnitTestsArray() 25 | { 26 | printf("RunUnitTestsArray\n"); 27 | 28 | std::array mystdArray = { 1, 2, 3, 4, 5, 6, 7, 8 }; crstl_unused(mystdArray); 29 | crstl::array crArray32 = { 1, 2, 3, 4, 5, 6, 7, 8 }; crstl_unused(crArray32); 30 | 31 | #if defined(CRSTL_UNIT_RANGED_FOR) 32 | 33 | size_t count = 0; 34 | for (const auto& item : crArray32) 35 | { 36 | (void)item; 37 | count++; 38 | } 39 | 40 | crstl_assert(crArray32.size() == count); crstl_unused(count); 41 | 42 | #endif 43 | 44 | #if defined(CRSTL_FEATURE_CONSTEXPR) 45 | 46 | count = 0; 47 | for (const auto& item : constexprArray) 48 | { 49 | (void)item; 50 | count++; 51 | } 52 | 53 | crstl_assert(constexprArray.size() == count); 54 | 55 | #endif 56 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_atomic.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/atomic.h" 7 | #endif 8 | 9 | #include 10 | 11 | void RunUnitTestsAtomic() 12 | { 13 | using namespace crstl_unit; 14 | 15 | crstl::atomic crAtomicU32; 16 | crAtomicU32++; 17 | crstl_check(crAtomicU32 == 1); 18 | 19 | crstl::atomic crAtomicI32; 20 | crAtomicI32++; 21 | crstl_check(crAtomicI32 == 1); 22 | 23 | crstl::atomic crAtomicChar8; 24 | crAtomicChar8++; 25 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_bit.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/bit.h" 7 | #endif 8 | 9 | #if CRSTL_CPPVERSION >= CRSTL_CPP20 10 | #include 11 | #endif 12 | 13 | void RunUnitTestsBit() 14 | { 15 | // firstbitlow 16 | 17 | int bitop_fbl0 = crstl::firstbitlow(0); 18 | crstl_check(bitop_fbl0 == -1); 19 | 20 | int bitop_fbl1 = crstl::firstbitlow(1); 21 | crstl_check(bitop_fbl1 == 0); 22 | 23 | int bitop_fbl2 = crstl::firstbitlow(2u); 24 | crstl_check(bitop_fbl2 == 1); 25 | 26 | int bitop_fbl3 = crstl::firstbitlow(3u); 27 | crstl_check(bitop_fbl3 == 0); 28 | 29 | int bitop_fbl4 = crstl::firstbitlow((uint64_t)0xffff000000000000); 30 | crstl_check(bitop_fbl4 == 48); 31 | 32 | // firstbithigh 33 | 34 | int bitop_fbh0 = crstl::firstbithigh(0); 35 | crstl_check(bitop_fbh0 == -1); 36 | 37 | int bitop_fbh1 = crstl::firstbithigh(0xffffffffu); 38 | crstl_check(bitop_fbh1 == 31); 39 | 40 | int bitop_fbh2 = crstl::firstbithigh(1); 41 | crstl_check(bitop_fbh2 == 0); 42 | 43 | int bitop_fbh3 = crstl::firstbithigh(3); 44 | crstl_check(bitop_fbh3 == 1); 45 | 46 | // countr_zero 47 | 48 | int bitop_crz0 = crstl::countr_zero(0u); 49 | crstl_check(bitop_crz0 == 32); 50 | 51 | int bitop_crz1 = crstl::countr_zero(0xffffffffu); 52 | crstl_check(bitop_crz1 == 0); 53 | 54 | int bitop_crz2 = crstl::countr_zero(1u); 55 | crstl_check(bitop_crz2 == 0); 56 | 57 | int bitop_crz3 = crstl::countr_zero(3u); 58 | crstl_check(bitop_crz3 == 0); 59 | 60 | int bitop_crz4 = crstl::countr_zero(0xf0000000u); 61 | crstl_check(bitop_crz4 == 28); 62 | 63 | int bitop_crz5 = crstl::countr_zero(0xf0f0f0f0u); 64 | crstl_check(bitop_crz5 == 4); 65 | 66 | int bitop_crz6 = crstl::countr_zero((char)0); 67 | crstl_check(bitop_crz6 == 8); 68 | 69 | int bitop_crz7 = crstl::countr_zero((unsigned char)0xff); 70 | crstl_check(bitop_crz7 == 0); 71 | 72 | // countl_zero 73 | 74 | int bitop_clz0 = crstl::countl_zero(0u); 75 | crstl_check(bitop_clz0 == 32); 76 | 77 | int bitop_clz1 = crstl::countl_zero(0xffffffffu); 78 | crstl_check(bitop_clz1 == 0); 79 | 80 | int bitop_clz2 = crstl::countl_zero(1u); 81 | crstl_check(bitop_clz2 == 31); 82 | 83 | int bitop_clz3 = crstl::countl_zero(3u); 84 | crstl_check(bitop_clz3 == 30); 85 | 86 | int bitop_clz4 = crstl::countl_zero(0x0000000fu); 87 | crstl_check(bitop_clz4 == 28); 88 | 89 | int bitop_clz5 = crstl::countl_zero(0x0f0f0f0fu); 90 | crstl_check(bitop_clz5 == 4); 91 | 92 | int bitop_clz6 = crstl::countl_zero((unsigned char)1u); 93 | crstl_check(bitop_clz6 == 7); 94 | 95 | // popcnt 96 | 97 | int bitop_pcnt0 = crstl::popcount((uint16_t)0u); 98 | crstl_check(bitop_pcnt0 == 0); 99 | 100 | int bitop_pcnt1 = crstl::popcount((uint32_t)0u); 101 | crstl_check(bitop_pcnt1 == 0); 102 | 103 | int bitop_pcnt2 = crstl::popcount((uint64_t)0u); 104 | crstl_check(bitop_pcnt2 == 0); 105 | 106 | int bitop_pcnt3 = crstl::popcount((uint16_t)0xffu); 107 | crstl_check(bitop_pcnt3 == 8); 108 | 109 | int bitop_pcnt4 = crstl::popcount((uint32_t)0xffffffu); 110 | crstl_check(bitop_pcnt4 == 24); 111 | 112 | int bitop_pcnt5 = crstl::popcount((uint64_t)0xffffffffffu); 113 | crstl_check(bitop_pcnt5 == 40); 114 | 115 | // rotl 116 | 117 | uint8_t bitop_rotl0 = crstl::rotl((uint8_t)129, 1); 118 | crstl_check(bitop_rotl0 == 3); 119 | 120 | uint16_t bitop_rotl1 = crstl::rotl((uint16_t)49410, 2); 121 | crstl_check(bitop_rotl1 == 1035); 122 | 123 | uint32_t bitop_rotl2 = crstl::rotl((uint32_t)262155, 4); 124 | crstl_check(bitop_rotl2 == 4194480); 125 | 126 | uint64_t bitop_rotl3 = crstl::rotl((uint64_t)16939, 6); 127 | crstl_check(bitop_rotl3 == 1084096); 128 | 129 | // rotr 130 | 131 | uint8_t bitop_rotr0 = crstl::rotr((uint8_t)3, 1); 132 | crstl_check(bitop_rotr0 == 129); 133 | 134 | uint16_t bitop_rotr1 = crstl::rotr((uint16_t)1035, 2); 135 | crstl_check(bitop_rotr1 == 49410); 136 | 137 | uint32_t bitop_rotr2 = crstl::rotr((uint32_t)4194480, 4); 138 | crstl_check(bitop_rotr2 == 262155); 139 | 140 | uint64_t bitop_rotr3 = crstl::rotr((uint64_t)1084096, 6); 141 | crstl_check(bitop_rotr3 == 16939); 142 | 143 | // byteswap 144 | 145 | uint8_t bitop_bswap0 = crstl::byteswap((uint8_t)144); 146 | crstl_check(bitop_bswap0 == 144); 147 | 148 | uint16_t bitop_bswap1 = crstl::byteswap((uint16_t)6712); 149 | crstl_check(bitop_bswap1 == 14362); 150 | 151 | uint32_t bitop_bswap2 = crstl::byteswap((uint32_t)8965); 152 | crstl_check(bitop_bswap2 == 86179840); 153 | 154 | uint64_t bitop_bswap3 = crstl::byteswap((uint64_t)98745621); 155 | crstl_check(bitop_bswap3 == 1566656756497514496); 156 | 157 | // bitcast 158 | 159 | uint32_t bitop_bcfu = crstl::bitcast(2.0f); 160 | crstl_check(bitop_bcfu == 1073741824); 161 | 162 | float bitop_bcuf = crstl::bitcast(1073741824); 163 | crstl_check(bitop_bcuf == 2.0f); 164 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_bitset.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/bitset.h" 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | void RunUnitTestsBitset() 13 | { 14 | using namespace crstl_unit; 15 | 16 | printf("RunUnitTestsBitset\n"); 17 | 18 | // Test with 64 bits 19 | 20 | crstl::bitset<64, uint64_t> crBitset64; 21 | crBitset64[32] = true; 22 | crstl_check(crBitset64[32] == true); 23 | 24 | crBitset64.set(32, false); 25 | crstl_check(crBitset64[32] == false); 26 | 27 | crBitset64.flip(32); 28 | crstl_check(crBitset64[32] == true); 29 | 30 | crBitset64.set(); 31 | crstl_check(crBitset64.all()); 32 | 33 | crBitset64.reset(); 34 | crstl_check(crBitset64.none()); 35 | 36 | // Test with 256 bits 37 | 38 | crstl::bitset<256, uint64_t> crBitset256; 39 | crBitset256[32] = true; 40 | crstl_check(crBitset256[32] == true); 41 | 42 | crBitset256.set(32, false); 43 | crstl_check(crBitset256[32] == false); 44 | 45 | crBitset256.flip(32); 46 | crstl_check(crBitset256[32] == true); 47 | 48 | crBitset256.set(); 49 | crstl_check(crBitset256.all()); 50 | 51 | crBitset256.reset(); 52 | crstl_check(crBitset256.none()); 53 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_deque.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/fixed_deque.h" 7 | #include "crstl/fixed_vector.h" 8 | #include "crstl/deque.h" 9 | #include "crstl/timer.h" 10 | #endif 11 | 12 | #include 13 | 14 | template 15 | struct static_sizeof; 16 | 17 | //crstl_constexpr14 void FixedDequeConstexprFunction() 18 | //{ 19 | // crstl::fixed_deque crFixedDeque; 20 | // 21 | // //crFixedDeque.push_back(); 22 | // //crFixedDeque.push_back_uninitialized(); 23 | // //crFixedDeque.push_back(Dummy()); 24 | // //static_assert(crFixedDeque.size() == 3, ""); 25 | //} 26 | 27 | #include 28 | #include 29 | 30 | void RunUnitTestsDeque() 31 | { 32 | printf("RunUnitTestsDeque\n"); 33 | using namespace crstl_unit; 34 | 35 | Dummy dummy(4, 5.0f); 36 | 37 | begin_test("fixed_deque"); 38 | { 39 | crstl::fixed_deque crDeque; 40 | crDeque.resize(16); 41 | crstl_check(crDeque.size() == 16); 42 | 43 | // We push and pop the same number of elements 44 | // many times and see that we're doing things 45 | // correctly 46 | for (size_t i = 0; i < 16; ++i) 47 | { 48 | crDeque.push_back(); 49 | crDeque.pop_front(); 50 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 51 | crDeque.emplace_back(1, 2.0f); 52 | #else 53 | crDeque.push_back(Dummy(1, 2.0f)); 54 | #endif 55 | crDeque.pop_front(); 56 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 57 | crDeque.emplace_front(2, 3.0f); 58 | #else 59 | crDeque.push_front(Dummy(1, 2.0f)); 60 | #endif 61 | crDeque.pop_back(); 62 | crDeque.push_back(); 63 | crDeque.pop_front(); 64 | } 65 | 66 | crstl_check(crDeque.size() == 16); 67 | 68 | crDeque.resize(16); 69 | crstl_check(crDeque.size() == 16); 70 | 71 | crstl::fixed_deque crDequeCopy; 72 | crDequeCopy = crDeque; 73 | crstl_check(crDequeCopy == crDeque); 74 | 75 | crDeque.resize(10); 76 | crstl_check(crDeque.size() == 10); 77 | 78 | crDeque.resize(0); 79 | crstl_check(crDeque.size() == 0); 80 | crstl_check(crDeque.begin() == crDeque.end()); 81 | 82 | for (int i = 0; i < 128; ++i) 83 | { 84 | crDeque.push_front(); 85 | crDeque.push_back(); 86 | crDeque.push_front(); 87 | crDeque.push_back(); 88 | crDeque.push_front(); 89 | crDeque.push_back(); 90 | crDeque.pop_front(); 91 | crDeque.pop_back(); 92 | crDeque.pop_back(); 93 | crDeque.pop_front(); 94 | crDeque.pop_back(); 95 | crDeque.pop_front(); 96 | } 97 | 98 | crstl_check(crDeque.size() == 0); 99 | crstl_check(crDeque.begin() == crDeque.end()); 100 | 101 | crDeque.push_front(); 102 | crDeque.push_front(Dummy()); 103 | crDeque.push_front(dummy); 104 | crDeque.push_front(); 105 | crDeque.push_front(); 106 | crDeque.push_front(); 107 | 108 | crDeque.push_back(); 109 | crDeque.push_back(Dummy()); 110 | crDeque.push_back(dummy); 111 | crDeque.push_back(); 112 | crDeque.push_back(); 113 | crDeque.push_back(); 114 | 115 | crDeque[0] = Dummy(42, 42.0f); 116 | crDeque[10] = Dummy(43, 43.0f); 117 | crDeque[11] = Dummy(44, 44.0f); 118 | 119 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 120 | crDeque.emplace_back(1, 2.0f); 121 | crDeque.emplace_front(2, 3.0f); 122 | #else 123 | crDeque.push_back(Dummy(1, 2.0f)); 124 | crDeque.push_front(Dummy(2, 3.0f)); 125 | #endif 126 | 127 | Dummy& backRef = crDeque.back(); 128 | backRef = Dummy(440, 440.0f); 129 | 130 | Dummy& frontRef = crDeque.front(); 131 | frontRef = Dummy(420, 420.0f); 132 | 133 | #if defined(CRSTL_UNIT_RANGED_FOR) 134 | { 135 | size_t count = 0; 136 | for (auto iter = crDeque.begin(); iter != crDeque.end(); ++iter) 137 | { 138 | ++count; 139 | } 140 | 141 | crstl_check(crDeque.size() == count); 142 | } 143 | 144 | { 145 | size_t count = 0; 146 | for (const Dummy& d : crDeque) 147 | { 148 | (void)d; 149 | ++count; 150 | } 151 | 152 | crstl_check(crDeque.size() == count); 153 | } 154 | #endif 155 | } 156 | end_test(); 157 | 158 | begin_test("deque"); 159 | { 160 | crstl::deque crDeque(512, Dummy()); 161 | crDeque.clear(); 162 | 163 | for (size_t i = 0; i < 200000; ++i) 164 | { 165 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 166 | int random = rand() % 11; 167 | #else 168 | int random = rand() % 9; 169 | #endif 170 | switch(random) 171 | { 172 | case 0: crDeque.push_back(); break; 173 | case 1: crDeque.push_back(Dummy(65, 66.0f)); break; 174 | case 2: crDeque.push_back(dummy); break; 175 | case 3: crDeque.push_front(); break; 176 | case 4: crDeque.push_front(Dummy(65, 66.0f)); break; 177 | case 5: crDeque.push_front(dummy); break; 178 | case 7: if (!crDeque.empty()) crDeque.pop_back(); break; 179 | case 8: if (!crDeque.empty()) crDeque.pop_front(); break; 180 | #if defined(CRSTL_FEATURE_VARIADIC_TEMPLATES) 181 | case 9: crDeque.emplace_back(67, 68.0f); break; 182 | case 10: crDeque.emplace_front(67, 68.0f); break; 183 | #endif 184 | } 185 | } 186 | 187 | crDeque.push_back(); 188 | crDeque.push_front(); 189 | crDeque.push_back(Dummy(65, 66.0f)); 190 | crDeque.push_front(Dummy(65, 66.0f)); 191 | crDeque.push_back(dummy); 192 | crDeque.push_front(dummy); 193 | 194 | crDeque[0] = Dummy(150, 162); 195 | 196 | crDeque.back() = Dummy(10, 22); 197 | 198 | crDeque.front() = Dummy(50, 62); 199 | 200 | crDeque.resize(33); 201 | 202 | crstl::deque crDequeResize; 203 | crDequeResize.resize(40); 204 | 205 | crDequeResize.resize(13); 206 | 207 | crDequeResize.resize_back(512); 208 | 209 | crDequeResize.resize_front(512); 210 | 211 | crDequeResize[0] = Dummy(65, 66.0f); 212 | crDequeResize.back() = Dummy(65, 66.0f); 213 | 214 | #if defined(CRSTL_UNIT_RANGED_FOR) 215 | { 216 | size_t count = 0; 217 | for (auto iter = crDeque.begin(); iter != crDeque.end(); ++iter) 218 | { 219 | ++count; 220 | } 221 | 222 | crstl_check(crDeque.size() == count); 223 | } 224 | 225 | { 226 | size_t count = 0; 227 | for (const Dummy& d : crDeque) 228 | { 229 | (void)d; 230 | ++count; 231 | } 232 | 233 | crstl_check(crDeque.size() == count); 234 | } 235 | #endif 236 | 237 | crDeque.clear(); 238 | crstl_check(crDeque.size() == 0); 239 | crstl_check(crDeque.begin() == crDeque.end()); 240 | } 241 | end_test(); 242 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_filesystem.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/filesystem.h" 7 | #include "crstl/utility/string_length.h" 8 | #endif 9 | 10 | #include 11 | 12 | void RunUnitTestsFilesystem() 13 | { 14 | printf("RunUnitTestsFilesystem\n"); 15 | 16 | using namespace crstl_unit; 17 | 18 | begin_test("filesystem"); 19 | { 20 | crstl::path temp_path = crstl::temp_directory_path() / "crstl_temp"; 21 | 22 | crstl::path temp_file_path = temp_path / "temp_file.txt"; 23 | crstl::path temp_file_copy_path = temp_path / "temp_file_copy.txt"; 24 | crstl::path temp_file_move_path = temp_path / "temp_file_move.txt"; 25 | const char* temp_text = "I am a temp text"; 26 | 27 | crstl::delete_file(temp_file_path.c_str()); 28 | crstl::delete_file(temp_file_copy_path.c_str()); 29 | crstl::delete_file(temp_file_move_path.c_str()); 30 | crstl_assert(!crstl::exists(temp_file_path.c_str())); 31 | crstl_assert(!crstl::exists(temp_file_copy_path.c_str())); 32 | crstl_assert(!crstl::exists(temp_file_move_path.c_str())); 33 | 34 | { 35 | crstl::file my_file(temp_file_path.c_str(), crstl::file_flags::create | crstl::file_flags::write); 36 | 37 | // Make sure we cannot delete a file that is already open 38 | crstl::filesystem_result delete_result = crstl::delete_file(temp_file_path.c_str()); 39 | crstl_assert(delete_result == crstl::filesystem_result::error_access_denied); 40 | 41 | // Write a bit of text 42 | my_file.write(temp_text, crstl::string_length(temp_text)); 43 | 44 | // File closes when out of scope 45 | } 46 | 47 | crstl::copy_file(temp_file_path.c_str(), temp_file_copy_path.c_str(), crstl::file_copy_options::none); 48 | crstl_assert(crstl::exists(temp_file_copy_path.c_str())); 49 | 50 | crstl::move_file(temp_file_path.c_str(), temp_file_move_path.c_str()); 51 | crstl_assert(!crstl::exists(temp_file_path.c_str())); 52 | crstl_assert(crstl::exists(temp_file_move_path.c_str())); 53 | 54 | if (!crstl::exists("C:/temp/temp_file.txt")) 55 | { 56 | 57 | } 58 | } 59 | end_test(); 60 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_function.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/fixed_function.h" 7 | #include "crstl/function.h" 8 | #endif 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | int DummyA = 4; 16 | 17 | void DummyVoidFunction() 18 | { 19 | printf("Hello %d\n", DummyA); 20 | } 21 | 22 | int DummyIntFunction(int value) 23 | { 24 | printf("DummyIntFunction %d\n", value); 25 | return -1; 26 | } 27 | 28 | int DummyIntFunction2(int value) 29 | { 30 | printf("DummyIntFunction2 %d\n", value); 31 | return -1; 32 | } 33 | 34 | int DummyReturnPassedInt(int value) 35 | { 36 | return value; 37 | } 38 | 39 | void FunctionPassByReferenceTest(const crstl::fixed_function<8, void(int)>& myFunction, int k) 40 | { 41 | myFunction(k); 42 | } 43 | 44 | struct OperatorStruct 45 | { 46 | OperatorStruct() {} 47 | 48 | void operator()(void) const 49 | { 50 | 51 | } 52 | }; 53 | 54 | void RunUnitTestsFunction() 55 | { 56 | printf("RunUnitTestsFunction\n"); 57 | 58 | using namespace crstl_unit; 59 | 60 | int a = 0; 61 | int b = 1; 62 | int c = 2; 63 | int d = 3; 64 | int e = 4; 65 | int f = 5; 66 | int g = 6; 67 | int h = 7; 68 | 69 | int rand1 = rand(); crstl_unused(rand1); 70 | int rand2 = rand(); crstl_unused(rand2); 71 | int rand3 = rand(); crstl_unused(rand3); 72 | int rand4 = rand(); crstl_unused(rand4); 73 | int rand5 = rand(); crstl_unused(rand5); 74 | 75 | std::function stdVoidFunction; 76 | 77 | std::function stdVoidFunction2 = []() 78 | { 79 | printf("stdVoidFunction2\n"); 80 | }; 81 | 82 | std::function stdVoidFunction3 = [a]() 83 | { 84 | printf("stdVoidFunction3 %d\n", a); 85 | }; 86 | 87 | std::function stdIntFunction; 88 | 89 | stdIntFunction = &DummyIntFunction; 90 | 91 | begin_test("fixed_function"); 92 | { 93 | crstl::fixed_function<8, void()> crVoidFunction; 94 | 95 | crstl::fixed_function<16, void()> crVoidFunction2 = []() 96 | { 97 | printf("crVoidFunction2\n"); 98 | }; 99 | 100 | crstl::fixed_function<8, void()> crVoidFunction3 = [a]() 101 | { 102 | printf("crVoidFunction3 %d\n", a); 103 | }; 104 | 105 | crstl::fixed_function<8, void()> crVoidFunction4 = &DummyVoidFunction; 106 | 107 | crstl::fixed_function<8, int(int)> crIntFunction = &DummyReturnPassedInt; 108 | 109 | crstl::fixed_function<8, int(int)> crIntFunctionNonConst = &DummyReturnPassedInt; 110 | 111 | crstl::fixed_function<8, int(int)> crIntFunctionMove = &DummyReturnPassedInt; 112 | 113 | const crstl::fixed_function<8, int(int)> crIntFunctionConst = &DummyReturnPassedInt; 114 | crstl_assert(crIntFunctionConst(rand1) == rand1); 115 | 116 | const crstl::fixed_function<8, void(void)> crMoveOnlyFunction = OperatorStruct(); 117 | 118 | crIntFunction = crIntFunctionNonConst; 119 | 120 | crstl::fixed_function<8, int(int)> crIntFunctionCopy1(crIntFunctionNonConst); 121 | crstl_assert(crIntFunctionCopy1(rand2) == rand2); 122 | 123 | crstl::fixed_function<8, int(int)> crIntFunctionCopy2(crIntFunctionConst); 124 | crstl_assert(crIntFunctionCopy2(rand3) == rand3); 125 | 126 | crstl::fixed_function<8, int(int)> crIntFunctionMoved(std::move(crIntFunctionMove)); 127 | crstl_assert(crIntFunctionMoved(rand4) == rand4); 128 | 129 | crstl::fixed_function<16, int(int)> crIntFunctionCopyDifferentSize(crIntFunctionNonConst); 130 | crstl_assert(crIntFunctionCopyDifferentSize(rand5) == rand5); 131 | 132 | // Call functions 133 | 134 | if (stdVoidFunction) 135 | { 136 | stdVoidFunction(); 137 | } 138 | 139 | if (crVoidFunction) 140 | { 141 | crVoidFunction(); 142 | } 143 | 144 | stdVoidFunction2(); 145 | crVoidFunction2(); 146 | 147 | stdVoidFunction3(); 148 | crVoidFunction3(); 149 | 150 | //stdVoidFunction4(); 151 | crVoidFunction4(); 152 | 153 | int i = crIntFunction(4); 154 | printf("IntFunction %d\n", i); 155 | } 156 | end_test(); 157 | 158 | begin_test("function"); 159 | { 160 | crstl::function crVoidFunction; 161 | 162 | crstl::function crVoidFunction2 = []() 163 | { 164 | printf("crVoidFunction2\n"); 165 | }; 166 | 167 | crstl::function crVoidFunction3 = [a, b, c, d, e, f, g, h]() 168 | { 169 | printf("crVoidFunction3 %d\n", a); 170 | printf("crVoidFunction3 %d\n", b); 171 | printf("crVoidFunction3 %d\n", c); 172 | printf("crVoidFunction3 %d\n", d); 173 | printf("crVoidFunction3 %d\n", e); 174 | printf("crVoidFunction3 %d\n", f); 175 | printf("crVoidFunction3 %d\n", g); 176 | printf("crVoidFunction3 %d\n", h); 177 | }; 178 | 179 | crVoidFunction3(); 180 | 181 | crstl::function crVoidFunction4 = &DummyVoidFunction; 182 | 183 | crstl::function crIntFunction = &DummyReturnPassedInt; 184 | 185 | crstl::function crIntFunctionNonConst = &DummyReturnPassedInt; 186 | 187 | crstl::function crIntFunctionMove = &DummyReturnPassedInt; 188 | 189 | const crstl::function crIntFunctionConst = &DummyReturnPassedInt; 190 | crstl_assert(crIntFunctionConst(rand1) == rand1); 191 | 192 | const crstl::function crMoveOnlyFunction = OperatorStruct(); 193 | 194 | crIntFunction = crIntFunctionNonConst; 195 | 196 | crstl::function crIntFunctionCopy1(crIntFunctionNonConst); 197 | crstl_assert(crIntFunctionCopy1(rand2) == rand2); 198 | 199 | crstl::function crIntFunctionCopy2(crIntFunctionConst); 200 | crstl_assert(crIntFunctionCopy2(rand3) == rand3); 201 | 202 | crstl::function crIntFunctionMoved(std::move(crIntFunctionMove)); 203 | crstl_assert(crIntFunctionMoved(rand4) == rand4); 204 | 205 | crstl::function crIntFunctionCopyDifferentSize(crIntFunctionNonConst); 206 | crstl_assert(crIntFunctionCopyDifferentSize(rand5) == rand5); 207 | } 208 | end_test(); 209 | 210 | //printf("Hello\n"); 211 | //printf("Hello %d\n", a); 212 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_pair.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/pair.h" 7 | #endif 8 | 9 | #include 10 | 11 | #include 12 | 13 | void RunUnitTestsPair() 14 | { 15 | printf("RunUnitTestsPair\n"); 16 | 17 | struct DummyC 18 | { 19 | DummyC() : i(0), f(0.0f) {} 20 | 21 | int i; 22 | float f; 23 | }; 24 | 25 | struct DummyA 26 | { 27 | DummyA() { i = 0; } 28 | DummyA(int k) : i(k) {} 29 | DummyA(DummyC dc) : i(dc.i) {} 30 | 31 | int i; 32 | }; 33 | 34 | struct DummyB 35 | { 36 | DummyB() { f = 0; } 37 | DummyB(float k) : f(k) {} 38 | DummyB(DummyC dc) { f = dc.f; } 39 | 40 | float f; 41 | }; 42 | 43 | std::pair stdPair; 44 | crstl::pair crPair; 45 | 46 | DummyA stdFirst = stdPair.first; crstl_unit_unused(stdFirst); 47 | DummyA crFirst = crPair.first; crstl_unit_unused(crFirst); 48 | 49 | DummyB stdSecond = stdPair.second; crstl_unit_unused(stdSecond); 50 | DummyB crSecond = crPair.second; crstl_unit_unused(crSecond); 51 | 52 | std::pair stdCopyPair(stdPair); crstl_unit_unused(stdCopyPair); 53 | crstl::pair crCopyPair(crPair); crstl_unit_unused(crCopyPair); 54 | 55 | std::pair stdAssignPair = stdPair; crstl_unit_unused(stdAssignPair); 56 | crstl::pair crAssignPair = crPair; crstl_unit_unused(crAssignPair); 57 | 58 | DummyC dummyC; 59 | 60 | #if defined(CRSTL_FEATURE_INITIALIZER_LISTS) 61 | 62 | std::pair stdInitPair = { 3, 4.0f }; crstl_unit_unused(stdInitPair); 63 | crstl::pair crInitPair = { 3, 4.0f }; crstl_unit_unused(crInitPair); 64 | 65 | #endif 66 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_path.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/fixed_path.h" 7 | #include "crstl/path.h" 8 | #include "crstl/string.h" 9 | #endif 10 | 11 | // Filesystem begins with C++17 12 | #if CRSTL_CPPVERSION >= CRSTL_CPP17 13 | #include 14 | #endif 15 | 16 | #include 17 | 18 | void RunUnitTestsPath() 19 | { 20 | printf("RunUnitTestsPath\n"); 21 | 22 | #if CRSTL_CPPVERSION >= CRSTL_CPP17 23 | using namespace crstl_unit; 24 | 25 | crstl::fixed_path512 fixedPath; 26 | 27 | crstl::string examplePaths[] = 28 | { 29 | "", 30 | ".", 31 | "..", 32 | //"/", // Fails unit tests due to incomplete implementation of parent_path() 33 | "foo", 34 | "foo.txt", 35 | "C:/foo/bar/", // Path ending in / 36 | "C:/foo/bar/foobar", // Path ending in filename without extension 37 | "C:/foo/bar/foobar.txt", // Path ending in filename with extension 38 | "C:/foo.bar/foo.bar/foobar.txt", // Path with dots ending in filename with extension 39 | "C:/foo.bar/foo.bar/foobar", // Path with dots ending in filename without extension 40 | }; 41 | 42 | for (size_t i = 0; i < sizeof(examplePaths) / sizeof(examplePaths[0]); ++i) 43 | { 44 | const crstl::string& examplePath = examplePaths[i]; 45 | 46 | std::filesystem::path stdPath = examplePath.c_str(); 47 | crstl::fixed_path512 crFixedPath = examplePath.c_str(); 48 | crstl::path crPath = examplePath.c_str(); 49 | 50 | // Check empty path concatenation 51 | { 52 | crstl::string stdPathFilename((stdPath / "foobar.exe").string().c_str()); 53 | stdPathFilename.replace_all('\\', '/'); 54 | 55 | crstl::string crFixedPathFilename((crFixedPath / "foobar.exe").c_str()); 56 | crstl_check(stdPathFilename == crFixedPathFilename); 57 | 58 | crstl::string crPathFilename((crPath / "foobar.exe").c_str()); 59 | crstl_check(stdPathFilename == crPathFilename); 60 | } 61 | 62 | // Check filename 63 | { 64 | crstl::string stdPathFilename(stdPath.filename().string().c_str()); 65 | 66 | crstl::string crFixedPathFilename(crFixedPath.filename().c_str()); 67 | crstl_check(stdPathFilename == crFixedPathFilename); 68 | 69 | crstl::string crPathFilename(crPath.filename().c_str()); 70 | crstl_check(stdPathFilename == crPathFilename); 71 | } 72 | 73 | // Check parent path 74 | { 75 | crstl::string stdPathParentPath(stdPath.parent_path().string().c_str()); 76 | 77 | crstl::string crFixedPathParentPath(crFixedPath.parent_path().c_str()); 78 | crstl_check(stdPathParentPath == crFixedPathParentPath); 79 | 80 | crstl::string crPathParentPath(crPath.parent_path().c_str()); 81 | crstl_check(stdPathParentPath == crPathParentPath); 82 | } 83 | 84 | // Check extension 85 | { 86 | crstl::string stdPathExtension(stdPath.extension().string().c_str()); 87 | 88 | crstl::string crFixedPathExtension(crFixedPath.extension().c_str()); 89 | crstl_check(crFixedPathExtension == stdPathExtension); 90 | 91 | crstl::string crPathExtension(crPath.extension().c_str()); 92 | crstl_check(crPathExtension == stdPathExtension); 93 | } 94 | 95 | // Check has_extension 96 | crstl_check(stdPath.has_extension() == crFixedPath.has_extension()); 97 | crstl_check(stdPath.has_extension() == crPath.has_extension()); 98 | } 99 | #endif 100 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_process.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/process.h" 7 | #endif 8 | 9 | #include 10 | 11 | void RunUnitTestsProcess() 12 | { 13 | printf("RunUnitTestsProcess\n"); 14 | 15 | crstl::process p; 16 | p = crstl::process("fxc.exe", "/?"); 17 | 18 | char buffer[16384]; 19 | p.read_stdout(buffer, 16384); 20 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_smartptr.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/unique_ptr.h" 7 | #include "crstl/intrusive_ptr.h" 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | std::vector> stdUniqueVector; 15 | 16 | std::vector> crUniqueVector; 17 | 18 | void RunUnitTestsSmartPtr() 19 | { 20 | printf("RunUnitTestsSmartPtr\n"); 21 | 22 | // Unique pointer 23 | 24 | std::unique_ptr stdUniquePtr = std::unique_ptr(new Dummy()); 25 | stdUniquePtr->PrintName(); 26 | stdUniquePtr = nullptr; 27 | 28 | crstl::unique_ptr crUniquePtr = crstl::unique_ptr(new Dummy()); 29 | crUniquePtr->PrintName(); 30 | crUniquePtr = nullptr; 31 | 32 | stdUniqueVector.push_back(std::unique_ptr(new Dummy())); 33 | crUniqueVector.push_back(crstl::unique_ptr(new Dummy())); 34 | 35 | std::unique_ptr stdNullUniquePtr = nullptr; 36 | stdNullUniquePtr = std::unique_ptr(new Dummy()); 37 | stdNullUniquePtr = nullptr; 38 | 39 | crstl::unique_ptr crNullUniquePtr = nullptr; 40 | crNullUniquePtr = crstl::unique_ptr(new Dummy()); 41 | crNullUniquePtr = nullptr; 42 | 43 | if (crUniquePtr) {} 44 | if (crUniquePtr != nullptr) {} 45 | if (crUniquePtr != crNullUniquePtr.get()) {} 46 | 47 | crstl::unique_ptr crArrayUniquePtr(new char[32]); 48 | std::unique_ptr stdArrayUniquePtr(new char[32]); 49 | 50 | crArrayUniquePtr = nullptr; 51 | 52 | // Intrusive pointer 53 | crstl::intrusive_ptr objectHandle = new RefCountDummy(); 54 | 55 | if (objectHandle) 56 | { 57 | 58 | } 59 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_thread.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/thread.h" 7 | #include "crstl/critical_section.h" 8 | #include "crstl/tuple.h" 9 | 10 | #include "crstl/timer.h" // For sleep timings 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | int ThreadFunctionWithParams(int a, int b, int c) 18 | { 19 | int sum = a + b + c; 20 | printf("ThreadFunctionWithParams %i\n", sum); 21 | return sum; 22 | } 23 | 24 | void RunUnitTestsThread() 25 | { 26 | printf("RunUnitTestsThread\n"); 27 | 28 | int i = 42; 29 | int a = 2; 30 | float b = 3.0f; 31 | char c = 'a'; 32 | 33 | crstl::tuple crTupleConstructorMove(2, 3.0f, 'a'); 34 | 35 | crstl::tuple crTupleConstructorConstRef(2, 3.0f, c); crstl_unused(crTupleConstructorConstRef); 36 | 37 | crstl::tuple crTupleBracketsMove = { 2, 3.0f, 'a' }; crstl_unused(crTupleBracketsMove); 38 | 39 | crstl::tuple crTupleBracketsConstRef = { a, b, 'l' }; crstl_unused(crTupleBracketsConstRef); 40 | 41 | crstl::tuple crTupleCopy(crTupleConstructorMove); 42 | 43 | size_t tuple_size = crTupleConstructorMove.size(); crstl_unused(tuple_size); 44 | 45 | float f_get = crstl::get<1>(crTupleBracketsMove); crstl_unused(f_get); 46 | 47 | char c_get = crstl::get<2>(crTupleBracketsConstRef); crstl_unused(c_get); 48 | 49 | int i_get = crTupleCopy.get<0>(); crstl_unused(i_get); 50 | 51 | crstl::thread_parameters params; 52 | 53 | params.debug_name = "Thread 1"; 54 | crstl::thread crThread1(params, []() 55 | { 56 | printf("Thread 1\n"); 57 | }); 58 | 59 | params.debug_name = "Lambda Thread With Capture"; 60 | crstl::thread crThread2(params, [i]() 61 | { 62 | crstl_unused(i); 63 | printf("Lambda Thread With Capture\n"); 64 | }); 65 | 66 | params.debug_name = "Lambda Thread With Capture 2"; 67 | crstl::thread crThread3(params, [&i]() 68 | { 69 | crstl_unused(i); 70 | printf("Lambda Thread With Capture 2\n"); 71 | }); 72 | 73 | params.debug_name = "ThreadFunctionWithParams"; 74 | crstl::thread crThread4(params, &ThreadFunctionWithParams, 1, 2, 3); 75 | 76 | crstl::thread crThread5 = crstl::thread(params, &ThreadFunctionWithParams, 1, 2, 3); 77 | 78 | crstl::timer yieldTimer(true); 79 | crstl::this_thread::yield(); 80 | printf("Yielded for %fms\n", yieldTimer.elapsed().milliseconds()); 81 | 82 | printf("Sleeping...\n"); 83 | crstl::timer leTimer(true); 84 | crstl::this_thread::sleep_for(1000); 85 | printf("Slept for %fms\n", leTimer.elapsed().milliseconds()); 86 | 87 | crThread1.join(); 88 | crThread2.join(); 89 | crThread3.join(); 90 | crThread4.join(); 91 | crThread5.join(); 92 | } -------------------------------------------------------------------------------- /unit_tests/unit_tests_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "unit_tests.h" 2 | 3 | #if defined(CRSTL_UNIT_MODULES) 4 | import crstl; 5 | #else 6 | #include "crstl/timer.h" 7 | #endif 8 | 9 | #include 10 | 11 | void RunUnitTestsTimer() 12 | { 13 | printf("RunUnitTestsTimer\n"); 14 | 15 | crstl::timer crTimer; 16 | 17 | crstl::time start_time = crTimer.elapsed(); 18 | 19 | for (int i = 0; i < 100000; ++i) 20 | { 21 | 22 | } 23 | 24 | crstl::time end_time = crTimer.elapsed(); 25 | 26 | crstl::time elapsed = end_time - start_time; 27 | 28 | printf("Timer Start: %f End: %f Elapsed: %f\n", start_time.milliseconds(), end_time.milliseconds(), elapsed.milliseconds()); 29 | 30 | crstl::cycle_timer crCycleTimer; 31 | 32 | for (int i = 0; i < 100000; ++i) 33 | { 34 | 35 | } 36 | 37 | printf("Ticks elapsed: %llu\n", (long long unsigned int)crCycleTimer.elapsed()); 38 | } 39 | --------------------------------------------------------------------------------