Remove Basic, ErrorCodes, add String_Builder, and fix some issues with String module
This commit is contained in:
parent
6c3d77d58b
commit
1a2de6e2f8
@ -4,7 +4,7 @@
|
||||
#include "lib/third_party/dear-imgui/imgui_widgets.cpp"
|
||||
#include "lib/third_party/dear-imgui/imgui_draw.cpp"
|
||||
#include "lib/third_party/dear-imgui/imgui_tables.cpp"
|
||||
#include "lib/third_party/dear-imgui/imgui_demo.cpp"
|
||||
// #include "lib/third_party/dear-imgui/imgui_demo.cpp"
|
||||
#include "lib/third_party/dear-imgui/imgui_impl_dx11.cpp"
|
||||
#include "lib/third_party/dear-imgui/imgui_impl_win32.cpp"
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#if ALLOCATOR_POISON_MEMORY_ON_ALLOCATION
|
||||
#define ALLOCATOR_INIT_VALUE 0xCD
|
||||
#else
|
||||
#define ALLOCATOR_INIT_VALUE 0xCD
|
||||
#define ALLOCATOR_INIT_VALUE 0
|
||||
#endif
|
||||
|
||||
enum class Allocator_Mode: s32 {
|
||||
@ -107,6 +107,10 @@ template <typename T> void zero_struct(T* src) {
|
||||
memset(src, 0, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> void poison_struct(T* src) {
|
||||
memset(src, 0xCD, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> T* copy_struct(T* src) {
|
||||
T* dst = New<T>(false);
|
||||
memcpy(dst, src, sizeof(T));
|
||||
|
||||
@ -132,7 +132,7 @@ void* arena_alloc (Arena* arena, s64 byte_count) {
|
||||
|
||||
if (result_end > arena->first_uncommitted_page) {
|
||||
if (result_end > arena_address_limit(arena)) {
|
||||
// #TODO: Log error here:
|
||||
printf("[Error] Failed to allocate because Arena is full and cannot expand!\n");
|
||||
Assert(false); // Failed to allocate because arena is full and cannot expand!
|
||||
} else {
|
||||
extend_committed_pages(arena, result_end);
|
||||
|
||||
@ -8,9 +8,11 @@ struct ArenaArray { // downcasts to an ArrayView.
|
||||
s64 count;
|
||||
T* data;
|
||||
s64 allocated;
|
||||
Arena* arena; // We can probably assume arena is &Array-32
|
||||
Arena* arena;
|
||||
|
||||
ArenaArray() {}
|
||||
ArenaArray() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
T& operator[] (s64 index) {
|
||||
#if ARRAY_ENABLE_BOUNDS_CHECKING
|
||||
@ -23,7 +25,7 @@ struct ArenaArray { // downcasts to an ArrayView.
|
||||
|
||||
// #NOTE: I am not defining arena_array_init (ArenaArray<T>*), because I do not want to
|
||||
// encourage it's usage!
|
||||
// #TODO: array_free vs arena_array_destroy or arena_array_delete or
|
||||
// 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);
|
||||
@ -37,15 +39,15 @@ ArenaArray<T>* arena_array_new (s64 preallocate_count, Arena_Reserve reserve_siz
|
||||
array->allocated = preallocate_count;
|
||||
}
|
||||
|
||||
array.count = 0;
|
||||
array.arena = new_arena;
|
||||
array.data = array_start(array);
|
||||
array->count = 0;
|
||||
array->arena = arena;
|
||||
array->data = array_start<T>(*array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
template <typename T> T* array_start (ArenaArray<T>& array) {
|
||||
return (array->arena->memory_base + ARRAY_ARENA_START_OFFSET);
|
||||
return (array.arena->memory_base + ARRAY_ARENA_START_OFFSET);
|
||||
}
|
||||
|
||||
template <typename T> bool is_empty (ArenaArray<T>& array) {
|
||||
@ -57,11 +59,11 @@ template <typename T> s64 memory_usage (ArenaArray<T>& array) {
|
||||
return arena_usage_committed_bytes(array.arena);
|
||||
}
|
||||
|
||||
template <typename T> void array_free (ArenaArray<T>& array) {
|
||||
array.count = 0;
|
||||
array.allocated = 0;
|
||||
|
||||
release_arena(array.arena, delete_extra_pages=true);
|
||||
template <typename T> void arena_array_free (ArenaArray<T>& array) {
|
||||
release_arena(array.arena, true);
|
||||
#if BUILD_DEBUG
|
||||
poison_struct(&array);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T> ArrayView<T> array_view (ArenaArray<T> array) {
|
||||
@ -97,6 +99,19 @@ template <typename T> ArrayView<T> to_view (ArenaArray<T>& array, s64 start_offs
|
||||
return av;
|
||||
}
|
||||
|
||||
template <typename T> void array_add (ArenaArray<T>& array, ArrayView<T> items) {
|
||||
T* current_point = &array.data[array.count];
|
||||
s64 final_count = array.allocated + items.count;
|
||||
|
||||
if (array.allocated < final_count) {
|
||||
array_reserve(array, final_count);
|
||||
}
|
||||
|
||||
memcpy(current_point, items.data, items.count * sizeof(T));
|
||||
|
||||
array.count += items.count;
|
||||
}
|
||||
|
||||
template <typename T> void array_add (ArenaArray<T>& array, T item) {
|
||||
maybe_grow(array);
|
||||
array.data[array.count] = item;
|
||||
@ -149,7 +164,7 @@ void array_arena_realloc (ArenaArray<u8>& array, s64 new_size, s64 old_size) {
|
||||
if (result_end > array.arena->first_uncommitted_page) {
|
||||
// Critical error if we run out of address space!
|
||||
if (result_end > arena_address_limit(array.arena)) {
|
||||
// #TODO Log error.
|
||||
printf("[Error] Failed to allocate because Arena is full and cannot expand!\n");
|
||||
Assert(false); // Failed to allocate because Arena is full and cannot expand
|
||||
return;
|
||||
}
|
||||
@ -173,6 +188,14 @@ template <typename T> void init_range (T* ptr, s64 start_offset, s64 end_offset)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void poison_range (ArenaArray<T>& array, s64 start, s64 count) {
|
||||
Assert(start >= 0 && start < array.count);
|
||||
Assert(start + count <= array.count);
|
||||
// Check that these ranges make sense
|
||||
T* start_address = &array[start];
|
||||
memset(start_address, 0xCD, count * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T> force_inline void array_reset (ArenaArray<T>& array) {
|
||||
// reset backing array:
|
||||
arena_reset(array.arena);
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
// #TODO: #Arena_Table #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
|
||||
|
||||
// API in Arena.h
|
||||
#include <mutex> // #TODO: Replace with Mutex (see OS_Win32.cpp)
|
||||
|
||||
@ -55,10 +60,13 @@ void release_arena (Arena* arena, bool delete_extra_pages) {
|
||||
|
||||
arenas_in_flight_count[reserve_index] -= 1;
|
||||
|
||||
// #TODO: Garbage collection if we have >> 64 in a particular table for a while.
|
||||
//
|
||||
// #garbage_collection
|
||||
if (arena_free_table[reserve_index].count > 64) {
|
||||
// release some arenas if required
|
||||
// arena_delete(...)
|
||||
// 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;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,9 @@ void platform_init (Arena* arena, s64 new_reserve) {
|
||||
VirtualAlloc(nullptr, (u64)page_aligned_reserve_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (address_start == nullptr) {
|
||||
// get error value and string?
|
||||
s32 error_code = GetLastError();
|
||||
printf("In Arena:platform_init, VirtualAlloc failed with code %d\n", error_code);
|
||||
return;
|
||||
// #TODO(LOG) log_error("In Arena:platform_init, VirtualAlloc failed with code %d\n", error_code)
|
||||
}
|
||||
|
||||
arena->memory_base = (u8*)address_start;
|
||||
@ -34,7 +33,7 @@ void free_pages_down_to (Arena* arena, s64 pages_to_keep) {
|
||||
if (arena == nullptr) return;
|
||||
Assert(pages_to_keep >= 1); // Always keep one page because we bootstrap a lot.
|
||||
s64 bytes_to_keep = pages_to_keep * PLATFORM_MEMORY_PAGE_SIZE;
|
||||
if (bytes_to_keep >= reserve_size(arena)) {
|
||||
if (bytes_to_keep > reserve_size(arena)) {
|
||||
Assert(false); // Break in debug builds, but release we just do nothing.
|
||||
return; // just do nothing here. Maybe we should assert?
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ template <typename T> bool is_resizable (Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is_valid(Array<T> src) {
|
||||
bool is_valid (Array<T> src) {
|
||||
if (src.count == 0) return true;
|
||||
if (src.count < 0) return false;
|
||||
if (src.data == nullptr) return false;
|
||||
@ -67,7 +67,7 @@ void array_zero (const Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Array<T> array_copy_zero(const Array<T>& src) {
|
||||
Array<T> array_copy_zero (const Array<T>& src) {
|
||||
if (!src.data || src.count == 0) {
|
||||
return Array<T>(); // Return an empty array
|
||||
}
|
||||
@ -79,7 +79,7 @@ Array<T> array_copy_zero(const Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Array<T> array_copy(const Array<T>& src) {
|
||||
Array<T> array_copy (const Array<T>& src) {
|
||||
if (!src.data || src.count == 0) {
|
||||
return Array<T>(); // Return an empty array
|
||||
}
|
||||
@ -91,12 +91,12 @@ Array<T> array_copy(const Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_reset_keeping_memory(Array<T>& src) {
|
||||
void array_reset_keeping_memory (Array<T>& src) {
|
||||
src.count = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_free(Array<T>& src) {
|
||||
void array_free (Array<T>& src) {
|
||||
if (!src.data) return;
|
||||
if (src.allocated == 0) return;
|
||||
if (src.allocator.proc != nullptr) {
|
||||
@ -110,7 +110,7 @@ void array_free(Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_initialize(Array<T>& src, s64 start, s64 end) {
|
||||
void array_initialize (Array<T>& src, s64 start, s64 end) {
|
||||
for (s64 i = start; i < end; i += 1) {
|
||||
// Really this can be one ini followed by a bunch of memcpy.
|
||||
// For long arrays we could power-of-two double the copy out, etc.
|
||||
@ -119,7 +119,7 @@ void array_initialize(Array<T>& src, s64 start, s64 end) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_reserve(Array<T>& src, s64 desired_items) {
|
||||
void array_reserve (Array<T>& src, s64 desired_items) {
|
||||
if (desired_items <= src.allocated) return;
|
||||
|
||||
src.data = nullptr;
|
||||
@ -137,7 +137,7 @@ void array_reserve(Array<T>& src, s64 desired_items) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_resize(Array<T>& src, s64 new_count, bool initialize=true) {
|
||||
void array_resize (Array<T>& src, s64 new_count, bool initialize=true) {
|
||||
if (src.count == new_count) return;
|
||||
|
||||
s64 old_count = src.count;
|
||||
@ -149,7 +149,7 @@ void array_resize(Array<T>& src, s64 new_count, bool initialize=true) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
force_inline void array_maybe_grow(Array<T>& src) {
|
||||
force_inline void array_maybe_grow (Array<T>& src) {
|
||||
if (src.count >= src.allocated) {
|
||||
// Replace with Basic.max(8, 2 * src.count).
|
||||
s64 reserve = 8;
|
||||
@ -159,14 +159,14 @@ force_inline void array_maybe_grow(Array<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T pop(Array<T>& src) {
|
||||
T pop (Array<T>& src) {
|
||||
auto result = src[src.count-1];
|
||||
src.count -= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void array_add(Array<T>& src, U new_item) {
|
||||
void array_add (Array<T>& src, U new_item) {
|
||||
static_assert(sizeof(U) <= sizeof(T));
|
||||
auto new_count = src.count + 1;
|
||||
array_maybe_grow(src);
|
||||
@ -178,7 +178,7 @@ void array_add(Array<T>& src, U new_item) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_add(Array<T>& src, T new_item) {
|
||||
void array_add (Array<T>& src, T new_item) {
|
||||
auto new_count = src.count + 1;
|
||||
array_maybe_grow(src);
|
||||
|
||||
@ -187,7 +187,7 @@ void array_add(Array<T>& src, T new_item) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
s64 array_find(Array<T>& src, T item) {
|
||||
s64 array_find (Array<T>& src, T item) {
|
||||
ForArray(i, src) {
|
||||
if (src[i] == item) return i;
|
||||
}
|
||||
@ -195,7 +195,7 @@ s64 array_find(Array<T>& src, T item) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_ordered_remove_by_index(Array<T>& src, s64 index) {
|
||||
void array_ordered_remove_by_index (Array<T>& src, s64 index) {
|
||||
Assert(index >= 0); Assert(index < src.count);
|
||||
|
||||
for (s64 i = index; i < src.count-1; i += 1) {
|
||||
@ -206,13 +206,13 @@ void array_ordered_remove_by_index(Array<T>& src, s64 index) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_ordered_remove_by_value(Array<T>& src, T item) {
|
||||
void array_ordered_remove_by_value (Array<T>& src, T item) {
|
||||
auto index = array_find(src, item);
|
||||
if (index != -1) { array_ordered_remove_by_index(src, index); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_unordered_remove_by_index(Array<T>& src, s64 index) {
|
||||
void array_unordered_remove_by_index (Array<T>& src, s64 index) {
|
||||
Assert(index >= 0); Assert(index < src.count);
|
||||
|
||||
auto last_index = src.count - 1;
|
||||
@ -225,7 +225,7 @@ 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 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) {
|
||||
@ -247,6 +247,11 @@ struct ArrayView {
|
||||
s64 count;
|
||||
T* data;
|
||||
|
||||
ArrayView(Array<T> array) {
|
||||
count = array.count;
|
||||
data = array.data;
|
||||
}
|
||||
|
||||
ArrayView() { count = 0; data = nullptr; }
|
||||
|
||||
ArrayView(s64 new_count, bool initialize=true) {
|
||||
@ -269,7 +274,7 @@ struct ArrayView {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool is_zero(ArrayView<T> src) {
|
||||
bool is_empty (ArrayView<T> src) {
|
||||
if (src.count == 0) return true;
|
||||
return false;
|
||||
}
|
||||
@ -278,26 +283,17 @@ bool is_zero(ArrayView<T> src) {
|
||||
// Whether or not this is an error is procedure specific, but for most
|
||||
// things, there is a default behavior that is expected.
|
||||
template <typename T>
|
||||
bool is_valid(ArrayView<T> src) {
|
||||
bool is_valid (ArrayView<T> src) {
|
||||
if (src.count < 0) return false;
|
||||
if (src.count == 0) return true;
|
||||
if (src.data == nullptr) return false;
|
||||
// #TODO: For debug builds we can use VirtualQuery to check if
|
||||
// all pages are writable, but that seems excessive for now.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// can also use ArrayView<T>(count, data) for initialization!
|
||||
template <typename T>
|
||||
ArrayView<T> array_view(s64 view_count, T* view_data) {
|
||||
ArrayView<T> av;
|
||||
av.count = view_count;
|
||||
av.data = view_data;
|
||||
return av;
|
||||
} // #unsafe, no abc
|
||||
|
||||
template <typename T>
|
||||
ArrayView<T> array_view(Array<T> array) {
|
||||
ArrayView<T> array_view (Array<T> array) {
|
||||
ArrayView<T> av;
|
||||
av.count = array.count;
|
||||
av.data = array.data;
|
||||
@ -305,7 +301,7 @@ ArrayView<T> array_view(Array<T> array) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayView<T> array_view(ArrayView<T> array, s64 start_index, s64 view_count) {
|
||||
ArrayView<T> array_view (ArrayView<T> array, s64 start_index, s64 view_count) {
|
||||
ArrayView<T> av;
|
||||
av.count = view_count; // check if count exceeds
|
||||
Assert(start_index + view_count <= array.count);
|
||||
@ -314,7 +310,7 @@ ArrayView<T> array_view(ArrayView<T> array, s64 start_index, s64 view_count) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayView<T> array_view(Array<T> array, s64 start_index, s64 view_count) {
|
||||
ArrayView<T> array_view (Array<T> array, s64 start_index, s64 view_count) {
|
||||
ArrayView<T> av;
|
||||
av.count = view_count; // check if count exceeds
|
||||
Assert(start_index + view_count <= array.count);
|
||||
@ -323,13 +319,13 @@ ArrayView<T> array_view(Array<T> array, s64 start_index, s64 view_count) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_reset_keeping_memory(ArrayView<T>& src) {
|
||||
void array_reset_keeping_memory (ArrayView<T>& src) {
|
||||
src.count = 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
ArrayView<T> array_copy(const ArrayView<T>& src) {
|
||||
ArrayView<T> array_copy (const ArrayView<T>& src) {
|
||||
if (!src.data || src.count == 0) {
|
||||
return ArrayView<T>(); // Return an empty array
|
||||
}
|
||||
@ -341,17 +337,18 @@ ArrayView<T> array_copy(const ArrayView<T>& src) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_free(ArrayView<T>& src) {
|
||||
void array_free (ArrayView<T>& src) {
|
||||
if (!src.data || src.count == 0) { return; }
|
||||
|
||||
// Use with caution!
|
||||
internal_free(src.data); // we just have to trust that the context.allocator is correct for this guy!
|
||||
|
||||
src.count = 0;
|
||||
src.data = nullptr;
|
||||
}
|
||||
|
||||
// Usage: `auto array = array_from_values<s32>(6,7,8,9,10,51);`
|
||||
template <typename T, typename... ArgValues>
|
||||
Array<T> NewArrayFromValues(ArgValues... args) {
|
||||
Array<T> array_from_values (ArgValues... args) {
|
||||
constexpr s64 N = sizeof...(ArgValues);
|
||||
auto array = Array<T>(N, /*initialize:*/false);
|
||||
T values[] = {args...};
|
||||
@ -362,8 +359,10 @@ Array<T> NewArrayFromValues(ArgValues... args) {
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
// Usage `auto view = array_view_from_values<s32>(1,2,3,4,5);`
|
||||
template <typename T, typename... ArgValues>
|
||||
ArrayView<T> NewArrayViewFromValues(ArgValues... args) {
|
||||
ArrayView<T> array_view_from_values (ArgValues... args) {
|
||||
constexpr s64 N = sizeof...(ArgValues);
|
||||
auto array = ArrayView<T>(N, /*initialize:*/false);
|
||||
T values[] = {args...};
|
||||
|
||||
@ -13,6 +13,10 @@
|
||||
#error "CPU not supported (yet)!"
|
||||
#endif
|
||||
|
||||
#include <stdio.h> // vsnprintf
|
||||
#include <cstdarg> // va_list, ...
|
||||
|
||||
|
||||
#if OS_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
@ -100,8 +104,6 @@ force_inline T Align (T value, s64 alignment) {
|
||||
return (T)intermediate;
|
||||
}
|
||||
|
||||
// #TODO: template this so it works with any pointer type
|
||||
// force_inline u8* Align_To_Cache_Line(u8* address)
|
||||
/*
|
||||
force_inline s64 Align_Forwards(s64 size, s64 alignment) {
|
||||
return (((size + alignment - 1) / alignment) * alignment);
|
||||
@ -167,9 +169,7 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
|
||||
#endif
|
||||
|
||||
// ForExpansions. Not sure if this is a good idea...
|
||||
// #TODO: Maybe remove these. I prefer verbose and clear over this.
|
||||
#define For(_idx_, _until_) for (s64 _idx_ = 0; _idx_ < _until_; ++_idx_)
|
||||
#define ForBetween(_idx_, _start_, _until_) for (s64 _idx_ = _start_; _idx_ < _until_; ++_idx_)
|
||||
// ↓TODO(Low priority): Maybe remove these. I prefer verbose and clear over this.
|
||||
#define ForArray(_idx_, _array_) for (s64 _idx_ = 0; _idx_ < (_array_).count; ++_idx_)
|
||||
#define ForArrayStartingAt(_it_, _array_, _start_) for (s64 _it_ = _start_; _it_ < (_array_).count; _it_ += 1)
|
||||
#define ForUpTo(_it_, _end_) for (s64 _it_ = 0; _it_ < _end_; _it_ += 1)
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
struct Thread; // hacky fwd declare
|
||||
|
||||
struct Thread_Context {
|
||||
ExpandableArena* temp; // Used for temporary allocations, scratch space.
|
||||
ExpandableArena* arena; // general purpose local arena
|
||||
@ -8,11 +10,9 @@ struct Thread_Context {
|
||||
u16 GPAllocator_alignment = 16;
|
||||
// Logger logger;
|
||||
// Stack_Trace* stack_trace;
|
||||
// #TODO: other debug information
|
||||
// #TODO:
|
||||
// Array<Thread*> threads_created; // maybe should be linked-list?
|
||||
// Thread* thread_that_created_me = nullptr; // so we can remove from above array
|
||||
// Mutex thread_context_mutex;
|
||||
|
||||
Array<Thread*> child_threads; // maybe should be linked-list?
|
||||
Thread* thread_that_created_me = nullptr; // so we can remove from above array
|
||||
|
||||
string thread_name;
|
||||
};
|
||||
|
||||
@ -1,483 +0,0 @@
|
||||
#include "Basic.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <math.h> // isnan, floor
|
||||
#include <stdlib.h> // qsort
|
||||
#include <cassert> // assert
|
||||
|
||||
|
||||
Native_Error* Basic_Difference2 (ArrayView<f64> input, ArrayView<f64>& output) {
|
||||
Array_Check(input);
|
||||
Array_Check(output);
|
||||
|
||||
// ensure enough room. Note output.count = input.count
|
||||
Assert(output.count >= input.count - 1);
|
||||
|
||||
ForUpTo(i, input.count-1) {
|
||||
output[i] = input[i + 1] - input[i];
|
||||
}
|
||||
|
||||
output.count = input.count - 1;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Mean2 (ArrayView<f64> input, f64* mean) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(mean);
|
||||
|
||||
f64 sum = 0;
|
||||
ForArray(i, input) {
|
||||
sum += input[i];
|
||||
}
|
||||
|
||||
(*mean) = (sum / (f64)input.count);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_QuickSortInPlace (ArrayView<f64> input) {
|
||||
Array_Check(input);
|
||||
|
||||
qsort(input.data, input.count, sizeof(double), qsort_doubles_comparator_nonnan);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Median2 (ArrayView<f64> unsorted_input, f64* median) {
|
||||
Array_Check(unsorted_input);
|
||||
Null_Pointer_Check(median);
|
||||
auto input_sorted = array_copy(unsorted_input);
|
||||
qsort(input_sorted.data, (u64)input_sorted.count, sizeof(f64), qsort_doubles_comparator_nonnan);
|
||||
|
||||
s64 middle_element_index = unsorted_input.count / 2;
|
||||
|
||||
if (unsorted_input.count % 2 == 1) {
|
||||
(*median) = input_sorted[middle_element_index];
|
||||
} else {
|
||||
(*median) = (input_sorted[middle_element_index - 1] + input_sorted[middle_element_index]) / 2.0;
|
||||
}
|
||||
|
||||
array_free(input_sorted);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_RescaleInPlace (ArrayView<f64> input, double min, double max) {
|
||||
Array_Check(input);
|
||||
if (max < min || max == min) { return New_Error("Min or max inputs are not valid!"); }
|
||||
|
||||
f64 smallest_element; f64 largest_element;
|
||||
auto error = Basic_Min2(input, &smallest_element);
|
||||
if (error != nullptr) return error;
|
||||
|
||||
error = Basic_Max2(input, &largest_element);
|
||||
if (error != nullptr) return error;
|
||||
|
||||
if (largest_element == smallest_element)
|
||||
return nullptr;
|
||||
|
||||
ForArray(i, input) {
|
||||
input[i] = (input[i] - smallest_element) / (largest_element - smallest_element) * (max - min) + min;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Min2 (ArrayView<f64> input, f64* min_out) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(min_out);
|
||||
|
||||
f64 min = input[0];
|
||||
ForArrayStartingAt(i, input, 1) {
|
||||
if (input[i] < min) {
|
||||
min = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
(*min_out) = min;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Max2 (ArrayView<f64> input, f64* max_out) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(max_out);
|
||||
|
||||
f64 max = input[0];
|
||||
ForArrayStartingAt(i, input, 1) {
|
||||
if (input[i] > max) {
|
||||
max = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
(*max_out) = max;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
double Basic_Max (double input1, double input2) {
|
||||
if (input1 > input2) return input1;
|
||||
else return input2;
|
||||
}
|
||||
|
||||
bool Basic_Is_Positive_Real (f32 input) {
|
||||
return (!(input <= 0.0 || isnan(input) || isinf(input)));
|
||||
}
|
||||
|
||||
bool Basic_Is_Positive_Real (f64 input) {
|
||||
return (!(input <= 0.0 || isnan(input) || isinf(input)));
|
||||
}
|
||||
|
||||
Native_Error* Basic_Standard_Deviation2 (ArrayView<f64> input, f64* stddev) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(stddev);
|
||||
|
||||
f64 mean = 0.0;
|
||||
Basic_Mean2(input, &mean);
|
||||
|
||||
f64 sum_of_squared_differences = 0;
|
||||
ForArray(i, input) {
|
||||
sum_of_squared_differences += (input[i] - mean) * (input[i] - mean);
|
||||
}
|
||||
|
||||
(*stddev) = sqrt(sum_of_squared_differences / (f64)input.count);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Variance2 (ArrayView<f64> input, f64* variance) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(variance);
|
||||
|
||||
f64 mean = 0.0;
|
||||
Basic_Mean2(input, &mean);
|
||||
|
||||
f64 sum_of_squared_differences = 0;
|
||||
ForArray(i, input) {
|
||||
sum_of_squared_differences += (input[i] - mean) * (input[i] - mean);
|
||||
}
|
||||
|
||||
f64 sample = 1;
|
||||
|
||||
(*variance) = (sum_of_squared_differences / (f64)(sample ? (input.count - 1) : input.count));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Root_Mean_Squared2 (ArrayView<f64> input, f64* rms) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(rms);
|
||||
|
||||
f64 square = 0;
|
||||
ForArray(i, input) {
|
||||
square += pow(input[i], 2);
|
||||
}
|
||||
f64 mean = (square / ((f64)input.count));
|
||||
|
||||
(*rms) = sqrt(mean);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_IndexSort2 (ArrayView<f64> input, ArrayView<s64> output) {
|
||||
Array_Check(input);
|
||||
Array_Check(output);
|
||||
|
||||
ForArray(i, input) { output[i] = i; }
|
||||
ForArray(i, input) {
|
||||
for (s64 j = i; j > 0; j -= 1) {
|
||||
if (input[output[j]] > input[output[j-1]]) {
|
||||
s64 temp = output[j];
|
||||
output[j] = output[j - 1];
|
||||
output[j - 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Count_Non_Nan2 (ArrayView<f64> input, s64* non_nan_count) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(non_nan_count);
|
||||
|
||||
s64 count = 0;
|
||||
|
||||
ForArray(i, input) {
|
||||
if (!isnan(input[i])) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
(*non_nan_count) = count;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Calculate_Percentile_New (ArrayView<f64> input, f64 percentile, f64* percentile_value_out) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(percentile_value_out);
|
||||
|
||||
Assert(percentile >= 0.0 && percentile <= 1.0);
|
||||
|
||||
qsort(input.data, input.count, sizeof(f64), qsort_doubles_comparator);
|
||||
s64 non_nan_count = 0;
|
||||
Assert(Basic_Count_Non_Nan2(input, &non_nan_count) == nullptr);
|
||||
|
||||
if (non_nan_count == 0) {
|
||||
(*percentile_value_out) = NAN;
|
||||
return New_Warning("All values in the input array are `NAN`!");
|
||||
}
|
||||
|
||||
auto r = percentile * non_nan_count;
|
||||
auto k = floor(r + 0.5);
|
||||
|
||||
auto kp1 = k + 1;
|
||||
|
||||
// Ratio between the K and K+1 rows:
|
||||
r = r - k;
|
||||
|
||||
// Find indices that are out of the range 1 to n and cap them:
|
||||
if (k < 1 || isnan(k)) {
|
||||
k = 1;
|
||||
}
|
||||
|
||||
// kp1 = min( kp1, n );
|
||||
if (non_nan_count < kp1) { kp1 = (f64)non_nan_count; }
|
||||
|
||||
// Use simple linear interpolation for the valid percentages:
|
||||
// y = (0.5+r).*x(kp1,:)+(0.5-r).*x(k,:); // yuck.
|
||||
s64 kp1_i = static_cast<s64>(kp1);
|
||||
s64 k_i = static_cast<s64>(k);
|
||||
|
||||
f64 y_first_part = (0.5 + r) * input[kp1_i - 1];
|
||||
f64 y_second_part = (0.5 - r) * input[k_i - 1];
|
||||
auto y = y_first_part + y_second_part;
|
||||
|
||||
// Make sure that values we hit exactly are copied rather than interpolated:
|
||||
if (r == -0.5) {
|
||||
(*percentile_value_out) = input[k_i - 1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure that identical values are copied rather than interpolated:
|
||||
if (input[k_i-1] == input[kp1_i-1]) {
|
||||
(*percentile_value_out) = input[k_i - 1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*percentile_value_out) = y;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_ReverseArrayInPlace (ArrayView<f64> input) {
|
||||
Array_Check(input);
|
||||
ForUpTo(i, input.count/2) {
|
||||
f64 temp = input[i];
|
||||
input[i] = input[input.count - i - 1];
|
||||
input[input.count - i - 1] = temp;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Native_Error* Basic_Reverse_Array (int* input, int input_length) {
|
||||
// for (int i = 0; i < input_length / 2; i++) {
|
||||
// // Swap the ith and (input_length - i - 1)th elements
|
||||
// int temp = input[i];
|
||||
// input[i] = input[input_length - i - 1];
|
||||
// input[input_length - i - 1] = temp;
|
||||
// }
|
||||
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// Native_Error* Basic_Reverse_Array (double* input, int input_length) {
|
||||
// for (int i = 0; i < input_length / 2; i++) {
|
||||
// // Swap the ith and (input_length - i - 1)th elements
|
||||
// double temp = input[i];
|
||||
// input[i] = input[input_length - i - 1];
|
||||
// input[input_length - i - 1] = temp;
|
||||
// }
|
||||
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// #TODO: This should be for NDArray or 2DArray. idk.
|
||||
Native_Error* Basic_2DArrayInvertMemoryOrder (ArrayView<f64> input, s64 first_dimension, s64 second_dimension, ArrayView<f64> output) {
|
||||
Array_Check(input);
|
||||
Array_Check(output);
|
||||
if (output.count < input.count) { return New_Error("`input.count` should not exceed `output.count`!"); }
|
||||
Assert(first_dimension * second_dimension == input.count);
|
||||
Assert(input.count == output.count);
|
||||
|
||||
ForUpTo(i, first_dimension) {
|
||||
ForUpTo(j, second_dimension) {
|
||||
output[j + second_dimension * i] = input[i + first_dimension * j];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool sort_doubles_comparator(double a, double b) {
|
||||
if (isnan(a)) return false; // NaN values are considered greater
|
||||
if (isnan(b)) return true; // Non-NaN values are considered smaller
|
||||
return a < b; // Normal comparison for non-NaN values
|
||||
}
|
||||
|
||||
int qsort_doubles_comparator_nonnan(const void* a, const void* b) {
|
||||
double val1 = (*(const double*)a);
|
||||
double val2 = (*(const double*)b);
|
||||
|
||||
if (val1 < val2) return -1;
|
||||
if (val1 > val2) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qsort_doubles_comparator(const void* a, const void* b) {
|
||||
double val1 = (*(const double*)a);
|
||||
double val2 = (*(const double*)b);
|
||||
if (isnan(val1)) return 1; // NaN values are considered greater
|
||||
if (isnan(val2)) return -1; // Non-NaN values are considered smaller
|
||||
|
||||
if (val1 < val2) return -1;
|
||||
if (val1 > val2) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Native_Error* Basic_CalculatePercentileNoSort (ArrayView<f64> input, f64 percentile, f64* percentile_value_out) {
|
||||
Array_Check(input);
|
||||
Null_Pointer_Check(percentile_value_out);
|
||||
|
||||
Assert(percentile >= 0.0 && percentile <= 1.0);
|
||||
|
||||
s64 non_nan_count = input.count;
|
||||
|
||||
auto r = percentile * non_nan_count;
|
||||
auto k = floor(r + 0.5);
|
||||
|
||||
auto kp1 = k + 1;
|
||||
|
||||
// Ratio between the K and K+1 rows:
|
||||
r = r - k;
|
||||
|
||||
// Find indices that are out of the range 1 to n and cap them:
|
||||
if (k < 1 || isnan(k)) {
|
||||
k = 1;
|
||||
}
|
||||
|
||||
// kp1 = min( kp1, n );
|
||||
if (non_nan_count < kp1) { kp1 = (f64)non_nan_count; }
|
||||
|
||||
// Use simple linear interpolation for the valid percentages:
|
||||
// y = (0.5+r).*x(kp1,:)+(0.5-r).*x(k,:); // yuck.
|
||||
s64 kp1_i = static_cast<s64>(kp1);
|
||||
s64 k_i = static_cast<s64>(k);
|
||||
|
||||
f64 y_first_part = (0.5 + r) * input[kp1_i - 1];
|
||||
f64 y_second_part = (0.5 - r) * input[k_i - 1];
|
||||
auto y = y_first_part + y_second_part;
|
||||
|
||||
// Make sure that values we hit exactly are copied rather than interpolated:
|
||||
if (r == -0.5) {
|
||||
(*percentile_value_out) = input[k_i - 1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure that identical values are copied rather than interpolated:
|
||||
if (input[k_i-1] == input[kp1_i-1]) {
|
||||
(*percentile_value_out) = input[k_i - 1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*percentile_value_out) = y;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Replace_Outliers2 (ArrayView<f64> input, f64 outlier_threshold) {
|
||||
Array_Check(input);
|
||||
Assert(outlier_threshold > 0);
|
||||
|
||||
auto input_copy = array_copy(input);
|
||||
|
||||
qsort(input_copy.data, input_copy.count, sizeof(f64), qsort_doubles_comparator_nonnan);
|
||||
f64 Q1 = 0.0;
|
||||
f64 Q3 = 0.0;
|
||||
|
||||
Assert(Basic_CalculatePercentileNoSort(input_copy, 0.25, &Q1) == nullptr);
|
||||
Assert(Basic_CalculatePercentileNoSort(input_copy, 0.75, &Q3) == nullptr);
|
||||
|
||||
f64 IQR = Q3 - Q1;
|
||||
f64 iqr_outlier_threshold = IQR * outlier_threshold;
|
||||
|
||||
// Identify points below Q1 - outlier_threshold, and above Q3 + outlier_threshold
|
||||
auto low_threshold = Q1 - iqr_outlier_threshold;
|
||||
auto high_threshold = Q3 + iqr_outlier_threshold;
|
||||
|
||||
ForArrayStartingAt(i, input, 1) {
|
||||
if (input[i] < low_threshold || input[i] > high_threshold) {
|
||||
input[i] = input[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
array_free(input_copy);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Native_Error* Basic_Replace_Values_Beyond_Threshold2 (ArrayView<f64> input, f64 low_threshold, f64 high_threshold, f64 replacement_value) {
|
||||
Array_Check(input);
|
||||
|
||||
ForArray(i, input) {
|
||||
if (input[i] < low_threshold || input[i] > high_threshold) {
|
||||
input[i] = replacement_value;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/* // #TODO: Replace with version that doesn't use Eigen
|
||||
Native_Error* Basic_Roots_To_Polynomials2 (ArrayView<f64> roots, ArrayView<f64> polynomials) {
|
||||
Array_Check(roots);
|
||||
|
||||
s64 root_count = roots.count;
|
||||
|
||||
if (root_count == 0) { return New_Error("`roots.count` is zero!"); }
|
||||
if (polynomials.count < root_count + 1) {
|
||||
return New_Error("`polynomials.count` should be roots.count + 1!");
|
||||
}
|
||||
|
||||
// For real roots
|
||||
Eigen::VectorXd roots_vec = Eigen::Map<Eigen::VectorXd>(roots.data, root_count);
|
||||
// c = [1 zeros(1,n,class(x))];
|
||||
Eigen::VectorXd c = Eigen::VectorXd::Zero(root_count + 1);
|
||||
c[0] = 1.0;
|
||||
// for j = 1:n
|
||||
// c[1] = c[1] - roots_vec[0] * c[0]; // Extract first index
|
||||
ForArray(i, roots) {
|
||||
// c(2:(j+1)) = c(2:(j+1)) - e(j).*c(1:j);
|
||||
Eigen::VectorXd val_temp = c.segment(1, i + 1) - roots_vec[i] * c.segment(0, i + 1);
|
||||
c.segment(1, i + 1) = val_temp;
|
||||
}
|
||||
// The result should be real if the roots are complex conjugates.
|
||||
memcpy(polynomials.data, c.data(), (root_count + 1) * sizeof(f64));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
*/
|
||||
|
||||
Complex exponential (Complex cx) {
|
||||
f64 e = std::exp(cx.real);
|
||||
return Complex(e * std::cos(cx.imag), e * std::sin(cx.imag));
|
||||
}
|
||||
|
||||
Complex conjugate (Complex cx) {
|
||||
return Complex(cx.real, -cx.imag);
|
||||
}
|
||||
|
||||
f64 fabs(Complex cx) {
|
||||
return sqrt(cx.real * cx.real + cx.imag * cx.imag);
|
||||
}
|
||||
145
lib/Base/Basic.h
145
lib/Base/Basic.h
@ -1,145 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int qsort_doubles_comparator_nonnan(const void* a, const void* b);
|
||||
int qsort_doubles_comparator(const void* a, const void* b);
|
||||
|
||||
// @brief Calculates difference and approximate derivative for 1-dimensional data
|
||||
// Caller needs to supply memory for output and understand that output_length = input_length - 1
|
||||
PROTOTYPING_API Native_Error* Basic_Difference2 (ArrayView<f64> input, ArrayView<f64>& output);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Mean2 (ArrayView<f64> input, f64* mean);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_QuickSortInPlace (ArrayView<f64> input);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Median2 (ArrayView<f64> unsorted_input, f64* median);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_RescaleInPlace (ArrayView<f64> input, double min, double max);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Min2 (ArrayView<f64> input, f64* min_out);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Max2 (ArrayView<f64> input, f64* max_out);
|
||||
|
||||
double Basic_Max (double input1, double input2);
|
||||
|
||||
bool Basic_Is_Positive_Real (f32 input);
|
||||
bool Basic_Is_Positive_Real (f64 input);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Standard_Deviation2 (ArrayView<f64> input, f64* stddev);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Variance2 (ArrayView<f64> input, f64* variance);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Root_Mean_Squared2 (ArrayView<f64> input, f64* rms);
|
||||
|
||||
// Sorts an array from largest to smallest, returning the indices of the sorted array
|
||||
PROTOTYPING_API Native_Error* Basic_IndexSort2 (ArrayView<f64> input, ArrayView<s64> output);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Count_Non_Nan2 (ArrayView<f64> input, s64* non_nan_count);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Calculate_Percentile_New (ArrayView<f64> input, f64 percentile, f64* percentile_value_out);
|
||||
|
||||
// Does not include sort, because sorting is slow, and we may need to call this multiple
|
||||
// times with the same sorted input.
|
||||
PROTOTYPING_API Native_Error* Basic_CalculatePercentileNoSort (ArrayView<f64> input, f64 percentile, f64* percentile_value_out);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_ReverseArrayInPlace (ArrayView<f64> input);
|
||||
// Native_Error* Basic_Reverse_Array (double* input, int input_length);
|
||||
// Native_Error* Basic_Reverse_Array (int* input, int input_length);
|
||||
|
||||
// Switches from row-order to column-order or vice-versa. #NOTE: you must know what the order
|
||||
// and dimensions of the data are to begin with!!
|
||||
PROTOTYPING_API Native_Error* Basic_2DArrayInvertMemoryOrder (ArrayView<f64> input, s64 first_dimension, s64 second_dimension, ArrayView<f64> output);
|
||||
|
||||
// In-place replacement of outliers (using interquartile method, with threshold of 1.5) with nearest values.
|
||||
PROTOTYPING_API Native_Error* Basic_Replace_Outliers2 (ArrayView<f64> input, f64 outlier_threshold=1.5);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Replace_Values_Beyond_Threshold2 (ArrayView<f64> input, f64 low_threshold, f64 high_threshold, f64 replacement_value);
|
||||
|
||||
PROTOTYPING_API Native_Error* Basic_Roots_To_Polynomials2 (ArrayView<f64> roots, ArrayView<f64> polynomials);
|
||||
|
||||
// #TODO: Basic_Find (returns indices of non-zero elements).
|
||||
// Need to make this generic, maybe using templates?
|
||||
// PROTOTYPING_API ArrayView<s32> Basic_Find(ArrayView<f64> x, void* condition);
|
||||
|
||||
// Add parameters for peak prominence, height, etc.
|
||||
// PROTOTYPING_API Native_Error* Basic_Find_Peaks (double* input, int input_length, int* peak_indices, int* peak_count);
|
||||
|
||||
struct Complex {
|
||||
f64 real; f64 imag;
|
||||
|
||||
Complex() { real = 0; imag = 0; }
|
||||
Complex(f64 _real) { real = _real; imag = 0; }
|
||||
Complex(f64 _real, f64 _imag) { real = _real; imag = _imag; }
|
||||
|
||||
Complex operator+(const Complex& other) const {
|
||||
return Complex(real + other.real, imag + other.imag);
|
||||
}
|
||||
|
||||
Complex operator-(const Complex& other) const {
|
||||
return Complex(real - other.real, imag - other.imag);
|
||||
}
|
||||
|
||||
Complex operator*(const Complex& other) const {
|
||||
return Complex(
|
||||
real * other.real - imag * other.imag,
|
||||
real * other.imag + imag * other.real
|
||||
);
|
||||
}
|
||||
|
||||
Complex operator/(const Complex& other) const {
|
||||
f64 denom = other.real * other.real + other.imag * other.imag;
|
||||
return Complex(
|
||||
(real * other.real + imag * other.imag) / denom,
|
||||
(imag * other.real - real * other.imag) / denom
|
||||
);
|
||||
}
|
||||
|
||||
Complex& operator+=(const Complex& other) {
|
||||
real += other.real;
|
||||
imag += other.imag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex& operator-=(const Complex& other) {
|
||||
real -= other.real;
|
||||
imag -= other.imag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex& operator*=(const Complex& other) {
|
||||
f64 r = real * other.real - imag * other.imag;
|
||||
f64 i = real * other.imag + imag * other.real;
|
||||
real = r;
|
||||
imag = i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex& operator/=(const Complex& other) {
|
||||
f64 denom = other.real * other.real + other.imag * other.imag;
|
||||
f64 r = (real * other.real + imag * other.imag) / denom;
|
||||
f64 i = (imag * other.real - real * other.imag) / denom;
|
||||
real = r;
|
||||
imag = i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Complex& other) const {
|
||||
return real == other.real && imag == other.imag;
|
||||
}
|
||||
|
||||
bool operator!=(const Complex& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
struct Complex32 { f32 real; f32 imag; };
|
||||
|
||||
Complex exponential (Complex cx);
|
||||
Complex conjugate (Complex cx);
|
||||
f64 fabs (Complex cx);
|
||||
@ -1,166 +0,0 @@
|
||||
enum ErrorSeverity: s32 {
|
||||
SEVERITY_WARNING = 0,
|
||||
SEVERITY_NON_FATAL = 1,
|
||||
SEVERITY_FATAL = 2
|
||||
};
|
||||
|
||||
// typedef struct string Native_Error;
|
||||
// Note: Native_Error should down-cast to a string.
|
||||
struct Native_Error {
|
||||
s64 count;
|
||||
u8* data;
|
||||
ErrorSeverity severity = SEVERITY_WARNING;
|
||||
};
|
||||
|
||||
#define Null_Pointer_Check(arg) \
|
||||
if (arg == nullptr) { \
|
||||
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is a null pointer.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
|
||||
}
|
||||
|
||||
#define Array_Check(arg) \
|
||||
if (!is_valid(arg)) { \
|
||||
return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid array.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); \
|
||||
}
|
||||
|
||||
#define String_Check(arg) \
|
||||
if (!Is_Valid(arg)) { return New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s is not a valid string.", __FILE__, __LINE__, __FUNCTION__, Stringify(arg)); }
|
||||
|
||||
#define Error_Check(error) \
|
||||
if (error != nullptr) { \
|
||||
return error; \
|
||||
}
|
||||
|
||||
// An error from which the program cannot continue (e.g. a segmentation fault)
|
||||
#define New_Fatal_Error(message) \
|
||||
New_Fatal_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
|
||||
|
||||
#define New_Error(message) \
|
||||
New_Error_Internal("%s:%d\n[%s] Error: %s.", __FILE__, __LINE__, __FUNCTION__, message)
|
||||
|
||||
#define New_Warning(message) \
|
||||
New_Warning_Internal("%s:%d\n[%s] Warning: %s.", __FILE__, __LINE__, __FUNCTION__, message)
|
||||
|
||||
Native_Error* New_Fatal_Error_Internal(char* raw_message, ...);
|
||||
|
||||
Native_Error* New_Error_Internal(char* raw_message, ...);
|
||||
|
||||
Native_Error* New_Warning_Internal(char* raw_message, ...);
|
||||
|
||||
Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity);
|
||||
|
||||
PROTOTYPING_API C_API Native_Error* Cleanup_Error(Native_Error* error);
|
||||
|
||||
PROTOTYPING_API C_API Native_Error* Native_Error_Test();
|
||||
|
||||
|
||||
#include "General_Purpose_Allocator.h"
|
||||
#include <stdio.h> // vsnprintf, printf
|
||||
#include <cstdarg> // va_list...
|
||||
|
||||
#define BREAK_ON_WARNINGS 0
|
||||
#define BREAK_ON_ERRORS 0
|
||||
#define BREAK_ON_FATAL_ERROR BUILD_DEBUG
|
||||
#define ALWAYS_PRINT_ERROR_MESSAGES BUILD_DEBUG
|
||||
|
||||
Native_Error* Create_New_Native_Error_Internal(char* format, va_list args) {
|
||||
constexpr s64 ERROR_BUFFER_COUNT = 512;
|
||||
|
||||
// push_allocator(GPAllocator());
|
||||
|
||||
auto error = New<Native_Error>(false);
|
||||
error->data = (u8*)GPAllocator_New(ERROR_BUFFER_COUNT);
|
||||
|
||||
// You MUST copy the va_list before using it more than once
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
error->count = (s64)vsnprintf((char*)error->data, (size_t)ERROR_BUFFER_COUNT, format, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Native_Error* New_Fatal_Error_Internal(char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
auto error = Create_New_Native_Error_Internal(format, args);
|
||||
va_end(args);
|
||||
|
||||
error->severity = SEVERITY_FATAL;
|
||||
#if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES
|
||||
printf("[FATAL ERROR] %.*s\n", (s32)error->count, (char*)error->data);
|
||||
#endif
|
||||
#if BREAK_ON_FATAL_ERROR
|
||||
debug_break();
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Native_Error* Native_Error_Callstack(Native_Error* new_error, Native_Error* old_error, ErrorSeverity severity) {
|
||||
// push_allocator(GPAllocator());
|
||||
|
||||
auto error_message = format_string("%s\n > %s", new_error->data, old_error->data).data;
|
||||
|
||||
Cleanup_Error(new_error);
|
||||
Cleanup_Error(old_error);
|
||||
|
||||
Native_Error* error_merged = New<Native_Error>(false);
|
||||
error_merged->data = (u8*)error_message;
|
||||
error_merged->count = strlen((char*)error_merged->data);
|
||||
error_merged->severity = severity;
|
||||
|
||||
return error_merged;
|
||||
}
|
||||
|
||||
Native_Error* Native_Error_Test() {
|
||||
// This is quite verbose, but w/e
|
||||
auto old_error = New_Error("Original error...");
|
||||
auto new_message = format_string("Failed to start stream. Error Code: %d", -1).data;
|
||||
auto new_error = New_Error(new_message);
|
||||
GPAllocator_Delete(new_message);
|
||||
|
||||
return Native_Error_Callstack(new_error, old_error, SEVERITY_NON_FATAL);
|
||||
}
|
||||
|
||||
Native_Error* New_Error_Internal(char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
auto error = Create_New_Native_Error_Internal(format, args);
|
||||
va_end(args);
|
||||
|
||||
error->severity = SEVERITY_NON_FATAL;
|
||||
#if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES
|
||||
printf("[ERROR (NON-FATAL)] %.*s\n", (s32)error->count, (char*)error->data);
|
||||
#endif
|
||||
#if BREAK_ON_ERRORS
|
||||
debug_break();
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Native_Error* New_Warning_Internal(char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
auto error = Create_New_Native_Error_Internal(format, args);
|
||||
va_end(args);
|
||||
|
||||
error->severity = SEVERITY_WARNING;
|
||||
#if BUILD_DEBUG && ALWAYS_PRINT_ERROR_MESSAGES
|
||||
printf("[WARNING] %.*s\n", (s32)error->count, (char*)error->data);
|
||||
#endif
|
||||
#if BREAK_ON_WARNINGS
|
||||
debug_break();
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Native_Error* Cleanup_Error(Native_Error* error) {
|
||||
if (error == nullptr) return nullptr;
|
||||
|
||||
GPAllocator_Delete(error->data);
|
||||
GPAllocator_Delete(error);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
30
lib/Base/ErrorType.cpp
Normal file
30
lib/Base/ErrorType.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
// #NOTE: To keep things simple, all allocations for Error should be via GPAllocator.
|
||||
// We really allocate two things: the Error struct and the error string copy.
|
||||
|
||||
enum class ErrorClass: s32 {
|
||||
NONE = 0, // should not be used, just to avoid a default value being assigned.
|
||||
WARNING = 1,
|
||||
ERROR = 2,
|
||||
FATAL = 3
|
||||
};
|
||||
|
||||
// #downcasts to string
|
||||
struct Error {
|
||||
s64 count;
|
||||
u8* data;
|
||||
ErrorClass severity = ErrorClass::NONE;
|
||||
Error* previous_error; // if we're passing errors up the callstack.
|
||||
|
||||
Arena* arena;
|
||||
};
|
||||
|
||||
string to_string (Error error) {
|
||||
return { error.count, error.data };
|
||||
}
|
||||
|
||||
// Will need to use __FILE__ and __LINE__ macros
|
||||
|
||||
// Error* new_error (string error_message, ErrorClass severity, Error* previous_error=nullptr);
|
||||
// Error* append_error (Error* old_error, Error* new_error);
|
||||
// void context_report_error (Error* error);
|
||||
// void cleanup_error (Error* error);
|
||||
16
lib/Base/Logger.h
Normal file
16
lib/Base/Logger.h
Normal file
@ -0,0 +1,16 @@
|
||||
// #TODO #Logger module
|
||||
// [ ] Add colored prints (See: Print_Color.jai)
|
||||
|
||||
// See Logger.jai in our jiim-dev-gui project for how to do fancy colored text.
|
||||
enum class Log_Level : s32 {
|
||||
TODO = -2,
|
||||
Trace = -1;
|
||||
None = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
Fatal_Error = 4,
|
||||
};
|
||||
|
||||
// log_function pointer
|
||||
typedef void (*Logger_Proc)(string log_message, ...);
|
||||
@ -1,127 +1,89 @@
|
||||
// #TODO: Integrate Allocator / context.allocator
|
||||
#include "String.h"
|
||||
|
||||
|
||||
// Need to sort out how formatted strings and string builders are allocated
|
||||
// Maybe just use context.allocator?
|
||||
// What about temp strings? use context.temp?
|
||||
|
||||
struct string {
|
||||
s64 count;
|
||||
u8* data;
|
||||
// Construct from a string literal or C-string
|
||||
string () { // default constructor
|
||||
count = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
string (char* cstr) {
|
||||
count = strlen(cstr);
|
||||
data = (u8*)cstr;
|
||||
}
|
||||
|
||||
string (s64 _count, char* str) { count = _count; data = (u8*)str; }
|
||||
string (s64 _count, u8* str) { count = _count; data = str; }
|
||||
};
|
||||
|
||||
// ~ API ~ #TODO
|
||||
string copy_string (string str);
|
||||
bool strings_match(string first_string, string second_string);
|
||||
|
||||
// Unicode stuff
|
||||
string wide_to_utf8 (u16* source, s32 length);
|
||||
|
||||
// string string_view(string n_string, int start_index, int view_count);
|
||||
// string copy_string(char* c_string);
|
||||
// void free(string& n_string);
|
||||
|
||||
bool is_valid(string n_string);
|
||||
bool is_c_string(string n_string);
|
||||
|
||||
char* to_c_string(string n_string);
|
||||
|
||||
string format_string(char* format, ...);
|
||||
string string_from_literal(char* literal);
|
||||
|
||||
#include "General_Purpose_Allocator.h"
|
||||
#include <stdio.h> // vsnprintf
|
||||
#include <cstdarg> // va_list, ...
|
||||
|
||||
bool is_c_string(string n_string) {
|
||||
return (n_string.data && n_string.data[n_string.count] == '\0');
|
||||
// #TODO #string module
|
||||
// [ ] I'm debating if string type should automatically null-terminate.
|
||||
// I personally do not like it, and think we should temp-copy c-strings as they're needed.
|
||||
bool is_valid (string s) {
|
||||
return (s.data != nullptr && s.count > 0);
|
||||
}
|
||||
|
||||
bool is_valid(string n_string) {
|
||||
return (n_string.data != nullptr && n_string.count > 0);
|
||||
bool is_c_string (string s) {
|
||||
return (s.data && s.data[s.count] == '\0');
|
||||
}
|
||||
|
||||
string copy_string (string str) {
|
||||
string new_str = {};
|
||||
u8* to_c_string (string s) {
|
||||
u8* result = (u8*)internal_alloc(s.count + 1);
|
||||
|
||||
new_str.count = str.count;
|
||||
new_str.data = (u8*)internal_alloc(str.count);
|
||||
memcpy(result, s.data, s.count);
|
||||
result[s.count] = '\0';
|
||||
|
||||
memcpy(new_str.data, str.data, str.count);
|
||||
|
||||
return new_str;
|
||||
return result;
|
||||
}
|
||||
|
||||
string format_string (char* format, ...) {
|
||||
constexpr s64 BUFFER_SIZE = 4096;
|
||||
string copy_string (string s) {
|
||||
Assert(s.count > 0);
|
||||
if (s.count <= 0)
|
||||
return "";
|
||||
string str = {};
|
||||
|
||||
string str = {0};
|
||||
str.count = s.count;
|
||||
str.data = (u8*)internal_alloc(s.count + 1);
|
||||
|
||||
str.data = NewArray<u8>(BUFFER_SIZE);
|
||||
memcpy(str.data, s.data, s.count);
|
||||
|
||||
str.data[str.count] = '\0'; // null-terminate for backwards compatibility?
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
// Note that this *is* null-terminated for compatibility.
|
||||
str.count = (s64)vsnprintf((char*)str.data, (size_t)BUFFER_SIZE, format, args);
|
||||
va_end(args);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
string copy_string(char* c_string) {
|
||||
string copy_string (char* c_string) {
|
||||
string str = {0};
|
||||
s64 string_length = strlen(c_string);
|
||||
if (string_length == 0)
|
||||
return "";
|
||||
|
||||
str.data = NewArray<u8>(string_length + 1);
|
||||
memcpy(str.data, c_string, string_length);
|
||||
str.count = string_length;
|
||||
|
||||
str.data[str.count] = '\0'; // null-terminate for backwards compatibility?
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool strings_match(string first_string, string second_string) {
|
||||
if (first_string.count != second_string.count) {
|
||||
return false;
|
||||
string to_string (ArrayView<u8> str) {
|
||||
return {str.count, str.data};
|
||||
}
|
||||
|
||||
void free (string& s) {
|
||||
internal_free(s.data);
|
||||
|
||||
s.data = nullptr;
|
||||
s.count = 0;
|
||||
}
|
||||
|
||||
force_inline string string_view (string s, s64 start_index, s64 view_count) {
|
||||
Assert(view_count >= 0); Assert(start_index >= 0);
|
||||
if (view_count < 0 || start_index < 0 || start_index >= s.count) return "";
|
||||
|
||||
s64 new_count = view_count;
|
||||
if (start_index + view_count > s.count) {
|
||||
new_count = s.count - start_index;
|
||||
}
|
||||
|
||||
for (s64 i = 0; i < first_string.count; i += 1) {
|
||||
if (first_string.data[i] != second_string.data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
return { new_count, s.data + start_index };
|
||||
}
|
||||
|
||||
string string_from_literal(char* literal) {
|
||||
string new_string;
|
||||
new_string.count = strlen(literal);
|
||||
new_string.data = (u8*) literal;
|
||||
|
||||
return new_string;
|
||||
string copy_string_view (string s, s64 start_index, s64 view_count) {
|
||||
// maybe redundant...
|
||||
return copy_string(string_view(s, start_index, view_count));
|
||||
}
|
||||
|
||||
void free(string& n_string) {
|
||||
internal_free(n_string.data);
|
||||
|
||||
n_string.data = nullptr;
|
||||
n_string.count = 0;
|
||||
bool strings_match (string first_string, string second_string) {
|
||||
return (first_string == second_string);
|
||||
}
|
||||
|
||||
// Unicode nonsense
|
||||
// #Unicode
|
||||
string wide_to_utf8 (u16* source, s32 length) {
|
||||
if (length == 0) return { };
|
||||
|
||||
@ -149,3 +111,93 @@ string wide_to_utf8 (u16* source, s32 length) {
|
||||
return utf8_string;
|
||||
}
|
||||
|
||||
string format_string (char* format, ...) {
|
||||
constexpr s64 BUFFER_SIZE = 4096;
|
||||
|
||||
string str = {0};
|
||||
|
||||
str.data = NewArray<u8>(BUFFER_SIZE);
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
// Note that this *is* null-terminated for compatibility.
|
||||
str.count = (s64)vsnprintf((char*)str.data, (size_t)BUFFER_SIZE, format, args);
|
||||
va_end(args);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve) {
|
||||
return arena_array_new<u8>(1, new_reserve);
|
||||
}
|
||||
|
||||
force_inline void append (String_Builder* sb, string s) {
|
||||
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
||||
}
|
||||
|
||||
void append (String_Builder* sb, ArrayView<string> strings) {
|
||||
s64 combined_length = 0;
|
||||
for (s64 i = 0; i < strings.count; i += 1) {
|
||||
combined_length += strings[i].count;
|
||||
}
|
||||
|
||||
s64 final_length = sb->count + combined_length;
|
||||
|
||||
if (sb->allocated < final_length) {
|
||||
array_reserve(*sb, final_length);
|
||||
}
|
||||
|
||||
for (s64 i = 0; i < strings.count; i += 1) {
|
||||
string s = strings[i];
|
||||
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
||||
}
|
||||
}
|
||||
|
||||
force_inline void append_no_add (String_Builder* sb, string s) {
|
||||
array_add(*sb, ArrayView<u8>(s.count, s.data));
|
||||
sb->count -= s.count;
|
||||
}
|
||||
|
||||
// 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, ...) {
|
||||
s64 expected_final_count = sb->count + format.count + 4096;
|
||||
|
||||
if (sb->allocated < expected_final_count) {
|
||||
array_reserve(*sb, expected_final_count);
|
||||
}
|
||||
|
||||
s64 buffer_size = sb->allocated - sb->count; // available space
|
||||
u8* current_point = &sb->data[sb->count];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
s64 print_count = (s64)vsnprintf((char*)current_point, (size_t)buffer_size, (char*)format.data, args);
|
||||
va_end(args);
|
||||
|
||||
sb->count += print_count;
|
||||
}
|
||||
|
||||
string string_view (String_Builder* sb) {
|
||||
// should probably ensure final byte is null terminated...
|
||||
append_no_add(sb, "\0"); // doesn't increment sb.count
|
||||
return to_string(to_view(*sb));
|
||||
}
|
||||
|
||||
// for when we want to keep the string builder around and recycle the memory.
|
||||
internal force_inline void reset (String_Builder* sb) {
|
||||
poison_range(*sb, 0, sb->count);
|
||||
reset_keeping_memory(*sb);
|
||||
}
|
||||
|
||||
force_inline string builder_to_string (String_Builder* sb) {
|
||||
string final_string = copy_string(to_string(to_view(*sb)));
|
||||
|
||||
free(sb);
|
||||
|
||||
return final_string;
|
||||
}
|
||||
|
||||
internal force_inline void free (String_Builder* sb) {
|
||||
arena_array_free(*sb);
|
||||
}
|
||||
|
||||
100
lib/Base/String.h
Normal file
100
lib/Base/String.h
Normal file
@ -0,0 +1,100 @@
|
||||
// #TODO: #strings:
|
||||
// [ ] Always null-terminate strings!
|
||||
// [ ] How do I accept variadic arguments of any type to my print function?
|
||||
// [ ] Need to sort out how formatted strings and string builders are allocated
|
||||
// [ ] Separate functions for temp alloc (tprint??)
|
||||
// [ ] API needs to be completely overhauled
|
||||
// [ ] I should also put path manipulation here or in a separate file.
|
||||
|
||||
struct string {
|
||||
s64 count;
|
||||
u8* data;
|
||||
// Construct from a string literal or C-string
|
||||
string () { // default constructor
|
||||
count = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
string (char* cstr) {
|
||||
count = strlen(cstr);
|
||||
data = (u8*)cstr;
|
||||
}
|
||||
|
||||
string (s64 _count, char* str) { count = _count; data = (u8*)str; }
|
||||
string (s64 _count, u8* str) { count = _count; data = str; }
|
||||
|
||||
bool operator==(const string& other) const {
|
||||
string first_string = *this;
|
||||
string second_string = other;
|
||||
// return strings_match(*this, other);
|
||||
if (first_string.count != second_string.count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (s64 i = 0; i < first_string.count; i += 1) {
|
||||
if (first_string.data[i] != second_string.data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct wstring {
|
||||
s64 count;
|
||||
u16* data;
|
||||
|
||||
wstring () { // default constructor
|
||||
count = 0;
|
||||
data = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// ~Keep these API
|
||||
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 (char* c_string); // #allocates, returned string is #null-terminated.
|
||||
string to_string (ArrayView<u8> str);
|
||||
void free(string& s);
|
||||
|
||||
// String manipulation & comparison
|
||||
force_inline string string_view (string s, s64 start_index, s64 view_count);
|
||||
string copy_string_view (string s, s64 start_index, s64 view_count);
|
||||
bool strings_match (string first_string, string second_string);
|
||||
|
||||
// #Unicode
|
||||
string wide_to_utf8 (u16* source, s32 length);
|
||||
// wstring utf8_to_wide (string source); TODO.
|
||||
|
||||
string format_string (char* format, ...);
|
||||
|
||||
// Parsing stuff:
|
||||
// is_white_space(char: u8)
|
||||
// advance
|
||||
// eat_spaces
|
||||
|
||||
// Print stuff
|
||||
// s64 string_to_int (string v, s32 base = 10, s64* remainder=nullptr);
|
||||
//
|
||||
|
||||
// #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);
|
||||
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, ...);
|
||||
string string_view (String_Builder* sb);
|
||||
internal force_inline void reset (String_Builder* sb);
|
||||
|
||||
force_inline string builder_to_string (String_Builder* sb); // returns string view
|
||||
internal force_inline void free (String_Builder* sb);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
// Thread_Group Internal Procedures
|
||||
// #NOTE: There is no logging in this implementation!
|
||||
|
||||
void init(Work_List* list) {
|
||||
Assert(list != nullptr);
|
||||
|
||||
@ -76,8 +78,6 @@ s64 thread_group_run (Thread* thread) {
|
||||
entry->thread_index = thread->index;
|
||||
entry->next = nullptr;
|
||||
|
||||
// #TODO(Log)
|
||||
|
||||
Thread_Continue_Status should_continue = Thread_Continue_Status::THREAD_CONTINUE;
|
||||
if (group->proc) {
|
||||
should_continue = group->proc(group, thread, entry->work);
|
||||
@ -103,8 +103,7 @@ s64 thread_group_run (Thread* thread) {
|
||||
for (s64 i = 0; i < info->work_steal_indices.count; i += 1) {
|
||||
entry = get_work(&group->worker_info[i].available);
|
||||
if (entry) {
|
||||
// #TODO(Log)
|
||||
break; // for
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,7 +226,7 @@ bool shutdown (Thread_Group* group, s32 timeout_milliseconds = -1) {
|
||||
|
||||
// Should have a shutdown_and_reset option too (see how I did it in prototyping-main)
|
||||
|
||||
void add_work (Thread_Group* group, void* work) { // string logging_name
|
||||
void add_work (Thread_Group* group, void* work) {
|
||||
Assert(group->worker_info.count > 0);
|
||||
|
||||
push_allocator(group->allocator);
|
||||
@ -235,7 +234,6 @@ void add_work (Thread_Group* group, void* work) { // string logging_name
|
||||
// Make a work entry, a linked list node that lets us queue and unqueue
|
||||
Work_Entry* entry = New<Work_Entry>();
|
||||
entry->work = work;
|
||||
// entry->logging_name = "";
|
||||
entry->issue_time = GetUnixTimestamp();
|
||||
|
||||
// Choose which thread will run this work.
|
||||
@ -251,8 +249,6 @@ void add_work (Thread_Group* group, void* work) { // string logging_name
|
||||
// Add this node to the linked list of available work for that thread:
|
||||
Work_List* list = &group->worker_info[thread_index].available;
|
||||
add_work(list, entry);
|
||||
|
||||
// #TODO: Log if necessary.
|
||||
}
|
||||
|
||||
ArrayView<void*> get_completed_work (Thread_Group* group) {
|
||||
@ -290,14 +286,13 @@ ArrayView<void*> get_completed_work (Thread_Group* group) {
|
||||
|
||||
if (!completed) continue;
|
||||
|
||||
// Reserve the output array. Probably doesn't help much. Note that
|
||||
// we are maybe adding small numbers of results over a larger number
|
||||
// of cores. Really if we want to be efficient here, we can build
|
||||
// #TODO: #Thread_Group #array_reserve - try to do this in two passes:
|
||||
// Note that we are maybe adding small numbers of results over a larger
|
||||
// number of cores. Really, if we want to be efficient here, we can build
|
||||
// a larger linked list out of the mini-lists we gather, and accumulate
|
||||
// the counts, then do the reserve all in one batch when we are done
|
||||
// looking at the threads. For simplicity this has not yet been done,
|
||||
// but it may not be much more complicated, actually.
|
||||
// #TODO(Musa) - do this^
|
||||
array_reserve(results, results.count + new_count);
|
||||
s64 old_count = results.count;
|
||||
|
||||
@ -305,8 +300,6 @@ ArrayView<void*> get_completed_work (Thread_Group* group) {
|
||||
array_add(results, completed->work);
|
||||
Work_Entry* next = completed->next;
|
||||
|
||||
// #TODO(Log)
|
||||
|
||||
internal_free(completed);
|
||||
completed = next;
|
||||
}
|
||||
@ -314,6 +307,6 @@ ArrayView<void*> get_completed_work (Thread_Group* group) {
|
||||
Assert(results.count == old_count + new_count);
|
||||
}
|
||||
|
||||
return {};
|
||||
return ArrayView<void*>(results);
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,6 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
struct Thread;
|
||||
// really hacky forward declares.
|
||||
struct Work_Entry;
|
||||
struct Worker_Info;
|
||||
@ -108,5 +107,4 @@ struct Thread_Group {
|
||||
bool initialized = false;
|
||||
bool started = false;
|
||||
bool should_exit = false;
|
||||
// bool enable_logging;
|
||||
};
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
// #TODO: #OS_Win32
|
||||
// [ ] #Thread cleanup: in `thread_deinit` is there any requirement to cleanup child threads?
|
||||
// [ ] #Exception handling code in `Win32_Exception_Filter`
|
||||
// [ ] #cpuid - enumerate CPUs and Thread count (current implementation doesn't work)
|
||||
|
||||
#if OS_WINDOWS
|
||||
constexpr s64 FILETIME_TO_UNIX = 116444736000000000i64;
|
||||
f64 GetUnixTimestamp() {
|
||||
f64 GetUnixTimestamp () {
|
||||
FILETIME fileTime;
|
||||
GetSystemTimePreciseAsFileTime(&fileTime);
|
||||
s64 ticks = ((s64)fileTime.dwHighDateTime << (s64)32) | (s64)fileTime.dwLowDateTime;
|
||||
return (ticks - FILETIME_TO_UNIX) / (10.0 * 1000.0 * 1000.0);
|
||||
}
|
||||
s64 GetUnixTimestampNanoseconds() {
|
||||
s64 GetUnixTimestampNanoseconds () {
|
||||
FILETIME fileTime;
|
||||
GetSystemTimePreciseAsFileTime(&fileTime);
|
||||
|
||||
@ -54,7 +59,7 @@ internal b32 win32_g_is_quiet = 0; // No console output
|
||||
internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) {
|
||||
if (win32_g_is_quiet) { ExitProcess(1); }
|
||||
|
||||
static volatile LONG first = 0;
|
||||
local_persist volatile LONG first = 0;
|
||||
if(InterlockedCompareExchange(&first, 1, 0) != 0)
|
||||
{ // prevent failures in other threads to popup same message box
|
||||
// this handler just shows first thread that crashes
|
||||
@ -62,7 +67,7 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs)
|
||||
for (;;) Sleep(1000);
|
||||
}
|
||||
|
||||
// #TODO: Exception handling code.
|
||||
// #Exception handling code (TODO)
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -98,7 +103,6 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
|
||||
|
||||
push_arena(get_thread_context()->arena);
|
||||
|
||||
// #TODO: Need to write Win32 abstraction layer first.
|
||||
{ OS_System_Info* info = &os_state_w32.system_info;
|
||||
info->logical_processor_count = (s32)sysinfo.dwNumberOfProcessors;
|
||||
info->page_size = sysinfo.dwPageSize;
|
||||
@ -109,6 +113,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
|
||||
info->large_pages_allowed = false;
|
||||
info->process_id = GetCurrentProcessId();
|
||||
}
|
||||
// #cpuid
|
||||
/*{ OS_System_Info* info = &os_state_w32.system_info;
|
||||
// [ ] Extract input args
|
||||
u32 length;
|
||||
@ -148,7 +153,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
|
||||
info->physical_core_count = (s32)all_cpus_count;
|
||||
info->primary_core_count = (s32)performance_core_count;
|
||||
}
|
||||
// info->secondary_core_count = #TODO;
|
||||
// info->secondary_core_count = ;
|
||||
*/
|
||||
{ OS_System_Info* info = &os_state_w32.system_info;
|
||||
u8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0};
|
||||
@ -224,7 +229,6 @@ internal void thread_deinit (Thread* thread) {
|
||||
}
|
||||
thread->os_thread.windows_thread = nullptr;
|
||||
|
||||
// #TODO: Thread cleanup:
|
||||
arena_delete(thread->context->temp);
|
||||
arena_delete(thread->context->arena);
|
||||
}
|
||||
@ -253,7 +257,7 @@ internal void lock (Mutex* mutex) {
|
||||
internal void unlock (Mutex* mutex) {
|
||||
LeaveCriticalSection(&mutex->csection);
|
||||
}
|
||||
internal void semaphore_init (Semaphore* sem, s32 initial_value = 0) {
|
||||
internal void semaphore_init (Semaphore* sem, s32 initial_value) {
|
||||
Assert(initial_value >= 0);
|
||||
sem->event = CreateSemaphoreW(nullptr, initial_value, 0x7fffffff, nullptr);
|
||||
}
|
||||
@ -264,13 +268,7 @@ internal void signal (Semaphore* sem) {
|
||||
ReleaseSemaphore(sem->event, 1, nullptr);
|
||||
}
|
||||
|
||||
enum class Wait_For_Result : s32 {
|
||||
SUCCESS = 0,
|
||||
TIMEOUT = 1,
|
||||
ERROR = 2 // can't use ERROR because of Windows.h *sigh*
|
||||
};
|
||||
|
||||
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds = -1) {
|
||||
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds) {
|
||||
DWORD res = 0;
|
||||
if (milliseconds < 0) {
|
||||
res = WaitForSingleObject(sem->event, INFINITE);
|
||||
@ -292,7 +290,7 @@ internal void condition_variable_init (Condition_Variable* cv) {
|
||||
internal void condition_variable_destroy (Condition_Variable* cv) {
|
||||
// No action required.
|
||||
}
|
||||
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1) {
|
||||
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms) {
|
||||
SleepConditionVariableCS(&cv->condition_variable, &mutex->csection, (DWORD)wait_time_ms);
|
||||
}
|
||||
internal void wake (Condition_Variable* cv) {
|
||||
@ -301,3 +299,18 @@ internal void wake (Condition_Variable* cv) {
|
||||
internal void wake_all (Condition_Variable* cv) {
|
||||
WakeAllConditionVariable(&cv->condition_variable);
|
||||
}
|
||||
|
||||
// #window_creation
|
||||
Window_Type create_window (string new_window_name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// #TODO: #window_creation
|
||||
// [ ] resize_window
|
||||
// [ ] position_window
|
||||
// [ ] toggle_fullscreen
|
||||
// [ ] get_dimensions
|
||||
|
||||
// #TODO: #window_interaction (mouse/keyboard)
|
||||
// [ ] get_mouse_pointer_position
|
||||
// [ ] ... What APIs do I need for Keyboard
|
||||
32
lib/OS/OS_Win32.h
Normal file
32
lib/OS/OS_Win32.h
Normal file
@ -0,0 +1,32 @@
|
||||
f64 GetUnixTimestamp ();
|
||||
s64 GetUnixTimestampNanoseconds ();
|
||||
|
||||
struct Condition_Variable;
|
||||
struct Semaphore;
|
||||
struct Mutex;
|
||||
struct OS_Thread;
|
||||
|
||||
enum class Wait_For_Result : s32 {
|
||||
SUCCESS = 0,
|
||||
TIMEOUT = 1,
|
||||
ERROR = 2 // can't use ERROR because of Windows.h *sigh*
|
||||
};
|
||||
|
||||
internal void mutex_init (Mutex* mutex);
|
||||
internal void mutex_destroy (Mutex* mutex);
|
||||
internal void lock (Mutex* mutex);
|
||||
internal void unlock (Mutex* mutex);
|
||||
|
||||
internal void semaphore_init (Semaphore* sem, s32 initial_value = 0);
|
||||
internal void semaphore_destroy (Semaphore* sem);
|
||||
internal void signal (Semaphore* sem);
|
||||
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds = -1);
|
||||
|
||||
internal void condition_variable_init (Condition_Variable* cv);
|
||||
internal void condition_variable_destroy (Condition_Variable* cv);
|
||||
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1);
|
||||
internal void wake (Condition_Variable* cv);
|
||||
internal void wake_all (Condition_Variable* cv);
|
||||
|
||||
// #window_creation
|
||||
typedef HWND Window_Type;
|
||||
17
lib_main.cpp
17
lib_main.cpp
@ -2,29 +2,34 @@
|
||||
// translation unit.
|
||||
// lib_main.cpp can be treated as a single-header library and added to a project like that.
|
||||
|
||||
// #TODO: This is quite disorganized. There must be a better way to do this by moving the
|
||||
// #TODO: #Library This is quite disorganized. There must be a better way to do this by moving the
|
||||
// typedefs and procedures that require forward declaration to the top with a metaprogram.
|
||||
// [ ] Linux / MacOS Ports
|
||||
|
||||
#include "lib/meta_generated.h"
|
||||
#include "lib/Base/Base.h"
|
||||
#include "lib/Base/Allocator.h"
|
||||
#include "lib/Base/Array.h"
|
||||
#include "lib/Base/String.cpp"
|
||||
#include "lib/Base/General_Purpose_Allocator.h"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "lib/OS/OS_Win32.h"
|
||||
#endif
|
||||
|
||||
#include "lib/Base/Arena.h"
|
||||
#include "lib/Base/Arena_Array.h"
|
||||
#include "lib/Base/String.cpp"
|
||||
#include "lib/Base/ErrorType.cpp"
|
||||
#include "lib/Base/Arena_Table.cpp"
|
||||
#include "lib/Base/Base_Thread_Context.h"
|
||||
#include "lib/Base/Expandable_Arena.h"
|
||||
|
||||
#include "lib/Base/ErrorCodes.cpp"
|
||||
#include "lib/Base/Arena.cpp"
|
||||
|
||||
#include "lib/Base/Base_Thread_Context.cpp"
|
||||
#include "lib/Base/Expandable_Arena.cpp"
|
||||
#include "lib/Base/Allocator.cpp"
|
||||
#include "lib/Base/General_Purpose_Allocator.cpp"
|
||||
#include "lib/Base/Basic.cpp"
|
||||
|
||||
// OS-Abstraction Layer
|
||||
#include "lib/Base/Threads.cpp"
|
||||
@ -38,7 +43,7 @@
|
||||
// #include "imgui-docking.cpp"
|
||||
|
||||
// #if OS_LINUX..
|
||||
// #include "src/OS_Linux.cpp" // #TODO: Future.
|
||||
// #include "src/OS_Linux.cpp"
|
||||
// #if OS_MACOS..
|
||||
// #include "src/OS_MacOS.cpp" // #TODO: Future.
|
||||
// #include "src/OS_MacOS.cpp"
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ internal void Main_Entry_Point (int argc, WCHAR **argv);
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "ImGui_Supplementary.cpp"
|
||||
|
||||
void ImGui_Application () {
|
||||
@ -80,7 +79,7 @@ void ImGui_Application () {
|
||||
ImGui_ImplWin32_Init(hwnd);
|
||||
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
|
||||
|
||||
// #TODO: Load fonts:
|
||||
// #TODO: #ImGUI - Load fonts:
|
||||
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
|
||||
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
|
||||
@ -135,16 +134,30 @@ void ImGui_Application () {
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// Simple dockspace:
|
||||
ImGui::DockSpaceOverViewport();
|
||||
|
||||
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
|
||||
if (show_demo_window) {
|
||||
// ImGui::ShowDemoWindow(&show_demo_window);
|
||||
{
|
||||
ImGui::Begin("Hello, world!");
|
||||
|
||||
if (ImGui::Button("Create New Window")) {
|
||||
// I think that create_window should take few parameters, and we have other APIs for
|
||||
// styling, positioning, etc. I just call this and want to get a window.
|
||||
// auto new_window = create_window(window_name);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Position recently created Window")) {
|
||||
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
||||
const f32 clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
||||
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
|
||||
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
@ -158,7 +171,7 @@ void ImGui_Application () {
|
||||
|
||||
// Present
|
||||
HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
|
||||
//HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
|
||||
// HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
|
||||
g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
|
||||
} // while (!done)
|
||||
|
||||
@ -172,13 +185,38 @@ void ImGui_Application () {
|
||||
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
}
|
||||
|
||||
string string_literal_example = "Hello, I am a string literal.";
|
||||
|
||||
internal void Main_Entry_Point (int argc, WCHAR **argv) {
|
||||
temp_reset();
|
||||
// tip: use auto_reset or auto_release with `get_thread_context()->arena`
|
||||
ImGui_Application();
|
||||
|
||||
// #TODO
|
||||
debug_break();
|
||||
|
||||
// String builder example:
|
||||
// OK. I can work with this.
|
||||
/*
|
||||
auto sb = new_string_builder(Arena_Reserve::Size_64K);
|
||||
append(sb, "string_literal_example");
|
||||
append(sb, " ");
|
||||
print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n");
|
||||
append(sb, " > ");
|
||||
print_to_builder(sb, "some size_t: %u", (u64)3982739867);
|
||||
append(sb, "\n");
|
||||
|
||||
// auto result = string_view(sb);
|
||||
auto result = builder_to_string(sb);
|
||||
printf((char*)result.data);
|
||||
|
||||
auto array = array_from_values<s32>(6,7,8,9,10,51);
|
||||
auto view = array_view_from_values<s32>(1,2,3,4,5);
|
||||
*/
|
||||
|
||||
// ImGui_Application();
|
||||
|
||||
|
||||
// #TODO: #Main - `Main_Entry_Point`
|
||||
// [ ] Setup Mouse and Keyboard Inputs
|
||||
// [ ] Launch second thread
|
||||
// OS_Create_Window();
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user