srt, work_queue, mutex
This commit is contained in:
@@ -15,6 +15,7 @@ struct thread_ctx_t {
|
|||||||
void *te_ctx;
|
void *te_ctx;
|
||||||
|
|
||||||
logger_t log;
|
logger_t log;
|
||||||
|
int thread_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ fn void log_basef(log_level_t level, s8_t file_and_line, const char *str, ...);
|
|||||||
|
|
||||||
#if PLATFORM_DEBUG_ASSERT
|
#if PLATFORM_DEBUG_ASSERT
|
||||||
#define assert_expr(x) (!(x) && (os_error_box(FILE_AND_LINE ": assertion failed: " #x "\n"), debug_break()))
|
#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
|
#else
|
||||||
#define assert_expr(x) (void)(x)
|
#define assert_expr(x) (void)(x)
|
||||||
#define assert(x) do { (void)(x); } while(0)
|
#define assert(x) do { (void)(x); } while(0)
|
||||||
|
|||||||
@@ -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;
|
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)) {
|
while (s8_seek(string, find, find_flag, &index)) {
|
||||||
s8_t before_match = s8_make(string.str, 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) {
|
if (flags & s8_split_inclusive) {
|
||||||
s8_t match = s8_make(string.str + index, find.len);
|
s8_t match = s8_make(string.str + index, find.len);
|
||||||
sb8_append(&result, match);
|
sb8_append(&result, match);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ enum {
|
|||||||
s8_split_none = 0,
|
s8_split_none = 0,
|
||||||
s8_split_ignore_case = 1,
|
s8_split_ignore_case = 1,
|
||||||
s8_split_inclusive = 2,
|
s8_split_inclusive = 2,
|
||||||
|
s8_split_cleanup = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { s8_ignore_case = 1 };
|
enum { s8_ignore_case = 1 };
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
||||||
tcx = thread_ctx;
|
tcx = thread_ctx;
|
||||||
if (frame->first_event->kind == app_event_kind_init) {
|
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, 4, 200);
|
||||||
mt_tweak_f32(_font_size, 30, 30, 30);
|
mt_tweak_f32(_font_size, 30, 30, 30);
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ void run_all_tests(void) {
|
|||||||
test_array();
|
test_array();
|
||||||
ui_test_text_replace();
|
ui_test_text_replace();
|
||||||
buffer16_test();
|
buffer16_test();
|
||||||
|
testing_out_things();
|
||||||
}// run_all_tests()
|
}// run_all_tests()
|
||||||
|
|||||||
@@ -92,3 +92,223 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
|||||||
rn_end();
|
rn_end();
|
||||||
ui_end_frame();
|
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);
|
||||||
|
}
|
||||||
@@ -947,6 +947,4 @@ fn_test void buffer16_test(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ma_end_scratch(scratch);
|
ma_end_scratch(scratch);
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
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(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) {
|
fn void ui_default_draw_box(ui_box_t *box) {
|
||||||
|
|||||||
Reference in New Issue
Block a user