diff --git a/src/core/core_ctx.h b/src/core/core_ctx.h index 1b45429..a0268c9 100644 --- a/src/core/core_ctx.h +++ b/src/core/core_ctx.h @@ -15,6 +15,7 @@ struct thread_ctx_t { void *te_ctx; logger_t log; + int thread_index; }; typedef enum { diff --git a/src/core/core_log.h b/src/core/core_log.h index a941592..79c961e 100644 --- a/src/core/core_log.h +++ b/src/core/core_log.h @@ -27,7 +27,7 @@ fn void log_basef(log_level_t level, s8_t file_and_line, const char *str, ...); #if PLATFORM_DEBUG_ASSERT #define assert_expr(x) (!(x) && (os_error_box(FILE_AND_LINE ": assertion failed: " #x "\n"), debug_break())) -#define assert(x) do { static int once = 1; for (;once;once=0) assert_expr(x); } while(0) +#define assert(x) do { assert_expr(x); } while(0) #else #define assert_expr(x) (void)(x) #define assert(x) do { (void)(x); } while(0) diff --git a/src/core/core_string.c b/src/core/core_string.c index b26f2b4..dca76c8 100644 --- a/src/core/core_string.c +++ b/src/core/core_string.c @@ -429,7 +429,14 @@ fn sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags) { s8_seek_t find_flag = flags & s8_split_ignore_case ? s8_seek_ignore_case : s8_seek_none; while (s8_seek(string, find, find_flag, &index)) { s8_t before_match = s8_make(string.str, index); - sb8_append(&result, before_match); + if (flags & s8_split_cleanup) { + before_match = s8_trim(before_match); + if (before_match.len) { + sb8_append(&result, before_match); + } + } else { + sb8_append(&result, before_match); + } if (flags & s8_split_inclusive) { s8_t match = s8_make(string.str + index, find.len); sb8_append(&result, match); diff --git a/src/core/core_string.h b/src/core/core_string.h index a660c61..e06375c 100644 --- a/src/core/core_string.h +++ b/src/core/core_string.h @@ -35,6 +35,7 @@ enum { s8_split_none = 0, s8_split_ignore_case = 1, s8_split_inclusive = 2, + s8_split_cleanup = 4, }; enum { s8_ignore_case = 1 }; diff --git a/src/prototype/main.c b/src/prototype/main.c index aa0ebcf..53361de 100644 --- a/src/prototype/main.c +++ b/src/prototype/main.c @@ -15,7 +15,7 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) { tcx = thread_ctx; if (frame->first_event->kind == app_event_kind_init) { - // run_all_tests(); + run_all_tests(); mt_tweak_f32(font_size, 30, 4, 200); mt_tweak_f32(_font_size, 30, 30, 30); diff --git a/src/prototype/prototype.gen.c b/src/prototype/prototype.gen.c index 4e9c88d..e304e93 100644 --- a/src/prototype/prototype.gen.c +++ b/src/prototype/prototype.gen.c @@ -13,4 +13,5 @@ void run_all_tests(void) { test_array(); ui_test_text_replace(); buffer16_test(); + testing_out_things(); }// run_all_tests() diff --git a/src/prototype/transcript_browser.c b/src/prototype/transcript_browser.c index a7319e7..60f0913 100644 --- a/src/prototype/transcript_browser.c +++ b/src/prototype/transcript_browser.c @@ -91,4 +91,224 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i ui_draw(); rn_end(); ui_end_frame(); +} + +/////////////////////////////// +// work queue + +#define WORK_FUNCTION(name) void name(void *data) +typedef WORK_FUNCTION(work_queue_callback_t); + +typedef struct { + work_queue_callback_t *callback; + void *data; +} work_queue_entry_t; + +typedef struct work_queue_t work_queue_t; +struct work_queue_t { + int32_t thread_count; + work_queue_entry_t entries[256]; + int64_t volatile index_to_write; + int64_t volatile index_to_read; + int64_t volatile completion_index; + int64_t volatile completion_goal; + void *semaphore; +}; + +typedef struct thread_startup_info_t thread_startup_info_t; +struct thread_startup_info_t { + uint32_t thread_id; + int32_t thread_index; + work_queue_t *queue; +}; + +fn int64_t atomic_increment(volatile int64_t *i) { + return InterlockedIncrement64(i); +} + +fn int64_t atomic_compare_and_swap(volatile int64_t *dst, int64_t exchange, int64_t comperand) { + return InterlockedCompareExchange64(dst, exchange, comperand); +} + +fn void work_queue_push(work_queue_t *wq, void *data, work_queue_callback_t *callback) { + uint32_t new_index = (wq->index_to_write + 1) % lengthof(wq->entries); + assert(new_index != wq->index_to_read); + + work_queue_entry_t *entry = wq->entries + wq->index_to_write; + entry->data = data; + entry->callback = callback; + + wq->completion_goal += 1; + _WriteBarrier(); + wq->index_to_write = new_index; + ReleaseSemaphore(wq->semaphore, 1, 0); +} + +fn b8 work_queue_try_doing_work(work_queue_t *wq) { + b8 should_sleep = false; + int64_t original_index_to_read = wq->index_to_read; + int64_t new_index_to_read = (original_index_to_read + 1) % lengthof(wq->entries); + if (original_index_to_read != wq->index_to_write) { + int64_t index = atomic_compare_and_swap(&wq->index_to_read, new_index_to_read, original_index_to_read); + if (index == original_index_to_read) { + work_queue_entry_t *entry = wq->entries + index; + entry->callback(entry->data); + atomic_increment(&wq->completion_index); + } + } else { + should_sleep = true; + } + return should_sleep; +} + +fn DWORD WINAPI work_queue_thread_entry(LPVOID param) { + thread_startup_info_t *ti = (thread_startup_info_t *)param; + + os_core_init(); + tcx->thread_index = ti->thread_index; + for (;;) { + if (work_queue_try_doing_work(ti->queue)) { + WaitForSingleObject(ti->queue->semaphore, INFINITE); + } + } +} + +fn void work_queue_init(work_queue_t *queue, uint32_t thread_count, thread_startup_info_t *info) { + queue->thread_count = thread_count; + queue->index_to_read = 0; + queue->index_to_write = 0; + queue->completion_index = 0; + queue->completion_goal = 0; + queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS); + assert(queue->semaphore != INVALID_HANDLE_VALUE); + + for (uint32_t i = 0; i < thread_count; i++) { + thread_startup_info_t *ti = info + i; + ti->thread_index = i; + ti->queue = queue; + + DWORD thread_id = 0; + HANDLE thread_handle = CreateThread(0, 0, work_queue_thread_entry, ti, 0, &thread_id); + assert(thread_handle != INVALID_HANDLE_VALUE); + ti->thread_id = thread_id; + CloseHandle(thread_handle); + } +} + +fn b8 work_queue_is_complete(work_queue_t *wq) { + b8 result = wq->completion_goal == wq->completion_index; + return result; +} + +fn void work_queue_wait(work_queue_t *wq) { + while (!work_queue_is_complete(wq)) { + work_queue_try_doing_work(wq); + } +} + +/////////////////////////////// +// mutex + +typedef struct mutex_t mutex_t; +struct mutex_t { + void *platform; +}; + +fn mutex_t mutex_create(void) { + HANDLE handle = CreateMutex(NULL, FALSE, NULL); + assert(handle); + mutex_t result = (mutex_t){(void *)handle}; + return result; +} + +fn void mutex_destroy(mutex_t mutex) { + BOOL result = CloseHandle(mutex.platform); + assert(result != 0); + unused(result); +} + +fn void mutex_lock(mutex_t mutex) { + DWORD result = WaitForSingleObject(mutex.platform, INFINITE); + assert(result == WAIT_OBJECT_0); + unused(result); +} + +fn void mutex_unlock(mutex_t mutex) { + BOOL result = ReleaseMutex(mutex.platform); + assert(result != 0); + unused(result); +} + +/////////////////////////////// +// parse srt + +typedef struct srt_entry_t srt_entry_t; +struct srt_entry_t { + srt_entry_t *next; + u16 hour, minute, second; + s8_t string; +}; + +typedef struct srt_result_t srt_result_t; +struct srt_result_t { + srt_entry_t *first; + srt_entry_t *last; +}; + +srt_result_t srt_parse(ma_arena_t *arena, s8_t filename) { + s8_t content = os_read(arena, filename); + sb8_t lines = s8_split(arena, content, s8("\n"), s8_split_cleanup); + + // srt format looks like this: + // 1 + // 00:00:01,000 --> 00:00:05,000 + // This is the first subtitle. + // + // or: + // section number + // time interval + // text + int section_number = 1; + srt_result_t result = {0}; + for (sb8_node_t *it = lines.first; it;) { + // parse section number + long num = strtol(it->str, NULL, 10); + assert(section_number == num); + section_number += 1; + it = it->next; + + // parse start of time interval + srt_entry_t entry = {0}; + entry.hour = (u16)strtol(it->str, NULL, 10); + entry.minute = (u16)strtol(it->str + 3, NULL, 10); + entry.second = (u16)strtol(it->str + 6, NULL, 10); + it = it->next; + + // parse text + s8_t next_section_number = s8_printf(arena, "%d", section_number); + while (it && !s8_are_equal(next_section_number, it->string)) { + b8 duplicate = result.last && s8_are_equal(it->string, result.last->string); + if (!duplicate) { + srt_entry_t *entry_copy = ma_push_type(arena, srt_entry_t); + entry_copy[0] = entry; + entry_copy->string = it->string; + SLLQ_APPEND(result.first, result.last, entry_copy); + } + it = it->next; + } + } + + return result; +} + +fn_test void testing_out_things(void) { + mutex_t mutex = mutex_create(); + mutex_lock(mutex); + mutex_unlock(mutex); + mutex_destroy(mutex); + + ma_temp_t scratch = ma_begin_scratch(); + srt_result_t result = srt_parse(scratch.arena, s8("D:\\videos\\zizek\\‘Hegel and the Spirit of Distrust’ Sven-Olov Wallenstein & Slavoj Žižek in Conversation [a7gEN-Rxr4c].en.srt")); + unused(result); + ma_end_scratch(scratch); } \ No newline at end of file diff --git a/src/ui/buffer16.c b/src/ui/buffer16.c index 7a7cfed..4506b93 100644 --- a/src/ui/buffer16.c +++ b/src/ui/buffer16.c @@ -947,6 +947,4 @@ fn_test void buffer16_test(void) { } ma_end_scratch(scratch); - - exit(0); } diff --git a/src/ui/ui.c b/src/ui/ui.c index 9815a8f..7e6be3d 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -454,7 +454,9 @@ fn void ui_text_input_draw(ui_box_t *box) { r2f32_t caret_rect = r2f32(co.rect.min.x + size_front.x, co.rect.min.y, co.rect.min.x + size_front.x + 1, co.rect.min.y + ui_em(1)); rn_draw_rect(caret_rect, co.text_color); } - rn_draw_rect_border(co.rect, co.border_color, box->border_thickness); + // rn_draw_rect_border(co.rect, co.border_color, 1); + r2f32_t bottom = r2f32_cut_bottom(&co.rect, box->border_thickness); + rn_draw_rect(bottom, co.text_color); } fn void ui_default_draw_box(ui_box_t *box) {