Lots of refactoring, overhaul logging system to use context (see log_error macro)
This commit is contained in:
parent
849b8210e0
commit
6970a59c35
@ -12,7 +12,7 @@ constexpr u16 ARENA_DEFAULT_ALIGNMENT = CPU_REGISTER_WIDTH_BYTES;
|
||||
#define ARENA_DEBUG BUILD_DEBUG
|
||||
|
||||
enum class Arena_Reserve: u8 {
|
||||
Size_64K = 0, // these are used as indices in Arena_Table!
|
||||
Size_64K = 0, // these are used as indices in Arena_Free_List!
|
||||
Size_2M = 1,
|
||||
Size_64M = 2,
|
||||
Size_2G = 3,
|
||||
@ -60,8 +60,8 @@ 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_Table.cpp)
|
||||
struct Arena_Table {
|
||||
// Interface API for normal use (idk how to explain - see Arena_Free_List.cpp)
|
||||
struct Arena_Free_List {
|
||||
Mutex mutex;
|
||||
s32 in_flight_count[6];
|
||||
Array<Arena*> free_table[6];
|
||||
|
||||
@ -1,71 +1,71 @@
|
||||
// #TODO: #Arena_Table #garbage_collection in `release_arena`
|
||||
// #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
|
||||
|
||||
global Arena_Table* arena_table;
|
||||
global Arena_Free_List* arena_free_list;
|
||||
|
||||
// Only call once from main thread!
|
||||
void initialize_arena_table (Allocator allocator) {
|
||||
mutex_init(&arena_table->mutex);
|
||||
Assert(arena_table != nullptr);
|
||||
if (arena_table->initialized)
|
||||
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 < 6; i += 1) {
|
||||
arena_table->in_flight_count[i] = 0;
|
||||
arena_table->free_table[i].allocator = allocator;
|
||||
array_reserve(arena_table->free_table[i], 64);
|
||||
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_table->in_flight[i].allocator = allocator;
|
||||
array_reserve(arena_table->in_flight[i], 64);
|
||||
arena_free_list->in_flight[i].allocator = allocator;
|
||||
array_reserve(arena_free_list->in_flight[i], 64);
|
||||
#endif
|
||||
}
|
||||
|
||||
arena_table->initialized = true;
|
||||
arena_free_list->initialized = true;
|
||||
}
|
||||
|
||||
Arena* next_arena (Arena_Reserve reserve_size) {
|
||||
Assert(arena_table != nullptr);
|
||||
Assert(arena_free_list != nullptr);
|
||||
Arena* arena;
|
||||
lock_guard(&arena_table->mutex);
|
||||
lock_guard(&arena_free_list->mutex);
|
||||
s64 reserve_index = (s64)reserve_size;
|
||||
|
||||
if (!arena_table->free_table[reserve_index].count) {
|
||||
if (!arena_free_list->free_table[reserve_index].count) {
|
||||
arena = bootstrap_arena(reserve_size, ARENA_DEFAULT_COMMIT_PAGE_COUNT);
|
||||
} else {
|
||||
arena = pop(arena_table->free_table[reserve_index]);
|
||||
arena = pop(arena_free_list->free_table[reserve_index]);
|
||||
}
|
||||
#if ARENA_DEBUG
|
||||
array_add(arena_table->in_flight[reserve_index], arena);
|
||||
array_add(arena_free_list->in_flight[reserve_index], arena);
|
||||
#endif
|
||||
|
||||
arena_table->in_flight_count[reserve_index] += 1;
|
||||
arena_free_list->in_flight_count[reserve_index] += 1;
|
||||
|
||||
unlock(&arena_table->mutex);
|
||||
unlock(&arena_free_list->mutex);
|
||||
|
||||
Assert(arena != nullptr);
|
||||
return arena;
|
||||
}
|
||||
|
||||
void release_arena (Arena* arena, bool delete_extra_pages) {
|
||||
Assert(arena_table != nullptr);
|
||||
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_table->mutex);
|
||||
lock_guard(&arena_free_list->mutex);
|
||||
s64 reserve_index = (s64)arena->reserve_size;
|
||||
|
||||
#if ARENA_DEBUG
|
||||
array_unordered_remove_by_value(arena_table->in_flight[reserve_index], arena, 1); // BUILD_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_table->free_table[reserve_index], arena);
|
||||
array_add(arena_free_list->free_table[reserve_index], arena);
|
||||
|
||||
arena_table->in_flight_count[reserve_index] -= 1;
|
||||
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;
|
||||
@ -1,3 +1,6 @@
|
||||
// #ArenaTableConfusion I just realized ArenaTable (a hash-table backed by an Arena allocator) may be confused with Arena_Table (which is a table tracking free Arenas)
|
||||
// Solution: rename Arena_Table, to Arena_Free_List.
|
||||
|
||||
// Should ArenaTable be bootstrapped like String_Builder?
|
||||
// see: new_string_builder
|
||||
template <typename T, typename U>
|
||||
|
||||
@ -6,12 +6,11 @@ internal void Bootstrap_Main_Thread_Context () {
|
||||
GPAllocator_Initialize_Allocation_Tracker();
|
||||
|
||||
// 1. Setup arena table
|
||||
arena_table = (Arena_Table*)GPAllocator_New(sizeof(Arena_Table), 64, true); // permanent allocation.
|
||||
memset(arena_table, 0, sizeof(Arena_Table));
|
||||
initialize_arena_table(GPAllocator());
|
||||
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());
|
||||
|
||||
// 2. Setup thread local context
|
||||
// #NewContext
|
||||
// 2. #NewContext Setup thread local context
|
||||
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
|
||||
|
||||
thread_local_context = New<Thread_Context>(allocator(arena_ex));
|
||||
@ -21,6 +20,7 @@ internal void Bootstrap_Main_Thread_Context () {
|
||||
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);
|
||||
|
||||
default_logger_initialize();
|
||||
thread_local_context->logger = {default_logger_proc, &default_logger};
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
struct Thread; // hacky fwd declare
|
||||
// #hacky fwd declares
|
||||
struct Thread;
|
||||
struct Error;
|
||||
struct Graphics;
|
||||
|
||||
struct Thread_Context {
|
||||
@ -21,6 +23,7 @@ struct Thread_Context {
|
||||
Allocator error_allocator = GPAllocator();
|
||||
Error* first_error = nullptr;
|
||||
Error* current_error = nullptr;
|
||||
Arena* error_arena;
|
||||
|
||||
// Graphics stuff:
|
||||
Graphics* graphics;
|
||||
@ -30,6 +33,7 @@ struct Thread_Context {
|
||||
thread_static Thread_Context* thread_local_context;
|
||||
|
||||
// #TODO #NewContext void create_thread_context (Thread_Context** context, string thread_name, bool is_main_thread);
|
||||
// Thread-context #Errors:
|
||||
|
||||
internal void Bootstrap_Main_Thread_Context ();
|
||||
|
||||
|
||||
@ -15,26 +15,154 @@ struct Error {
|
||||
ErrorClass severity = ErrorClass::NONE;
|
||||
s32 thread_id;
|
||||
s32 source_line;
|
||||
s32 path_length;
|
||||
char* path;
|
||||
string file_path;
|
||||
string function_name;
|
||||
|
||||
// Linked list to errors
|
||||
Error* previous_error; // if we're passing errors up the callstack.
|
||||
Error* next_error;
|
||||
|
||||
// Arena* arena;
|
||||
|
||||
// Error () { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// string create_error(ErrorClass severity, string fmt, ...) {
|
||||
|
||||
// }
|
||||
|
||||
string to_string (Error error) {
|
||||
return { error.count, error.data };
|
||||
char* error_severity (ErrorClass severity) {
|
||||
switch (severity) {
|
||||
case ErrorClass::NONE: {
|
||||
} break;
|
||||
case ErrorClass::WARNING: {
|
||||
return "[WARNING]";
|
||||
} break;
|
||||
case ErrorClass::ERROR: {
|
||||
return "[ERROR]";
|
||||
} break;
|
||||
case ErrorClass::FATAL: {
|
||||
return "[FATAL ERROR]";
|
||||
} break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void push_error (Error* new_error);
|
||||
|
||||
string to_string (Error* error) {
|
||||
return { error->count, error->data };
|
||||
}
|
||||
|
||||
#define log_fatal_error(fmt, ...) \
|
||||
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::FATAL, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define log_error(fmt, ...) \
|
||||
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::ERROR, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define log_warning(fmt, ...) \
|
||||
Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::WARNING, fmt, ##__VA_ARGS__)
|
||||
|
||||
Error* new_error (ErrorClass severity, string error_string) {
|
||||
Error* error = New<Error>();
|
||||
error->count = error_string.count;
|
||||
error->data = error_string.data;
|
||||
error->severity = severity;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// #TODO: // void OS_Log_Error_With_Code // OS-SPECIFIC CALLING GetLastError, etc.
|
||||
// Then remove all instances of log_error_code_and_string()
|
||||
//
|
||||
|
||||
void Log_Error_2 (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) {
|
||||
Assert(thread_context() != nullptr);
|
||||
push_arena(thread_context()->error_arena);
|
||||
|
||||
String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K);
|
||||
// #TODO: prepend severity, other information
|
||||
print_to_builder(sb, "%s ", error_severity(severity));
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
print_to_builder_internal(sb, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
append(sb, "\n");
|
||||
|
||||
string error_string = builder_to_string(sb);
|
||||
Error* error = new_error(severity, error_string);
|
||||
// Additional information
|
||||
error->thread_id = thread_context()->thread_idx;
|
||||
error->source_line = line_number;
|
||||
error->file_path = copy_string(file_path);
|
||||
error->function_name = copy_string(function_name);
|
||||
// Note: we don't need to assign previous_error or next_error, as that is done by the thread_context when we #push_error
|
||||
error->previous_error = nullptr;
|
||||
error->next_error = nullptr;
|
||||
|
||||
push_error(error);
|
||||
}
|
||||
|
||||
void push_error (Error* new_error) {
|
||||
Assert(new_error != nullptr);
|
||||
if (new_error == nullptr) return;
|
||||
Error* current_error = thread_context()->current_error;
|
||||
if (current_error) {
|
||||
current_error->next_error = new_error;
|
||||
new_error->previous_error = current_error;
|
||||
} else {
|
||||
thread_context()->first_error = new_error;
|
||||
new_error->previous_error = nullptr;
|
||||
}
|
||||
thread_context()->current_error = new_error;
|
||||
|
||||
switch (new_error->severity) {
|
||||
case ErrorClass::NONE:
|
||||
case ErrorClass::WARNING: {
|
||||
print(to_string(new_error));
|
||||
} break;
|
||||
case ErrorClass::ERROR:
|
||||
case ErrorClass::FATAL: {
|
||||
print_error(to_string(new_error));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_errors () { // Reset pointers and reset error_arena
|
||||
arena_reset(thread_context()->error_arena); // maybe overwrite memory?
|
||||
thread_context()->first_error = nullptr;
|
||||
thread_context()->current_error = nullptr;
|
||||
}
|
||||
|
||||
void clear_error (Error* error) {
|
||||
// If we want to clear a specific error (simply remove from the list)
|
||||
Assert(error != nullptr);
|
||||
bool is_current_error = (thread_context()->current_error == error);
|
||||
bool is_first_error = (thread_context()->first_error == error);
|
||||
|
||||
Error* current_error = thread_context()->first_error;
|
||||
if (current_error == nullptr) return; // no errors in linked list.
|
||||
|
||||
while (current_error != error && current_error != nullptr) {
|
||||
current_error = current_error->next_error;
|
||||
}
|
||||
Assert(current_error != nullptr);
|
||||
if (current_error == nullptr) return; // shouldn't happen
|
||||
Error* the_previous_error = current_error->previous_error;
|
||||
Error* the_next_error = current_error->next_error;
|
||||
// Remove current_node from linked list:
|
||||
the_previous_error->next_error = the_next_error;
|
||||
the_next_error->previous_error = the_previous_error;
|
||||
if (is_first_error && is_current_error) { // It matches the only item in the list, just empty the list:
|
||||
clear_errors();
|
||||
return;
|
||||
}
|
||||
if (is_first_error) {
|
||||
// the_next_error becomes new first error.
|
||||
thread_context()->first_error = the_next_error;
|
||||
}
|
||||
if (is_current_error) {
|
||||
// the current_error becomes the previous error
|
||||
thread_context()->current_error = the_previous_error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Will need to use __FILE__ and __LINE__ macros
|
||||
|
||||
// Error* new_error (string error_message, ErrorClass severity, Error* previous_error=nullptr);
|
||||
|
||||
@ -46,7 +46,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_table->initialized);
|
||||
Assert(arena_free_list->initialized);
|
||||
|
||||
Arena* arena = (Arena*)arena_ex->current;
|
||||
|
||||
|
||||
@ -1,32 +1,9 @@
|
||||
void log_error (string fmt, ...) {
|
||||
String_Builder* sb = thread_context()->log_builder;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
print_to_builder(sb, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// Append newline if needed
|
||||
string result = string_view(sb);
|
||||
bool ends_with_newline = (result.data[result.count-1] == '\n');
|
||||
if (!ends_with_newline) {
|
||||
append(sb, "\n");
|
||||
}
|
||||
|
||||
string message = string_view(sb);
|
||||
|
||||
Logger* logger = context_logger();
|
||||
logger->proc(message, Log_Level::Error, logger->data);
|
||||
|
||||
reset_string_builder(sb, true);
|
||||
}
|
||||
|
||||
void log (string fmt, ...) {
|
||||
String_Builder* sb = thread_context()->log_builder;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
print_to_builder(sb, fmt, args);
|
||||
print_to_builder_internal(sb, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// Append newline if needed
|
||||
@ -48,3 +25,8 @@ void print (string message) {
|
||||
Logger* logger = context_logger();
|
||||
logger->proc(message, Log_Level::None, logger->data);
|
||||
}
|
||||
|
||||
void print_error (string error_message) {
|
||||
Logger* logger = context_logger();
|
||||
logger->proc(error_message, Log_Level::Error, logger->data);
|
||||
}
|
||||
@ -65,7 +65,6 @@ void default_logger_initialize() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void log_error (string fmt, ...);
|
||||
void log (string fmt, ...);
|
||||
void print (string message);
|
||||
|
||||
void print_error (string error_message);
|
||||
|
||||
@ -37,18 +37,18 @@ template <typename T> force_inline void AddArray (Serializer* serializer, ArrayV
|
||||
AddArray_NoSize(serializer, view);
|
||||
}
|
||||
|
||||
template <typename T> force_inline void AddString (Serializer* serializer, string s) {
|
||||
force_inline void AddString (Serializer* serializer, string s) {
|
||||
Add(serializer, s.count);
|
||||
AddArray_NoSize(serializer, to_view(s));
|
||||
}
|
||||
|
||||
template <typename T> force_inline void AddString32 (Serializer* serializer, string s) {
|
||||
force_inline void AddString32 (Serializer* serializer, string s) {
|
||||
u32 string_length = (u32)s.count;
|
||||
Add(serializer, string_length);
|
||||
AddArray_NoSize(serializer, to_view(s));
|
||||
}
|
||||
|
||||
template <typename T> force_inline void AddString16 (Serializer* serializer, string s) {
|
||||
force_inline void AddString16 (Serializer* serializer, string s) {
|
||||
u16 string_length = (u16)s.count;
|
||||
Add(serializer, string_length);
|
||||
AddArray_NoSize(serializer, to_view(s));
|
||||
|
||||
@ -199,7 +199,7 @@ force_inline void append_no_add (String_Builder* sb, string s) {
|
||||
|
||||
// Unfortunately this follows the printf format, which is annoying.
|
||||
// I'd rather have something like fmt::
|
||||
void print_to_builder (String_Builder* sb, string format, va_list args) {
|
||||
void print_to_builder_internal (String_Builder* sb, string format, va_list args) {
|
||||
s64 expected_final_count = max_array_size(*sb);// amount to reserve
|
||||
|
||||
if (sb->allocated < expected_final_count) {
|
||||
|
||||
@ -117,9 +117,10 @@ 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 (String_Builder* sb, string format, va_list args);
|
||||
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);
|
||||
|
||||
force_inline string builder_to_string (String_Builder* sb); // returns string view
|
||||
// #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);
|
||||
|
||||
@ -53,7 +53,7 @@ struct Worker_Info {
|
||||
u8 padding1[48];
|
||||
};
|
||||
static_assert(sizeof(Worker_Info) % 64 == 0); // This MUST be padded to cache line!
|
||||
enum class Thread_Continue_Status : s32 {
|
||||
enum class Thread_Continue_Status: s32 {
|
||||
THREAD_STOP = 0,
|
||||
THREAD_CONTINUE = 1
|
||||
};
|
||||
|
||||
@ -6,6 +6,10 @@ void run_pre_setup_tests() {
|
||||
|
||||
string string_literal_example = "Hello, I am a string literal.";
|
||||
void run_post_setup_tests() {
|
||||
debug_break();
|
||||
log_warning("Hello, I am a warning :), and I am a big number: %lld", (s64)293509357);
|
||||
log_error("Hello, I am an error - %s", "I'm another string");
|
||||
log_error("This is an error");
|
||||
printf("Running post-setup tests...\n");
|
||||
// See: main_thread_base_entry_point
|
||||
{ Timed_Block_Print("string_builder_testing");
|
||||
|
||||
87
lib/OS/OS_Filesystem.cpp
Normal file
87
lib/OS/OS_Filesystem.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
// So NTFS (and most systems) sort the tree by default in lexicographical descending order.
|
||||
// For lookups, this often isn't that useful if you only know substrings of the path.
|
||||
|
||||
|
||||
// struct BTNode {
|
||||
// u16 key_count;
|
||||
// BTNode* keys;
|
||||
// u16 allocated;
|
||||
// };
|
||||
|
||||
// struct B_Tree {
|
||||
// BTNode* root;
|
||||
// Allocator allocator;
|
||||
// };
|
||||
|
||||
// A compact collection of data with sorting indices
|
||||
// Maybe we can make B+ trees for sorting according to
|
||||
// size and modtime.
|
||||
|
||||
// It really doesn't make sense to store data in memory as a B-tree except
|
||||
// if we need ordered insertions and deletes.
|
||||
//
|
||||
|
||||
// Returns offset
|
||||
force_inline s32 AddString_NoCount (Serializer* serializer, u8* data, u8 count) { // #TODO: , bool null_terminate=false
|
||||
u8* current_point = &serializer->data[serializer->count];
|
||||
s64 final_count = serializer->allocated + (count * sizeof(u8));
|
||||
|
||||
if (serializer->allocated < final_count) {
|
||||
array_reserve(*serializer, final_count);
|
||||
}
|
||||
|
||||
memcpy(current_point, data, count * sizeof(u8));
|
||||
serializer->count += count * sizeof(u8);
|
||||
}
|
||||
|
||||
constexpr s64 DFS_Preallocation_Count = 4194304; // 2^22
|
||||
|
||||
// template <typename Length_Type>
|
||||
struct DFS_Array {
|
||||
Serializer* strings;
|
||||
|
||||
ArenaArray<u32>* offsets; // offsets into string_arena
|
||||
ArenaArray<u8>* lengths; // this type may vary <hmmm> Not sure if I should make it a template argument. Seems yucky.
|
||||
ArenaArray<u64>* modtimes;
|
||||
ArenaArray<u64>* sizes;
|
||||
ArenaArray<s32>* parent_indices;
|
||||
|
||||
s64 index; // current index when inserting;
|
||||
|
||||
// #Temporary arrays for linking???
|
||||
ArenaArray<u32>* record_ids;
|
||||
ArenaArray<u32>* parent_ids;
|
||||
|
||||
// #TODO: Sort indices (should these be trees?)
|
||||
// ArenaArray<s32> indices_sorted_by_modtime;
|
||||
// ArenaArray<s32> indices_sorted_by_size;
|
||||
};
|
||||
|
||||
void initialize (DFS_Array* dfsa) {
|
||||
Assert(dfsa != nullptr);
|
||||
dfsa->strings = new_serializer(Arena_Reserve::Size_2G);
|
||||
|
||||
dfsa->offsets = arena_array_new<u32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
|
||||
dfsa->lengths = arena_array_new<u8> (DFS_Preallocation_Count, Arena_Reserve::Size_2G);
|
||||
dfsa->modtimes = arena_array_new<u64>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
|
||||
dfsa->sizes = arena_array_new<u64>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
|
||||
dfsa->parent_indices = arena_array_new<s32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
|
||||
|
||||
dfsa->index = 0;
|
||||
}
|
||||
|
||||
struct Dense_FS { // Link to OS_Drive
|
||||
OS_Drive* drive; // backlink for reference.
|
||||
DFS_Array paths;
|
||||
DFS_Array files;
|
||||
|
||||
ArenaTable<u32, s32> path_table; // <entry_id, array_offset>.
|
||||
};
|
||||
|
||||
void initialize (Dense_FS* dfs, OS_Drive* drive) {
|
||||
Assert(drive != nullptr);
|
||||
dfs->drive = drive;
|
||||
initialize(&dfs->paths);
|
||||
initialize(&dfs->files);
|
||||
table_init(&dfs->path_table, DFS_Preallocation_Count);
|
||||
}
|
||||
@ -222,9 +222,10 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
|
||||
}
|
||||
|
||||
s64 this_thread_index = InterlockedIncrement(&next_thread_index);
|
||||
// #NewContext
|
||||
// 2. #NewContext Setup NEW thread local context
|
||||
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
|
||||
|
||||
// #NOTE: we don't assign thread_local_context until we hit the #thread_entry_point
|
||||
thread->context = New<Thread_Context>(allocator(arena_ex));
|
||||
thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
|
||||
thread->context->arena = arena_ex;
|
||||
@ -232,6 +233,7 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
|
||||
thread->context->thread_idx = (s32)this_thread_index;
|
||||
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->os_thread.windows_thread = windows_thread;
|
||||
thread->os_thread.windows_thread_id = windows_thread_id;
|
||||
@ -338,7 +340,7 @@ internal string get_error_string (OS_Error_Code error_code) {
|
||||
return result; // trim_right(result, "\r\n");
|
||||
}
|
||||
|
||||
internal void log_error_code_and_string () {
|
||||
internal void log_error_code_and_string () { // #TODO: replace with call to log_error_with_code
|
||||
OS_Error_Code error_code = GetLastError();
|
||||
log_error(" > GetLastError code: %d, %s\n", error_code, get_error_string(error_code).data);
|
||||
}
|
||||
@ -372,8 +374,9 @@ internal File file_open (string file_path, bool for_writing, bool keep_existing_
|
||||
}
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE && log_errors) {
|
||||
OS_Error_Code error_code = GetLastError();
|
||||
log_error("Could not open file `%s`, code: %d, %s", file_path, error_code, get_error_string(error_code).data);
|
||||
// OS_Error_Code error_code = GetLastError();
|
||||
log_error("Could not open file `%s`", file_path);
|
||||
log_error_code_and_string();
|
||||
}
|
||||
|
||||
File file;
|
||||
@ -390,6 +393,7 @@ internal bool file_read (File file, u8* data, s64 bytes_to_read_count, s64* byte
|
||||
// ignore bytes_read_count if null.
|
||||
if (data == nullptr) {
|
||||
log_error("file_read called with null destination pointer.\n");
|
||||
log_error_code_and_string();
|
||||
if (bytes_read_count) (*bytes_read_count) = 0;
|
||||
return false;
|
||||
}
|
||||
@ -574,7 +578,7 @@ internal void os_enumerate_monitors () {
|
||||
if (!global_win32_state.system_info.monitors_enumerated) {
|
||||
// should reset array?
|
||||
if (!EnumDisplayMonitors(nullptr, nullptr, monitor_enum_proc, 0)) {
|
||||
log_error("Failed to enumerate monitors\n");
|
||||
log_fatal_error("Failed to enumerate monitors\n");
|
||||
Assert(false); // Failed to enumerate monitors
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
f64 GetUnixTimestamp ();
|
||||
s64 GetUnixTimestampNanoseconds ();
|
||||
|
||||
enum class Wait_For_Result : s32 {
|
||||
enum class Wait_For_Result: s32 {
|
||||
SUCCESS = 0,
|
||||
TIMEOUT = 1,
|
||||
ERROR = 2 // can't use ERROR because of Windows.h *sigh*
|
||||
@ -165,7 +165,7 @@ File_System Win32_filesystem_from_string (string s) {
|
||||
Assert(false);
|
||||
return File_System::Unknown;
|
||||
}
|
||||
|
||||
struct Dense_FS; // #Temp forward declare!
|
||||
struct Win32_Drive {
|
||||
string label;
|
||||
string volume_name;
|
||||
@ -180,8 +180,11 @@ struct Win32_Drive {
|
||||
// Not sure if this should be here...
|
||||
// f32 enumeration_time;
|
||||
// f64 last_seen_alive_timestamp;
|
||||
Dense_FS* data;
|
||||
};
|
||||
|
||||
typedef Win32_Drive OS_Drive;
|
||||
|
||||
bool os_window_is_minimized (Window_Type window);
|
||||
|
||||
bool os_main_window_is_minimized ();
|
||||
|
||||
@ -141,38 +141,31 @@ bool NTFS_read_internal (NTFS_MFT_Internal* mft, void* buffer, u64 from, u64 cou
|
||||
return bytes_accessed_internal == count;
|
||||
}
|
||||
|
||||
enum class NTFS_Block_Flag : s32 {
|
||||
idk = 0,
|
||||
idk2 = 1,
|
||||
};
|
||||
|
||||
struct NTFS_MFT_Copy {
|
||||
u32 error_code;
|
||||
// Array<ArrayView<u8>> mft_blocks; // idk if this is correct!
|
||||
// Array<NTFS_Block_Flag> mft_flags;
|
||||
};
|
||||
|
||||
// #TODO: make a version where we just reconstruct the MFT as a deque in memory.
|
||||
NTFS_MFT_Copy* NTFS_MFT_read_raw (string drive_path) {
|
||||
// #TODO: Release resources if we face an early return!
|
||||
Dense_FS* NTFS_MFT_read_raw (string drive_path) {
|
||||
Table<string, Win32_Drive>* drive_table = &global_win32_state.system_info.drives;
|
||||
// We should be able to fetch from the table using table_find_pointer
|
||||
bool just_added = false;
|
||||
Win32_Drive* drive = table_find_or_add(drive_table, drive_path, &just_added);
|
||||
Assert(just_added == false && drive != nullptr);
|
||||
if (drive == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Assert(context_allocator() != temp()); // pointless as we're releasing temp end-of-scope
|
||||
Allocator primary_allocator = context_allocator();
|
||||
|
||||
auto_release_temp();
|
||||
push_allocator(temp());
|
||||
|
||||
NTFS_MFT_Copy* mft_copy = New<NTFS_MFT_Copy>(primary_allocator); //use context allocator.
|
||||
|
||||
string drive_letter = Win32_drive_letter(drive_path);
|
||||
|
||||
string create_file_target = format_string("\\\\.\\%s:", drive_letter.data);
|
||||
HANDLE file_handle = CreateFileA((LPCSTR)create_file_target.data, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
log_error("CreateFileA failed on target %s", create_file_target.data);
|
||||
log_error_code_and_string();
|
||||
mft_copy->error_code = GetLastError();
|
||||
return mft_copy;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
push_allocator(primary_allocator);
|
||||
@ -271,11 +264,11 @@ NTFS_MFT_Copy* NTFS_MFT_read_raw (string drive_path) {
|
||||
NTFS_FileNameAttributeHeader* fileNameAttribute = (NTFS_FileNameAttributeHeader*)attribute;
|
||||
|
||||
if (fileNameAttribute->namespaceType != 2 && !fileNameAttribute->nonResident) {
|
||||
file.parent_id = (u32)fileNameAttribute->parentRecordNumber; // truncate
|
||||
file.record_id = fileRecord->recordNumber;
|
||||
file.name_count = fileNameAttribute->fileNameLength;
|
||||
file.name_data = (u16*)fileNameAttribute->fileName;
|
||||
file.name_utf8 = wide_to_utf8(file.name_data, file.name_count);
|
||||
file.parent_id = (u32)fileNameAttribute->parentRecordNumber; // truncate
|
||||
file.record_id = fileRecord->recordNumber;
|
||||
file.name_count = fileNameAttribute->fileNameLength;
|
||||
file.name_data = (u16*)fileNameAttribute->fileName;
|
||||
file.name_utf8 = wide_to_utf8(file.name_data, file.name_count);
|
||||
file.file_modtime = (u64)fileNameAttribute->modificationTime;
|
||||
file.is_directory = fileRecord->isDirectory;
|
||||
// We need to get size from the data attribute
|
||||
|
||||
@ -31,15 +31,15 @@
|
||||
#include "lib/Base/Serializer.h"
|
||||
#include "lib/Base/Serializer.cpp"
|
||||
|
||||
#include "lib/Base/ErrorType.cpp"
|
||||
#include "lib/Base/Base_Thread_Context.h"
|
||||
#include "lib/Base/Expandable_Arena.h"
|
||||
#include "lib/Base/Timing.h"
|
||||
#include "lib/Base/Arena_Table.cpp"
|
||||
#include "lib/Base/Arena_Free_List.cpp"
|
||||
|
||||
#include "lib/Base/Arena.cpp"
|
||||
|
||||
#include "lib/Base/Base_Thread_Context.cpp"
|
||||
#include "lib/Base/ErrorType.cpp"
|
||||
#include "lib/Base/Logger.cpp"
|
||||
#include "lib/Base/Expandable_Arena.cpp"
|
||||
#include "lib/Base/Allocator.cpp"
|
||||
@ -47,6 +47,7 @@
|
||||
|
||||
// OS-Abstraction Layer
|
||||
#include "lib/Base/Threads.cpp"
|
||||
#include "lib/OS/OS_Filesystem.cpp"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "lib/OS/OS_Win32.cpp"
|
||||
|
||||
@ -26,7 +26,7 @@ void Ex1_Unregister_Global_Hotkeys () {
|
||||
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-Alt-Space");
|
||||
log_error("Failed to register global hotkey Ctrl-Shift-Alt-Space");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -371,8 +371,8 @@ void ImGui_Debug_Panel () {
|
||||
if (ImGui::Button(button_label)) {
|
||||
push_arena(thread_context()->arena);
|
||||
// auto_release(thread_context()->arena);
|
||||
auto result = NTFS_MFT_read_raw(entry->value.label);
|
||||
if (result->error_code != 0) {
|
||||
auto dfs = NTFS_MFT_read_raw(entry->value.label);
|
||||
if (dfs == nullptr) {
|
||||
log("[NTFS_MFT_read_raw] operation failed");
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,9 +20,9 @@ void Explorer_ImGui_Application_Win32 () {
|
||||
HWND hwnd = get_main_window().window;
|
||||
|
||||
success = Win32_Load_Main_Window_Icon("tmp.ico");
|
||||
if (!success) { log_error("Failed to load tmp.ico"); }
|
||||
if (!success) { log_warning("Failed to load tmp.ico"); }
|
||||
success = Win32_Load_Main_Window_Icon_Minimized("tmp_min.ico");
|
||||
if (!success) { log_error("Failed to load tmp_min.ico"); }
|
||||
if (!success) { log_warning("Failed to load tmp_min.ico"); }
|
||||
|
||||
// :Win32_Initialize_Extended
|
||||
Win32_Set_Main_Icon();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user