[wip] This is going to require some work to disentangle...

This commit is contained in:
Musa Mahmood 2025-12-17 11:39:54 -05:00
parent 4b48ab696b
commit d9cd1c618c
25 changed files with 672 additions and 555 deletions

View File

@ -17,6 +17,9 @@ SET (CMAKE_VERBOSE_MAKEFILE ON)
if (MSVC)
# Suppress warning: C++ exception handler used, but unwind semantics are not enabled.
add_compile_options(/wd4530)
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(-D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
add_compile_options(-D_CRT_NONSTDC_NO_DEPRECATE)
endif()
SET (EXE_NAME "mexplore_v2")
@ -29,12 +32,41 @@ SET (SRC_FILES
lib/third_party/dear-imgui/imgui_impl_dx11.cpp
lib/third_party/dear-imgui/imgui_impl_win32.cpp
exe_main.cpp
#lib_main
lib/Base/Base.cpp
lib/Base/Allocator.cpp
lib/Base/General_Purpose_Allocator.cpp
#lib/Base/Base_Thread_Context.cpp
#lib/Base/Arena.cpp
#lib/Base/String.cpp
#lib/Base/ErrorType.cpp
#lib/Base/Logger.cpp
#lib/Base/Expandable_Arena.cpp
#lib/Base/Threads.cpp
#lib/OS/OS_Filesystem.cpp
#lib/Base/Thread_Group.cpp
#lib/Base/Unicode.cpp
#lib/Base/RadixSort.cpp
lib/OS/OS_Win32.cpp
#lib/Base/run_tests.cpp
#exe_main
#src/ImGui_Supplementary.cpp
#src/String_Analysis.cpp
#src/explorer_main.cpp
#src/Base_Entry_Point.cpp
# Not in main project: for future opengl backend
#lib/Graphics.cpp
)
SET (INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/lib
${PROJECT_SOURCE_DIR}/lib/Base
${PROJECT_SOURCE_DIR}/lib/OS
${PROJECT_SOURCE_DIR}/lib/UI
${PROJECT_SOURCE_DIR}/lib/third_party
)
@ -49,6 +81,7 @@ endif()
SET (SYSTEM_LIBRARIES
)
add_executable(${EXE_NAME} ${SRC_FILES})
#add_executable(${EXE_NAME} ${SRC_FILES})
add_library(${EXE_NAME} STATIC ${SRC_FILES})
target_include_directories(${EXE_NAME} PRIVATE ${INCLUDE_DIRS})
target_link_libraries(${EXE_NAME} PRIVATE ${LINK_LIBS_DIRS} ${SYSTEM_LIBRARIES})

View File

@ -1,3 +1,6 @@
#include "Allocator.h"
#include "Base_Thread_Context.h"
void* internal_alloc (s64 size) {
Allocator allocator = context_allocator();
void* result = allocator.proc(Allocator_Mode::ALLOCATE, size, 0, nullptr, allocator.data);
@ -16,9 +19,4 @@ void internal_free (void* memory) {
allocator.proc(Allocator_Mode::DEALLOCATE, 0, 0, memory, allocator.data);
}
void print_context_allocator () {
Assert(thread_context()->allocator.proc);
char* result = (char*)thread_context()->allocator.proc(Allocator_Mode::DETAILS, 0, 0, nullptr, thread_context()->allocator.data);
log_none("Current allocator details: %s", result);
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "Base.h"
#define ALLOCATOR_DEBUG_MODE 1
#define ALLOCATOR_POISON_MEMORY_ON_ALLOCATION \
(BUILD_DEBUG && ALLOCATOR_DEBUG_MODE)
@ -120,6 +122,4 @@ template <typename T> T* copy_struct (T* src) {
memcpy(dst, src, sizeof(T));
}
// #TODO: Turn this into a macro that also provides the
// file, line number to the print.
void print_context_allocator ();

View File

@ -309,4 +309,115 @@ void* fixed_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 o
}
return nullptr;
}
}
// #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
#include "Base.h"
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

@ -1,5 +1,9 @@
#pragma once
#include "Base.h"
#include "Array.h"
#include "Allocator.h"
struct ExpandableArena; // fwd declare #temp
#if OS_WINDOWS

View File

@ -1,5 +1,7 @@
#pragma once
#include "Arena.h"
constexpr s64 ARRAY_ARENA_START_OFFSET = 64;
template <typename T>

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

@ -1,6 +1,8 @@
// Strongly influenced by Array.jai in Basic module.
#pragma once
#include "Allocator.h"
// For Arena-Backed arrays use ArenaArray
MSVC_RUNTIME_CHECKS_OFF

22
lib/Base/Base.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "Base.h"
// #TEMPORARY
// #include "Allocator.h"
// #include "Array.h"
// #include "General_Purpose_Allocator.h"
// #include "Arena.h"
// #include "Arena_Array.h"
// #include "MString.h"
// #include "Hash_Functions.h"
// #include "Hash_Table.h"
// #include "Arena_Hash_Table.h"
#if OS_WINDOWS
// # include "OS_Win32.h"
#endif
// #include "Layout.h"
// #include "Logger.h"
// #include "Serializer.h"
// #include "Base_Thread_Context.h"
// #include "Expandable_Arena.h"
// #include "Timing.h"

View File

@ -408,67 +408,6 @@ template <typename T> T clamp (T v, T min, T max) {
return x;
}
// #thread_primitives #move?
#if OS_WINDOWS
struct Condition_Variable {
CONDITION_VARIABLE condition_variable;
};
struct Semaphore {
HANDLE event;
};
struct Mutex {
CRITICAL_SECTION csection;
};
struct OS_Thread {
HANDLE windows_thread;
s32 windows_thread_id;
};
struct File {
HANDLE handle;
};
internal void mutex_init (Mutex* mutex);
internal void mutex_destroy (Mutex* mutex);
internal void lock (Mutex* mutex);
internal void unlock (Mutex* mutex);
#endif
#define POSIX_THREADS OS_LINUX || OS_MACOS || OS_IOS || OS_ANDROID
#if OS_MACOS
struct Semaphore {
task_t owner;
semaphore_t event = 0;
};
#endif
#if OS_LINUX || OS_ANDROID
struct Semaphore {
sem_t semaphore;
};
#endif
#if OS_IS_UNIX // #posix threads
struct OS_Thread {
pthread_t thread_handle;
Semaphore is_alive;
Semaphore suspended;
b32 is_done;
};
#endif
#define lock_guard(x) \
Mutex_Lock_Guard Concat(_auto_lock_guard_, __LINE__)(x)
struct Mutex_Lock_Guard {
Mutex* mutex;
Mutex_Lock_Guard (Mutex* mutex) {
this->mutex = mutex;
lock(mutex);
}
~Mutex_Lock_Guard () {
unlock(mutex);
}
};
// Helper macros for raw arrays:
#define ArrayCount(array) sizeof(array) / sizeof(array[0])
// For-loop construct macros

View File

@ -1,4 +1,5 @@
// See Context_Base in jai, and TCTX in raddebugger:
#include "Base_Thread_Context.h"
internal void Bootstrap_Main_Thread_Context () {
// Timed_Block_Print_No_Context("Bootstrap_Main_Thread_Context");

View File

@ -1,31 +1,37 @@
// #hacky fwd declares
struct Error;
struct Graphics;
#pragma once
#include "Base.h"
#include "General_Purpose_Allocator.h"
// #include "Arena.h"
// #include "Logger.h"
#include "MString.h"
// #include "Threads.h"
// #include "Expandable_Arena.h"
struct Thread_Context {
ExpandableArena* temp; // Used for temporary allocations, scratch space.
ExpandableArena* arena; // general purpose local arena
// ExpandableArena* temp; // Used for temporary allocations, scratch space.
// ExpandableArena* arena; // general purpose local arena
Allocator allocator;
s32 thread_idx;
// u16 _padding0;
u16 GPAllocator_alignment = 16;
Logger logger = {nullptr, nullptr};
String_Builder* log_builder;
// Logger logger = {nullptr, nullptr};
// String_Builder* log_builder;
// Stack_Trace* stack_trace;
Array<Thread*> child_threads; // maybe should be linked-list?
// 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();
Error* first_error = nullptr;
Error* current_error = nullptr;
Arena* error_arena;
// Error* first_error = nullptr;
// Error* current_error = nullptr;
// Arena* error_arena;
// Graphics stuff:
Graphics* graphics;
// Graphics* graphics;
void* userdata; // for appending other arenas, etc.
};
@ -53,3 +59,13 @@ struct Push_Allocator {
}
};
// #TODO: Turn this into a macro that also provides the
// file, line number to the print.
void print_context_allocator ();
// void print_context_allocator () {
// Assert(thread_context()->allocator.proc);
// char* result = (char*)thread_context()->allocator.proc(Allocator_Mode::DETAILS, 0, 0, nullptr, thread_context()->allocator.data);
// log_none("Current allocator details: %s", result);
// }

View File

@ -1,3 +1,7 @@
#include "General_Purpose_Allocator.h"
#include "Base_Thread_Context.h"
#include "Threads.h"
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
global General_Allocator gAllocator; // @Shared
global Mutex allocator_mutex;

View File

@ -1,5 +1,9 @@
#pragma once
#include "Base.h"
#include "Allocator.h"
#include "Array.h"
#define GP_ALLOCATOR_TRACK_ALLOCATIONS BUILD_DEBUG
#define GP_ALLOCATOR_VERY_DEBUG BUILD_DEBUG && 0
@ -21,6 +25,7 @@
#define Aligned_Realloc(old_sz, ptr, sz, align) gp_aligned_realloc(old_sz, ptr, sz, align)
#define Aligned_Free(ptr) std::free(ptr)
#endif
struct Allocation {
s64 size;
void* memory;

View File

@ -1,3 +1,5 @@
#include "MString.h"
constexpr u32 HASH_INIT = 5381;
u32 sdbm_hash (void* data, s64 size) {
u32 h = HASH_INIT;

View File

@ -1,4 +1,9 @@
#pragma once
#include "Base.h"
#include "Array.h"
#include "Arena_Array.h"
// #TODO: #strings:
// [ ] see: #Parsing stuff:
// [?] How do I accept variadic arguments of any type to my print function?

View File

@ -1,3 +1,5 @@
#include "MString.h"
// #NOTE: All string building, printing and copying operations SHOULD null-terminate the
// strings for backwards compatibility reasons. #FIX if something doesn't follow this rule!
bool is_valid (string s) {

62
lib/Base/Threads.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
// #thread_primitives #move?
#if OS_WINDOWS
struct Condition_Variable {
CONDITION_VARIABLE condition_variable;
};
struct Semaphore {
HANDLE event;
};
struct Mutex {
CRITICAL_SECTION csection;
};
struct OS_Thread {
HANDLE windows_thread;
s32 windows_thread_id;
};
struct File {
HANDLE handle;
};
#endif
#define POSIX_THREADS OS_LINUX || OS_MACOS || OS_IOS || OS_ANDROID
#if OS_MACOS
struct Semaphore {
task_t owner;
semaphore_t event = 0;
};
#endif
#if OS_LINUX || OS_ANDROID
struct Semaphore {
sem_t semaphore;
};
#endif
#if OS_IS_UNIX // #posix threads
struct OS_Thread {
pthread_t thread_handle;
Semaphore is_alive;
Semaphore suspended;
b32 is_done;
};
#endif
internal void mutex_init (Mutex* mutex);
internal void mutex_destroy (Mutex* mutex);
internal void lock (Mutex* mutex);
internal void unlock (Mutex* mutex);
#define lock_guard(x) \
Mutex_Lock_Guard Concat(_auto_lock_guard_, __LINE__)(x)
struct Mutex_Lock_Guard {
Mutex* mutex;
Mutex_Lock_Guard (Mutex* mutex) {
this->mutex = mutex;
lock(mutex);
}
~Mutex_Lock_Guard () {
unlock(mutex);
}
};

View File

@ -1,3 +1,6 @@
#pragma once
#include "Base.h"
// #Timing API:
#define System_Timed_Block_Print(name) \
system_timed_block_print Concat(_sys_timed_block_print_guard, __LINE__)(name)

View File

@ -5,6 +5,8 @@
// before terminating.
// Or we can move child threads up to the parent?
#include "OS_Win32.h"
constexpr s64 FILETIME_TO_UNIX = 116444736000000000i64;
f64 GetUnixTimestamp () {
FILETIME fileTime;

View File

@ -1,6 +1,15 @@
#pragma once
#include "Base.h"
#include "Array.h"
#include "Threads.h"
#pragma comment(lib, "user32")
#pragma comment(lib, "shell32")
#include <shellapi.h>
#include <math.h>
#include <d3d11.h>
#include <dwmapi.h>
f64 GetUnixTimestamp ();
s64 GetUnixTimestampNanoseconds ();

View File

@ -49,7 +49,6 @@
// OS-Abstraction Layer
#include "lib/Base/Threads.cpp"
#include "lib/OS/OS_Filesystem.cpp"
#include "lib/Base/Thread_Group.cpp"
#if OS_WINDOWS

View File

@ -1,3 +1,6 @@
#include "Base_Entry_Point.h"
#include "Timing.h"
internal void Main_Entry_Point (int argc, WCHAR **argv);
#if OS_WINDOWS

View File

@ -1,361 +0,0 @@
struct ExplorerUI {
// u8 search_input[64];
// u8 secondary_input[64];
};
struct Explorer {
// A bunch of flags?
// Icon cache?
// Array<Image_Data> frame_textures;
};
struct Ex1_NTFS_Enumeration {
b32 initialized;
b32 threads_started;
ArrayView<Thread> threads;
Array<Thread*> threads_in_flight;
};
// #Ex1_global_state
global ExplorerUI explorer_ui;
global Explorer explorer;
global Ex1_NTFS_Enumeration ex1_ntfs;
#define HOTKEY_ID_BRING_TO_FOREGROUND 1
#define VK_SPACE_KEY_CODE 0x20
// #define HOTKEY_ID_HIDE_TITLEBAR
void Ex1_Unregister_Global_Hotkeys () {
UnregisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND);
}
bool Ex1_Register_Global_Hotkeys () {
if (!RegisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND, MOD_CONTROL|MOD_SHIFT|MOD_ALT, VK_SPACE_KEY_CODE)) {
Assert(false);
log_error("Failed to register global hotkey Ctrl-Shift-Alt-Space");
return false;
}
return true;
}
#define USE_CTRL true
#define USE_SHIFT true
#define USE_ALT true
#define TRIGGER_ONCE true
#define TRIGGER_EVERY_FRAME false
#define KEY_UNUSED false
// #program_hotkeys (Not specific to Ex1)
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 };
bool Ex1_check_key_combinations() {
update_global_keyboard_state();
// #program_hotkeys
if (check_key_combination(&program_exit_hotkey)) {
return true;
}
if (check_key_combination(&program_minimize_hotkey)) {
Win32_Minimize_Window_To_Tray(get_main_window_pointer());
}
// #Ex1_hotkeys
return false;
}
// #Workspaces are FOR DEVELOPMENT ONLY.
struct Ex1_Workspace {
s32 path_select;
s32 file_select;
RadixSort file_size_radix;
RadixSort file_modtime_radix;
RadixSort dir_modtime_radix;
bool sort_completed;
// Reordered strings:
ArrayView<string> files_sorted_by_size;
ArrayView<string> files_sorted_by_modtime;
};
global Ex1_Workspace ex1w;
void free_ex1_workspace_and_reset () {
if (ex1w.sort_completed) {
push_allocator(GPAllocator());
radix_sort_free(&ex1w.file_size_radix);
radix_sort_free(&ex1w.file_modtime_radix);
radix_sort_free(&ex1w.dir_modtime_radix);
array_free(ex1w.files_sorted_by_size);
array_free(ex1w.files_sorted_by_modtime);
zero_struct(&ex1w);
}
}
// #TODO: Move all sort stuff to OS_Win32?
// Make a general version of this that takes two ArrayView<T> and reorders.
// There's no need to do this until we have the filtered results.
void os_win32_reorder_files_by_radix (RadixSort* r, ArrayView<string>* files, bool reverse_order=false) {
Timed_Block_Print("os_win32_reorder_files_by_radix");
// Where are my source files!?
(*files) = ArrayView<string>(r->ranks.count);
for_each(f, (*files)) {
// (*files)[f] = get_file_copy(stfe, r->ranks[f]);
(*files)[f] = get_file_string_view(stfe, r->ranks[f]);
}
}
void Ex1_show_enumeration_workspace () { using namespace ImGui;
push_imgui_window("Enumerated Data Workspace");
if (!ex1w.sort_completed) {
push_allocator(GPAllocator());
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);
ArrayView<u64> file_modtimes = to_view(*stfe->files.modtimes);
radix_sort_u64(&ex1w.file_modtime_radix, file_modtimes.data, (u32)file_modtimes.count);
ArrayView<u64> dirs_modtimes = to_view(*stfe->dirs.modtimes);
radix_sort_u64(&ex1w.dir_modtime_radix, dirs_modtimes.data, (u32)dirs_modtimes.count);
// Create ArrayView<string>, ArrayView<u64> sizes, and ArrayView<u64> modtimes
os_win32_reorder_files_by_radix(&ex1w.file_size_radix, &ex1w.files_sorted_by_size);
os_win32_reorder_files_by_radix(&ex1w.file_modtime_radix, &ex1w.files_sorted_by_modtime);
// reordering by the rank permutations generated by RadixSort.
ex1w.sort_completed = true;
}
if (!ex1w.sort_completed) { return; }
SeparatorText("Files ordered by modtime");
s32 file_count = (s32)ex1w.files_sorted_by_modtime.count;
SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
string file_name = copy_string(ex1w.files_sorted_by_modtime[ex1w.file_select]);
Text("%s", file_name.data);
u32 radix_index = rank(&ex1w.file_modtime_radix, ex1w.file_select);
Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data);
SeparatorText("Files ordered by size");
file_count = (s32)ex1w.files_sorted_by_size.count;
// SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
file_name = copy_string(ex1w.files_sorted_by_size[ex1w.file_select]);
Text("%s", file_name.data);
radix_index = rank(&ex1w.file_size_radix, ex1w.file_select);
Text("size: %s", format_bytes(get_file_size_bytes(stfe, radix_index)).data);
// Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data);
if (Button("Count unique UTF-8 characters")) {
count_unique_utf8_chars();
}
Text("unique_codepoints_utf32.count: %", unique_codepoints_utf32.count);
for_each(u, unique_codepoints_utf32) {
Text("[%d] Code point as hex: 0x%X", u, unique_codepoints_utf32[u]);
}
Text("files_sorted_by_size size in bytes: %lld", ex1w.files_sorted_by_size.count * sizeof(string));
Text("files_sorted_by_modtime size in bytes: %lld", ex1w.files_sorted_by_modtime.count * sizeof(string));
for (s64 i = 1; i < 128; i += 1) {
u8 cstring[2] = {};
cstring[0] = (u8)i;
cstring[1] = 0;
Text("codepoint[0x%X]: %s, count: %lld", i, cstring, count_ascii_codepoints[i]);
}
}
void Ex1_Control_Panel () { using namespace ImGui;
f64 frame_time = GetUnixTimestamp();
Table<string, OS_Drive*>* drive_table = get_drive_table();
push_imgui_window("Control Panel");
// if (Button("Debug break")) { debug_break(); }
if (/*Button("Discover drives") ||*/!table_is_valid(drive_table)) { Win32_Discover_Drives(); }
// Text("ntfs_workspace_files_loaded: %s", ntfs_workspace_files_loaded()? "true": "false");
// if (ntfs_workspace_files_loaded()) {
// Ex1_show_ntfs_workspace();
// return;
// }
bool all_drives_enumerated = stfe && stfe->thread_completed;
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 (!all_drives_enumerated) {
// Text("drive_table is valid: %d", table_is_valid(drive_table));
for_each(i, drives) {
OS_Drive* drive = drives[i];
Text(" > [%d] drive letter: %s (is_present: %d)", i + 1, drive->label.data, drive->is_present);
if (drive->time_to_enumerate != 0) {
SameLine();
Text("Enumerated in %.2f seconds", drive->time_to_enumerate);
}
// SameLine();
// if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) {
// push_arena(thread_context()->arena);
// Error* error = NTFS_MFT_read_raw(drive);
// }
}
}
s32 drives_enumerated = 0;
for_each(d, drives) {
OS_Drive* drive = drives[d];
if (!drives[d]->time_to_enumerate) { continue; }
if (!drives[d]->data) { continue; }
if (drives[d]->data->paths.offsets->count > 0) {
drives_enumerated += 1;
}
}
// 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.
Text("fixed file_path: %s", file_path.data);
if (!all_drives_enumerated && file_exists(file_path)) { // #autoload
Deserialize_ST_File_Enumeration(file_path);
}
if (drives.count > 0 && !all_drives_enumerated && file_exists(file_path) && Button("Load from file (this machine)")) {
Deserialize_ST_File_Enumeration(file_path);
// Deserialize_Win32_Drives(file_path);
}
// if (file_enum_multithreading_started()) {
// if (thread_is_done(drive_enumeration->master_thread)) {
// push_allocator(GPAllocator());
// // Thread* thread = drive_enumeration->master_thread;
// // auto task = thread_task(Drive_Enumeration);
// // Nothing to free?
// thread_deinit(drive_enumeration->master_thread, true);
// }
// }
// #FileEnumerationST
if (stfe && stfe->thread_started) {
// print dirs enumerated, etc
if (!stfe->thread_completed) Text("Enumeration Thread Active (elapsed: %s)", format_time_seconds(frame_time-stfe->start_time).data);
if (stfe->dirs.offsets) {
s64 dirs_enumerated = stfe->dirs.offsets->count;
Text("Dirs enumerated: %lld", dirs_enumerated);
// if (!stfe->thread_completed) Text("Current dir: %s", get_last_path_copy(stfe).data);
}
if (stfe->files.offsets) {
s64 files_enumerated = stfe->files.offsets->count;
Text("Files enumerated: %lld", files_enumerated);
// if (!stfe->thread_completed) Text("Current file: %s", get_last_file_copy(stfe).data);
}
if (stfe->thread_completed) {
Text("String bytes stored: %s", format_bytes(stfe->dirs.strings->count + stfe->files.strings->count).data);
Text("Elapsed time: %s", format_time_seconds(stfe->end_time-stfe->start_time).data);
}
}
if (stfe && stfe->thread_started && !stfe->thread_completed) {
Assert(stfe->master_thread != nullptr);
if (thread_is_done(stfe->master_thread)) {
thread_deinit(stfe->master_thread, true);
stfe->thread_completed = true;
}
}
if (all_drives_enumerated && Button("Save enumerated data")) {
if (!Serialize_ST_File_Enumeration(file_path)) {
log_error("Failed to write enumerated files");
os_log_error();
}
}
if (all_drives_enumerated && Button("Reset State")) {
free_ex1_workspace_and_reset();
free_stfe_and_reset();
return;
}
if (all_drives_enumerated) {
Ex1_show_enumeration_workspace();
}
if (drives.count > 0 && !all_drives_enumerated && Button("Enumerate all NTFS drives")) { // && ex1_ntfs.initialized
os_run_file_enumeration_single_threaded();
// os_run_file_enumeration_multithreaded(); // #disabled for now
return;
}
}
void ImGui_Debug_Panel () { using namespace ImGui;
push_allocator(temp());
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);
}
}
SeparatorText("Child Threads");
SeparatorText("Errors");
ArrayView<Error*> errors = get_all_errors(thread_context());
if (errors.count && Button("Clear all errors")) {
clear_errors(thread_context());
errors.count = 0;
}
for_each(e, errors) {
auto button_label = format_cstring("Clear##%d", e);
if (Button(button_label)) {
clear_error(thread_context(), errors[e]);
continue;
}
SameLine();
Text(" [%d] %s", e, to_string(errors[e]).data);
}
Spacing();
if (Button("Push some error")) {
log_warning("This is a warning.");
log_error("... and this is an error.");
}
End();
}

View File

@ -1,4 +1,366 @@
#include "Ex1.cpp"
// #include "
struct ExplorerUI {
// u8 search_input[64];
// u8 secondary_input[64];
};
struct Explorer {
// A bunch of flags?
// Icon cache?
// Array<Image_Data> frame_textures;
};
struct Ex1_NTFS_Enumeration {
b32 initialized;
b32 threads_started;
ArrayView<Thread> threads;
Array<Thread*> threads_in_flight;
};
// #Ex1_global_state
global ExplorerUI explorer_ui;
global Explorer explorer;
global Ex1_NTFS_Enumeration ex1_ntfs;
#define HOTKEY_ID_BRING_TO_FOREGROUND 1
#define VK_SPACE_KEY_CODE 0x20
// #define HOTKEY_ID_HIDE_TITLEBAR
void Ex1_Unregister_Global_Hotkeys () {
UnregisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND);
}
bool Ex1_Register_Global_Hotkeys () {
if (!RegisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND, MOD_CONTROL|MOD_SHIFT|MOD_ALT, VK_SPACE_KEY_CODE)) {
Assert(false);
log_error("Failed to register global hotkey Ctrl-Shift-Alt-Space");
return false;
}
return true;
}
#define USE_CTRL true
#define USE_SHIFT true
#define USE_ALT true
#define TRIGGER_ONCE true
#define TRIGGER_EVERY_FRAME false
#define KEY_UNUSED false
// #program_hotkeys (Not specific to Ex1)
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 };
bool Ex1_check_key_combinations() {
update_global_keyboard_state();
// #program_hotkeys
if (check_key_combination(&program_exit_hotkey)) {
return true;
}
if (check_key_combination(&program_minimize_hotkey)) {
Win32_Minimize_Window_To_Tray(get_main_window_pointer());
}
// #Ex1_hotkeys
return false;
}
// #Workspaces are FOR DEVELOPMENT ONLY.
struct Ex1_Workspace {
s32 path_select;
s32 file_select;
RadixSort file_size_radix;
RadixSort file_modtime_radix;
RadixSort dir_modtime_radix;
bool sort_completed;
// Reordered strings:
ArrayView<string> files_sorted_by_size;
ArrayView<string> files_sorted_by_modtime;
};
global Ex1_Workspace ex1w;
void free_ex1_workspace_and_reset () {
if (ex1w.sort_completed) {
push_allocator(GPAllocator());
radix_sort_free(&ex1w.file_size_radix);
radix_sort_free(&ex1w.file_modtime_radix);
radix_sort_free(&ex1w.dir_modtime_radix);
array_free(ex1w.files_sorted_by_size);
array_free(ex1w.files_sorted_by_modtime);
zero_struct(&ex1w);
}
}
// #TODO: Move all sort stuff to OS_Win32?
// Make a general version of this that takes two ArrayView<T> and reorders.
// There's no need to do this until we have the filtered results.
void os_win32_reorder_files_by_radix (RadixSort* r, ArrayView<string>* files, bool reverse_order=false) {
Timed_Block_Print("os_win32_reorder_files_by_radix");
// Where are my source files!?
(*files) = ArrayView<string>(r->ranks.count);
for_each(f, (*files)) {
// (*files)[f] = get_file_copy(stfe, r->ranks[f]);
(*files)[f] = get_file_string_view(stfe, r->ranks[f]);
}
}
void Ex1_show_enumeration_workspace () { using namespace ImGui;
push_imgui_window("Enumerated Data Workspace");
if (!ex1w.sort_completed) {
push_allocator(GPAllocator());
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);
ArrayView<u64> file_modtimes = to_view(*stfe->files.modtimes);
radix_sort_u64(&ex1w.file_modtime_radix, file_modtimes.data, (u32)file_modtimes.count);
ArrayView<u64> dirs_modtimes = to_view(*stfe->dirs.modtimes);
radix_sort_u64(&ex1w.dir_modtime_radix, dirs_modtimes.data, (u32)dirs_modtimes.count);
// Create ArrayView<string>, ArrayView<u64> sizes, and ArrayView<u64> modtimes
os_win32_reorder_files_by_radix(&ex1w.file_size_radix, &ex1w.files_sorted_by_size);
os_win32_reorder_files_by_radix(&ex1w.file_modtime_radix, &ex1w.files_sorted_by_modtime);
// reordering by the rank permutations generated by RadixSort.
ex1w.sort_completed = true;
}
if (!ex1w.sort_completed) { return; }
SeparatorText("Files ordered by modtime");
s32 file_count = (s32)ex1w.files_sorted_by_modtime.count;
SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
string file_name = copy_string(ex1w.files_sorted_by_modtime[ex1w.file_select]);
Text("%s", file_name.data);
u32 radix_index = rank(&ex1w.file_modtime_radix, ex1w.file_select);
Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data);
SeparatorText("Files ordered by size");
file_count = (s32)ex1w.files_sorted_by_size.count;
// SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
file_name = copy_string(ex1w.files_sorted_by_size[ex1w.file_select]);
Text("%s", file_name.data);
radix_index = rank(&ex1w.file_size_radix, ex1w.file_select);
Text("size: %s", format_bytes(get_file_size_bytes(stfe, radix_index)).data);
// Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data);
if (Button("Count unique UTF-8 characters")) {
count_unique_utf8_chars();
}
Text("unique_codepoints_utf32.count: %", unique_codepoints_utf32.count);
for_each(u, unique_codepoints_utf32) {
Text("[%d] Code point as hex: 0x%X", u, unique_codepoints_utf32[u]);
}
Text("files_sorted_by_size size in bytes: %lld", ex1w.files_sorted_by_size.count * sizeof(string));
Text("files_sorted_by_modtime size in bytes: %lld", ex1w.files_sorted_by_modtime.count * sizeof(string));
for (s64 i = 1; i < 128; i += 1) {
u8 cstring[2] = {};
cstring[0] = (u8)i;
cstring[1] = 0;
Text("codepoint[0x%X]: %s, count: %lld", i, cstring, count_ascii_codepoints[i]);
}
}
void Ex1_Control_Panel () { using namespace ImGui;
f64 frame_time = GetUnixTimestamp();
Table<string, OS_Drive*>* drive_table = get_drive_table();
push_imgui_window("Control Panel");
// if (Button("Debug break")) { debug_break(); }
if (/*Button("Discover drives") ||*/!table_is_valid(drive_table)) { Win32_Discover_Drives(); }
// Text("ntfs_workspace_files_loaded: %s", ntfs_workspace_files_loaded()? "true": "false");
// if (ntfs_workspace_files_loaded()) {
// Ex1_show_ntfs_workspace();
// return;
// }
bool all_drives_enumerated = stfe && stfe->thread_completed;
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 (!all_drives_enumerated) {
// Text("drive_table is valid: %d", table_is_valid(drive_table));
for_each(i, drives) {
OS_Drive* drive = drives[i];
Text(" > [%d] drive letter: %s (is_present: %d)", i + 1, drive->label.data, drive->is_present);
if (drive->time_to_enumerate != 0) {
SameLine();
Text("Enumerated in %.2f seconds", drive->time_to_enumerate);
}
// SameLine();
// if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) {
// push_arena(thread_context()->arena);
// Error* error = NTFS_MFT_read_raw(drive);
// }
}
}
s32 drives_enumerated = 0;
for_each(d, drives) {
OS_Drive* drive = drives[d];
if (!drives[d]->time_to_enumerate) { continue; }
if (!drives[d]->data) { continue; }
if (drives[d]->data->paths.offsets->count > 0) {
drives_enumerated += 1;
}
}
// 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.
Text("fixed file_path: %s", file_path.data);
if (!all_drives_enumerated && file_exists(file_path)) { // #autoload
Deserialize_ST_File_Enumeration(file_path);
}
if (drives.count > 0 && !all_drives_enumerated && file_exists(file_path) && Button("Load from file (this machine)")) {
Deserialize_ST_File_Enumeration(file_path);
// Deserialize_Win32_Drives(file_path);
}
// if (file_enum_multithreading_started()) {
// if (thread_is_done(drive_enumeration->master_thread)) {
// push_allocator(GPAllocator());
// // Thread* thread = drive_enumeration->master_thread;
// // auto task = thread_task(Drive_Enumeration);
// // Nothing to free?
// thread_deinit(drive_enumeration->master_thread, true);
// }
// }
// #FileEnumerationST
if (stfe && stfe->thread_started) {
// print dirs enumerated, etc
if (!stfe->thread_completed) Text("Enumeration Thread Active (elapsed: %s)", format_time_seconds(frame_time-stfe->start_time).data);
if (stfe->dirs.offsets) {
s64 dirs_enumerated = stfe->dirs.offsets->count;
Text("Dirs enumerated: %lld", dirs_enumerated);
// if (!stfe->thread_completed) Text("Current dir: %s", get_last_path_copy(stfe).data);
}
if (stfe->files.offsets) {
s64 files_enumerated = stfe->files.offsets->count;
Text("Files enumerated: %lld", files_enumerated);
// if (!stfe->thread_completed) Text("Current file: %s", get_last_file_copy(stfe).data);
}
if (stfe->thread_completed) {
Text("String bytes stored: %s", format_bytes(stfe->dirs.strings->count + stfe->files.strings->count).data);
Text("Elapsed time: %s", format_time_seconds(stfe->end_time-stfe->start_time).data);
}
}
if (stfe && stfe->thread_started && !stfe->thread_completed) {
Assert(stfe->master_thread != nullptr);
if (thread_is_done(stfe->master_thread)) {
thread_deinit(stfe->master_thread, true);
stfe->thread_completed = true;
}
}
if (all_drives_enumerated && Button("Save enumerated data")) {
if (!Serialize_ST_File_Enumeration(file_path)) {
log_error("Failed to write enumerated files");
os_log_error();
}
}
if (all_drives_enumerated && Button("Reset State")) {
free_ex1_workspace_and_reset();
free_stfe_and_reset();
return;
}
if (all_drives_enumerated) {
Ex1_show_enumeration_workspace();
}
if (drives.count > 0 && !all_drives_enumerated && Button("Enumerate all NTFS drives")) { // && ex1_ntfs.initialized
os_run_file_enumeration_single_threaded();
// os_run_file_enumeration_multithreaded(); // #disabled for now
return;
}
}
void ImGui_Debug_Panel () { using namespace ImGui;
push_allocator(temp());
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);
}
}
SeparatorText("Child Threads");
SeparatorText("Errors");
ArrayView<Error*> errors = get_all_errors(thread_context());
if (errors.count && Button("Clear all errors")) {
clear_errors(thread_context());
errors.count = 0;
}
for_each(e, errors) {
auto button_label = format_cstring("Clear##%d", e);
if (Button(button_label)) {
clear_error(thread_context(), errors[e]);
continue;
}
SameLine();
Text(" [%d] %s", e, to_string(errors[e]).data);
}
Spacing();
if (Button("Push some error")) {
log_warning("This is a warning.");
log_error("... and this is an error.");
}
End();
}
void Explorer_ImGui_Application_Win32 () {
{ // :Win32_Initialize_Extended