Replace build scripts with cmake (#3)

Reviewed-on: #3
Co-authored-by: Musa <musasmahmood@gmail.com>
Co-committed-by: Musa <musasmahmood@gmail.com>
This commit is contained in:
Musa Mahmood 2025-12-19 02:28:51 +00:00 committed by Musa Mahmood
parent d1182f3abd
commit 55ce432097
40 changed files with 888 additions and 616 deletions

60
CMakeLists.txt Normal file
View File

@ -0,0 +1,60 @@
############# README ############# README ############# README ############
# Option to choose between shared or static library
# Pass this as an argument as follows when configuring the project:
# `cmake -S . -B build
# then build it with either `Release` or `Debug` option:
# `cmake --build build --config Release`
############ /README ############ /README ############ /README ############
cmake_minimum_required(VERSION 3.20)
project(musa-explorer-cpp)
# Use C++11
SET (CMAKE_CXX_STANDARD 11)
SET (CMAKE_VERBOSE_MAKEFILE ON)
if (MSVC)
# Suppress warning: C++ exception handler used, but unwind semantics are not enabled.
add_compile_options(/wd4530)
endif()
SET (EXE_NAME "mexplore_v2")
SET (SRC_FILES
lib/third_party/dear-imgui/imgui.cpp
lib/third_party/dear-imgui/imgui_widgets.cpp
lib/third_party/dear-imgui/imgui_draw.cpp
lib/third_party/dear-imgui/imgui_tables.cpp
lib/third_party/dear-imgui/imgui_impl_dx11.cpp
lib/third_party/dear-imgui/imgui_impl_win32.cpp
exe_main.cpp
)
SET (INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/lib
${PROJECT_SOURCE_DIR}/lib/third_party
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
SET (LINK_LIB_DIRS
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
SET (LINK_LIB_DIRS
)
endif()
add_executable(${EXE_NAME} ${SRC_FILES})
target_include_directories(${EXE_NAME} PRIVATE ${INCLUDE_DIRS})
target_link_libraries(${EXE_NAME} PRIVATE ${LINK_LIBS_DIRS})
message(STATUS "Build type: $<CONFIG>")
add_custom_command(TARGET ${EXE_NAME} POST_BUILD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E echo "Running post-build script..."
COMMAND cmd.exe /c "${CMAKE_SOURCE_DIR}/copy_files.cmd" $<CONFIG>
COMMENT "Running custom post-build script."
)

View File

@ -1,5 +1,49 @@
build either with `build.cmd Debug` or `build.cmd Release`
Run `build_imgui_lib.cmd` to build imgui dependency
Both scripts must be run in x64 Native Tools Command Prompt for VS 20xx.
# Build Overview
You can also build with `jai build.jai - build_exe` or `jai build.jai - build_exe release`
- #TODO: Explain build configuration: library, exe
- Why do we use CMake (even though I hate it)? (because it's industry standard?)
- Plans to use a metaprogram to modify code for certain reasons (e.g. getting stack traces)
## Configuration
This project uses CMake to configure and build. Currently only Win32 is supported.
```
cmake -S . -B build
```
## Build
To build in debug mode:
```
cmake --build build --config Debug
```
To build in release mode:
```
cmake --build build --config Release
```
## Roadmap for Supporting Other platforms
- Linux
- MacOS
- Android
- iOS
# APIs
## Base Layer
### Thread-local Context
## OS Platform Layer
## Debug Tooling
# Explorer Application Notes
## File Enumeration
## Sorting
## Searching Algorithms
## Multithreading

View File

@ -1,56 +0,0 @@
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Usage: build.cmd [Debug^|Release]
exit /b 1
)
set CONFIG=%~1
if /I "%CONFIG%"=="Debug" (
set CFLAGS=/MDd /Od /Zi /DDEBUG /DEBUG -diagnostics:caret -diagnostics:column -D_CRT_SECURE_NO_WARNINGS -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING -D_CRT_NONSTDC_NO_DEPRECATE -D_USE_MATH_DEFINES
set LIB_PATH=bin
set IMGUI_LIB_PATH=bin\Debug\dear-imgui.lib
) else if /I "%CONFIG%"=="Release" (
set CFLAGS=/MD /O2 /DNDEBUG
set LIB_PATH=bin
set IMGUI_LIB_PATH=bin\Release\dear-imgui.lib
) else (
echo Invalid configuration: %CONFIG%
echo Usage: build.cmd [Debug^|Release]
exit /b 1
)
set OBJDIR=obj
if not exist "%LIB_PATH%" mkdir "%LIB_PATH%"
if not exist obj mkdir obj
set LIB_NAME=musa
set SRC_FILES=^
exe_main.cpp
set INCLUDE_DIRS=^
/Isrc ^
/Ilib ^
/Ilib\api ^
/Ithird_party
set LINK_LIBS=^
%IMGUI_LIB_PATH%
rem NOTE: it defaults to C++11, so /std:c++11 is redundant
rem SHARED LIBRARY:
rem echo Building DLL (%CONFIG%)
rem cl /nologo /EHsc /DWIN32 /wd4530 %CFLAGS% %INCLUDE_DIRS% %SRC_FILES% /LD /Fe%LIB_PATH%\%LIB_NAME%.dll /Fo%OBJDIR%\ /link %LINK_LIBS%
echo Building EXE (%CONFIG%)...
cl /nologo /EHsc /DWIN32 /wd4530 %CFLAGS% %INCLUDE_DIRS% %SRC_FILES% /link /MACHINE:AMD64 %LINK_LIBS% /OUT:%LIB_PATH%\%LIB_NAME%.exe
rem cleanup...
rd /s /q "%OBJDIR%"
rem echo Running post-build script...
rem nothing to do (for now).
endlocal

150
build.jai
View File

@ -1,150 +0,0 @@
// Some notes:
// To generate the intermediate to see how many lines are being compiled, in x64 Native Tools Command Prompt for VS2022 or whatever
// cl /P /EP exe_main.cpp
// tokei exe_main.i
VERSION :: "0.2";
#run,stallable build_cpp_project();
LIB_BASE_NAME :: "musa-lib";
EXE_BASE_NAME :: "musa";
LIB_SOURCE_FILENAMES :: string.["lib_main.cpp"];
EXE_SOURCE_FILENAMES :: string.["exe_main.cpp"];
INCLUDE_DIRS :: string.[
"src",
"lib",
"lib/api",
"third_party"
];
build_cpp_project :: () {
start := seconds_since_init();
set_build_options_dc(.{do_output=false});
options := get_build_options();
args := options.compile_time_command_line;
compile_debug := true;
if array_find(args, "release") { compile_debug = false; }
build_lib := array_find(args, "build_lib");
build_exe := array_find(args, "build_exe");
generate_meta_file(compile_debug);
if build_lib build_cpp_lib(compile_debug);
if build_exe build_cpp_exe(compile_debug);
print("\nFull build time: % seconds\n\n", FF(seconds_since_init() - start, 3));
}
build_cpp_exe :: (compile_debug: bool) {
extra: [..]string;
if os_target == {
case .WINDOWS;
array_add(*extra, "-D_CRT_SECURE_NO_WARNINGS", "-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING", "-D_CRT_NONSTDC_NO_DEPRECATE");
array_add(*extra, "-D_USE_MATH_DEFINES"); // , "-D_WIN32_WINNT=0x0A00", "/utf-8"
array_add(*extra, "/MT"); // Static - The default
for INCLUDE_DIRS array_add(*extra, tprint("/I%", it));
case; assert(false, "Other OSes not supported yet!");
}
exe_path := tprint("bin/%", EXE_BASE_NAME);
make_directory_if_it_does_not_exist("bin");
LINK_LIBS: []string;
if (compile_debug) { LINK_LIBS = .["bin/Debug/dear-imgui.lib"]; }
else { LINK_LIBS = .["bin/Release/dear-imgui.lib"]; }
success := build_cpp_executable(exe_path, ..EXE_SOURCE_FILENAMES, debug=compile_debug, extra=extra, library_files=LINK_LIBS);
print("\nbuild_cpp_executable, success: %\n", success);
}
build_cpp_lib :: (compile_debug: bool) -> bool {
lib_path := tprint("bin/Debug/%", LIB_BASE_NAME);
if !compile_debug {
lib_path = tprint("bin/Release/%", LIB_BASE_NAME);
}
lib_directory := path_strip_filename(lib_path);
make_directory_if_it_does_not_exist(lib_directory, recursive = true);
print("Output lib_path: %\n\n", lib_path);
extra: [..]string;
if os_target == {
case .WINDOWS;
array_add(*extra, "-D_CRT_SECURE_NO_WARNINGS", "-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING", "-D_CRT_NONSTDC_NO_DEPRECATE");
array_add(*extra, "-D_USE_MATH_DEFINES"); // , "-D_WIN32_WINNT=0x0A00", "/utf-8"
// array_add(*extra, "/MD"); // Dynamic
array_add(*extra, "/MT"); // Static - The default
for INCLUDE_DIRS array_add(*extra, tprint("/I%", it));
case .LINUX;
array_add(*extra, "-fPIC");
array_add(*extra, tprint("-I/%/%", #filepath, "JIIM_Library"));
for INCLUDE_DIRS array_add(*extra, tprint("-I/%/%", #filepath, it));
case; assert(false, "Other OSes not supported yet!");
}
success := build_cpp_static_lib(lib_path, ..LIB_SOURCE_FILENAMES, debug=compile_debug, extra=extra);
print("\nbuild_cpp_static_lib, success: %\n", success);
return success;
}
META_GENERATED_HEADER_FILE_PATH :: "lib/meta_generated.h";
cpu_target: CPU_Tag = .X64;
os_target: Operating_System_Tag = .WINDOWS;
generate_meta_file :: (debug: bool) {
sb: String_Builder;
append(*sb, "#pragma once\n\n");
print_to_builder(*sb, "const char* MUSA_LIB_VERSION = \"%\";\n", VERSION);
print_to_builder(*sb, "#define BUILD_DEBUG %\n", cast(s32)debug);
print_to_builder(*sb, "#define OS_WINDOWS %\n", ifx os_target == .WINDOWS then 1 else 0);
print_to_builder(*sb, "#define OS_LINUX %\n", ifx os_target == .LINUX then 1 else 0);
print_to_builder(*sb, "#define OS_MACOS %\n", ifx os_target == .MACOS then 1 else 0);
print_to_builder(*sb, "#define OS_ANDROID %\n", ifx os_target == .ANDROID then 1 else 0);
print_to_builder(*sb, "#define OS_IOS %\n", ifx os_target == .IOS then 1 else 0);
print_to_builder(*sb, "#define ARCH_CPU_X64 %\n", ifx cpu_target == .X64 then 1 else 0);
print_to_builder(*sb, "#define ARCH_CPU_ARM64 %\n", ifx cpu_target == .ARM64 then 1 else 0);
os_is_unix := os_target == .MACOS || os_target == .LINUX || os_target == .IOS || os_target == .ANDROID;
print_to_builder(*sb, "#define OS_IS_UNIX %\n", ifx os_is_unix then 1 else 0);
print_to_builder(*sb, "#define COMPILER_MSVC %\n", ifx os_target == .WINDOWS then 1 else 0);
print_to_builder(*sb, "#define COMPILER_CLANG %\n", ifx os_target != .WINDOWS then 1 else 0);
print_to_builder(*sb, "#define ARRAY_ENABLE_BOUNDS_CHECKING %\n", cast(s32)debug);
append(*sb, "#define COMPILER_GCC 0\n");
meta_file_data := builder_to_string(*sb);
write_entire_file(META_GENERATED_HEADER_FILE_PATH, meta_file_data);
print("Generated meta header at % for % on %\n", META_GENERATED_HEADER_FILE_PATH, cpu_target, os_target);
}
#import "Basic";
#import "BuildCpp";
#import "Compiler";
#import "File";
#import "File_Utilities";
#import "String";
#import "System";
#import "Process";
#if OS == .WINDOWS {
#import "Ico_File";
#import "Windows_Resources";
#import "Windows_Utf8";
}
// Note: Other operating systems are not supported for this application (yet).
FF :: (val: float64, width:=1) -> FormatFloat #expand {
return formatFloat(val, trailing_width = width, zero_removal=.NO);
} @Utility

View File

@ -1,51 +0,0 @@
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Usage: build_imgui_lib.cmd [Debug^|Release]
exit /b 1
)
set CONFIG=%~1
if /I "%CONFIG%"=="Debug" ( rem /Z7 embeds debug info into PDB so we don't need to pass /Fd to cl
set CFLAGS=/MTd /Od /Z7 /DDEBUG /DEBUG -diagnostics:caret -diagnostics:column -D_CRT_SECURE_NO_WARNINGS -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING -D_CRT_NONSTDC_NO_DEPRECATE -D_USE_MATH_DEFINES
set LIB_PATH=bin/Debug
) else if /I "%CONFIG%"=="Release" (
set CFLAGS=/MT /O2 /DNDEBUG
set LIB_PATH=bin/Release
) else (
echo Invalid configuration: %CONFIG%
echo Usage: build_imgui_lib.cmd [Debug^|Release]
exit /b 1
)
set OBJDIR=obj
if not exist "%LIB_PATH%" mkdir "%LIB_PATH%"
if not exist obj mkdir obj
set LIB_NAME=dear-imgui
set SRC_FILES=^
lib\third_party\dear-imgui\imgui.cpp ^
lib\third_party\dear-imgui\imgui_widgets.cpp ^
lib\third_party\dear-imgui\imgui_draw.cpp ^
lib\third_party\dear-imgui\imgui_tables.cpp ^
lib\third_party\dear-imgui\imgui_impl_dx11.cpp ^
lib\third_party\dear-imgui\imgui_impl_win32.cpp
rem lib\third_party\dear-imgui\imgui_demo.cpp ^
set INCLUDE_DIRS=^
/Ilib\third_party
set LINK_LIBS=
REM Build STATIC LIBRARY: rem /Fd%LIB_PATH%/%LIB_NAME%.pdb can be passed to cl if compiling with /Zi
echo Building static LIB (%CONFIG%)
cl /nologo /EHsc /DWIN32 /wd4530 %CFLAGS% %INCLUDE_DIRS% %SRC_FILES% /c /Fo%OBJDIR%\
lib /OUT:%LIB_PATH%\%LIB_NAME%.lib %OBJDIR%\*.obj %LINK_LIBS%
rd /s /q "%OBJDIR%"
endlocal

17
copy_files.cmd Normal file
View File

@ -0,0 +1,17 @@
@echo off
set CONFIG=%1
echo Build configuration is: %CONFIG%
if /i "%CONFIG%"=="Debug" (
echo Copying files to debug directory
copy /Y "extras\fonts\RobotoMono-Regular.ttf" "build\Debug\RobotoMono-Regular.ttf"
copy /Y "extras\icons\tmp.ico" "build\Debug\tmp.ico"
copy /Y "extras\icons\tmp_min.ico" "build\Debug\tmp_min.ico"
:: #TODO: Copy DLLs: Varjo
rem copy /Y "third_party\bin\some_dll.dll" "build/Debug\some_dll.dll"
) else if /i "%CONFIG%"=="Release" (
echo Copying files to release directory
copy /Y "extras\fonts\RobotoMono-Regular.ttf" "build\Release\RobotoMono-Regular.ttf"
copy /Y "extras\icons\tmp.ico" "build\Release\tmp.ico"
copy /Y "extras\icons\tmp_min.ico" "build\Release\tmp_min.ico"
)

View File

@ -1,6 +1,6 @@
// Treat library files as a single-file header (single translation unit)
#include "lib_main.cpp"
// Toggles:
#define BASE_RUN_TESTS 0
#define BUILD_EXPLORER_APP_WIN32 1
#define BUILD_CUSTOM_GUI 0
@ -17,6 +17,7 @@
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "msvcrt.lib")
#include "lib/third_party/dear-imgui/imgui.h"
#include "lib/third_party/dear-imgui/imgui_impl_win32.h"

70
extras/imgui.ini Normal file
View File

@ -0,0 +1,70 @@
[Window][Debug##Default]
Pos=0,0
Size=85,85
Collapsed=0
[Window][Hello, world!]
Size=1582,874
Collapsed=0
DockId=0x00000002,0
[Window][Dear ImGui Demo]
Pos=0,22
Size=2124,1511
Collapsed=0
DockId=0xC0DFADC4,0
[Window][DockSpace Demo]
Size=2560,1533
Collapsed=0
[Window][Dear ImGui Metrics/Debugger]
ViewportPos=1947,173
ViewportId=0x366E23FF
Size=435,462
Collapsed=0
[Window][WindowOverViewport_11111111]
Pos=0,0
Size=3391,1672
Collapsed=0
[Window][Font Settings]
Pos=0,1556
Size=1371,116
Collapsed=0
DockId=0x00000001,0
[Window][Test panel]
Pos=192,318
Size=691,540
Collapsed=0
[Window][Debug Panel]
Pos=1374,0
Size=2017,1269
Collapsed=0
DockId=0x00000005,0
[Window][Control Panel]
Pos=1374,1272
Size=2017,400
Collapsed=0
DockId=0x00000006,0
[Window][Enumerated Data Workspace]
Pos=0,0
Size=1371,1672
Collapsed=0
DockId=0x00000002,0
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=225,261 Size=3391,1672 Split=X Selected=0x1FC7AC8C
DockNode ID=0x00000003 Parent=0x08BD597D SizeRef=1371,1672 Split=Y
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1582,1553 CentralNode=1 Selected=0x671FC263
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1582,116 Selected=0x355F9D19
DockNode ID=0x00000004 Parent=0x08BD597D SizeRef=2017,1672 Split=Y Selected=0xD2C573A7
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=1351,1269 Selected=0xD2C573A7
DockNode ID=0x00000006 Parent=0x00000004 SizeRef=1351,400 Selected=0xF930105C
DockSpace ID=0xC0DFADC4 Pos=0,51 Size=2560,1511 CentralNode=1 Selected=0x5E5F7166

View File

@ -82,7 +82,11 @@ bool arena_commit_first_pages (Arena* arena, s64 commit_size, s64 start_offset)
return true;
}
Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count) {
// Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count) {
Arena* bootstrap_arena_internal (Arena_Reserve new_reserve, s32 default_commit_page_count, string file_path,
string function_name, s32 line_number) {
// + Save thread ID/name MAKE A COPY OBVIOUSLY! + PUSH default_allocator!
// WE USE default_allocator because this arena may be used to back an array!
s64 commit_size = default_commit_page_count * PLATFORM_MEMORY_PAGE_SIZE;
Assert(commit_size <= reserve_size(new_reserve));
@ -93,11 +97,20 @@ Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count
return nullptr;
}
memcpy(arena_ptr, &new_arena, sizeof(Arena));
memcpy(arena_ptr, &new_arena, sizeof(Arena));
arena_ptr->current_point = arena_start(arena_ptr);
arena_set_bootstrap_flag(arena_ptr);
#if BUILD_DEBUG
// #TODO: use thread_context()->stack_trace if present instead?
{ arena_ptr->file_path = file_path;
arena_ptr->function_name = function_name;
arena_ptr->line_number = line_number;
add_arena_to_in_use_list(arena_ptr);
}
#endif
return arena_ptr;
}
@ -309,4 +322,29 @@ void* fixed_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 o
}
return nullptr;
}
}
force_inline void initialize_arenas_in_use_list () {
#if BUILD_DEBUG
if (arenas_in_use.allocated > 0) return;
mutex_init(&arenas_in_use_mutex);
arenas_in_use.allocator = default_allocator();
array_reserve(arenas_in_use, 256);
#endif
}
force_inline void add_arena_to_in_use_list (Arena* arena) {
#if BUILD_DEBUG
Assert(arenas_in_use.allocated > 0); // check we initialized!
lock_guard(&arenas_in_use_mutex);
array_add(arenas_in_use, arena);
#endif
}
force_inline void remove_arena_from_in_use_list (Arena* arena) {
#if BUILD_DEBUG
Assert(arenas_in_use.allocated > 0); // check we initialized!
lock_guard(&arenas_in_use_mutex);
array_unordered_remove_by_value(arenas_in_use, arena, 1);
#endif
}

View File

@ -3,8 +3,8 @@
struct ExpandableArena; // fwd declare #temp
#if OS_WINDOWS
constexpr u32 ARENA_DEFAULT_COMMIT_PAGE_COUNT = 16; // 16 * 4k page = 64kB
constexpr s64 ARENA_DEFAULT_COMMIT_SIZE_BYTES = 65536;
const u32 ARENA_DEFAULT_COMMIT_PAGE_COUNT = 16; // 16 * 4k page = 64kB
const s64 ARENA_DEFAULT_COMMIT_SIZE_BYTES = 65536;
#endif
constexpr u16 ARENA_DEFAULT_ALIGNMENT = CPU_REGISTER_WIDTH_BYTES;
@ -65,22 +65,30 @@ struct Arena {
Arena_Reserve reserve_size = Arena_Reserve::Size_64K;
Arena_Flags flags = Arena_Flags::None;
u32 initial_commit_page_count = ARENA_DEFAULT_COMMIT_PAGE_COUNT;
#if BUILD_DEBUG
string file_path;
string function_name;
s32 line_number;
#endif
};
typedef void* (*Memory_Wipe_Function)(void* memory, u64 byte_count);
void* arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
// Interface API for normal use (idk how to explain - see Arena_Free_List.cpp)
void initialize_arena_free_list (Allocator allocator);
Arena* next_arena (Arena_Reserve reserve_size);
void release_arena (Arena* arena, bool delete_extra_pages=true);
// Main API
Arena* bootstrap_arena (Arena_Reserve new_reserve, s32 default_commit_page_count);
#if BUILD_DEBUG
#define bootstrap_arena(_reserve_) \
bootstrap_arena_internal((_reserve_), (s32)ARENA_DEFAULT_COMMIT_PAGE_COUNT, __FILE__, __FUNCTION__, __LINE__)
#else
#define bootstrap_arena(_reserve_) \
bootstrap_arena_internal((_reserve_), (s32)ARENA_DEFAULT_COMMIT_PAGE_COUNT)
#endif
void arena_init (Arena* arena, Arena_Reserve new_reserve, s32 default_commit_page_count=16); // For when we're *not* bootstrapping arenas: (I'm debating if we should keep this..)
Arena* bootstrap_arena_internal (Arena_Reserve new_reserve, s32 commit_page_count=ARENA_DEFAULT_COMMIT_PAGE_COUNT,
string file_path="", string function_name="", s32 line_number=0);
void arena_init (Arena* arena, Arena_Reserve new_reserve, s32 commit_page_count=16); // For when we're *not* bootstrapping arenas: (I'm debating if we should keep this..)
bool arena_commit_first_pages (Arena* arena, s64 commit_size, s64 start_offset=0); // This is useful for initializing arenas (arena_init), and for starting Arena-backed arrays.
void arena_clear_flags (Arena* arena);
@ -142,7 +150,7 @@ struct Push_Alignment { // #rename to Arena_Push_Alignment?
// #FixedArena is a super simple arena where you allocate a fixed block up front (fully committed),
// and use it as-is.
// #NOTE: we can save space be always backing with a known allocator (e.g. GPAllocator()).
// #NOTE: we can save space be always backing with a known allocator (e.g. default_allocator()).
struct FixedArena {
ArrayView<u8> memory;
s64 cursor;
@ -152,6 +160,36 @@ struct FixedArena {
void* fixed_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
// #FixedArena API
FixedArena* bootstrap_fixed_arena (s64 size, Allocator backing_allocator);
FixedArena* bootstrap_fixed_arena (s64 size, Allocator backing_allocator = { default_allocator_proc, nullptr });
force_inline void destroy_arena (FixedArena* arena);
Allocator allocator (FixedArena* arena);
s64 bytes_in_use (ArrayView<Arena*> arenas) {
// does not include overhead from committed pages!
s64 sum = 0;
for (s64 i = 0; i < arenas.count; i += 1) {
sum += arena_usage_bytes(arenas[i]);
}
return sum;
}
s64 committed_bytes (ArrayView<Arena*> arenas) {
s64 sum = 0;
for (s64 i = 0; i < arenas.count; i += 1) {
sum += arena_usage_committed_bytes(arenas[i]);
}
return sum;
}
#if BUILD_DEBUG
global Mutex arenas_in_use_mutex;
global Array<Arena*> arenas_in_use;
force_inline void initialize_arenas_in_use_list ();
force_inline void add_arena_to_in_use_list(Arena* arena);
force_inline void remove_arena_from_in_use_list (Arena* arena);
#endif

View File

@ -1,6 +1,10 @@
#pragma once
#if BUILD_DEBUG
constexpr s64 ARRAY_ARENA_START_OFFSET = 2 * 64; // sizeof(Arena)+sizeof(array)
#else
constexpr s64 ARRAY_ARENA_START_OFFSET = 64;
#endif
template <typename T>
struct ArenaArray { // #downcasts to an ArrayView.
@ -28,7 +32,7 @@ struct ArenaArray { // #downcasts to an ArrayView.
// Use arena_array_free to reset
template <typename T>
ArenaArray<T>* arena_array_new (s64 preallocate_count, Arena_Reserve reserve_size) {
Arena* arena = next_arena(reserve_size);
Arena* arena = bootstrap_arena(reserve_size);
push_arena(arena);
push_alignment(arena, 1);
ArenaArray<T>* array = New<ArenaArray<T>>(true);
@ -41,13 +45,22 @@ ArenaArray<T>* arena_array_new (s64 preallocate_count, Arena_Reserve reserve_siz
array->count = 0;
array->arena = arena;
// #TODO: Should align to next cache line!
array->data = array_start<T>(*array);
return array;
}
template <typename T> T* array_start (ArenaArray<T>& array) {
return (T*)(array.arena->memory_base + ARRAY_ARENA_START_OFFSET);
T* memory = (T*)(array.arena->memory_base + ARRAY_ARENA_START_OFFSET);
Assert((u8*)memory >= (u8*)array.arena->current_point);
return memory;
}
s64 max_array_size (ArenaArray<u8>& array) {
u8* address_limit = array.arena->memory_base + reserve_size(array.arena);
u8* address_start = array_start(array);
return (s64)(address_limit - address_start);
}
template <typename T> bool is_valid (ArenaArray<T>* array) {
@ -65,12 +78,8 @@ template <typename T> s64 memory_usage (ArenaArray<T>& array) {
return arena_usage_committed_bytes(array.arena);
}
template <typename T> void arena_array_free (ArenaArray<T>& array, bool delete_pages=true) {
release_arena(array.arena, delete_pages);
array.arena = nullptr;
#if BUILD_DEBUG
poison_struct(&array);
#endif
template <typename T> void arena_array_free (ArenaArray<T>& array) {
arena_delete(array.arena);
}
template <typename T> ArrayView<T> array_view (ArenaArray<T> array) {
@ -164,10 +173,6 @@ template <typename T> void array_resize (ArenaArray<T>& array, s64 desired_item_
}
}
s64 max_array_size (ArenaArray<u8>& array) {
return reserve_size(array.arena) - sizeof(Arena) - sizeof(ArenaArray<u8>);
}
void array_arena_realloc (ArenaArray<u8>& array, s64 new_size, s64 old_size) {
Assert(new_size <= max_array_size(array));
@ -204,6 +209,7 @@ template <typename T> void init_range (T* ptr, s64 start_offset, s64 end_offset)
template <typename T> void array_poison_range (ArenaArray<T>& array, s64 start, s64 count) {
#if BUILD_DEBUG
if (count == 0) return;
Assert(start >= 0 && start < array.count);
Assert(start + count <= array.count);
// Check that these ranges make sense

View File

@ -1,108 +0,0 @@
// #TODO: #Arena_Free_List #garbage_collection in `release_arena`
// [ ] Garbage collection if we have >> 64 in a particular table for a while.
// There should be some parameters regarding what the upper limit for idle
// committed pages should be and a heuristic for maximum number of arenas waiting
struct Arena_Free_List {
Mutex mutex;
s32 in_flight_count[Arena_Reserve_Count];
Array<Arena*> free_table[Arena_Reserve_Count];
#if ARENA_DEBUG
Array<Arena*> in_flight[Arena_Reserve_Count];
#endif
b32 initialized;
};
global Arena_Free_List* arena_free_list;
// Only call once from main thread!
void initialize_arena_free_list (Allocator allocator) {
mutex_init(&arena_free_list->mutex);
Assert(arena_free_list != nullptr);
if (arena_free_list->initialized)
return;
for (s32 i = 0; i < Arena_Reserve_Count; i += 1) {
arena_free_list->in_flight_count[i] = 0;
arena_free_list->free_table[i].allocator = allocator;
array_reserve(arena_free_list->free_table[i], 64);
#if ARENA_DEBUG
arena_free_list->in_flight[i].allocator = allocator;
array_reserve(arena_free_list->in_flight[i], 64);
#endif
}
arena_free_list->initialized = true;
}
Arena* next_arena (Arena_Reserve reserve_size) {
Assert(arena_free_list != nullptr);
Arena* arena;
lock_guard(&arena_free_list->mutex);
s64 reserve_index = (s64)reserve_size;
if (!arena_free_list->free_table[reserve_index].count) {
arena = bootstrap_arena(reserve_size, ARENA_DEFAULT_COMMIT_PAGE_COUNT);
} else {
arena = pop(arena_free_list->free_table[reserve_index]);
}
#if ARENA_DEBUG
array_add(arena_free_list->in_flight[reserve_index], arena);
#endif
arena_free_list->in_flight_count[reserve_index] += 1;
Assert(arena != nullptr);
return arena;
}
void release_arena (Arena* arena, bool delete_extra_pages) {
Assert(arena_free_list != nullptr);
Assert(arena != nullptr);
Assert(arena_is_bootstrapped(arena));
// Only put into free table if arena is bootstrapped?
lock_guard(&arena_free_list->mutex);
s64 reserve_index = (s64)arena->reserve_size;
#if ARENA_DEBUG
array_unordered_remove_by_value(arena_free_list->in_flight[reserve_index], arena, 1); // BUILD_DEBUG!
#endif
arena_reset_keeping_memory(arena);
if (delete_extra_pages) {
free_pages_down_to(arena, arena->initial_commit_page_count);
}
array_add(arena_free_list->free_table[reserve_index], arena);
arena_free_list->in_flight_count[reserve_index] -= 1;
// #TODO #garbage_collection
// if (arena_free_table[reserve_index].count > 64) {
// s64 arenas_to_delete_count = arena_free_table[reserve_index].count - 64;
// while (arenas_to_delete_count > 0) {
// arena_delete(arena_free_table[arena_free_table.count-1]);
// array_unordered_remove_by_index(..);
// arenas_to_delete_count -= 1;
// }
// }
}
s64 bytes_in_use (ArrayView<Arena*> arenas) {
// does not include overhead from committed pages!
s64 sum = 0;
for (s64 i = 0; i < arenas.count; i += 1) {
sum += arena_usage_bytes(arenas[i]);
}
return sum;
}
s64 committed_bytes (ArrayView<Arena*> arenas) {
s64 sum = 0;
for (s64 i = 0; i < arenas.count; i += 1) {
sum += arena_usage_committed_bytes(arenas[i]);
}
return sum;
}

View File

@ -60,6 +60,14 @@ void free_pages_down_to (Arena* arena, s64 pages_to_keep) {
void arena_delete (Arena* arena) {
if (!is_valid(arena)) return;
#if BUILD_DEBUG
{ //default_allocator_free(arena->file_path.data);
//default_allocator_free(arena->function_name.data);
remove_arena_from_in_use_list(arena);
}
#endif
bool arena_was_boostrapped = (arena->flags & Arena_Flags::Is_Bootstrapped) == Arena_Flags::Is_Bootstrapped;
// s64 size_tmp = reserve_size(arena);

View File

@ -235,7 +235,6 @@ void array_unordered_remove_by_index (Array<T>& src, s64 index) {
template <typename T>
s64 array_unordered_remove_by_value (Array<T>& src, T item, s64 max_count_to_remove) {
s64 removed_count = 0;
for (s64 i = 0; i < src.count; i += 1) {
if (src[i] == item) {
removed_count += 1;

View File

@ -1,6 +1,161 @@
#pragma once
#define LANG_CPP 1
// Some of these macros are ""borrowed"" from nick aversano | source: https://github.com/nickav/na/blob/main/na.h
// #OS_Platform
#if defined(_WIN32)
#define OS_WINDOWS 1
#elif defined(__APPLE__)
#define OS_MACOS 1
#elif defined(__linux__)
#define OS_LINUX 1
#endif
#if !defined(OS_WINDOWS)
#define OS_WINDOWS 0
#endif
#if !defined(OS_LINUX)
#define OS_LINUX 0
#endif
#if !defined(OS_MACOS)
#define OS_MACOS 0
#endif
#if defined(__cplusplus)
#define LANG_CPP 1
#else
#define LANG_C 1
#endif
// #Compiler: Language
#if !defined(LANG_CPP)
#define LANG_CPP 0
#endif
#if !defined(LANG_C)
#define LANG_C 0
#endif
// #Compiler: Vendor
#if defined(__clang__)
#define COMPILER_CLANG 1
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#elif defined(__GNUC__) || defined(__GNUG__)
#define COMPILER_GCC 1
#endif
#if !defined(COMPILER_MSVC)
#define COMPILER_MSVC 0
#endif
#if !defined(COMPILER_GCC)
#define COMPILER_GCC 0
#endif
#if !defined(COMPILER_CLANG)
#define COMPILER_CLANG 0
#endif
#if COMPILER_MSVC
#if _MSC_VER >= 1930
#define COMPILER_MSVC_YEAR 2022
#elif _MSC_VER >= 1920
#define COMPILER_MSVC_YEAR 2019
#elif _MSC_VER >= 1910
#define COMPILER_MSVC_YEAR 2017
#elif _MSC_VER >= 1900
#define COMPILER_MSVC_YEAR 2015
#elif _MSC_VER >= 1800
#define COMPILER_MSVC_YEAR 2013
#elif _MSC_VER >= 1700
#define COMPILER_MSVC_YEAR 2012
#elif _MSC_VER >= 1600
#define COMPILER_MSVC_YEAR 2010
#elif _MSC_VER >= 1500
#define COMPILER_MSVC_YEAR 2008
#elif _MSC_VER >= 1400
#define COMPILER_MSVC_YEAR 2005
#else
#define COMPILER_MSVC_YEAR 0
#endif
#endif
// #Architecture: CPU Vendor
#if defined(_WIN32)
#if defined(_M_AMD64)
#define ARCH_CPU_X64 1
#elif defined(_M_IX86)
#define ARCH_CPU_X86 1
#elif defined(_M_ARM64)
#define ARCH_CPU_ARM64 1
#elif defined(_M_ARM)
#define ARCH_CPU_ARM32 1
#endif
#else
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define ARCH_CPU_X64 1
#elif defined(i386) || defined(__i386) || defined(__i386__)
#define ARCH_CPU_X86 1
#elif defined(__aarch64__)
#define ARCH_CPU_ARM64 1
#elif defined(__arm__)
#define ARCH_CPU_ARM32 1
#endif
#endif
#if !defined(ARCH_CPU_X64)
#define ARCH_CPU_X64 0
#endif
#if !defined(ARCH_CPU_X86)
#define ARCH_CPU_X86 0
#endif
#if !defined(ARCH_CPU_ARM64)
#define ARCH_CPU_ARM64 0
#endif
#if !defined(ARCH_CPU_ARM32)
#define ARCH_CPU_ARM32 0
#endif
// #Architecture: Register Width
#if defined(ARCH_CPU_X64) || defined(ARCH_CPU_ARM64)
#define ARCH_64BIT 1
#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM32)
#define ARCH_32BIT 1
#endif
#if !defined(ARCH_64BIT)
#define ARCH_64BIT 0
#endif
#if !defined(ARCH_32BIT)
#define ARCH_32BIT 0
#endif
// #Architecture: Endianness
static const int __arch_endian_check_num = 1;
#define ARCH_LITTLE_ENDIAN (*(char *)&__arch_endian_check_num == 1)
#define ARCH_BIG_ENDIAN (!ARCH_LITTLE_ENDIAN)
#if defined(_MSC_VER)
#ifdef _DEBUG
#define BUILD_DEBUG 1
#else
#define BUILD_DEBUG 0
#endif
#elif defined(__GNUC__) || defined(__clang__)
#ifndef NDEBUG
#define BUILD_DEBUG 1
#else
#define BUILD_DEBUG 0
#endif
#endif
// #ifndef NDEBUG
// #define BUILD_DEBUG 1
// #else
// #define BUILD_DEBUG 0
// #endif
#define BUILD_CONSOLE_INTERFACE BUILD_DEBUG
#include <stdio.h> // vsnprintf
@ -191,6 +346,7 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
#define temp() allocator(thread_context()->temp)
#define context_allocator() thread_context()->allocator
#define context_logger() &thread_context()->logger
#define context_builder() thread_context()->string_builder
// CHECK THAT THESE ARE CORRECT!
constexpr f32 TAU = 6.283185f;

View File

@ -3,24 +3,33 @@
internal void Bootstrap_Main_Thread_Context () {
// Timed_Block_Print_No_Context("Bootstrap_Main_Thread_Context");
// 0. Setup general allocator
GPAllocator_Initialize_Allocation_Tracker();
default_allocator_Initialize_Allocation_Tracker();
// 1. Setup arena table
arena_free_list = (Arena_Free_List*)GPAllocator_New(sizeof(Arena_Free_List), 64, true); // permanent allocation.
memset(arena_free_list, 0, sizeof(Arena_Free_List));
initialize_arena_free_list(GPAllocator());
// 1. Setup arena free list
// #note: the arena free list is disabled because I'm not convinced it's a good idea.
// It would allow us to cache arenas to load address space very quickly (much faster than calling VirtualAlloc), but
// it adds complexity and makes it difficult to know when you're doing something stupid, because memory is still writeable
// and readable after it's "freed" with `release_arena`. So for prototyping purposes, we just release the whole arena.
// arena_free_list = (Arena_Free_List*)default_allocator_new(sizeof(Arena_Free_List), 64, true); // permanent allocation.
// memset(arena_free_list, 0, sizeof(Arena_Free_List));
// initialize_arena_free_list(default_allocator());
// 1b. Setup arena in-use list:
initialize_arenas_in_use_list();
// 2. #NewContext Setup thread local context
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
ExpandableArena* arena_ex = bootstrap_expandable_arena(Arena_Reserve::Size_64M);
thread_local_context = New<Thread_Context>(allocator(arena_ex));
thread_local_context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
thread_local_context->arena = arena_ex;
thread_local_context->allocator = allocator(arena_ex);
thread_local_context->thread_idx = 0;
thread_local_context->thread_name = "Main Thread";
thread_local_context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread_local_context->error_arena = next_arena(Arena_Reserve::Size_64M);
thread_local_context->temp = bootstrap_expandable_arena(Arena_Reserve::Size_2M);
thread_local_context->arena = arena_ex;
thread_local_context->allocator = allocator(arena_ex);
thread_local_context->thread_idx = 0;
thread_local_context->thread_name = "Main Thread";
thread_local_context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread_local_context->string_builder = new_string_builder(Arena_Reserve::Size_2M);
thread_local_context->error_arena = bootstrap_arena(Arena_Reserve::Size_64M);
default_logger_initialize();
thread_local_context->logger = {default_logger_proc, &default_logger};
@ -55,6 +64,7 @@ force_inline void set_thread_context (Thread_Context* new_context) {
thread_local_context = new_context;
}
// #Note: Both functions will free next arenas, we only worry about keeping memory in the first arena (typically 64MB).
void temp_reset_keeping_memory() {
Thread_Context* context = thread_context();
arena_reset(context->temp, false);

View File

@ -1,4 +1,17 @@
// #hacky fwd declares
struct Source_Code_Location {
string file_name;
string function_name;
s32 line_number;
};
struct Stack_Trace_Node {
Stack_Trace_Node* next;
// Information
Source_Code_Location data;
s32 call_depth;
};
struct Error;
struct Graphics;
@ -9,17 +22,18 @@ struct Thread_Context {
Allocator allocator;
s32 thread_idx;
// u16 _padding0;
u16 GPAllocator_alignment = 16;
u16 default_allocator_alignment = 16;
Logger logger = {nullptr, nullptr};
String_Builder* log_builder;
// Stack_Trace* stack_trace;
String_Builder* log_builder; // String builder used by log() and log_error_internal()
String_Builder* string_builder; // Secondary builder just for convenience!
Stack_Trace_Node* stack_trace; // use `list(stack_trace)` in watch window of raddbg to view as array!
Array<Thread*> child_threads; // maybe should be linked-list?
Thread_Context* parent_thread_context = nullptr; // so we can remove from above array
string thread_name;
Allocator error_allocator = GPAllocator();
Allocator error_allocator = default_allocator();
Error* first_error = nullptr;
Error* current_error = nullptr;
Arena* error_arena;
@ -44,12 +58,118 @@ struct Push_Allocator {
Push_Allocator (Allocator new_allocator) {
context = thread_context();
old_allocator = context->allocator;
context->allocator = new_allocator;
if (this->context != nullptr) {
old_allocator = context->allocator;
context->allocator = new_allocator;
} else {
old_allocator = default_allocator();
}
}
~Push_Allocator () {
context->allocator = old_allocator;
if (this->context != nullptr) {
context->allocator = old_allocator;
}
}
};
// #stack_trace
#define ENABLE_STACK_TRACE BUILD_DEBUG
void push_stack_trace_internal (Thread_Context* context, string file_name, string function_name, s32 line_number) {
if (context == nullptr) return;
Assert(context != nullptr);
// #no_context allocation
Stack_Trace_Node* new_node = (Stack_Trace_Node*)default_allocator_new(sizeof(Stack_Trace_Node));
new_node->data.file_name = file_name;
new_node->data.function_name = function_name;
new_node->data.line_number = line_number;
new_node->next = nullptr;
if (context->stack_trace == nullptr) {
new_node->call_depth = 1;
} else {
new_node->call_depth = context->stack_trace->call_depth + 1;
new_node->next = context->stack_trace;
}
context->stack_trace = new_node;
}
void pop_stack_trace_internal (Thread_Context* context) {
if (context == nullptr) return;
Stack_Trace_Node* old_node = context->stack_trace;
context->stack_trace = old_node->next;
default_allocator_free(old_node);
}
#if ENABLE_STACK_TRACE
#define stack_trace() \
Push_Stack_Trace Concat(_push_stack_trace_guard_, __LINE__)(__FILE__, __FUNCTION__, __LINE__)
#else
#define stack_trace()
#endif
struct Push_Stack_Trace {
Thread_Context* context;
Push_Stack_Trace (string file_name, string function_name, s32 line_number) {
context = thread_context();
push_stack_trace_internal(context, file_name, function_name, line_number);
}
~Push_Stack_Trace () {
pop_stack_trace_internal(context);
}
};
// #TODO: precede with something like: os_write_string_unsynchronized("Fatal Error!\n\nStack trace:", true);
string generate_stack_trace (Thread_Context* context) {
// #no_context - we want this to work even if context is utterly broken.
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
Stack_Trace_Node* node = context->stack_trace;
print_to_builder(sb, "Thread index: %d, thread name: %s\n\n", context->thread_idx, context->thread_name.data);
while (node) {
append(sb, format_string("%s:%d: %s\n", node->data.file_name.data, node->data.line_number, node->data.function_name.data));
node = node->next;
}
append(sb, "\n");
push_allocator(default_allocator());
string stack_trace_copy = builder_to_string(sb);
free_string_builder(sb);
return stack_trace_copy;
}
// We don't want to use context logger here!
void print_stack_trace () {
Thread_Context* context = thread_context();
Stack_Trace_Node* node = context->stack_trace;
constexpr bool TO_STANDARD_ERROR = true;
os_write_string_unsynchronized(generate_stack_trace(context), TO_STANDARD_ERROR);
// while (node) {
// os_write_string_unsynchronized(node->data.file_name, TO_STANDARD_ERROR);
// string line_number_str = format_string(":%d: ", node->data.line_number); // maybe I shouldn't do this?
// os_write_string_unsynchronized(line_number_str, TO_STANDARD_ERROR);
// // os_write_string_unsynchronized("'", TO_STANDARD_ERROR);
// os_write_string_unsynchronized(node->data.function_name, TO_STANDARD_ERROR);
// os_write_string_unsynchronized("\n", TO_STANDARD_ERROR);
// node = node->next;
// }
}

View File

@ -54,19 +54,19 @@ string to_string (Error* error) {
}
#define log_todo(fmt, ...) \
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::TODO, fmt, ##__VA_ARGS__)
log_error_internal(__FILE__, __FUNCTION__, __LINE__, ErrorClass::TODO, fmt, ##__VA_ARGS__)
#define log_fatal_error(fmt, ...) \
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::FATAL, fmt, ##__VA_ARGS__)
log_error_internal(__FILE__, __FUNCTION__, __LINE__, ErrorClass::FATAL, fmt, ##__VA_ARGS__)
#define log_error(fmt, ...) \
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::ERROR, fmt, ##__VA_ARGS__)
log_error_internal(__FILE__, __FUNCTION__, __LINE__, ErrorClass::ERROR, fmt, ##__VA_ARGS__)
#define log_warning(fmt, ...) \
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::WARNING, fmt, ##__VA_ARGS__)
log_error_internal(__FILE__, __FUNCTION__, __LINE__, ErrorClass::WARNING, fmt, ##__VA_ARGS__)
#define log_none(fmt, ...) \
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::NONE, fmt, ##__VA_ARGS__)
log_error_internal(__FILE__, __FUNCTION__, __LINE__, ErrorClass::NONE, fmt, ##__VA_ARGS__)
Error* new_error (ErrorClass severity, string error_string) {
Error* error = New<Error>();
@ -77,12 +77,12 @@ Error* new_error (ErrorClass severity, string error_string) {
return error;
}
void Log_Error_2 (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) {
void log_error_internal (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) {
auto tctx = thread_context();
Assert(tctx != nullptr);
push_arena(tctx->error_arena);
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
String_Builder* sb = thread_context()->log_builder;
print_to_builder(sb, "%s ", error_severity(severity));
@ -93,7 +93,10 @@ void Log_Error_2 (string file_path, string function_name, s32 line_number, Error
append(sb, "\n");
string error_string = builder_to_string(sb);
string error_string = copy_string(string_view(sb));
reset_string_builder(sb);
Error* error = new_error(severity, error_string);
// Additional information
error->thread_id = tctx->thread_idx;

View File

@ -1,5 +1,11 @@
ExpandableArena* expandable_arena_new (Arena_Reserve starting_reserve, s32 commit_page_count) {
ExpandableArena* new_arena = (ExpandableArena*)bootstrap_arena(starting_reserve, commit_page_count);
ExpandableArena* bootstrap_expandable_arena_internal (Arena_Reserve new_reserve, s32 commit_page_count,
string file_path, string function_name, s32 line_number) {
ExpandableArena* new_arena = (ExpandableArena*)bootstrap_arena_internal(
new_reserve,
commit_page_count,
file_path,
function_name,
line_number);
// Note: beyond first 32 bytes (sizeof(Arena)) ExpandableArena will not be initialized,
// so we do it here:
new_arena->current = (Arena*)new_arena;
@ -8,13 +14,17 @@ ExpandableArena* expandable_arena_new (Arena_Reserve starting_reserve, s32 commi
new_arena->next_arenas = Array<Arena*>(); // next_arenas will be uninitialized, so we have to do this
// We have to use malloc because if we reset this new arena, all the data will be lost
// We don't want to tie the lifetime of next_arenas to this expandable arena.
new_arena->next_arenas.allocator = GPAllocator();
new_arena->next_arenas.allocator = default_allocator();
array_reserve(new_arena->next_arenas, 8);
return new_arena;
}
// force_inline ExpandableArena* bootstrap_expandable_arena (Arena_Reserve new_reserve, s32 commit_page_count) {
// return bootstrap_expandable_arena_internal(new_reserve, commit_page_count, __FILE__, __FUNCTION__, __LINE__);
// }
void* expandable_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
ExpandableArena* arena = (ExpandableArena*)allocator_data;
Assert(arena != nullptr);
@ -58,7 +68,7 @@ void* expandable_arena_alloc (ExpandableArena* arena_ex, s64 byte_count) {
Assert(arena_ex != nullptr);
Assert(arena_ex->memory_base != nullptr); // must be initialized before calling.
Assert(is_valid(arena_ex));
Assert(arena_free_list->initialized);
// Assert(arena_free_list->initialized);
Arena* arena = (Arena*)arena_ex->current;
@ -73,7 +83,7 @@ void* expandable_arena_alloc (ExpandableArena* arena_ex, s64 byte_count) {
new_min_reserve = arena->reserve_size;
}
Arena* new_arena = next_arena(new_min_reserve);
Arena* new_arena = bootstrap_arena(new_min_reserve);
new_arena->alignment = arena_ex->alignment;
new_arena->flags = arena_ex->flags;
@ -106,8 +116,7 @@ Allocator allocator (ExpandableArena* arena_ex) {
return { expandable_arena_allocator_proc, arena_ex };
}
// #TODO: currently this keeps the final arena's memory. Fix this!
// This is not implemented correctly!
// last arena is the arena we want to pop to.
void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point) {
// going backwards from end of arena list
@ -116,7 +125,6 @@ void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_
return;
}
// for (s64 i = arena_ex->next_arenas.count-1; i >= 0; i -= 1) {
for_each_reverse(i, arena_ex->next_arenas) {
Arena* arena = arena_ex->next_arenas[i];
if (arena == last_arena) { // return to starting_point
@ -124,8 +132,14 @@ void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_
arena_ex->current->current_point = starting_point;
break;
}
release_arena(arena);
array_unordered_remove_by_index(arena_ex->next_arenas, i);
arena_delete(arena);
// We can just decrement the count because arenas are added and removed in order. see: pop()
arena_ex->next_arenas.count -= 1;
}
if (last_arena == (Arena*)arena_ex) {
arena_ex->current = (Arena*)arena_ex;
arena_ex->current->current_point = starting_point;
}
}
@ -134,7 +148,7 @@ void arena_reset (ExpandableArena* arena_ex, bool free_extra_pages) {
// Free expansion arenas in `next_arenas`
for (s64 i = 0; i < arena_ex->next_arenas.count; i += 1) {
release_arena(arena_ex->next_arenas[i], free_extra_pages);
arena_delete(arena_ex->next_arenas[i]);
}
// Reset next_arenas

View File

@ -5,25 +5,28 @@
// DO NOT MERGE WITH `Arena`, we need fixed size arenas so that we can back
// `ArenaArray`s.
struct ExpandableArena {
u8* current_point = nullptr;
u8* memory_base = nullptr;
u8* first_uncommitted_page = nullptr;
u16 alignment = CPU_REGISTER_WIDTH_BYTES;
Arena_Reserve reserve_size = Arena_Reserve::Size_64K;
Arena_Flags flags = Arena_Flags::None;
u32 initial_commit_page_count = ARENA_DEFAULT_COMMIT_PAGE_COUNT;
// Note that this downcasts to Arena*, so can be initialized in the same way.
struct ExpandableArena : Arena {
Arena* current;
Array<Arena*> next_arenas;
};
ExpandableArena* expandable_arena_new (Arena_Reserve starting_reserve=Arena_Reserve::Size_64K, s32 commit_page_count=8);
#if BUILD_DEBUG
#define bootstrap_expandable_arena(_reserve_) \
bootstrap_expandable_arena_internal((_reserve_), (s32)ARENA_DEFAULT_COMMIT_PAGE_COUNT, __FILE__, __FUNCTION__, __LINE__)
#else
#define bootstrap_expandable_arena(_reserve_) \
bootstrap_expandable_arena_internal((_reserve_), (s32)ARENA_DEFAULT_COMMIT_PAGE_COUNT)
#endif
ExpandableArena* bootstrap_expandable_arena_internal (Arena_Reserve new_reserve=Arena_Reserve::Size_64K, s32 commit_page_count=ARENA_DEFAULT_COMMIT_PAGE_COUNT,
string file_path="", string function_name="", s32 line_number=0);
// ExpandableArena* bootstrap_expandable_arena_internal (Arena_Reserve starting_reserve=Arena_Reserve::Size_64K, s32 commit_page_count=8);
void* expandable_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
bool is_valid (ExpandableArena* arena);
void* expandable_arena_alloc (ExpandableArena* arena_ex, s64 byte_count);
u8* expandable_arena_start (ExpandableArena* arena_ex);
Allocator allocator (ExpandableArena* arena_ex);
void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point);
void arena_reset (ExpandableArena* arena_ex, bool free_extra_pages=true);
void arena_reset (ExpandableArena* arena_ex, bool free_extra_pages);
force_inline void arena_delete (ExpandableArena* arena_ex);

View File

@ -1,6 +1,7 @@
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
global General_Allocator gAllocator; // @Shared
global Mutex allocator_mutex;
global bool default_allocator_show_small_allocations = true;
#endif
#if !COMPILER_MSVC
@ -32,23 +33,23 @@ General_Allocator* get_general_allocator_data() {
constexpr s64 Allocation_Tracking_Is_Enabled = GP_ALLOCATOR_TRACK_ALLOCATIONS;
bool GPAllocator_Tracking_Enabled () {
bool default_allocator_Tracking_Enabled () {
return Allocation_Tracking_Is_Enabled != 0;
}
void GPAllocator_Initialize_Allocation_Tracker () {
void default_allocator_Initialize_Allocation_Tracker () {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
mutex_init(&allocator_mutex);
constexpr s64 alignment = 64;
s64 item_count_max = 64 * 4096;
s64 total_allocation_size = item_count_max * sizeof(Allocation);
auto memory = Aligned_Alloc(total_allocation_size, alignment); // @MemoryLeak (intentional)
gAllocator.allocations = Array<Allocation>(item_count_max, memory, item_count_max, GPAllocator());
gAllocator.allocations = Array<Allocation>(item_count_max, memory, item_count_max, default_allocator());
gAllocator.allocations.count = 0; // Init to zero.
#endif
}
bool GPAllocator_Is_This_Yours (void* old_memory) {
bool default_allocator_Is_This_Yours (void* old_memory) {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
lock_guard(&allocator_mutex);
@ -94,7 +95,7 @@ void Remove_Allocation(void* old_memory) {
#endif
}
void* GPAllocator_New (s64 new_size, s64 alignment, bool initialize) {
void* default_allocator_new (s64 new_size, s64 alignment, bool initialize) {
// Fallback allocator: _aligned_malloc, which is MSVC's version of std::aligned_alloc
auto memory = Aligned_Alloc(new_size, alignment);
@ -107,11 +108,11 @@ void* GPAllocator_New (s64 new_size, s64 alignment, bool initialize) {
return memory;
}
void* GPAllocator_Resize (s64 old_size, void* old_memory, s64 new_size, s64 alignment, bool initialize) {
void* default_allocator_realloc (s64 old_size, void* old_memory, s64 new_size, s64 alignment, bool initialize) {
Assert((alignment % 8) == 0 && (alignment != 0));
if (old_memory == nullptr) {
return GPAllocator_New(new_size, alignment);
return default_allocator_new(new_size, alignment);
}
// Debug version: _aligned_realloc_dbg
@ -128,7 +129,7 @@ void* GPAllocator_Resize (s64 old_size, void* old_memory, s64 new_size, s64 alig
return new_memory_address;
}
void GPAllocator_Delete (void* memory) {
void default_allocator_free (void* memory) {
if (memory == nullptr) return;
Aligned_Free(memory);
Remove_Allocation(memory);
@ -136,32 +137,32 @@ void GPAllocator_Delete (void* memory) {
// printf("[GP] Deleting memory %p\n", memory);
}
Allocator GPAllocator () {
return { GPAllocator_Proc, nullptr };
Allocator default_allocator () {
return { default_allocator_proc, nullptr };
}
void* GPAllocator_Proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
void* default_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
u16 alignment = 16; // default alignment
Thread_Context* context = thread_context();
if (context) alignment = context->GPAllocator_alignment;
if (context) alignment = context->default_allocator_alignment;
switch (mode) {
case Allocator_Mode::ALLOCATE: {
return GPAllocator_New(requested_size, alignment);
return default_allocator_new(requested_size, alignment);
} break;
case Allocator_Mode::RESIZE: {
void* result = GPAllocator_Resize(old_size, old_memory, requested_size, alignment);
void* result = default_allocator_realloc(old_size, old_memory, requested_size, alignment);
// NOTE: The _aligned_realloc function already copies the old memory, so there's
// no need to copy the old memory block here.
return result;
} break;
case Allocator_Mode::DEALLOCATE: {
GPAllocator_Delete(old_memory); // unused
default_allocator_free(old_memory); // unused
} break;
case Allocator_Mode::DETAILS: {
Assert(allocator_data == nullptr);
return "GPAllocator";
return "default_allocator";
} break;
}

View File

@ -38,17 +38,18 @@ struct General_Allocator {
General_Allocator* get_general_allocator_data();
constexpr u16 GPAllocator_Default_Alignment = 16;
constexpr u16 default_allocator_Default_Alignment = 16;
Allocator GPAllocator ();
Allocator default_allocator ();
void* GPAllocator_Proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
void* default_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data);
void* GPAllocator_New (s64 new_size, s64 alignment=16, bool initialize=true);
void* GPAllocator_Resize (s64 old_size, void* old_memory, s64 new_size, s64 alignment=16, bool initialize=true);
void GPAllocator_Delete (void* memory);
void* default_allocator_new (s64 new_size, s64 alignment=16, bool initialize=true);
void* default_allocator_realloc (s64 old_size, void* old_memory, s64 new_size, s64 alignment=16, bool initialize=true);
void default_allocator_free (void* memory);
bool GPAllocator_Is_This_Yours (void* old_memory);
void GPAllocator_Initialize_Allocation_Tracker ();
bool GPAllocator_Tracking_Enabled ();
bool default_allocator_Is_This_Yours (void* old_memory);
void default_allocator_Initialize_Allocation_Tracker ();
bool default_allocator_Tracking_Enabled ();
// #TODO: I want to be able to tag any allocations in debug mode.

View File

@ -29,4 +29,4 @@ void print (string message) {
void print_error (string error_message) {
Logger* logger = context_logger();
logger->proc(error_message, Log_Level::Error, logger->data);
}
}

View File

@ -33,6 +33,21 @@ string copy_string (string s) {
return str;
}
string copy_string_no_context (string s) {
if (s.count <= 0)
return "";
string str = {};
str.count = s.count;
str.data = (u8*)default_allocator_new(s.count + 1);
memcpy(str.data, s.data, s.count);
str.data[str.count] = '\0'; // null-terminate for backwards compatibility?
return str;
}
string copy_string (char* c_string) {
string str = {};
s64 string_length = strlen(c_string);
@ -171,7 +186,7 @@ string format_string_no_context (char* format, ...) {
string str = {};
str.data = (u8*)GPAllocator_New(BUFFER_SIZE);
str.data = (u8*)default_allocator_new(BUFFER_SIZE);
va_list args;
va_start(args, format);
@ -267,7 +282,7 @@ internal force_inline void reset_string_builder (String_Builder* sb, bool keep_m
force_inline string builder_to_string (String_Builder* sb) {
string final_string = copy_string(to_string(to_view(*sb)));
free_string_builder(sb);
// reset_string_builder(sb, false); // maybe this shouldn't be here! Too magical...
return final_string;
}

View File

@ -77,6 +77,7 @@ bool is_valid (string s);
bool is_c_string (string s);
u8* to_c_string (string s); // #allocates
string copy_string (string s); // #allocates, returned string is #null-terminated.
string copy_string_no_context (string s);
string copy_string (char* c_string); // #allocates, returned string is #null-terminated.
string to_string (ArrayView<u8> str);
ArrayView<u8> to_view (string s);
@ -112,23 +113,3 @@ string trim_right (string s, string chars=DEFAULT_SPACES, bool replace_with_zero
// Need an API for inserting various types (ints, floats, etc.) into a String_Builder, and advancing
// the count.
// #string_builder
// #limitations This won't be as fast as Jon's String_Builder in jai because we're backing it with an
// Arena, which requires a variable number of cycles depending on if our process has
// memory available already. It also has a max capacity depending on what Arena_Reserve we choose.
// That being said, the implementation is much simpler.
typedef ArenaArray<u8> String_Builder; // struct String_Builder
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve=Arena_Reserve::Size_64K);
force_inline void append (String_Builder* sb, string s);
void append (String_Builder* sb, ArrayView<string> strings);
// This should probably be called append_but_do_not_increment_count
internal force_inline void append_no_add (String_Builder* sb, string s); // for appending null terminators, does not increment count.
void print_to_builder (String_Builder* sb, string format, ...);
void print_to_builder_internal (String_Builder* sb, string format, va_list args);
string string_view (String_Builder* sb);
internal force_inline void reset_string_builder (String_Builder* sb, bool keep_memory=false);
// #rename copy_string_and_free_builder
force_inline string builder_to_string (String_Builder* sb); // returns copy and frees string_builder
internal force_inline void free_string_builder (String_Builder* sb);

26
lib/Base/String_Builder.h Normal file
View File

@ -0,0 +1,26 @@
// #string_builder
// #limitations This won't be as fast as Jon's String_Builder in jai because we're backing it with an
// Arena, which calls VirtualAlloc, which is much slower than just using stack space to start.
// It also has a max capacity depending on what Arena_Reserve we choose.
// That being said, the implementation is much simpler, and we can keep it around for a bit.
// We can make it a lot faster by always having a string builder available in the thread_context,
// and just fetching that when we need it.
typedef ArenaArray<u8> String_Builder; // struct String_Builder
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve=Arena_Reserve::Size_64K);
force_inline void append (String_Builder* sb, string s);
void append (String_Builder* sb, ArrayView<string> strings);
// This should probably be called append_but_do_not_increment_count
internal force_inline void append_no_add (String_Builder* sb, string s); // for appending null terminators, does not increment count.
void print_to_builder (String_Builder* sb, string format, ...);
void print_to_builder_internal (String_Builder* sb, string format, va_list args);
string string_view (String_Builder* sb);
internal force_inline void reset_string_builder (String_Builder* sb, bool keep_memory=false);
// #rename copy_string_and_free_builder
force_inline string builder_to_string (String_Builder* sb); // returns copy and frees string_builder
internal force_inline void free_string_builder (String_Builder* sb);

View File

@ -79,6 +79,10 @@ string format_bytes (s64 bytes, s32 trailing_width = 3) {
unit_index += 1;
}
// This makes the trailing width param kinda pointless... idk.
if (unit_index == 0) trailing_width = 0;
// if (unit_index == 1) trailing_width = 2;
switch (trailing_width) {
case 0: return format_string("%.0f %s", count_f64, units[unit_index].data);
case 1: return format_string("%.1f %s", count_f64, units[unit_index].data);

View File

@ -19,7 +19,9 @@ void run_post_setup_tests() {
// String builder example:
// OK. I can work with this.
auto sb = new_string_builder(Arena_Reserve::Size_64K);
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
append(sb, "string_literal_example");
append(sb, " ");
print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n");
@ -45,11 +47,11 @@ void run_post_setup_tests() {
log("Done. Success: %d\n", success);
// push_allocator(allocator(thread_context()->arena));
push_allocator(GPAllocator());
push_allocator(default_allocator());
string file_path = "D:/Work/OpenBCI/ToolZ/prototyping-gui-main/modules/native-proto-lib/native-sdk-prototyping/src/SignalProcessing.cpp";
ArrayView<u8> file_data = read_entire_file(file_path, true);
ArrayView<u8> file_data = read_entire_file(file_path, true, true);
log("file_data: \n");
log("%s\n", file_data.data);
log("%s\n", file_data.data); // #note this is not null-terminated
}
{ // Test hashing:
u64 start = 0x512585;

View File

@ -56,7 +56,7 @@ Graphics* graphics_thread_init () {
Assert(context != nullptr);
push_allocator(GPAllocator());
push_allocator(default_allocator());
if (context->graphics == nullptr) {
context->graphics = New<Graphics>(true);

View File

@ -84,8 +84,8 @@ string get_full_path (string drive_label, Dense_FS* dfs, s32 first_parent_index,
array_add(path_list_reverse, drive_letter_with_colon);
// reset_string_builder(sb, true);
String_Builder* sb = new_string_builder();
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
for (s64 i = path_list_reverse.count-1; i >= 0; i -= 1) {
append(sb, path_list_reverse[i]);

View File

@ -59,7 +59,7 @@ struct OS_System_Info {
// #Monitors
b32 monitors_enumerated;
Array<Monitor> monitors; // Back with GPAllocator
Array<Monitor> monitors; // Back with default_allocator
// #Drives
Table<string, OS_Drive*> drives; // should we just store ptrs to OS_Drive? I think so..
@ -136,7 +136,13 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs)
for (;;) Sleep(1000);
}
// #TODO: Runtime assertion failed?
// #Exception handling code (TODO)
if (thread_context()->stack_trace) {
os_write_string_unsynchronized("\n[Win32_Exception_Filter] Stack Trace\n", true);
print_stack_trace();
}
ExitProcess(1);
@ -144,7 +150,10 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs)
}
// internal void Main_Entry_Point (int argc, WCHAR **argv);
internal void Win32_Entry_Point (int argc, WCHAR **argv) {
internal void Win32_Entry_Point (int argc, WCHAR **argv) { stack_trace();
os_write_string_unsynchronized("Fatal Error!\n\nStack trace: ", true);
print_stack_trace();
// Timed_Block_Print("Win32_Entry_Point");
// See: w32_entry_point_caller(); (raddebugger)
SetUnhandledExceptionFilter(&Win32_Exception_Filter);
@ -226,7 +235,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
}
{ OS_System_Info* info = &global_win32_state.system_info;
info->monitors.allocator = GPAllocator();
info->monitors.allocator = default_allocator();
u8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0};
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
if(GetComputerNameA((char*)buffer, &size)) {
@ -236,7 +245,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
}
{ OS_Process_Info* info = &global_win32_state.process_info;
info->windows.allocator = GPAllocator();
info->windows.allocator = default_allocator();
DWORD length = GetCurrentDirectoryW(0, 0);
// This can be freed later when we call temp_reset();
u16* memory = NewArray<u16>(temp(), length + 1);
@ -247,7 +256,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
// Setup event arena, allocators for Array<> types.
if (!global_win32_state.process_info.event_arena) {
global_win32_state.process_info.event_arena = next_arena(Arena_Reserve::Size_64K);
global_win32_state.process_info.event_arena = bootstrap_arena(Arena_Reserve::Size_64K);
}
// [ ] Get Working directory (info->working_path)
@ -282,20 +291,21 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name)
s64 this_thread_index = InterlockedIncrement(&next_thread_index);
// 2. #NewContext Setup NEW thread local context
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
ExpandableArena* arena_ex = bootstrap_expandable_arena(Arena_Reserve::Size_64M);
push_arena(arena_ex);
// #NOTE: we don't assign thread_local_context until we hit the #thread_entry_point
thread->context = New<Thread_Context>();
thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
thread->context->arena = arena_ex;
thread->context->allocator = allocator(arena_ex);
thread->context->thread_idx = (s32)this_thread_index;
thread->context->temp = bootstrap_expandable_arena(Arena_Reserve::Size_2M);
thread->context->arena = arena_ex;
thread->context->allocator = allocator(arena_ex);
thread->context->thread_idx = (s32)this_thread_index;
// #NOTE: This will disappear once the thread is de-initted. If we want this string, copy it!
thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread->context->error_arena = next_arena(Arena_Reserve::Size_64M);
thread->context->logger = {default_logger_proc, &default_logger};
thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread->context->string_builder = new_string_builder(Arena_Reserve::Size_2M);
thread->context->error_arena = bootstrap_arena(Arena_Reserve::Size_64M);
thread->context->logger = {default_logger_proc, &default_logger};
thread->context->parent_thread_context = thread_context();
@ -322,7 +332,7 @@ internal void thread_deinit (Thread* thread,bool zero_thread) {
array_reset(*thread->context->log_builder);
free_string_builder(thread->context->log_builder);
release_arena(thread->context->error_arena);
arena_delete(thread->context->error_arena);
arena_delete(thread->context->temp);
arena_delete(thread->context->arena); // must come last because thread->context is allocated with this arena!
@ -548,7 +558,7 @@ internal bool file_set_position (File file, s64 position) {
return (bool)SetFilePointerEx(file.handle, position_li, nullptr, FILE_BEGIN);
}
internal ArrayView<u8> read_entire_file (File file) {
ArrayView<u8> read_entire_file (File file, bool add_null_terminator) {
ArrayView<u8> file_data;
bool result = file_length(file, &file_data.count);
@ -556,7 +566,13 @@ internal ArrayView<u8> read_entire_file (File file) {
result = file_set_position(file, 0);
if (!result) return {};
file_data.data = NewArray<u8>(file_data.count, false);
s64 total_file_size = file_data.count;
if (add_null_terminator) {
total_file_size += 1;
}
file_data.data = NewArray<u8>(total_file_size, false);
if (file_data.data == nullptr) return {};
s64 bytes_read = 0;
@ -566,17 +582,21 @@ internal ArrayView<u8> read_entire_file (File file) {
return {};
}
if (add_null_terminator) {
file_data[total_file_size-1] = 0;
}
Assert(bytes_read == file_data.count);
file_data.count = bytes_read;
return file_data;
}
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors) {
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator, bool log_errors) {
File f = file_open(file_path, false, false, log_errors);
if (!file_is_valid(f)) return {};
ArrayView<u8> file_data = read_entire_file(f);
ArrayView<u8> file_data = read_entire_file(f, add_null_terminator);
file_close(&f);
@ -1009,11 +1029,11 @@ s32 os_cpu_secondary_core_count () {
// #Drives
constexpr u64 Win32_Max_Path_Length = 260;
bool Win32_Discover_Drives () {
push_allocator(GPAllocator());
push_allocator(default_allocator());
// Initialize drive_table if necessary.
Table<string, OS_Drive*>* drive_table = get_drive_table();
if (!drive_table->allocated) {
drive_table->allocator = GPAllocator();
drive_table->allocator = default_allocator();
// #TODO(Low priority): #hash_table need a macro for initializing with string keys!
drive_table->hash_function = string_hash_function_fnv1a;
drive_table->compare_function = string_keys_match;
@ -1068,8 +1088,13 @@ bool Win32_Discover_Drives () {
Win32_Max_Path_Length, &serial_number, &max_comp_len, &file_system_flags,
(LPWSTR)file_system_name, Win32_Max_Path_Length)) {
drive->label = drive_label;
drive->volume_name = wide_to_utf8(volume_name);
if (drive->volume_name == "") { drive->volume_name = copy_string("Local Disk"); }
if (volume_name[0] == 0) {
drive->volume_name = copy_string("Local Disk");
} else {
drive->volume_name = wide_to_utf8(volume_name);
}
if (drive->volume_name == "") { drive->volume_name = copy_string("Local Disk"); } // Probably redundant??
drive->type = (Win32_Drive_Type)drive_type;
{ push_allocator(temp());
drive->file_system = Win32_filesystem_from_string(wide_to_utf8(file_system_name));
@ -1172,7 +1197,7 @@ struct ST_File_Enumeration { // global state
global ST_File_Enumeration* stfe;
void free_stfe_and_reset () {
push_allocator(GPAllocator());
push_allocator(default_allocator());
array_free(stfe->drives);
@ -1337,7 +1362,7 @@ s64 win32_file_enum_thread_proc (Thread* thread) {
}
void os_run_file_enumeration_single_threaded () {
push_allocator(GPAllocator());
push_allocator(default_allocator());
stfe = New<ST_File_Enumeration>();
(*stfe) = {
@ -1395,7 +1420,7 @@ bool Serialize_ST_File_Enumeration (string file_path) {
bool Deserialize_ST_File_Enumeration (string file_path) {
Timed_Block_Print("Deserialize_ST_File_Enumeration");
push_allocator(GPAllocator());
push_allocator(default_allocator());
if (!stfe) stfe = New<ST_File_Enumeration>();
(*stfe) = {
{},
@ -1407,7 +1432,7 @@ bool Deserialize_ST_File_Enumeration (string file_path) {
push_allocator(temp());
auto_release_temp();
Deserializer deserializer = read_entire_file(file_path, true);
Deserializer deserializer = read_entire_file(file_path, false, true);
if (deserializer.count == 0) return false;
auto d = &deserializer;

View File

@ -41,8 +41,8 @@ internal bool file_length (File file, s64* length);
internal bool file_length (string file_path, s64* length);
internal s64 file_current_position (File file);
internal bool file_set_position (File file, s64 position);
internal ArrayView<u8> read_entire_file (File file);
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors=false);
internal ArrayView<u8> read_entire_file (File file, bool add_null_terminator);
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator=false, bool log_errors=false);
// use to_byte_view to convert ArrayView<non-u8> to ArrayView<u8>
internal bool file_write (File* file, void* data, s64 length);

View File

@ -20,7 +20,7 @@ struct File_Enumeration_Thread_Results { // #userdata
};
void initialize (File_Enumeration_Thread_Results* fcr) { // Preallocate for 2^22 files:
fcr->arena = next_arena(Arena_Reserve::Size_2G);
fcr->arena = bootstrap_arena(Arena_Reserve::Size_2G);
fcr->d_offsets = arena_array_new<u32>(4194304, Arena_Reserve::Size_2G);
fcr->d_lengths = arena_array_new<s16>(4194304, Arena_Reserve::Size_2G);
@ -83,7 +83,7 @@ string path_from_parent_index (Thread_Group* group, Parent_Index pid, Parent_Ind
// This is much stupider and more complicated than I would like, unfortunately.
string directory_get_full_path (Thread_Group* group, Parent_Index pid, string dir_name) {
push_allocator(GPAllocator()); // to copy from String_Builder
push_allocator(default_allocator()); // to copy from String_Builder
Array<string> paths;
paths.allocator = temp();
@ -105,7 +105,9 @@ string directory_get_full_path (Thread_Group* group, Parent_Index pid, string di
}
// go in reverse order and add together string
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
String_Builder* sb = context_builder();
reset_string_builder(sb, true);
for (s64 i = paths.count-1; i >= 0; i -= 1) {
append(sb, paths[i]);
append(sb, "\\");
@ -165,7 +167,7 @@ Thread_Continue_Status file_enumeration_thread_group_proc (Thread_Group* group,
// particular problem. This data can be rescued before we
File_Enumeration_Thread_Results* results;
if (!thread->context->userdata) {
thread->context->userdata = New<File_Enumeration_Thread_Results>(GPAllocator());
thread->context->userdata = New<File_Enumeration_Thread_Results>(default_allocator());
initialize((File_Enumeration_Thread_Results*)thread->context->userdata);
}
results = (File_Enumeration_Thread_Results*)thread->context->userdata;
@ -220,7 +222,7 @@ Thread_Continue_Status file_enumeration_thread_group_proc (Thread_Group* group,
add_record(results, &find_data, name, next_index, is_directory);
if (is_directory) {
push_allocator(GPAllocator());
push_allocator(default_allocator());
auto new_work = New<Enumeration_Work>(false);
new_work->first_directory = directory_get_full_path(group, pi, name);
new_work->parent = next_index;
@ -245,12 +247,12 @@ s64 multithreaded_file_enumeration_master_proc (Thread* thread) {
s32 thread_count = os_cpu_physical_core_count();
push_allocator(GPAllocator());
push_allocator(default_allocator());
thread_group_init(file_enum_thread_group, thread_count, file_enumeration_thread_group_proc, true);
for_each(d, task->drives) {
auto work = New<Enumeration_Work>(GPAllocator(), false); //replace with arena bootstrap?
auto work = New<Enumeration_Work>(default_allocator(), false); //replace with arena bootstrap?
work->first_directory = task->drives[d]->label; // this includes the colon-slash, (e.g. `C:\`).
work->parent = {-1, -1}; // #HACK: (s32)d
work->is_root = true;

View File

@ -229,7 +229,7 @@ Error* NTFS_MFT_read_raw (OS_Drive* drive) {
Assert(data_attribute != nullptr);
// #dense_fs_alloc
drive->data = New<Dense_FS>(GPAllocator());
drive->data = New<Dense_FS>(default_allocator());
initialize(drive->data, drive);
NTFS_RunHeader* dataRun = (NTFS_RunHeader*)((u8*)data_attribute + data_attribute->dataRunsOffset);
@ -457,7 +457,7 @@ bool Deserialize_Win32_Drives (string file_path) {
push_allocator(temp());
auto_release_temp();
Deserializer deserializer = read_entire_file(file_path, true);
Deserializer deserializer = read_entire_file(file_path, false, true);
if (deserializer.count == 0) return false;
auto d = &deserializer;
@ -468,11 +468,11 @@ bool Deserialize_Win32_Drives (string file_path) {
Assert(magic_number == Win32_Drive_Magic_Number);
Read(d, &drive_count);
ntfs_workspace.arena = next_arena(Arena_Reserve::Size_64G);
ntfs_workspace.arena = bootstrap_arena(Arena_Reserve::Size_64G);
push_arena(ntfs_workspace.arena);
Assert(ntfs_workspace.drives.count == 0);
array_resize(ntfs_workspace.supplementary, drive_count);
// ntfs_workspace.drives.allocator = GPAllocator();
// ntfs_workspace.drives.allocator = default_allocator();
log("[Deserialize_Win32_Drives] drive_count: %d", drive_count);
@ -589,7 +589,7 @@ void ntfs_create_enumeration_threads (s32 thread_count) {
if (!ex1_ntfs.initialized) { Timed_Block_Print("Thread initialization (ntfs)");
ex1_ntfs.initialized = true;
ex1_ntfs.threads = ArrayView<Thread>(thread_count);
ex1_ntfs.threads_in_flight.allocator = GPAllocator();
ex1_ntfs.threads_in_flight.allocator = default_allocator();
for_each(t, ex1_ntfs.threads) {
string thread_name = format_string("ntfs_enumeration_thread#%d", t);
bool success = thread_init(&ex1_ntfs.threads[t], ntfs_enumeration_thread_proc, thread_name);
@ -665,7 +665,7 @@ void Ex1_show_ntfs_workspace () { using namespace ImGui;
i, ex1_ntfs.threads[i].proc != nullptr, ex1_ntfs.threads[i].context != nullptr, ex1_ntfs.threads[i].data != nullptr);
}*/
/*// #NTFS_MFT_RAW
push_allocator(GPAllocator());
push_allocator(default_allocator());
Array<ArrayView<OS_Drive*>> drive_split;
drive_split.allocator = temp(); // this is only needed for this frame
@ -711,7 +711,7 @@ void Ex1_show_ntfs_workspace () { using namespace ImGui;
ex1_ntfs.threads_started = true;
for (s64 t = 0; t < active_thread_count; t += 1) {
Thread* thread = &ex1_ntfs.threads[t];
Arena* thread_arena = next_arena(Arena_Reserve::Size_64K);
Arena* thread_arena = bootstrap_arena(Arena_Reserve::Size_64K);
push_arena(thread_arena);
auto thread_data = New<NTFS_Enumeration_Task>();
thread_data->pool = thread_arena;
@ -727,14 +727,14 @@ void Ex1_show_ntfs_workspace () { using namespace ImGui;
for_each(t, ex1_ntfs.threads_in_flight) {
if (thread_is_done(ex1_ntfs.threads_in_flight[t])) {
push_allocator(GPAllocator());
push_allocator(default_allocator());
Thread* thread = ex1_ntfs.threads_in_flight[t];
auto task = thread_task(NTFS_Enumeration_Task);
array_free(task->drives);
// make sure to retreive any data you need to from here!
release_arena(task->pool);
arena_delete(task->pool);
thread_deinit(ex1_ntfs.threads_in_flight[t], false);
array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t);
@ -746,7 +746,7 @@ void Ex1_show_ntfs_workspace () { using namespace ImGui;
/* #NTFS_MFT_RAW
if (ex1_ntfs.threads_started && !ex1_ntfs.threads_in_flight.count) {
// All threads are complete, we're free to clean up remaining memory
push_allocator(GPAllocator());
push_allocator(default_allocator());
array_free(ex1_ntfs.threads);
array_free(ex1_ntfs.threads_in_flight);

View File

@ -1,16 +0,0 @@
#pragma once
const char* MUSA_LIB_VERSION = "0.2";
#define BUILD_DEBUG 1
#define OS_WINDOWS 1
#define OS_LINUX 0
#define OS_MACOS 0
#define OS_ANDROID 0
#define OS_IOS 0
#define ARCH_CPU_X64 1
#define ARCH_CPU_ARM64 0
#define OS_IS_UNIX 0
#define COMPILER_MSVC 1
#define COMPILER_CLANG 0
#define ARRAY_ENABLE_BOUNDS_CHECKING 1
#define COMPILER_GCC 0

View File

@ -10,14 +10,15 @@
// I'll see where it's used most often and see if I can make macros or templates to make
// them easier to use.
#include "lib/meta_generated.h"
// #include "lib/meta_generated.h"
#include "lib/Base/Base.h"
#include "lib/Base/Allocator.h"
#include "lib/Base/Array.h"
#include "lib/Base/General_Purpose_Allocator.h"
#include "lib/Base/String.h"
#include "lib/Base/Arena.h"
#include "lib/Base/Arena_Array.h"
#include "lib/Base/String.h"
#include "lib/Base/String_Builder.h"
#include "lib/Base/Hash_Functions.h"
#include "lib/Base/Hash_Table.h"
#include "lib/Base/Arena_Hash_Table.h"
@ -32,7 +33,7 @@
#include "lib/Base/Base_Thread_Context.h"
#include "lib/Base/Expandable_Arena.h"
#include "lib/Base/Timing.h"
#include "lib/Base/Arena_Free_List.cpp"
// #include "lib/Base/Arena_Free_List.cpp"
#include "lib/Base/Arena.cpp"
#include "lib/Base/String.cpp"

View File

@ -7,6 +7,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv);
return 0;
}
#else
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
#include <cstdlib> // globals __argc, __wargv
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) {
@ -16,7 +17,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv);
#endif
#endif
internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point
internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point: Main + Context Setup
// #TODO: Check if base frequency is even available.
u32 base_frequency = (u32)CPU_Base_Frequency();
set_cpu_base_frequency(base_frequency); // REQUIRED FOR TIMING MODULE! will depend on CPU
@ -28,6 +29,8 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point
// before setting up the thread context!
Bootstrap_Main_Thread_Context();
stack_trace(); // #stack_trace: #entry_point (first stack trace)
#if OS_WINDOWS
Win32_Entry_Point(argc, argv);
#endif
@ -37,7 +40,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point
#if BASE_RUN_TESTS
run_post_setup_tests();
#endif
#if BUILD_EXPLORER_APP_WIN32 // #entry_point
#if BUILD_EXPLORER_APP_WIN32 // #entry_point: Custom program
Explorer_ImGui_Application_Win32(); // #rename
#endif
#if BUILD_CUSTOM_GUI

View File

@ -50,6 +50,9 @@ bool Ex1_Register_Global_Hotkeys () {
global Key_Combination program_exit_hotkey { ImGuiKey_W, USE_CTRL, USE_SHIFT, KEY_UNUSED, TRIGGER_ONCE };
global Key_Combination program_minimize_hotkey { ImGuiKey_W, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE };
global Key_Combination decrease_font_size_hotkey { ImGuiKey_Minus, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE };
global Key_Combination increase_font_size_hotkey { ImGuiKey_Equal, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE };
bool Ex1_check_key_combinations() {
update_global_keyboard_state();
// #program_hotkeys
@ -59,6 +62,12 @@ bool Ex1_check_key_combinations() {
if (check_key_combination(&program_minimize_hotkey)) {
Win32_Minimize_Window_To_Tray(get_main_window_pointer());
}
if (check_key_combination(&decrease_font_size_hotkey)) { // see ImGui_Show_Font_Info.
imgui_default_font.current_size = clamp<s64>(imgui_default_font.current_size - 1, 0, 5);
}
if (check_key_combination(&increase_font_size_hotkey)) {
imgui_default_font.current_size = clamp<s64>(imgui_default_font.current_size + 1, 0, 5);
}
// #Ex1_hotkeys
return false;
}
@ -73,7 +82,8 @@ struct Ex1_Workspace {
RadixSort dir_modtime_radix;
bool sort_completed;
// Reordered strings:
// Reordered strings: #note: these are NOT required to save in memory, better to search and
// sort the filtered strings using the solved radices
ArrayView<string> files_sorted_by_size;
ArrayView<string> files_sorted_by_modtime;
};
@ -82,7 +92,7 @@ global Ex1_Workspace ex1w;
void free_ex1_workspace_and_reset () {
if (ex1w.sort_completed) {
push_allocator(GPAllocator());
push_allocator(default_allocator());
radix_sort_free(&ex1w.file_size_radix);
radix_sort_free(&ex1w.file_modtime_radix);
@ -112,7 +122,7 @@ void Ex1_show_enumeration_workspace () { using namespace ImGui;
push_imgui_window("Enumerated Data Workspace");
if (!ex1w.sort_completed) {
push_allocator(GPAllocator());
push_allocator(default_allocator());
Timed_Block_Print("radix_sort_u64: file sizes, file modtimes, directory modtimes");
ArrayView<u64> sizes = to_view(*stfe->files.sizes);
radix_sort_u64(&ex1w.file_size_radix, sizes.data, (u32)sizes.count);
@ -186,13 +196,12 @@ void Ex1_Control_Panel () { using namespace ImGui;
push_allocator(temp());
ArrayView<OS_Drive*> drives = os_get_available_drives(); // only includes drives that are ready.
if (!USN_Journal_Monitoring_Ready(drives[0]) && Button("Enable USN Monitoring for all drives")) {
Win32_Enable_USN_Journal_Monitoring(drives);
}
if (USN_Journal_Monitoring_Ready(drives[0]) && Button("Query USN Journal")) {
Query_USN_Journal(drives);
}
// if (!USN_Journal_Monitoring_Ready(drives[0]) && Button("Enable USN Monitoring for all drives")) {
// Win32_Enable_USN_Journal_Monitoring(drives);
// }
// if (USN_Journal_Monitoring_Ready(drives[0]) && Button("Query USN Journal")) {
// Query_USN_Journal(drives);
// }
if (!all_drives_enumerated) {
// Text("drive_table is valid: %d", table_is_valid(drive_table));
@ -225,8 +234,8 @@ void Ex1_Control_Panel () { using namespace ImGui;
// bool all_drives_enumerated = !ex1_ntfs.threads_in_flight.count
// && (drives_enumerated == drives.count);
// string file_path = format_string_temp("%s_DriveData.bin", os_get_machine_name().data);
string file_path = "D:/Projects/Cpp/Musa-Cpp-Lib-V2/bin/MUSA-PC3_DriveData.bin";// FIXED path.
string file_path = format_string_temp("D:/TempSync/Filesystem_Data/%s_DriveData.bin", os_get_machine_name().data);
// string file_path = "D:/TempSync/Filesystem_Data/MUSA-PC3_DriveData.bin";
Text("fixed file_path: %s", file_path.data);
if (!all_drives_enumerated && file_exists(file_path)) { // #autoload
Deserialize_ST_File_Enumeration(file_path);
@ -238,7 +247,7 @@ void Ex1_Control_Panel () { using namespace ImGui;
// if (file_enum_multithreading_started()) {
// if (thread_is_done(drive_enumeration->master_thread)) {
// push_allocator(GPAllocator());
// push_allocator(default_allocator());
// // Thread* thread = drive_enumeration->master_thread;
// // auto task = thread_task(Drive_Enumeration);
// // Nothing to free?
@ -304,37 +313,33 @@ void ImGui_Debug_Panel () { using namespace ImGui;
Begin("Debug Panel");
// #cpuid
Text("[cpus] physical: %d, logical: %d, primary: %d, secondary: %d", os_cpu_physical_core_count(), os_cpu_logical_core_count(), os_cpu_primary_core_count(), os_cpu_secondary_core_count());
{ SeparatorText("Arena In-Use List");
lock_guard(&arena_free_list->mutex);
for (u8 i = 0; i < Arena_Reserve_Count; i += 1) {
#if ARENA_DEBUG
auto t = format_cstring(
" [%s] in_use: %d, committed_bytes: %s",
format_bytes(Arena_Sizes[i], 0).data,
arena_free_list->in_flight_count[i],
format_bytes(committed_bytes(arena_free_list->in_flight[i])).data
);
#else
auto t = format_cstring(
" [%s] in_use: %d, committed_bytes: %s",
format_bytes(Arena_Sizes[i], 0).data,
arena_free_list->in_flight_count[i],
"disabled in release mode"
);
#endif
Text(t);
}
SeparatorText("Arena Free List");
for (u8 i = 0; i < Arena_Reserve_Count; i += 1) {
auto t = format_cstring(
" [%s] free: %d, committed_bytes: %s",
format_bytes(Arena_Sizes[i], 0).data,
(s32)arena_free_list->free_table[i].count,
format_bytes(committed_bytes(arena_free_list->free_table[i])).data
);
Text(t);
#if BUILD_DEBUG
SeparatorText("Default Allocator Allocations");
{ lock_guard(&allocator_mutex);
// Replace this with two sliders min and max to filter allocations?
Checkbox("Show small allocations (<1kB)", &default_allocator_show_small_allocations);
auto allocations = to_view(get_general_allocator_data()->allocations);
Text("%s in %lld allocations",
format_bytes(get_general_allocator_data()->total_bytes_allocated).data,
allocations.count);
for_each(a, allocations) {
if (!default_allocator_show_small_allocations && allocations[a].size < 1024) {
continue;
}
Text(" [%lld] ptr: %p (size: %s, alignment: %d)",
a, allocations[a].memory, format_bytes(allocations[a].size).data, allocations[a].alignment);
}
}
SeparatorText("Arenas in Use");
{ lock_guard(&arenas_in_use_mutex);
Assert(arenas_in_use.allocated > 0);
for_each(a, arenas_in_use) {
auto arena = arenas_in_use[a];
if (!is_valid(arena)) continue;
Text("[%d], source %s:%d (%s)", a, arena->file_path.data, arena->line_number, arena->function_name.data);
}
}
#endif // BUILD_DEBUG
SeparatorText("Child Threads");
SeparatorText("Errors");
ArrayView<Error*> errors = get_all_errors(thread_context());

View File

@ -39,7 +39,7 @@ void count_unique_chars_from_string (string s) {
}
void count_unique_utf8_chars () { Timed_Block_Print("count_unique_utf8_chars");
unique_codepoints_utf32.allocator = GPAllocator();
unique_codepoints_utf32.allocator = default_allocator();
Assert(stfe != nullptr);
for (s64 i = 0; i < stfe->dirs.offsets->count; i += 1) {

View File

@ -83,7 +83,7 @@ void Explorer_ImGui_Application_Win32 () {
string font_file_name = "RobotoMono-Regular.ttf";
imgui_default_font.sizes.allocator = GPAllocator();
imgui_default_font.sizes.allocator = default_allocator();
imgui_default_font.font_name = font_file_name;
for (s64 i = 0; i < ArrayCount(imgui_font_sizes); i += 1) {
@ -151,7 +151,7 @@ void Explorer_ImGui_Application_Win32 () {
ImGui_Debug_Panel();
Ex1_Control_Panel();
ImGui_Show_Font_Info();
// ImGui_Show_Font_Info();
ImGui_Pop_Default_Font();