Implement RadixSort and sort by modification time and size.
This commit is contained in:
parent
d1d775075d
commit
bd8729e8ae
@ -254,3 +254,59 @@ struct Auto_Release {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #FixedArena procedures:
|
||||||
|
FixedArena* bootstrap_fixed_arena (s64 size, Allocator backing_allocator) {
|
||||||
|
push_allocator(backing_allocator);
|
||||||
|
Assert(size >= sizeof(FixedArena));
|
||||||
|
ArrayView<u8> memory = ArrayView<u8>(size);
|
||||||
|
FixedArena* result = (FixedArena*)memory.data;
|
||||||
|
result->memory = memory;
|
||||||
|
result->cursor = sizeof(FixedArena);
|
||||||
|
result->allocator = backing_allocator;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
force_inline void destroy_arena (FixedArena* arena) {
|
||||||
|
Delete(arena->allocator, arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
Allocator allocator (FixedArena* arena) {
|
||||||
|
return { fixed_arena_allocator_proc, arena };
|
||||||
|
}
|
||||||
|
|
||||||
|
void* fixed_arena_allocator_proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
|
||||||
|
constexpr s64 DEFAULT_ALIGNMENT = 16; // maybe make this modifiable as part of FixedArena struct?
|
||||||
|
FixedArena* arena = (FixedArena*)allocator_data;
|
||||||
|
Assert(arena != nullptr);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case Allocator_Mode::ALLOCATE: {
|
||||||
|
arena->cursor = Align<s64>(arena->cursor, DEFAULT_ALIGNMENT);
|
||||||
|
void* result = &arena->memory[arena->cursor];
|
||||||
|
arena->cursor += requested_size;
|
||||||
|
Assert(arena->cursor <= arena->memory.count);
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
case Allocator_Mode::RESIZE: {
|
||||||
|
arena->cursor = Align<s64>(arena->cursor, DEFAULT_ALIGNMENT);
|
||||||
|
void* result = &arena->memory[arena->cursor];
|
||||||
|
arena->cursor += requested_size;
|
||||||
|
Assert(arena->cursor <= arena->memory.count);
|
||||||
|
s64 size_to_copy = old_size < requested_size ? old_size : requested_size;
|
||||||
|
if (result && size_to_copy) { memcpy(result, old_memory, size_to_copy); }
|
||||||
|
return result;
|
||||||
|
} break;
|
||||||
|
case Allocator_Mode::DEALLOCATE: {
|
||||||
|
return nullptr; // unused
|
||||||
|
} break;
|
||||||
|
case Allocator_Mode::DETAILS: {
|
||||||
|
if (allocator_data == nullptr) {
|
||||||
|
return "fixed_arena_allocator_proc: data pointer is null!";
|
||||||
|
}
|
||||||
|
return "fixed_arena_allocator_proc: with valid data";
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
@ -139,3 +139,19 @@ struct Push_Alignment { // #rename to Arena_Push_Alignment?
|
|||||||
// Do this later:
|
// Do this later:
|
||||||
// arena_lock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|
// arena_lock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|
||||||
// arena_unlock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|
// arena_unlock_pages :: (using arena: *Arena, start_address: *u8, byte_count: s64)
|
||||||
|
|
||||||
|
// #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()).
|
||||||
|
struct FixedArena {
|
||||||
|
ArrayView<u8> memory;
|
||||||
|
s64 cursor;
|
||||||
|
Allocator allocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
force_inline void destroy_arena (FixedArena* arena);
|
||||||
|
Allocator allocator (FixedArena* arena);
|
||||||
|
|||||||
@ -107,6 +107,7 @@ Allocator allocator (ExpandableArena* arena_ex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #TODO: currently this keeps the final arena's memory. Fix this!
|
// #TODO: currently this keeps the final arena's memory. Fix this!
|
||||||
|
// This is not implemented correctly!
|
||||||
void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point) {
|
void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point) {
|
||||||
// going backwards from end of arena list
|
// going backwards from end of arena list
|
||||||
|
|
||||||
|
|||||||
128
lib/Base/RadixSort.cpp
Normal file
128
lib/Base/RadixSort.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
struct RadixSort {
|
||||||
|
ArrayView<u32> ranks;
|
||||||
|
ArrayView<u32> ranks2;
|
||||||
|
Allocator allocator;
|
||||||
|
bool valid_ranks;
|
||||||
|
};
|
||||||
|
|
||||||
|
void radix_sort_init (RadixSort* r, u32 items_to_allocate) {
|
||||||
|
if (r->allocator.proc == nullptr) {
|
||||||
|
r->allocator = context_allocator();
|
||||||
|
}
|
||||||
|
push_allocator(r->allocator);
|
||||||
|
r->ranks = ArrayView<u32>(items_to_allocate);
|
||||||
|
r->ranks2 = ArrayView<u32>(items_to_allocate);
|
||||||
|
r->valid_ranks = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void radix_sort_free (RadixSort* r) {
|
||||||
|
Assert(r->allocator.proc != nullptr);
|
||||||
|
push_allocator(r->allocator);
|
||||||
|
array_free(r->ranks);
|
||||||
|
array_free(r->ranks2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RadixSort provides an array of indices in sorted order.
|
||||||
|
u32 rank (RadixSort* r, s64 i) {
|
||||||
|
Assert(r != nullptr);
|
||||||
|
#if ARRAY_ENABLE_BOUNDS_CHECKING
|
||||||
|
if (i < 0 || i >= r->ranks.count) { debug_break(); /*INDEX OOB*/ }
|
||||||
|
#endif
|
||||||
|
return r->ranks[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void create_histograms (RadixSort* r, T* buffer, u32 count, u32* histogram) {
|
||||||
|
constexpr u32 bucket_count = sizeof(T);
|
||||||
|
// Init bucket pointers:
|
||||||
|
u32* h[bucket_count] = {};
|
||||||
|
for (u32 i = 0; i < bucket_count; i += 1) {
|
||||||
|
h[i] = histogram + (256 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build histogram:
|
||||||
|
u8* p = (u8*)buffer;
|
||||||
|
u8* pe = (p + count * sizeof(T));
|
||||||
|
|
||||||
|
while (p != pe) {
|
||||||
|
h[0][*p] += 1; p += 1;
|
||||||
|
if (bucket_count > 1) { // how to make compile time if?
|
||||||
|
h[1][*p] += 1; p += 1;
|
||||||
|
|
||||||
|
if (bucket_count > 2) {
|
||||||
|
h[2][*p] += 1; p += 1;
|
||||||
|
h[3][*p] += 1; p += 1;
|
||||||
|
|
||||||
|
if (bucket_count == 8) {
|
||||||
|
h[4][*p] += 1; p += 1;
|
||||||
|
h[5][*p] += 1; p += 1;
|
||||||
|
h[6][*p] += 1; p += 1;
|
||||||
|
h[7][*p] += 1; p += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void radix_sort (RadixSort* r, T* input, u32 count) {
|
||||||
|
constexpr u32 T_SIZE = sizeof(T);
|
||||||
|
// Allocate histograms & offsets on the stack:
|
||||||
|
u32 histogram [256 * T_SIZE] = {};
|
||||||
|
u32* link [256];
|
||||||
|
|
||||||
|
create_histograms(r, input, count, histogram);
|
||||||
|
|
||||||
|
// Radix sort, j is the pass number, (0 = LSB, P = MSB)
|
||||||
|
for (u32 j = 0; j < T_SIZE; j += 1) {
|
||||||
|
u32* h = &histogram[j * 256];
|
||||||
|
|
||||||
|
u8* input_bytes = (u8*)input;
|
||||||
|
input_bytes += j; // Assumes little endian!
|
||||||
|
|
||||||
|
if (h[input_bytes[0]] == count) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create offsets
|
||||||
|
link[0] = r->ranks2.data;
|
||||||
|
for (u32 i = 1; i < 256; i += 1) { // 1..255
|
||||||
|
link[i] = link[i-1] + h[i-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform Radix Sort
|
||||||
|
if (!r->valid_ranks) {
|
||||||
|
for (u32 i = 0; i < count; i += 1) {
|
||||||
|
*link[input_bytes[i*T_SIZE]] = i;
|
||||||
|
link[input_bytes[i*T_SIZE]] += 1;
|
||||||
|
}
|
||||||
|
r->valid_ranks = true;
|
||||||
|
} else {
|
||||||
|
for (u32 i = 0; i < count; i += 1) {
|
||||||
|
u32 idx = r->ranks[i];
|
||||||
|
*link[input_bytes[idx*T_SIZE]] = idx;
|
||||||
|
link[input_bytes[idx*T_SIZE]] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap pointers for next pass. Valid indices - the most recent ones - are in ranks after the swap.
|
||||||
|
ArrayView<u32> ranks2_temp = r->ranks2;
|
||||||
|
r->ranks2 = r->ranks;
|
||||||
|
r->ranks = ranks2_temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All values were equal; generate linear ranks
|
||||||
|
if (!r->valid_ranks) {
|
||||||
|
for (u32 i = 0; i < count; i += 1) {
|
||||||
|
r->ranks[i] = i;
|
||||||
|
r->valid_ranks = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: For a small number of elements it's more efficient to use insertion sort
|
||||||
|
void radix_sort_u64 (RadixSort* r, u64* input, u32 count) {
|
||||||
|
if (input == nullptr || count == 0) return;
|
||||||
|
if (r->ranks.count == 0) {
|
||||||
|
radix_sort_init(r, count);
|
||||||
|
}
|
||||||
|
radix_sort(r, input, count);
|
||||||
|
}
|
||||||
@ -1214,6 +1214,16 @@ string get_file_copy (ST_File_Enumeration* stfe, s64 index) {
|
|||||||
string file = {strlength, string_ptr};
|
string file = {strlength, string_ptr};
|
||||||
return copy_string(file);
|
return copy_string(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string get_file_string_view (ST_File_Enumeration* stfe, s64 index) {
|
||||||
|
STFE_Results* r = &stfe->files;
|
||||||
|
s64 strlength = (*r->lengths)[index];
|
||||||
|
u32 offset = (*r->offsets)[index];
|
||||||
|
u8* string_ptr = &r->strings->data[offset];
|
||||||
|
string file = {strlength, string_ptr};
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
string get_path_copy (ST_File_Enumeration* stfe, s64 index) {
|
string get_path_copy (ST_File_Enumeration* stfe, s64 index) {
|
||||||
STFE_Results* r = &stfe->dirs;
|
STFE_Results* r = &stfe->dirs;
|
||||||
Assert(index >= 0 && index < count_paths(stfe));
|
Assert(index >= 0 && index < count_paths(stfe));
|
||||||
@ -1261,6 +1271,7 @@ s64 win32_file_enum_thread_proc (Thread* thread) {
|
|||||||
push_allocator(temp());
|
push_allocator(temp());
|
||||||
auto_release_temp();
|
auto_release_temp();
|
||||||
// This needs to be null-terminated:
|
// This needs to be null-terminated:
|
||||||
|
// #TODO: Replace this #LIFO array with an arena-backed FIFO stack (singly linked-list).
|
||||||
string next_directory = copy_string(pop(paths_to_enumerate)); // LIFO. maybe not the best way?
|
string next_directory = copy_string(pop(paths_to_enumerate)); // LIFO. maybe not the best way?
|
||||||
wstring wildcard_name = utf8_to_wide(format_string("%s\\*", next_directory.data));
|
wstring wildcard_name = utf8_to_wide(format_string("%s\\*", next_directory.data));
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include "lib/Base/Arena.cpp"
|
#include "lib/Base/Arena.cpp"
|
||||||
#include "lib/Base/String.cpp"
|
#include "lib/Base/String.cpp"
|
||||||
|
#include "lib/Base/RadixSort.cpp"
|
||||||
|
|
||||||
#include "lib/Base/Base_Thread_Context.cpp"
|
#include "lib/Base/Base_Thread_Context.cpp"
|
||||||
#include "lib/Base/ErrorType.cpp"
|
#include "lib/Base/ErrorType.cpp"
|
||||||
|
|||||||
71
src/Ex1.cpp
71
src/Ex1.cpp
@ -124,15 +124,37 @@ void Ex1_show_ntfs_workspace () { using namespace ImGui;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #Workspaces are FOR DEVELOPMENT ONLY.
|
||||||
struct Ex1_Workspace {
|
struct Ex1_Workspace {
|
||||||
s32 path_select;
|
s32 path_select;
|
||||||
s32 file_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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void reorder_files_by_radix (RadixSort* r, ArrayView<string>* files, bool reverse_order=false) {
|
||||||
|
Timed_Block_Print("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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
global Ex1_Workspace ex1w;
|
global Ex1_Workspace ex1w;
|
||||||
|
|
||||||
void Ex1_show_enumeration_workspace () { using namespace ImGui;
|
void Ex1_show_enumeration_workspace () { using namespace ImGui;
|
||||||
push_imgui_window("Enumerated Data Workspace");
|
push_imgui_window("Enumerated Data Workspace");
|
||||||
// #TODO SliderInt for each
|
|
||||||
|
/*
|
||||||
SliderInt("Select path index", &ex1w.path_select, 0, count_paths(stfe)-1);
|
SliderInt("Select path index", &ex1w.path_select, 0, count_paths(stfe)-1);
|
||||||
Text("%s", get_path_copy(stfe, ex1w.path_select).data);
|
Text("%s", get_path_copy(stfe, ex1w.path_select).data);
|
||||||
Text("time modified: %s", format_time_datetime(get_path_modtime(stfe, ex1w.path_select)).data);
|
Text("time modified: %s", format_time_datetime(get_path_modtime(stfe, ex1w.path_select)).data);
|
||||||
@ -141,7 +163,44 @@ void Ex1_show_enumeration_workspace () { using namespace ImGui;
|
|||||||
Text("%s", get_file_copy(stfe, ex1w.file_select).data);
|
Text("%s", get_file_copy(stfe, ex1w.file_select).data);
|
||||||
Text("size: %s", format_bytes(get_file_size_bytes(stfe, ex1w.file_select)).data);
|
Text("size: %s", format_bytes(get_file_size_bytes(stfe, ex1w.file_select)).data);
|
||||||
Text("time modified: %s", format_time_datetime(get_file_modtime(stfe, ex1w.file_select)).data);
|
Text("time modified: %s", format_time_datetime(get_file_modtime(stfe, ex1w.file_select)).data);
|
||||||
|
*/
|
||||||
// #TODO: size, modtime
|
// #TODO: size, modtime
|
||||||
|
|
||||||
|
if (!ex1w.sort_completed || Button("sort file sizes")) {
|
||||||
|
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
|
||||||
|
reorder_files_by_radix(&ex1w.file_size_radix, &ex1w.files_sorted_by_size);
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (ex1w.sort_completed) {
|
||||||
|
SeparatorText("Files ordered by size");
|
||||||
|
s32 file_count = (s32)ex1w.files_sorted_by_size.count;
|
||||||
|
// SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
|
||||||
|
string file_name = copy_string(ex1w.files_sorted_by_size[ex1w.file_select]);
|
||||||
|
Text("%s", file_name.data);
|
||||||
|
u32 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ex1_Control_Panel () { using namespace ImGui;
|
void Ex1_Control_Panel () { using namespace ImGui;
|
||||||
@ -195,9 +254,9 @@ void Ex1_Control_Panel () { using namespace ImGui;
|
|||||||
// && (drives_enumerated == drives.count);
|
// && (drives_enumerated == drives.count);
|
||||||
|
|
||||||
string file_path = format_string_temp("%s_DriveData.bin", os_get_machine_name().data);
|
string file_path = format_string_temp("%s_DriveData.bin", os_get_machine_name().data);
|
||||||
if (!all_drives_enumerated && file_exists(file_path)) {
|
// if (!all_drives_enumerated && file_exists(file_path)) {
|
||||||
Deserialize_ST_File_Enumeration(file_path);
|
// Deserialize_ST_File_Enumeration(file_path);
|
||||||
}
|
// }
|
||||||
if (drives.count > 0 && !all_drives_enumerated && file_exists(file_path) && Button("Load from file (this machine)")) {
|
if (drives.count > 0 && !all_drives_enumerated && file_exists(file_path) && Button("Load from file (this machine)")) {
|
||||||
Deserialize_ST_File_Enumeration(file_path);
|
Deserialize_ST_File_Enumeration(file_path);
|
||||||
// Deserialize_Win32_Drives(file_path);
|
// Deserialize_Win32_Drives(file_path);
|
||||||
@ -368,12 +427,12 @@ void ImGui_Debug_Panel () { using namespace ImGui;
|
|||||||
push_allocator(temp());
|
push_allocator(temp());
|
||||||
|
|
||||||
Begin("Debug Panel");
|
Begin("Debug Panel");
|
||||||
SeparatorText("ex1_ntfs");
|
/*SeparatorText("ex1_ntfs");
|
||||||
Text("Threads in flight count: %d", ex1_ntfs.threads_in_flight.count);
|
Text("Threads in flight count: %d", ex1_ntfs.threads_in_flight.count);
|
||||||
for_each(i, ex1_ntfs.threads) {
|
for_each(i, ex1_ntfs.threads) {
|
||||||
Text(" [%d] initialized: %d, has_context: %d, has_data: %d",
|
Text(" [%d] initialized: %d, has_context: %d, has_data: %d",
|
||||||
i, ex1_ntfs.threads[i].proc != nullptr, ex1_ntfs.threads[i].context != nullptr, ex1_ntfs.threads[i].data != nullptr);
|
i, ex1_ntfs.threads[i].proc != nullptr, ex1_ntfs.threads[i].context != nullptr, ex1_ntfs.threads[i].data != nullptr);
|
||||||
}
|
}*/
|
||||||
// #cpuid
|
// #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());
|
// 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");
|
{ SeparatorText("Arena In-Use List");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user