From e7cdf6fe804e1c01179afc3e40b761144cd0b057 Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 25 Jan 2023 00:26:24 +0000 Subject: Ported add_int64 and sub_int64 to jai. --- unused.jai | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 unused.jai (limited to 'unused.jai') diff --git a/unused.jai b/unused.jai new file mode 100644 index 0000000..3529a46 --- /dev/null +++ b/unused.jai @@ -0,0 +1,50 @@ +checked_add :: (a: $T, b: T) -> result: T, overflow: bool +#modify { + if T.type == .INTEGER return; + T = null; +} +{ + overflow: bool; + result: T = a + b; + + info := type_info(T); + if info.signed { + // (+A) + (+B) = −C + // (−A) + (−B) = +C + if ((a > 0) && (b > 0) && (result < 0)) || ((a < 0) && (b < 0) && (result > 0)) { + overflow = true; + } + } else { + if result < a { + overflow = true; + } + } + + return result, overflow; +} + +checked_sub :: (a: $T, b: T) -> result: T, overflow: bool +#modify { + if T.type == .INTEGER return; + T = null; +} +{ + overflow: bool; + result: T = a - b; + + info := type_info(T); + if info.signed { + // (+A) − (−B) = −C + // (−A) − (+B) = +C + if ((a > 0) && (b < 0) && (result < 0)) || ((a < 0) && (b > 0) && (result > 0)) { + overflow = true; + } + } else { + if result > a { + overflow = true; + } + } + + return result, overflow; +} + -- cgit v1.2.3 From 027f9648e00ba40c1e64da0827c93299106503dc Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 10 Feb 2023 19:35:41 +0000 Subject: Temporary code to check for memory owners. --- ttt.jai | 82 +++++++++++++++++--------------------------------------------- unused.jai | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 61 deletions(-) (limited to 'unused.jai') diff --git a/ttt.jai b/ttt.jai index fc2e5a3..345e3cc 100644 --- a/ttt.jai +++ b/ttt.jai @@ -1226,68 +1226,30 @@ main :: () { } app_directory = join(app_directory, "/", APP_FOLDER_NAME, allocator = temp); // TODO Change to default allocator because we want to keep this during runtime. - // TODO DEBUG - // Check what's going on with the temp allocator: - // Is it really the responsible for these paths? - // It seems that the next beta (after 0.1.055b) compiler allows us to check this pretty easily. - /* - // - // An example that uses several different allocators, then asks them all - // who owns which memory. - // - // Note that this is probably not the kind of thing you want to do at runtime - // in the steady state, as it may not be very fast, but it could be a very helpful - // debugging facility. - // - - - #import "Basic"; - #import "Pool"; - #import "Flat_Pool"; - #import "rpmalloc"; - - main :: () { - pool: Pool; - flat: Flat_Pool; - - a := context.default_allocator; - b := Allocator.{pool_allocator_proc, *pool}; - c := Allocator.{flat_pool_allocator_proc, *flat}; - d := Allocator.{rpmalloc_allocator_proc, null}; - - d.proc(.STARTUP, 0, 0, null, null); // rpmalloc needs explicit init right now, but others don't. - - ma := alloc(1000, allocator=a); - mb := alloc(1000, allocator=b); - mc := alloc(1000, allocator=c); - md := alloc(1000, allocator=d); - - report_who_owns(ma, a, b, c, d); - report_who_owns(mb, a, b, c, d); - report_who_owns(mc, a, b, c, d); - report_who_owns(md, a, b, c, d); - } - - report_who_owns :: (memory: *void, allocators: .. Allocator) { - someone_owns_this := false; - - print("Querying all allocators for address: %\n", memory); - - for allocators { - caps, name := get_capabilities(it); - assert((caps & .IS_THIS_YOURS) != 0); // It had better be claiming to support this! - - yours := cast(bool) it.proc(.IS_THIS_YOURS, 0, 0, memory, it.data); - print("[%] says \"%\"\n", yours, name); - - someone_owns_this ||= yours; - } - - assert(someone_owns_this); - } +// print(">%:%:%\n", temp, temp.data, context.temporary_storage.data); + print("#%\n", <%:%\n", app_directory.count, *app_directory.data); +// free(app_directory); + + /* WIP TEST THIS +some_pointer : *void = get_something(); +is_it_mine : bool = xx allocator.allocator_proc(.IS_THIS_YOURS, 0, 0, some_pointer, null); */ -// free(app_directory); + + reset_temporary_storage(); + a := join("wow", "testsesesrserse", "2222222222222222"); + b := join("wow", "testsesesrserse", "2222222222222222"); + c := join("wow", "testsesesrserse", "2222222222222222"); + d := join("wow", "testsesesrserse", "2222222222222222"); print("bazinga: '%'\n", app_directory); // if make_directory_if_it_does_not_exist(app_directory, false) == false { // print_error("Failed to create app directory '%'.", app_directory); diff --git a/unused.jai b/unused.jai index 3529a46..04997c5 100644 --- a/unused.jai +++ b/unused.jai @@ -1,3 +1,71 @@ + + + +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + + +// Check what's going on with the temp allocator: +// Is it really the responsible for these paths? +// It seems that the next beta (after 0.1.055b) compiler allows us to check this pretty easily. +// +// +// An example that uses several different allocators, then asks them all +// who owns which memory. +// +// Note that this is probably not the kind of thing you want to do at runtime +// in the steady state, as it may not be very fast, but it could be a very helpful +// debugging facility. +// + +#import "Basic"; +#import "Pool"; +#import "Flat_Pool"; +#import "rpmalloc"; + +main :: () { + pool: Pool; + flat: Flat_Pool; + + a := context.default_allocator; + b := Allocator.{pool_allocator_proc, *pool}; + c := Allocator.{flat_pool_allocator_proc, *flat}; + d := Allocator.{rpmalloc_allocator_proc, null}; + + d.proc(.STARTUP, 0, 0, null, null); // rpmalloc needs explicit init right now, but others don't. + + ma := alloc(1000, allocator=a); + mb := alloc(1000, allocator=b); + mc := alloc(1000, allocator=c); + md := alloc(1000, allocator=d); + + report_who_owns(ma, a, b, c, d); + report_who_owns(mb, a, b, c, d); + report_who_owns(mc, a, b, c, d); + report_who_owns(md, a, b, c, d); +} + +report_who_owns :: (memory: *void, allocators: .. Allocator) { + someone_owns_this := false; + + print("Querying all allocators for address: %\n", memory); + + for allocators { + caps, name := get_capabilities(it); + assert((caps & .IS_THIS_YOURS) != 0); // It had better be claiming to support this! + + yours := cast(bool) it.proc(.IS_THIS_YOURS, 0, 0, memory, it.data); + print("[%] says \"%\"\n", yours, name); + + someone_owns_this ||= yours; + } + + assert(someone_owns_this); +} + + +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + + checked_add :: (a: $T, b: T) -> result: T, overflow: bool #modify { if T.type == .INTEGER return; @@ -47,4 +115,3 @@ checked_sub :: (a: $T, b: T) -> result: T, overflow: bool return result, overflow; } - -- cgit v1.2.3 From 93943539ab2c553d68677d273ca08b135006b09a Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 27 Mar 2023 23:49:08 +0100 Subject: Added step iterator. --- unused.jai | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'unused.jai') diff --git a/unused.jai b/unused.jai index 04997c5..b995d0d 100644 --- a/unused.jai +++ b/unused.jai @@ -1,4 +1,32 @@ +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + + +Step_Iterator :: struct { + min: int; + max: int; + step: int; +} + +step_iterator :: (min: int, max: int, step: int) -> Step_Iterator { + return .{ min, max, step }; +} +for_expansion :: (iterator: Step_Iterator, body: Code, flags: For_Flags) #expand { + iteration_count: int; + for <=cast(bool)(flags & .REVERSE) i: iterator.min..iterator.max { + iteration_count += 1; + if iteration_count % iterator.step == 0 continue; + + `it := i; + `it_index := void; + + #insert body; + } +} + +for step_iterator(0, 10, 2) { + log("%", it); +} // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // -- cgit v1.2.3 From 9b207176f11e00fe0d3cff3ede331ef0b7954b40 Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 6 Apr 2023 00:34:02 +0100 Subject: Fixed mvprintw_time and ported select/active code. --- ttt.jai | 122 ++++++++++++++++++++++++++----------------------------------- unused.jai | 2 + 2 files changed, 54 insertions(+), 70 deletions(-) (limited to 'unused.jai') diff --git a/ttt.jai b/ttt.jai index 4d4421f..45a8bac 100644 --- a/ttt.jai +++ b/ttt.jai @@ -243,7 +243,6 @@ replace_char :: (str: string, find: u8, replace: u8) -> string { // Prints, on row y and column x, the time using 5 characters centered on space. // Returns the result of a call to mvprintw. mvprintw_time :: (y: s32, x: s32, time: s64, space: s32) -> int { - return 0; // TODO NOT WORKING TIME_CHARS :: 5; assert(space >= TIME_CHARS); @@ -260,12 +259,12 @@ mvprintw_time :: (y: s32, x: s32, time: s64, space: s32) -> int { return mvprintw(y, x, "%*s%3jds %*s", left_padding, "", time, right_padding, ""); } else if (time < 100 * SECONDS_IN_HOUR) { - hours := cast(float64)time / cast(float64)SECONDS_IN_HOUR; + hours := time / SECONDS_IN_HOUR; minutes := (time - (hours * SECONDS_IN_HOUR) ) / SECONDS_IN_MINUTE; return mvprintw(y, x, "%*s%02jd:%02jd%*s", left_padding, "", hours, minutes, right_padding, ""); } else if (time < xx (9999.5 * SECONDS_IN_DAY)) { - value := cast(float64)time / cast(float64)SECONDS_IN_DAY; + value := cast(float64) time / SECONDS_IN_DAY; decimals := ifx time >= xx 99.95 * SECONDS_IN_DAY then 0 else ifx time >= xx 9.995 * SECONDS_IN_DAY then 1 else @@ -273,7 +272,7 @@ mvprintw_time :: (y: s32, x: s32, time: s64, space: s32) -> int { return mvprintw(y, x, "%*s%4.*fd%*s", left_padding, "", decimals, value, right_padding, ""); } else if (time < xx (9999.5 * SECONDS_IN_YEAR)) { - value := cast(float64)time / cast(float64)SECONDS_IN_YEAR; + value := cast(float64) time / SECONDS_IN_YEAR; decimals := ifx time >= xx 99.95 * SECONDS_IN_YEAR then 0 else ifx time >= xx 9.995 * SECONDS_IN_YEAR then 1 else @@ -885,43 +884,45 @@ bool append_to_csv(task_st *task, const char *path) { fclose(file); return true; } - +*/ // Selects task by index. // Index gets clamped to [0, db->count[. -void select_task_by_index(database_st *db, ptrdiff_t index) { - assert(db != NULL); - db->selected_task = db->count == 0 ? -1 - : index < 0 ? 0 - : index >= db->count ? db->count - 1 - : index; +select_task_by_index :: (db: *Database, index: s64) { + assert(db != null); + db.selected_idx = ifx db.tasks.count == 0 then -1 else + ifx index < 0 then 0 else + ifx index >= db.tasks.count then db.tasks.count - 1 else + index; } // Selects task by delta relative to currently selected task. -void select_task_by_delta(database_st *db, ptrdiff_t delta) { - assert(db != NULL); - ptrdiff_t idx = (delta > 0 && db->selected_task > PTRDIFF_MAX - delta) ? PTRDIFF_MAX - : (delta < 0 && db->selected_task < PTRDIFF_MIN + delta) ? PTRDIFF_MIN - : db->selected_task + delta; +select_task_by_delta :: (db: *Database, delta: s64) { + assert(db != null); + // TODO I bet there's a better way to do this... maybe use a clamp or range or something. + idx := + ifx (delta > 0 && db.selected_idx > S64_MAX - delta) then S64_MAX else + ifx (delta < 0 && db.selected_idx < S64_MIN - delta) then S64_MIN else + db.selected_idx + delta; select_task_by_index(db, idx); } - +/* // Selects task. -void select_task(database_st *db, task_st *task) { - assert(db != NULL); - assert(task != NULL); - assert(task >= db->tasks && task - db->tasks < db->count); - db->selected_task = task - db->tasks; +select_task :: (db: *Database, task: *Task) { + assert(db != null); + assert(task != null); + assert(task >= db.tasks.data && task - db.tasks.data < db.tasks.count); + db.selected_idx = task - db.tasks.data; } - +*/ // Set active task. // Passing task as NULL de-activates any previously active task. -void set_active_task(database_st *db, task_st *task) { - assert(db != NULL); - assert(task == NULL || (task >= db->tasks && task < &db->tasks[db->count])); +set_active_task :: (db: *Database, task: *Task) { + assert(db != null); + assert(task == null || (task >= db.tasks.data && task <= *db.tasks[db.tasks.count-1])); // TODO Improve this check. update_times(db); - db->active_task = (task == NULL) ? -1 : task - db->tasks; + db.active_idx = ifx (task == null) then -1 else task - db.tasks.data; } - +/* // Returns true when database is full. bool is_database_full(database_st *db) { assert(db != NULL); @@ -1132,9 +1133,9 @@ draw_tui :: (db: *Database, layout: *Layout) { idx_start := (db.selected_idx / layout_tasks_rows) * layout_tasks_rows; // Display up to rows allowed by the layout, or less if reached end of database. idx_stop := idx_start + (ifx layout_tasks_rows > db.tasks.count - idx_start then db.tasks.count - idx_start else layout_tasks_rows); - for idx: idx_start..idx_stop-1 { + for task_idx: idx_start..idx_stop-1 { //for (size_t idx = idx_start; idx < idx_stop; idx++) { - task := *db.tasks[idx]; + task := *db.tasks[task_idx]; y += 1; x = 0; @@ -1158,15 +1159,12 @@ draw_tui :: (db: *Database, layout: *Layout) { // Task times. total_time = 0; for 0..NUM_WEEK_DAYS-1 { - //for (int idx = 0; idx < NUM_WEEK_DAYS; idx++) { x += 1; - - day_idx := (idx + FIRST_DAY_OF_WEEK) % NUM_WEEK_DAYS; - + day_idx := (it + FIRST_DAY_OF_WEEK) % NUM_WEEK_DAYS; column_width = layout.columns[L_DAYS_IDX + day_idx].width; - task_stime := task.times[day_idx]; - total_time = add_int64(total_time, task_stime); - mvprintw_time(xx y, xx x, task_stime, xx column_width); + task_time := task.times[day_idx]; + total_time = add_int64(total_time, task_time); + mvprintw_time(xx y, xx x, task_time, xx column_width); x += column_width; } @@ -1760,18 +1758,15 @@ main :: () { select_task(db, active_task); break; } - - case '\n': - case ' ': { - if (db != &database || selected_task == NULL) { - break; - } - set_active_task(db, (active_task == selected_task) ? NULL : selected_task); + */ + case #char "\n"; #through; + case #char " "; + if (db != *database || selected_task == null) break; + set_active_task(db, ifx (active_task == selected_task) then null else selected_task); active_task = get_active_task(db); trigger_autosave(); - break; - } - + + /* case '\t': { if (db == &database) { if (import_from_csv(&archive, ar_file_path) == false) { @@ -1823,37 +1818,24 @@ main :: () { trigger_autosave(); break; } - - case KEY_HOME: { + */ + case KEY_HOME; select_task_by_index(db, 0); - break; - } - - case KEY_UP: { + + case KEY_UP; select_task_by_delta(db, -1); - break; - } - - case KEY_PPAGE: { + + case KEY_PPAGE; select_task_by_delta(db, -layout_tasks_rows); - break; - } - case KEY_END: { - select_task_by_index(db, db->count-1); - break; - } + case KEY_END; + select_task_by_index(db, db.tasks.count-1); - case KEY_DOWN: { + case KEY_DOWN; select_task_by_delta(db, 1); - break; - } - case KEY_NPAGE: { + case KEY_NPAGE; select_task_by_delta(db, layout_tasks_rows); - break; - } -*/ } if (is_terminal_too_small) { diff --git a/unused.jai b/unused.jai index b995d0d..264fa88 100644 --- a/unused.jai +++ b/unused.jai @@ -1,3 +1,5 @@ + print(">%<", S64_MIN + delta, to_standard_error = true); + // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // -- cgit v1.2.3 From 1c54c3afa2677c001836f84c66cdff1f381e79fe Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 6 Apr 2023 16:51:38 +0100 Subject: Remove obsolete code and allow archive toggle. --- ttt.jai | 144 ++++++++++++++----------------------------------------------- unused.jai | 25 +++++++++++ 2 files changed, 58 insertions(+), 111 deletions(-) (limited to 'unused.jai') diff --git a/ttt.jai b/ttt.jai index ec35973..7b49eff 100644 --- a/ttt.jai +++ b/ttt.jai @@ -328,6 +328,7 @@ get_selected_task :: inline (db: Database) -> *Task { add_task :: (db: *Database, task: Task) -> success: bool { assert(db != null, "Parameter 'db' is null."); + // TODO Remove old implementation... or use the dynamics arrays? // idx := db.tasks.count; // maybe_grow(*db.tasks); // TODO Check for errors? // db.tasks.count += 1; @@ -346,7 +347,6 @@ add_task :: (db: *Database, task: Task) -> success: bool { ifx current_capacity > (S64_MAX >> 1) then S64_MAX else current_capacity << 1; - print("expanding from % to %\n", current_capacity, new_capacity); new_tasks := realloc(db.tasks.data, new_capacity * size_of(Task), current_capacity * size_of(Task)); if (new_tasks == null) { print_error("Failed to expand database."); @@ -758,13 +758,9 @@ import_from_csv :: (db: *Database, path: string) -> bool { data: string; is_using_map := false; if size >= 1<<30 { - print("big file with % MB\n", size>>20); // TODO - map, success = map_entire_file_start(path); - data = map.data; - is_using_map = true; + assert(false, "Parsing big files not implemented yet."); } else { - print("small file with % B\n", size); // TODO data, success = read_entire_file(path); } defer if is_using_map then map_entire_file_end(*map); else free(data.data); @@ -857,7 +853,6 @@ import_from_csv :: (db: *Database, path: string) -> bool { // reset_temporary_storage(); // } } - print("temp: %\n", context.temporary_storage.total_bytes_occupied >> 20); reset_temporary_storage(); return true; @@ -1245,13 +1240,7 @@ free_memory :: () { free(ar_file_path); //reset_temporary_storage(); } - /* -void exit_gracefully(int signal) { - flushinp(); - ungetch('q'); -} - void read_input_to_string_buffer_with_space(int row, int column, int style, int length, int space) { assert(length < string_buffer_size); assert(space < string_buffer_size); @@ -1482,12 +1471,6 @@ main :: () { initialize_tui(); - // TODO Remove this?! - //signal(SIGTERM, exit_gracefully); - //signal(SIGINT, exit_gracefully); - //signal(SIGQUIT, exit_gracefully); - //signal(SIGHUP, exit_gracefully); - layout := *layouts[Layouts.COMPACT]; db := *database; @@ -1766,32 +1749,30 @@ main :: () { */ case #char "\n"; #through; case #char " "; - if (db != *database || selected_task == null) break; + if (db != *database || selected_task == null) + break; // BUG The break does not work inside an if_case. Review this and all other usages. set_active_task(db, ifx (active_task == selected_task) then null else selected_task); active_task = get_active_task(db); trigger_autosave(); - - /* - case '\t': { - if (db == &database) { - if (import_from_csv(&archive, ar_file_path) == false) { - reset_database(&archive); + + case #char "\t"; + if (db == *database) { + if (import_from_csv(*archive, ar_file_path) == false) { + reset_database(*archive); print_error("Failed to load archive."); break; } - db = &archive; + db = *archive; } else { - if (export_to_csv(&archive, ar_file_path) == false) { + if (export_to_csv(*archive, ar_file_path) == false) { print_error("Failed to store archive."); break; } - reset_database(&archive); - db = &database; + reset_database(*archive); + db = *database; } - break; - } - +/* case 'a': case 'A': { if (db != &database || selected_task == NULL || selected_task == active_task) { @@ -1857,86 +1838,27 @@ main :: () { } // Save any unsaved changes. -// show_processing(); + show_processing(); error_saving := false; -// if (db == &archive) { -// if (export_to_csv(&archive, ar_file_path) == false) { -// print_error("Failed to save archive."); -// error_saving |= true; -// } -// } -// if (countdown_to_autosave > 0 || is_autosave_enabled == false) { -// if (store_database(&database, db_file_path) == false) { -// print_error("Failed to save database."); -// error_saving |= true; -// } -// } -// if (error_saving) { -// print_error("Press any key to close."); -// draw_error_window(); -// timeout(INPUT_AWAIT_INF); -// getch(); -// } + if (db == *archive) { + if (export_to_csv(archive, ar_file_path) == false) { + print_error("Failed to save archive."); + error_saving |= true; + } + } + if (countdown_to_autosave > 0 || is_autosave_enabled == false) { + if (store_database(database, db_file_path) == false) { + print_error("Failed to save database."); + error_saving |= true; + } + } + if (error_saving) { + print_error("Press any key to close."); + draw_error_window(); + timeout(INPUT_AWAIT_INF); + getch(); + } endwin(); exit(xx ifx error_saving then 1 else 0); } - -/* -main :: () { - - print("TNL %\n", TASK_NAME_LENGTH); - print("TNB %\n", TASK_NAME_BYTES); - - home, success := get_home_directory(); - print("home '%' | success '%'\n", home, success); - - print("Task Time Tracker version %\n", VERSION); - print("Copyright 2022 Daniel Martins\n"); - print("License GPL-3.0-or-later\n"); - -// TODO More binding examples here https://github.com/vrcamillo/jai-tracy -// short : s16 -// int : s32 -// long : s64 (int) -// single : float32 (float) -// double : float64 - - stdscr := initscr(); - curs_set(0); - noecho(); - box(stdscr, 0, 0); - while true { - key := getch(); - if key == #char "q" break; - mvaddstr(1, 1, "> wow alçapão <"); - } - endwin(); - -} -*/ - -// TODO DEBUG -print_owner_allocator :: (tag: string, memory: *void) { - owner := "unkown"; - - if true == xx context.allocator.proc(.IS_THIS_YOURS, 0, 0, memory, null) then owner = "default"; - else if true == xx temp.proc(.IS_THIS_YOURS, 0, 0, memory, null) then owner = "temp"; - - print("'%' belongs to '%'\n", tag, owner); -} - -// TODO DEBUG -print_database :: (db: Database) { - for db.tasks { - print("% | % : % : % : % : % : % : %\n", cast(string)it.name, - it.times[0], - it.times[1], - it.times[2], - it.times[3], - it.times[4], - it.times[5], - it.times[6] - ); - } -} diff --git a/unused.jai b/unused.jai index 264fa88..a218aef 100644 --- a/unused.jai +++ b/unused.jai @@ -1,5 +1,30 @@ print(">%<", S64_MIN + delta, to_standard_error = true); + +// TODO DEBUG +print_owner_allocator :: (tag: string, memory: *void) { + owner := "unkown"; + + if true == xx context.allocator.proc(.IS_THIS_YOURS, 0, 0, memory, null) then owner = "default"; + else if true == xx temp.proc(.IS_THIS_YOURS, 0, 0, memory, null) then owner = "temp"; + + print("'%' belongs to '%'\n", tag, owner); +} +// TODO DEBUG +print_database :: (db: Database) { + for db.tasks { + print("% | % : % : % : % : % : % : %\n", cast(string)it.name, + it.times[0], + it.times[1], + it.times[2], + it.times[3], + it.times[4], + it.times[5], + it.times[6] + ); + } +} + // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // -- cgit v1.2.3 From 81ba4ca925c4e66b107bb7df737b8fdd51eee9ac Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 14 Apr 2023 09:03:25 +0100 Subject: Using resizable array to hold tasks. Implemented create new task, rename task. Fixed case break bug. --- .gitignore | 1 + curses.jai | 4 +- ttt.jai | 344 ++++++++++++++++++++++++------------------------------------- unused.jai | 3 +- 4 files changed, 140 insertions(+), 212 deletions(-) create mode 100644 .gitignore (limited to 'unused.jai') diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30bcfa4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.build/ diff --git a/curses.jai b/curses.jai index 6dc448d..737b9c2 100644 --- a/curses.jai +++ b/curses.jai @@ -82,12 +82,13 @@ ncurses :: #library "libncurses"; stdscr : *WINDOW; initscr :: () -> *WINDOW #foreign ncurses; -getch :: () -> s32 #foreign ncurses; +getch :: () -> s32 #foreign ncurses; endwin :: () -> void #foreign ncurses; cbreak :: () -> void #foreign ncurses; start_color :: () -> void #foreign ncurses; use_default_colors :: () -> void #foreign ncurses; flushinp :: () -> s32 #foreign ncurses; +echo :: () -> s32 #foreign ncurses; keypad :: (win: *WINDOW, bf: bool) -> s32 #foreign ncurses; ungetch :: (ch: s32) -> s32 #foreign ncurses; @@ -98,6 +99,7 @@ erase :: () -> s32 #foreign ncurses curs_set :: (visibility: s32) -> s32 #foreign ncurses; mvaddstr :: (y: s32, x: s32, str: *u8) -> s32 #foreign ncurses; mvprintw :: (y: s32, x: s32, fmt: *u8, args: ..Any) -> s32 #foreign ncurses; +mvgetnstr :: (y: s32, x: s32, str: *u8, n: s32) -> s32 #foreign ncurses; noecho :: () -> s32 #foreign ncurses; box :: (win: *WINDOW, verch: u8, horch: u8) -> s32 #foreign ncurses; init_pair :: (pair: s16, f: s16, b: s16) -> s32 #foreign ncurses; diff --git a/ttt.jai b/ttt.jai index 454586d..ad93b3b 100644 --- a/ttt.jai +++ b/ttt.jai @@ -17,7 +17,7 @@ // - release : jai ttt.jai -import_dir . -quiet -x64 -release // - debug : jai ttt.jai -import_dir . -quiet -x64 -#import "Basic"()(MEMORY_DEBUGGER=true); +#import "Basic"()(MEMORY_DEBUGGER=true); // TODO Remove after final debug sessions. #import "System"; #import "Math"; #import "POSIX"; @@ -57,8 +57,7 @@ Database :: struct { active_idx : s64 = -1; selected_idx : s64 = -1; total_times : [NUM_WEEK_DAYS] s64; - tasks : [] Task; - capacity : s64; + tasks : [..] Task; } SIZE_OF_TASK :: #run size_of(Task); @@ -327,96 +326,14 @@ contains_task :: inline (db: *Database, task: *Task) -> bool { return task >= db.tasks.data && task - db.tasks.data < db.tasks.count; } -// Adds a task to the database. +// Adds a task to the database and returns it. // If necessary, expands database capacity. -// Returns success. -add_task :: (db: *Database, task: Task) -> success: bool { +add_task :: (db: *Database, task: Task = .{}) -> task: *Task { assert(db != null, "Parameter 'db' is null."); - - // TODO Remove old implementation... or use the dynamics arrays? -// idx := db.tasks.count; -// maybe_grow(*db.tasks); // TODO Check for errors? -// db.tasks.count += 1; -// db.tasks[idx] = task; - - if (db.tasks.count == S64_MAX) { - print_error("Database reached maximum capacity."); - return false; - } - - // If necessary, expand database capacity. - current_capacity := db.capacity; - if ((db.tasks.count + 1) > current_capacity) { - new_capacity := - ifx current_capacity == 0 then 8 else - ifx current_capacity > (S64_MAX >> 1) then S64_MAX else - current_capacity << 1; - - new_tasks := realloc(db.tasks.data, new_capacity * SIZE_OF_TASK, current_capacity * SIZE_OF_TASK); - if (new_tasks == null) { - print_error("Failed to expand database."); - return false; - } - - db.tasks.data = new_tasks; - db.capacity = new_capacity; - } - - db.tasks.count += 1; - db.tasks[db.tasks.count-1] = task; - - // Adjust selected task. - if (db.selected_idx < 0) { - db.selected_idx = db.tasks.count-1; - } - - return true; + array_add(*db.tasks, task); + return *db.tasks[db.tasks.count-1]; } /* -// Creates new task stored at location given by task pointer. -// If necessary, expands database capacity. -// Returns success. -bool create_task(database_st *db, task_st **task) { - assert(db != NULL); - assert(task != NULL); - - if (db->count >= MAX_DATABASE_TASKS) { - print_error("Database reached maximum capacity."); - return false; - } - - // If necessary, expand database capacity. - size_t current_capacity = db->capacity; - if ((db->count + 1) > current_capacity) { - size_t new_capacity = - current_capacity == 0 ? 2 : - current_capacity > (MAX_DATABASE_TASKS >> 1) ? MAX_DATABASE_TASKS : - current_capacity << 1; - - task_st *new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_ST); - if (new_tasks == NULL) { - print_error("Failed to expand database."); - return false; - } - - db->capacity = new_capacity; - db->tasks = new_tasks; - } - - // Prepare new task. - *task = &db->tasks[db->count]; - memset(*task, 0, SIZEOF_TASK_ST); - - db->count++; - - // Adjust selected task. - if (db->selected_task < 0) { - db->selected_task = db->count-1; - } - - return true; -} - // Duplicates the given task. Duplicated task is appended to the database. // Returns success. bool duplicate_task(database_st *db, task_st *task) { @@ -449,6 +366,7 @@ bool duplicate_task(database_st *db, task_st *task) { // If possible, shrinks the database capacity. // Returns success. delete_task :: (db: *Database, task: *Task) -> bool { + assert(db != null); assert(task != null); assert(contains_task(db, task)); @@ -460,7 +378,8 @@ delete_task :: (db: *Database, task: *Task) -> bool { // Move tasks after the index position to their new positions. index := task - db.tasks.data; - memmove(task, task + 1, (db.tasks.count - index - 1) * SIZE_OF_TASK); // FIXME memmove is not implemented + for index..db.tasks.count-2 + db.tasks[it] = db.tasks[it+1]; db.tasks.count -= 1; // Adjust selected task. @@ -477,16 +396,31 @@ delete_task :: (db: *Database, task: *Task) -> bool { } // If possible, shrink database capacity. - current_capacity := db.capacity; - if (db.tasks.count <= (current_capacity >> 2)) { - new_capacity := current_capacity >> 1; - new_tasks := realloc(db.tasks.data, new_capacity * SIZEOF_TASK_ST); - if (new_tasks == null && new_capacity > 0) { - print_error("Failed to shrink database."); - return false; + // TODO Do we really want to make this fuss? + current_capacity := db.tasks.allocated; + if (db.tasks.count < (current_capacity >> 2)) { + new_capacity := 1 << (get_msb(db.tasks.count) + 2); + my_array_reserve_nonpoly(xx *db.tasks, new_capacity, SIZE_OF_TASK); + } + + get_msb :: (value: s64) -> msb: s64, found: bool { + result: s64 = ---; + #asm { + mov val: gpr, value; + bsr result, val; } - db.capacity = new_capacity; - db.tasks.data = new_tasks; + return result * xx cast(bool)value, xx value; // If value is zero: return `0, false`. + } + + my_array_reserve_nonpoly :: (array: *[..] *void, desired_items: s64, size: s64) -> success: bool { + if !array.allocator.proc remember_allocators(array); + + new_array_data := realloc(array.data, desired_items * size, array.allocated * size, array.allocator); + if new_array_data == null return false; + + array.data = new_array_data; + array.allocated = desired_items; + return true; } return true; @@ -698,11 +632,13 @@ load_database :: (db: *Database, path: string) -> success: bool { assert(read_success == true, "Failed to read database info from '%'.", path); // Reserve database capacity for tasks. - db.tasks.data = alloc(db.tasks.count * SIZE_OF_TASK); - db.capacity = db.tasks.count; + tasks_count := db.tasks.count; + Initialize(*db.tasks); // Cleanup whatever was read from file. + array_reserve(*db.tasks, tasks_count); // Read database tasks. - file_read(file, db.tasks.data, SIZE_OF_TASK * db.tasks.count); + file_read(file, db.tasks.data, SIZE_OF_TASK * tasks_count); + db.tasks.count = tasks_count; // Make sure we are reading all the file. buffer: u8; @@ -744,17 +680,16 @@ export_to_csv :: (db: Database, path: string) -> success: bool { // Imports CSV file into database. // Returns success. import_from_csv :: (db: *Database, path: string) -> bool { - - // TODO WIP - + // TODO Review code. assert(db != null, "Parameter 'db' is null."); assert(xx path, "Parameter 'path' is empty."); error_code: s64; - // Check file size - file_info: stat_t; - error_code = sys_stat(path, *file_info); // TODO Check for error. - size := file_info.st_size; + // Check file size TODO Read based on file size + //file_info: stat_t; + //error_code = sys_stat(path, *file_info); // TODO Check for error. + //size := file_info.st_size; + size := 0; success: bool; map: Map_File_Info; @@ -828,35 +763,37 @@ import_from_csv :: (db: *Database, path: string) -> bool { return "", false; } - // Parse CSV lines. - line := csv; - while (true) { - line, success := consume_next_line(*csv); -// line, success := next_line(*csv); - if success == false break; - - task: Task; - csv_values := split(line, ","); // TODO Temporary memory... if file is too big this may break. - - // Import task name. - name_length := min(task.name.count, csv_values[0].count); - memcpy(task.name.data, csv_values[0].data, name_length); - truncate_string(xx task.name, name_length); - - advance(*csv_values); - for csv_values { - parsed_value := string_to_int(it); - task.times[it_index] = parsed_value; - db.total_times[it_index] = add_int64(db.total_times[it_index], parsed_value); + { // Parse CSV lines. + auto_release_temp(); // TODO Needs to be tested. + line := csv; + while (true) { + line, success := consume_next_line(*csv); + // line, success := next_line(*csv); + if success == false break; + + task: Task; + csv_values := split(line, ","); // TODO Temporary memory... if file is too big this may break. + + // Import task name. + name_length := min(task.name.count, csv_values[0].count); + memcpy(task.name.data, csv_values[0].data, name_length); + truncate_string(xx task.name, name_length); + + advance(*csv_values); + for csv_values { + parsed_value := string_to_int(it); + task.times[it_index] = parsed_value; + db.total_times[it_index] = add_int64(db.total_times[it_index], parsed_value); + } + + add_task(db, *task); + // TODO Check this old code and remove it if not necessary. + // if context.temporary_storage.total_bytes_occupied > (100<<20) { + // print("temp: %\n", context.temporary_storage.total_bytes_occupied >> 20); + // reset_temporary_storage(); + // } } - - add_task(db, *task); -// if context.temporary_storage.total_bytes_occupied > (100<<20) { -// print("temp: %\n", context.temporary_storage.total_bytes_occupied >> 20); -// reset_temporary_storage(); -// } } - reset_temporary_storage(); return true; } @@ -928,13 +865,11 @@ set_active_task :: (db: *Database, task: *Task) { update_times(db); db.active_idx = ifx (task == null) then -1 else task - db.tasks.data; } -/* + // Returns true when database is full. -bool is_database_full(database_st *db) { - assert(db != NULL); - return db->count >= MAX_DATABASE_TASKS; +is_database_full :: inline (db: Database) -> bool { + return db.tasks.count >= MAX_DATABASE_TASKS; } -*/ INPUT_TIMEOUT_MS :: 1000; INPUT_AWAIT_INF :: -1; @@ -1243,23 +1178,22 @@ free_memory :: () { free(ar_file_path); //reset_temporary_storage(); } -/* -void read_input_to_string_buffer_with_space(int row, int column, int style, int length, int space) { - assert(length < string_buffer_size); - assert(space < string_buffer_size); - + +read_input_with_space :: (row: int, column: int, style: s32, length: int, space: int) -> string { + str := talloc_string(length); + memset(str.data, 0, str.count); attron(style | A_UNDERLINE); - mvprintw(row, column, "%*s", space, ""); + mvprintw(xx row, xx column, "%*s", space, ""); echo(); curs_set(1); - memset(string_buffer, 0, string_buffer_size); - mvgetnstr(row, column, string_buffer, length); - truncate_string_utf8(string_buffer, length); + mvgetnstr(xx row, xx column, str.data, xx length); + truncate_string(str, length); noecho(); curs_set(0); attrset(A_NORMAL); + return str; } - +/* void read_input_to_string_buffer(int row, int column, int style, int length) { read_input_to_string_buffer_with_space(row, column, style, length, length); } @@ -1306,20 +1240,23 @@ read_enter_confirmation :: (row: int, style: int, message: string) -> bool { return getch() == #char "\n"; } - main :: () { - defer report_memory_leaks(); // TODO DEBUG + defer report_memory_leaks(); // TODO Remove after final debug sessions. defer free_memory(); { // Initialize app directory. + auto_release_temp(); home_dir, success_dir := get_home_directory(); // Returns system owned memory. if success_dir == false { home_dir = "."; } - home_path, success_path := get_absolute_path(home_dir); // Returns temporary memory. TODO LEAK + home_path, success_path := get_absolute_path(home_dir); // Returns temporary memory. + + + if success_path == false { print_error("Failed to find home directory '%'.", home_dir); exit(1); @@ -1331,9 +1268,9 @@ main :: () { make_directory_if_it_does_not_exist(app_directory, recursive = true); } - + { // Initialize database and archive files if needed. - + auto_release_temp(); if (file_exists(db_file_path) == false) { if (store_database(database, db_file_path) == false) { print_error("Failed to initialize database."); @@ -1475,14 +1412,28 @@ main :: () { initialize_tui(); - layout := *layouts[Layouts.COMPACT]; db := *database; + layout := *layouts[Layouts.COMPACT]; flushinp(); ungetch(KEY_RESIZE); while (true) { + + if (is_terminal_too_small) { + INVALID_WINDOW_MESSAGE :: "Terminal is too small: minimum 60x3."; + mvaddstr(size_y / 2, (size_x - xx INVALID_WINDOW_MESSAGE.count) / 2, INVALID_WINDOW_MESSAGE); + } + else { + draw_tui(db, layout); + draw_error_window(); + } + + reset_temporary_storage(); + timeout(INPUT_TIMEOUT_MS); key := getch(); if key == #char "q" || key == #char "Q" break; + update_times(*database); + timeout(INPUT_AWAIT_INF); active_task := get_active_task(db); selected_task := get_selected_task(db); @@ -1494,9 +1445,6 @@ main :: () { else ifx (db.selected_idx < 0) then 1 else (db.selected_idx % layout_tasks_rows) + NUM_HEADER_ROWS; - timeout(INPUT_AWAIT_INF); - update_times(*database); - if key == { // When getch() times out. @@ -1530,24 +1478,20 @@ main :: () { //} update_layout(); layout = *layouts[ifx size_x > 100 then Layouts.NORMAL else Layouts.COMPACT]; -/* - case 'n': - case 'N':{ - if (is_database_full(db)) { + + case #char "n"; #through; + case #char "N"; + if is_database_full(db) { read_enter_confirmation(selected_task_row, error_style, " Unable to create entry: database is full. "); - break; + continue; } // Create new task. - task_st *new_task; - if (create_task(db, &new_task) == false) { - break; - } - - // Set new task name. - time_t now_utc = time(NULL); - struct tm *now_local = localtime(&now_utc); - strftime(new_task->name, TASK_NAME_BYTES, "%Y-%m-%d %H:%M:%S", now_local); + now_utc := current_time_consensus(); + now_local := to_calendar(now_utc, .LOCAL); + name := calendar_to_iso_string(now_local); + new_task := add_task(db); + memcpy(new_task.name.data, name.data, min(Task.name.count, name.count)); // Select new task. select_task(db, new_task); @@ -1557,28 +1501,21 @@ main :: () { // Force rename action. flushinp(); ungetch(KEY_F(2)); - break; - } - - case KEY_F(2): { - if (selected_task == NULL) { - break; - } + + case KEY_F2; + if (selected_task == null) continue; - read_input_to_string_buffer_with_space(selected_task_row, 1, action_style, TASK_NAME_LENGTH, size_x - 2); - if (is_empty_string(string_buffer) == false) { - replace_char(string_buffer, '\t', ' '); - replace_char(string_buffer, '\v', ' '); - replace_char(string_buffer, '\f', ' '); - replace_char(string_buffer, '\r', ' '); - memcpy(selected_task->name, string_buffer, TASK_NAME_BYTES); + // Change task name. + // TODO remove TASK_NAME_LENGTH + input := read_input_with_space(selected_task_row, 1, action_style, TASK_NAME_LENGTH, size_x - 2); + if is_empty_string(input) == false { + replace_chars(input, "\t\x0B\x0C\r", #char " "); + memcpy(selected_task.name.data, input.data, min(Task.name.count, input.count)); trigger_autosave(); } - break; - } - */ + case KEY_BACKSPACE; - if (selected_task == null) break; // BUG + if (selected_task == null) continue; if (read_enter_confirmation(selected_task_row, action_style, " Press enter to reset task. ") == true) { reset_task_times(db, selected_task); @@ -1586,7 +1523,7 @@ main :: () { } case KEY_DC; // Delete - if (selected_task == null || selected_task == active_task) break; // BUG + if (selected_task == null || selected_task == active_task) continue; if (read_enter_confirmation(selected_task_row, action_style, " Press enter to delete task. ") == true) { delete_task(db, selected_task); @@ -1736,13 +1673,12 @@ main :: () { case #char "t"; #through; case #char "T"; - if (active_task == null) break; // BUG + if (active_task == null) continue; select_task(db, active_task); case #char "\n"; #through; case #char " "; - if (db != *database || selected_task == null) - break; // BUG The break does not work inside an if_case. Review this and all other usages. + if (db != *database || selected_task == null) continue; set_active_task(db, ifx (active_task == selected_task) then null else selected_task); active_task = get_active_task(db); trigger_autosave(); @@ -1752,14 +1688,14 @@ main :: () { if (import_from_csv(*archive, ar_file_path) == false) { reset_database(*archive); print_error("Failed to load archive."); - break; + continue; } db = *archive; } else { if (export_to_csv(*archive, ar_file_path) == false) { print_error("Failed to store archive."); - break; + continue; } reset_database(*archive); db = *database; @@ -1815,18 +1751,6 @@ main :: () { case KEY_NPAGE; select_task_by_delta(db, layout_tasks_rows); } - - if (is_terminal_too_small) { - INVALID_WINDOW_MESSAGE :: "Terminal is too small: minimum 60x3."; - mvaddstr(size_y / 2, (size_x - xx INVALID_WINDOW_MESSAGE.count) / 2, INVALID_WINDOW_MESSAGE); - } - else { - draw_tui(db, layout); - draw_error_window(); - } - - timeout(INPUT_TIMEOUT_MS); - } // Save any unsaved changes. diff --git a/unused.jai b/unused.jai index a218aef..97d9f88 100644 --- a/unused.jai +++ b/unused.jai @@ -1,4 +1,5 @@ - print(">%<", S64_MIN + delta, to_standard_error = true); +auto_release_temp(); // TODO Needs to be tested. +print(">%<", S64_MIN + delta, to_standard_error = true); // TODO DEBUG print_owner_allocator :: (tag: string, memory: *void) { -- cgit v1.2.3 From fa1b8ea54646f1a0f3eadef33e3a660b875cc1ff Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 17 Aug 2023 09:36:02 +0100 Subject: WIP Code cleanup. --- ttt.jai | 147 ++++++++++++++++++++++++++++++++++++++++--------------------- unused.jai | 12 +++++ 2 files changed, 109 insertions(+), 50 deletions(-) (limited to 'unused.jai') diff --git a/ttt.jai b/ttt.jai index 68d3e33..22c6156 100644 --- a/ttt.jai +++ b/ttt.jai @@ -30,10 +30,7 @@ // TODO List: -// [ ] Decide once and for all if we're calling them entries or tasks. -// [ ] Test shrinking mechanism... this may be done by using the coalescing function. -// [ ] Every time we add or remove entries to the database, it may be reallocated, -// thus making the selected_task and active_task pointers invalid. Check this is not messing up the app. +// [ ] Every time we add or remove tasks to the database, it may be reallocated, thus making the selected_task and active_task pointers invalid. Check if this is messing up the app. VERSION :: "2.0"; // Use only 3 chars (to fit layouts). YEAR :: "2023"; @@ -41,7 +38,7 @@ FIRST_DAY_OF_WEEK :: 1; // (0-6, Sunday = 0). NUM_WEEK_DAYS :: 7; // TODO This has to go - Just to be more clear about what we're looping about. NAME_SIZE :: 72; // TODO Use this instead of Task.name.count ? -APP_FOLDER_NAME :: ".task_time_tracker_test"; // TODO Using _v2 to avoid erasing my work data. +APP_FOLDER_NAME :: ".task_time_tracker_test"; // TODO Using different folder to avoid erasing my work data. DB_FILE_NAME :: "database.bin"; AR_FILE_NAME :: "archive.csv"; DB_FILE_SIGN_STR :: "TTT:B:02"; @@ -70,17 +67,6 @@ Database :: struct { tasks : [..] Task; } -SIZE_OF_TASK :: #run size_of(Task); -// const char DB_FILE_SIGN[] = DB_FILE_SIGN_STR; -// const size_t DB_FILE_SIGN_LENGTH = sizeof(DB_FILE_SIGN_STR)-1; -// const size_t SIZEOF_TASK_ST = sizeof(task_st); -// const size_t SIZEOF_DATABASE_ST = sizeof(database_st); -// const size_t SIZEOF_CHAR = sizeof(char); -// const size_t SIZEOF_INT64 = sizeof(int64_t); -// -// -// - database : Database; archive : Database; is_autosave_enabled := true; @@ -88,8 +74,7 @@ countdown_to_autosave := -1; app_directory : string; db_file_path : string; ar_file_path : string; -// char *string_buffer = NULL; // A temporary buffer for localized actions. Please avoid data leaks and out-of-bounds errors. -// size_t string_buffer_size = 0; + size_x : s32; size_y : s32; pos_x : s32; @@ -176,22 +161,17 @@ is_equal_to_any :: (to_compare :string, test_a :string, test_b :string) -> bool return to_compare == test_a || to_compare == test_b; } -// Given an UTF8 encoded string, truncate it to length bytes without breaking any UTF8 character. -// The string should have capacity for at least length + 1. -// The terminating null byte ('\0') is not included in length. -// Returns the truncated string length. +// Count digits required to represent number on base. Sign is discarded. +count_digits :: (number: s64, base: s64 = 10) -> s64 { + if number == 0 return 1; + return cast(s64) floor( log(cast(float64)abs(number)) / log(cast(float64)abs(base)) ) + 1; +} Text_Encoding :: enum u8 #specified { ASCII :: 1; UTF8 :: 2; } -// Count digits required to represent number on base. Sign is discarded. -count_digits :: (number: s64, base: s64 = 10) -> s64 { - if number == 0 return 1; - return cast(s64) floor( log(cast(float64)abs(number)) / log(cast(float64)abs(base)) ) + 1; -} - // Truncates the string to the length provided or shorter, in case of UTF8 strings that require so. // Truncation is done by zeroing the tail of the string in place. // Returns length of truncated string. @@ -360,9 +340,10 @@ delete_task :: (using db: *Database, index: s64) -> bool { // TODO Maybe use `us } // Try to shrink database capacity if using more than 2MB. - if (tasks.allocated >> 2) > tasks.count && tasks.allocated * SIZE_OF_TASK > 2_000_000 { + size_of_task := size_of(Task); + if (tasks.allocated >> 2) > tasks.count && tasks.allocated * size_of_task > 2_000_000 { new_capacity := tasks.allocated >> 1; - new_tasks_data := realloc(tasks.data, new_capacity * SIZE_OF_TASK, tasks.allocated * SIZE_OF_TASK, tasks.allocator); + new_tasks_data := realloc(tasks.data, new_capacity * size_of_task, tasks.allocated * size_of_task, tasks.allocator); if new_tasks_data != null { tasks.data = new_tasks_data; tasks.allocated = new_capacity; @@ -467,22 +448,88 @@ update_times :: (db: *Database) { } } +x_count: s64 = 0; +x_average: float64 = 0.0; + + // Recalculates database totals. update_total_times :: (db: *Database) { assert(db != null, ASSERT_NOT_NULL, "db"); + // print(">>>xpto: %<<<\n", totals); + // print(">>>sizeA: %<<<\n", size_of([7] s64)); + // print(">>>sizeB: %<<<\n", NUM_WEEK_DAYS * size_of(s64)); + // print(">>>sizeC: %<<<\n", size_of(type_of(db.total_times))); + // Initialize(*totals.data); + // print(">>>xpto: %<<<\n", totals); + // totals.data = .[]; + + // TODO WIP + Check what size there is for a [..] s64... + + print(">>%<<", db.total_times.count); + // memset(totals.data, 0, db.total_times.count * size_of(s64)); + // memset(db.total_times.data, 0, size_of(type_of(db.total_times))); + + for xpto: 1..20 { + + start := current_time_monotonic(); + + for * total : db.total_times { < string { + builder: String_Builder; + defer reset(*builder); + for 0..6 { + print_to_builder(*builder, code, it); + } + return builder_to_string(*builder); + } + totals: []s64 = db.total_times; - memset(totals.data, 0, NUM_WEEK_DAYS * size_of(s64)); for db.tasks { times : []s64 = it.times; - totals[0] = add(totals[0], times[0]); - totals[1] = add(totals[1], times[1]); - totals[2] = add(totals[2], times[2]); - totals[3] = add(totals[3], times[3]); - totals[4] = add(totals[4], times[4]); - totals[5] = add(totals[5], times[5]); - totals[6] = add(totals[6], times[6]); + #insert #run unroll_week("totals[%1] = add(totals[%1], times[%1]);"); } + + + + stop := current_time_monotonic(); + average: float64 = xx to_microseconds(stop-start); + x_average = (average + x_count * x_average) / (x_count + 1); + x_count += 1; + } + + print("Measured % us.\n", x_average); } // Resets the times of the provided task (and adjusts database totals). @@ -559,7 +606,7 @@ store_database :: (db: Database, path: string) -> success: bool { file_write(*file, DB_FILE_SIGN_STR); file_write(*file, *db, size_of(Database)); - file_write(*file, db.tasks.data, SIZE_OF_TASK * db.tasks.count); + file_write(*file, db.tasks.data, size_of(Task) * db.tasks.count); return true; } @@ -602,7 +649,7 @@ load_database :: (db: *Database, path: string) -> success: bool { array_reserve(*db.tasks, tasks_count); // Read database tasks. - file_read(file, db.tasks.data, SIZE_OF_TASK * tasks_count); + file_read(file, db.tasks.data, size_of(Task) * tasks_count); db.tasks.count = tasks_count; // Make sure we are reading all the file. @@ -1279,9 +1326,9 @@ main :: () { print("- All data files are stored in '%'.\n", app_directory); print(" If the home directory is undefined, './%' will be used.\n", APP_FOLDER_NAME); write_strings( - " The database entries are stored in binary format on the 'database.bin' file.\n", + " The database tasks are stored in binary format on the 'database.bin' file.\n", " The archived entries are stored in CSV format on the 'archive.csv' file.\n", - "- During intensive tasks such as saving to file or recalculating totals times,\n", + "- During intensive operations such as saving or recalculating totals times,\n", " a diamond symbol is shown on the top left corner.\n" ); exit(0); @@ -1435,7 +1482,7 @@ main :: () { case #char "n"; #through; case #char "N"; if is_database_full(db) { - read_enter_confirmation(selected_task_row, error_style, " Unable to create entry: database is full. "); + read_enter_confirmation(selected_task_row, error_style, " Unable to create task: database is full. "); continue; } @@ -1563,12 +1610,12 @@ main :: () { if selected_task == null continue; if is_database_full(db) { - read_enter_confirmation(selected_task_row, error_style, " Unable to duplicate entry: database is full. "); + read_enter_confirmation(selected_task_row, error_style, " Unable to duplicate task: database is full. "); continue; } if (add_task(db, selected_task) == null) { - print_error("Failed to duplicate entry."); + print_error("Failed to duplicate task."); continue; } trigger_autosave(); @@ -1612,7 +1659,7 @@ main :: () { if (db != *database || selected_task == null || selected_task == active_task) continue; if (append_to_csv(selected_task, ar_file_path) == false) { - print_error("Failed to archive entry."); + print_error("Failed to archive task."); continue; } delete_task(db, db.selected_idx); @@ -1624,12 +1671,12 @@ main :: () { if (db != *archive || selected_task == null) continue; if is_database_full(*database) { - read_enter_confirmation(selected_task_row, error_style, " Unable to restore entry: database is full. "); + read_enter_confirmation(selected_task_row, error_style, " Unable to restore task: database is full. "); continue; } if (add_task(*database, selected_task) == null) { - print_error("Failed to restore entry."); + print_error("Failed to restore task."); continue; } delete_task(db, db.selected_idx); @@ -1692,13 +1739,13 @@ main :: () { for db.tasks { if (append_to_csv(it, ar_file_path) == false) { - print_error("Failed to archive entry."); // TODO Improve this. + print_error("Failed to archive task."); // TODO Improve this. } reset_task_times(db, it_index); } trigger_autosave(); - // Coalesce similar entries. + // Coalesce similar tasks. case #char "c"; #through; case #char "C"; if (db.tasks.count <= 0) continue; diff --git a/unused.jai b/unused.jai index 97d9f88..9e05904 100644 --- a/unused.jai +++ b/unused.jai @@ -1,5 +1,17 @@ auto_release_temp(); // TODO Needs to be tested. print(">%<", S64_MIN + delta, to_standard_error = true); + +// MEASURE PERFORMANCE +{ + #import "Basic"; + start := current_time_monotonic(); + // Code to measure. + stop := current_time_monotonic(); + print("Measured % ns.\n", to_nanoseconds(stop-start)); + + // Cumulative average: + CA_n+1 = (x_n+1 + n*CA_n ) / (n + 1) +} // TODO DEBUG print_owner_allocator :: (tag: string, memory: *void) { -- cgit v1.2.3 From 2037cba123cb98f367024346d2d882a505fd961e Mon Sep 17 00:00:00 2001 From: dam Date: Tue, 30 Apr 2024 12:54:13 +0100 Subject: Fixed TUI\unix from c_octal-to-jai mis-conversion. --- modules/TUI/unix.jai | 201 +++++++-------------------------------------------- unused.jai | 145 +++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 173 deletions(-) (limited to 'unused.jai') diff --git a/modules/TUI/unix.jai b/modules/TUI/unix.jai index bb22030..e1c0b3a 100644 --- a/modules/TUI/unix.jai +++ b/modules/TUI/unix.jai @@ -1,11 +1,5 @@ #scope_file -/* -TODO : then do a good implementation of the libc functions about attributes... -*/ - -USE_LIBC :: true; - #import "Atomics"; #import "System"; #import "POSIX"; @@ -13,7 +7,7 @@ USE_LIBC :: true; // Queue selector used in tcflush(...). // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-struct.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html - QueueSelector :: enum s32 { + Queue_Selector :: enum s32 { #if OS == { case .LINUX; TCIFLUSH :: 0; // Discard data received but not yet read. @@ -30,7 +24,7 @@ USE_LIBC :: true; // Optional actions used in tcsetattr(...). // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-tcflow.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html - OptionalActions :: enum s32 { + Optional_Actions :: enum s32 { TCSANOW :: 0; // Change immediately. TCSADRAIN :: 1; // Change when pending output is written. TCSAFLUSH :: 2; // Flush pending input before changing. @@ -74,7 +68,7 @@ USE_LIBC :: true; ICRNL :: 0x00000100; // Map CR to NL on input. #if OS == { - + case .LINUX; IXON :: 0x00000400; // Enable start/stop output control. IXANY :: 0x00000800; // Any character will restart after stop. @@ -84,12 +78,11 @@ USE_LIBC :: true; IXON :: 0x00000200; // Enable start/stop output control. IXANY :: 0x00000400; // Any character will restart after stop. IXOFF :: 0x00000800; // Enable start/stop input control. - } } // Output modes. - // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios.h + // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-c_oflag.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html Output_Modes :: enum_flags u32 { #if OS == { @@ -113,7 +106,7 @@ USE_LIBC :: true; } // Control modes. - // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios.h + // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-c_cflag.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html Control_Modes :: enum u32 { #if OS == { @@ -147,64 +140,54 @@ USE_LIBC :: true; } // Local modes. - // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios.h + // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-c_lflag.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html Local_Modes :: enum_flags u32 { #if OS == { case .LINUX; ISIG :: 0x00000001; // Enable signals. - ICANON :: 0x00000002; // Do erase and kill processing. + ICANON :: 0x00000002; // Canonical input (erase and kill processing). ECHO :: 0x00000008; // Enable echo. ECHOE :: 0x00000010; // Visual erase for ERASE. ECHOK :: 0x00000020; // Echo NL after KILL. ECHONL :: 0x00000040; // Echo NL even if ECHO is off. - NOFLSH :: 0x00000080; // Disable flush after interrupt. + NOFLSH :: 0x00000080; // Disable flush after interrupt or quit. TOSTOP :: 0x00000100; // Send SIGTTOU for background output. IEXTEN :: 0x00008000; // Enable DISCARD and LNEXT. case .MACOS; - ISIG :: 0x00000080; // Enable signals. - ICANON :: 0x00000100; // Do erase and kill processing. + ISIG :: 0x00000080; // Enable signals INTR, QUIT, [D]SUSP. + ICANON :: 0x00000100; // Canonicalize input lines. ECHO :: 0x00000008; // Enable echo. ECHOE :: 0x00000002; // Visual erase for ERASE. ECHOK :: 0x00000004; // Echo NL after KILL. ECHONL :: 0x00000010; // Echo NL even if ECHO is off. NOFLSH :: 0x80000000; // Disable flush after interrupt. - TOSTOP :: 0x00400000; // Send SIGTTOU for background output. + TOSTOP :: 0x00400000; // Stop background jobs from output. IEXTEN :: 0x00000400; // Enable DISCARD and LNEXT. } } // Control Characters - TODO WIP - // LINUX : ??? + // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-c_cc.h // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html Control_Chars :: enum u8 { - VINTR :: 0; - VQUIT :: 1; - VERASE :: 2; - VKILL :: 3; - VEOF :: 4; - VTIME :: 5; // Time-out value (tenths of a second) [!ICANON]. - VMIN :: 6; // Minimum number of bytes read at once [!ICANON]. - VSWTC :: 7; - VSTART :: 8; - VSTOP :: 9; - VSUSP :: 10; - VEOL :: 11; - VREPRINT :: 12; - VDISCARD :: 13; - VWERASE :: 14; - VLNEXT :: 15; - VEOL2 :: 16; - } - + // Unused consts: + // VINTR, VQUIT, VERASE, VKILL, VEOF, VSWTC, VSTART, VSTOP, VSUSP, VEOL, VREPRINT, VDISCARD, VWERASE, VLNEXT, VEOL2 + + #if OS == { -#if USE_LIBC { - // Required to do unlocking input. - libc :: #system_library "libc"; + case .LINUX; + VTIME :: 5; // Time-out value (tenths of a second) [!ICANON]. + VMIN :: 6; // Minimum number of bytes read at once [!ICANON]. + case .MACOS; + VTIME :: 17; // Time-out value (tenths of a second) [!ICANON]. + VMIN :: 16; // Minimum number of bytes read at once [!ICANON]. + } + } + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc; @@ -213,134 +196,6 @@ USE_LIBC :: true; // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc; -} -else { - - // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html - tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 { - - #if OS == .LINUX { - TCSETS :: 0x5402; - TCSETSW :: 0x5403; - TCSETSF :: 0x5404; - tcflag_t :: u32; - cc_t :: u8; - __KERNEL_NCCS :: 19; - __kernel_termios :: struct { - c_iflag : tcflag_t; // input mode flags - c_oflag : tcflag_t; // output mode flags - c_cflag : tcflag_t; // control mode flags - c_lflag : tcflag_t; // local mode flags - c_line : cc_t; // line discipline - c_cc : [__KERNEL_NCCS]cc_t; // control characters - }; - - k_termios: __kernel_termios; - cmd: u64; - if optional_actions == { - case xx OptionalActions.TCSANOW; - cmd = TCSETS; - - case xx OptionalActions.TCSADRAIN; - cmd = TCSETSW; - - case xx OptionalActions.TCSAFLUSH; - cmd = TCSETSF; - - case; - return EINVAL; - } - // k_termios.c_iflag = termios_p.c_iflag & ~IBAUD0; - k_termios.c_iflag = xx termios_p.c_iflag; - k_termios.c_oflag = xx termios_p.c_oflag; - k_termios.c_cflag = xx termios_p.c_cflag; - k_termios.c_lflag = xx termios_p.c_lflag; - k_termios.c_line = xx termios_p.c_line; - // #if _HAVE_C_ISPEED && _HAVE_STRUCT_TERMIOS_C_ISPEED - // k_termios.c_ispeed = termios_p->c_ispeed; - // #endif - // #if _HAVE_C_OSPEED && _HAVE_STRUCT_TERMIOS_C_OSPEED - // k_termios.c_ospeed = termios_p->c_ospeed; - // #endif - memcpy(*k_termios.c_cc[0], *termios_p.c_cc[0], __KERNEL_NCCS * 1);//size_of(cc_t)); - return ioctl(fd, cmd, *k_termios); - } - #if OS == .MACOS { - // return __ioctl (fd, TIOCSETAF, termios_p); - #assert(false, "NOT IMPLEMENTED"); - } - return 0; - } - - // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html - tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 { - TCSETS :: 0x5402; - TCSETSW :: 0x5403; - TCSETSF :: 0x5404; - tcflag_t :: u32; - cc_t :: u8; - __KERNEL_NCCS :: 19; - __kernel_termios :: struct { - c_iflag : tcflag_t; // input mode flags - c_oflag : tcflag_t; // output mode flags - c_cflag : tcflag_t; // control mode flags - c_lflag : tcflag_t; // local mode flags - c_line : cc_t; // line discipline - c_cc : [__KERNEL_NCCS]cc_t; // control characters - }; - - - // int - // __tcgetattr (int fd, struct termios *termios_p) - // { - // struct __kernel_termios k_termios; - k_termios: __kernel_termios; - retval: int; - retval = ioctl(fd, TCGETS, *k_termios); - if retval == 0 { - termios_p.c_iflag = xx k_termios.c_iflag; - termios_p.c_oflag = xx k_termios.c_oflag; - termios_p.c_cflag = xx k_termios.c_cflag; - termios_p.c_lflag = xx k_termios.c_lflag; - termios_p.c_line = xx k_termios.c_line; - // #if _HAVE_STRUCT_TERMIOS_C_ISPEED - // # if _HAVE_C_ISPEED - // termios_p->c_ispeed = k_termios.c_ispeed; - // # else - // termios_p->c_ispeed = k_termios.c_cflag & (CBAUD | CBAUDEX); - // # endif - // #endif - // #if _HAVE_STRUCT_TERMIOS_C_OSPEED - // # if _HAVE_C_OSPEED - // termios_p->c_ospeed = k_termios.c_ospeed; - // # else - // termios_p->c_ospeed = k_termios.c_cflag & (CBAUD | CBAUDEX); - // # endif - // #endif - size_of_cc_t := __KERNEL_NCCS * 1; - memcpy(*termios_p.c_cc[0], *k_termios.c_cc[0], size_of_cc_t); - // memset(*termios_p.c_cc[0] + size_of_cc_t + 1, _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * 1); - // - // if (sizeof (cc_t) == 1 || _POSIX_VDISABLE == 0 || (unsigned char) _POSIX_VDISABLE == (unsigned char) -1) { - // memset (__mempcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)), _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * sizeof (cc_t)); - // } - // else - // { - // memcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)); - // for (size_t cnt = __KERNEL_NCCS; cnt < NCCS; ++cnt) { - // termios_p->c_cc[cnt] = _POSIX_VDISABLE; - // } - // } - } - return xx retval; - } - - // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html - tcflush :: inline (fd: s32, queue_selector: s32) -> s32 { - TCFLSH :: 0x540B; - return ioctl(fd, TCFLSH, queue_selector); - } -} //////////////////////////////////////////////////////////////////////////////// @@ -396,7 +251,7 @@ OS_prepare_terminal :: () -> success := true { raw_tio_mode.c_cc[Control_Chars.VMIN] = 1; raw_tio_mode.c_cc[Control_Chars.VTIME] = 0; - error = tcsetattr(STDIN_FILENO, xx OptionalActions.TCSANOW, *raw_tio_mode); + error = tcsetattr(STDIN_FILENO, xx Optional_Actions.TCSANOW, *raw_tio_mode); if error { error_code, error_string := get_error_value_and_string(); log_error("Failed to set raw_tio_mode: code %, %", error_code, error_string); @@ -410,7 +265,7 @@ OS_prepare_terminal :: () -> success := true { OS_reset_terminal :: inline () -> success := true { restore_resize_handler(); - error := tcsetattr(STDIN_FILENO, xx OptionalActions.TCSANOW, *initial_tio_mode); + error := tcsetattr(STDIN_FILENO, xx Optional_Actions.TCSANOW, *initial_tio_mode); if error { error_code, error_string := get_error_value_and_string(); log_error("Failed to set initial_tio_mode: code %, %", error_code, error_string); @@ -420,7 +275,7 @@ OS_reset_terminal :: inline () -> success := true { } OS_flush_input :: inline () -> success := true { - error := tcflush(STDIN_FILENO, xx QueueSelector.TCIFLUSH); + error := tcflush(STDIN_FILENO, xx Queue_Selector.TCIFLUSH); if error { error_code, error_string := get_error_value_and_string(); log_error("Failed to flush input: code %, %", error_code, error_string); diff --git a/unused.jai b/unused.jai index 9e05904..0425f8d 100644 --- a/unused.jai +++ b/unused.jai @@ -37,6 +37,151 @@ print_database :: (db: Database) { ); } } + +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + +// Implementation of tcsetattr, tcgetattr, and tcflush using only 'ioctl' which is provided by jai. + +#if USE_LIBC { + + libc :: #system_library "libc"; + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html + tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc; + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html + tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc; + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html + tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc; +} +else { + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html + tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 { + + #if OS == .LINUX { + TCSETS :: 0x5402; + TCSETSW :: 0x5403; + TCSETSF :: 0x5404; + tcflag_t :: u32; + cc_t :: u8; + __KERNEL_NCCS :: 19; + __kernel_termios :: struct { + c_iflag : tcflag_t; // input mode flags + c_oflag : tcflag_t; // output mode flags + c_cflag : tcflag_t; // control mode flags + c_lflag : tcflag_t; // local mode flags + c_line : cc_t; // line discipline + c_cc : [__KERNEL_NCCS]cc_t; // control characters + }; + + k_termios: __kernel_termios; + cmd: u64; + if optional_actions == { + case xx Optional_Actions.TCSANOW; + cmd = TCSETS; + + case xx Optional_Actions.TCSADRAIN; + cmd = TCSETSW; + + case xx Optional_Actions.TCSAFLUSH; + cmd = TCSETSF; + + case; + return EINVAL; + } + // k_termios.c_iflag = termios_p.c_iflag & ~IBAUD0; + k_termios.c_iflag = xx termios_p.c_iflag; + k_termios.c_oflag = xx termios_p.c_oflag; + k_termios.c_cflag = xx termios_p.c_cflag; + k_termios.c_lflag = xx termios_p.c_lflag; + k_termios.c_line = xx termios_p.c_line; + // #if _HAVE_C_ISPEED && _HAVE_STRUCT_TERMIOS_C_ISPEED + // k_termios.c_ispeed = termios_p->c_ispeed; + // #endif + // #if _HAVE_C_OSPEED && _HAVE_STRUCT_TERMIOS_C_OSPEED + // k_termios.c_ospeed = termios_p->c_ospeed; + // #endif + memcpy(*k_termios.c_cc[0], *termios_p.c_cc[0], __KERNEL_NCCS * 1);//size_of(cc_t)); + return ioctl(fd, cmd, *k_termios); + } + #if OS == .MACOS { + // return __ioctl (fd, TIOCSETAF, termios_p); + #assert(false, "NOT IMPLEMENTED"); + } + return 0; + } + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html + tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 { + TCSETS :: 0x5402; + TCSETSW :: 0x5403; + TCSETSF :: 0x5404; + tcflag_t :: u32; + cc_t :: u8; + __KERNEL_NCCS :: 19; + __kernel_termios :: struct { + c_iflag : tcflag_t; // input mode flags + c_oflag : tcflag_t; // output mode flags + c_cflag : tcflag_t; // control mode flags + c_lflag : tcflag_t; // local mode flags + c_line : cc_t; // line discipline + c_cc : [__KERNEL_NCCS]cc_t; // control characters + }; + + + // int + // __tcgetattr (int fd, struct termios *termios_p) + // { + // struct __kernel_termios k_termios; + k_termios: __kernel_termios; + retval: int; + retval = ioctl(fd, TCGETS, *k_termios); + if retval == 0 { + termios_p.c_iflag = xx k_termios.c_iflag; + termios_p.c_oflag = xx k_termios.c_oflag; + termios_p.c_cflag = xx k_termios.c_cflag; + termios_p.c_lflag = xx k_termios.c_lflag; + termios_p.c_line = xx k_termios.c_line; + // #if _HAVE_STRUCT_TERMIOS_C_ISPEED + // # if _HAVE_C_ISPEED + // termios_p->c_ispeed = k_termios.c_ispeed; + // # else + // termios_p->c_ispeed = k_termios.c_cflag & (CBAUD | CBAUDEX); + // # endif + // #endif + // #if _HAVE_STRUCT_TERMIOS_C_OSPEED + // # if _HAVE_C_OSPEED + // termios_p->c_ospeed = k_termios.c_ospeed; + // # else + // termios_p->c_ospeed = k_termios.c_cflag & (CBAUD | CBAUDEX); + // # endif + // #endif + size_of_cc_t := __KERNEL_NCCS * 1; + memcpy(*termios_p.c_cc[0], *k_termios.c_cc[0], size_of_cc_t); + // memset(*termios_p.c_cc[0] + size_of_cc_t + 1, _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * 1); + // + // if (sizeof (cc_t) == 1 || _POSIX_VDISABLE == 0 || (unsigned char) _POSIX_VDISABLE == (unsigned char) -1) { + // memset (__mempcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)), _POSIX_VDISABLE, (NCCS - __KERNEL_NCCS) * sizeof (cc_t)); + // } + // else + // { + // memcpy (&termios_p->c_cc[0], &k_termios.c_cc[0], __KERNEL_NCCS * sizeof (cc_t)); + // for (size_t cnt = __KERNEL_NCCS; cnt < NCCS; ++cnt) { + // termios_p->c_cc[cnt] = _POSIX_VDISABLE; + // } + // } + } + return xx retval; + } + + // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html + tcflush :: inline (fd: s32, queue_selector: s32) -> s32 { + TCFLSH :: 0x540B; + return ioctl(fd, TCFLSH, queue_selector); + } +} // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // -- cgit v1.2.3 From 8b312bad33617f9b718232141d2855d80cb8b912 Mon Sep 17 00:00:00 2001 From: dam Date: Fri, 3 May 2024 00:32:06 +0100 Subject: WIP : Cleaning up TUI module. --- modules/TUI/module.jai | 178 +++++++++++++------------------------------- modules/TUI/palette_24b.jai | 2 +- snake.jai | 6 +- ttt.jai | 51 ++++++------- unused.jai | 15 ++++ 5 files changed, 95 insertions(+), 157 deletions(-) (limited to 'unused.jai') diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai index 991dc74..d44edab 100644 --- a/modules/TUI/module.jai +++ b/modules/TUI/module.jai @@ -1,9 +1,7 @@ -#module_parameters(COLOR_MODE := 24); +#module_parameters(COLOR_MODE_BITS := 24); #scope_file -// - fix/implement/finish `TODO` on `TUI\module` (use some sort of buffet to reduce io/print calls) - #if OS == { case .LINUX; #load "unix.jai"; @@ -22,14 +20,14 @@ #load "key_map.jai"; #run { - assert(COLOR_MODE == 4 || COLOR_MODE == 8 || COLOR_MODE == 24, "Invalid COLOR_MODE. Valid values are 4, 8, or 24 (default)."); + assert(COLOR_MODE_BITS == 4 || COLOR_MODE_BITS == 8 || COLOR_MODE_BITS == 24, "Invalid COLOR_MODE_BITS. Valid values are 4, 8, or 24 (default)."); assert(input_buffer.count >= KEY_SIZE, "The input buffer size must be capable to hold an entire Key, which should be able to hold an UTF8 code (4 bytes) or a terminal escape code (6 bytes)."); } #scope_export; -// Special Graphics Characters -Drawings :: struct { +// Special Graphics Characters. +Drawings :: struct #type_info_none { Blank :: "\x5F"; Diamond :: "\x60"; Checkerboard :: "\x61"; @@ -64,7 +62,9 @@ Drawings :: struct { CenteredDot :: "\x7E"; } -Commands :: struct { +// Terminal Escape Codes. +Commands :: struct #type_info_none { + // Screen buffers AlternateScreenBuffer :: "\e[?1049h"; MainScreenBuffer :: "\e[?1049l"; @@ -118,7 +118,7 @@ Commands :: struct { CursorNormalMode :: "\e[?1l"; } -#if COLOR_MODE == 4 { +#if COLOR_MODE_BITS == 4 { #load "palette_4b.jai"; set_colors :: inline (foreground: Palette, background: Palette) { @@ -127,7 +127,7 @@ Commands :: struct { cast(u8)foreground + 30, cast(u8)background + 40); } } -else #if COLOR_MODE == 8 { +else #if COLOR_MODE_BITS == 8 { #load "palette_8b.jai"; set_colors :: inline (foreground: Palette, background: Palette) { @@ -153,19 +153,21 @@ else { } } -#add_context tui_style: Style; - Style :: struct { - #if COLOR_MODE == 4 || COLOR_MODE == 8 { + #if COLOR_MODE_BITS == 4 || COLOR_MODE_BITS == 8 { background: Palette; foreground: Palette; } else { background: Color_24b; foreground: Color_24b; } - + background = Palette.BLACK; foreground = Palette.WHITE; + + // TODO Make this work... + use_default_background_color := false; + use_default_foreground_color := false; bold: bool; underline: bool; @@ -198,9 +200,6 @@ using_style :: (style: Style) #expand { `defer set_style(__style); } -// TODO Maybe make the OS_* procedures as inline?! -// TODO Terminal action codes are encoded with values incompatible with UTF-8 to avoid collisions. - /* We wanted the Key type to represent either UTF-8 encoded characters and also keyboard keys. The UTF-8 only requires up to 4 bytes, but some keyboard keys return up to 6 bytes. @@ -238,7 +237,7 @@ to_string :: inline (key: Key) -> string { // TODO FIXME TEMPORARY MEMORY return str; } -is_escape_code :: inline (key: Key) -> bool { +is_escape_code :: (key: Key) -> bool { beginsWithEscape := ((key & 0xFF) ^ #char "#") == 0; hasSomethingElse := (key & (~0xFF)) != 0; return beginsWithEscape && hasSomethingElse; @@ -281,6 +280,8 @@ Keys :: struct #type_info_none { F12 : Key : #run to_key("#f12"); } +#add_context tui_style: Style; + active := false; input_buffer : [1024] u8; @@ -296,7 +297,7 @@ module_logger :: (message: string, data: *void, info: Log_Info) { } assert_is_active :: inline () { - assert(active, "TUI is not ready. Please call TUI.start() before."); // TODO Improve error message... and maybe use the logger and return success=false flag + assert(active, "Please call TUI.setup_terminal() before using this procedure."); // TODO Improve error message... and maybe use the logger and return success=false flag } //////////////////////////////////////////////////////////////////////////////// @@ -376,12 +377,15 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key { return to_key(to_parse); } -// TODO Review me! -read_input :: (count_limit: int = -1, terminators: .. u8) -> string { +// TODO Review me and add some comments. +read_input :: (count_limit: int = -1, terminators: .. u8) -> string, success := true { assert_is_active(); - - assert(count_limit >= 0 || terminators.count > 0, "Infinite loop detected, aborting."); // TODO Maybe just return!? Or simply return success=false with #must - + + if count_limit < 0 && terminators.count <= 0 { + log_error("Invalid arguments passed to read_input(): (%).\n", count_limit); // TODO Improve error message. + return "", false; + } + if count_limit < 0 { builder: String_Builder; init_string_builder(*builder); @@ -436,7 +440,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { Escape discards the input returning an empty string and a Escape key. Resize discards the input returning an empty string and a Resize key. */ - + assert_is_active(); assert(count_limit >= 0, "Invalid value on count_limit parameter."); // TODO Too agressive str := alloc_string(count_limit); @@ -452,7 +456,7 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { chars_count := count_characters(str); - // Preview input. + // Preview input. TODO Optimize using temp_builder if is_visible { set_cursor_position(x, y); write_string(str); @@ -524,13 +528,8 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { return result, key; } -start :: () -> success := true #must { +setup_terminal :: () -> success := true #must { if active == true return; - - if inited == false { - init_string_builder(*temp_builder,, temp); // TODO Testing - inited = true; - } input_string.data = input_buffer.data; input_string.count = 0; @@ -557,7 +556,7 @@ start :: () -> success := true #must { return; } -stop :: () -> success := true #must { +reset_terminal :: () -> success := true #must { if active == false return; active = false; @@ -578,138 +577,61 @@ stop :: () -> success := true #must { } flush_input :: () { + assert_is_active(); OS_flush_input(); input_string.data = input_buffer.data; input_string.count = 0; } - - -// Using String_Builder improves: -// - procedure time is now ~12x faster; -// - reduces CPU usage by ~ -average: float64 = 0; -counter: float64 = 0; -inited:=false; -temp_builder: String_Builder; - -#if false { +// TODO What if we return success and fail when input arguments are invalid? draw_box :: (x: int, y: int, width: int, height: int) { - t0 := current_time_monotonic(); - assert_is_active(); - - // TODO Check if using a String_Builder improves performance (measure it)! - // TODO Validate input parameters against the terminal size. - assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments."); - - auto_release_temp(); - - tmp_string: string; - - tmp_string = tprint(Commands.SetCursorPosition, y, x); - write_strings( - Commands.DrawingMode, - tmp_string, - Drawings.CornerTL - ); + assert_is_active(); //TODO NOT NEEDED - for 1..width-2 { - write_string(Drawings.LineH); - } - write_string(Drawings.CornerTR); - - for idx: y+1..y+height-2 { - tmpL := tprint(Commands.SetCursorPosition, idx, x); - tmpR := tprint(Commands.SetCursorPosition, idx, x+width-1); - write_strings( - tmpL, - Drawings.LineV, - tmpR, - Drawings.LineV); - } - - tmpBL := tprint(Commands.SetCursorPosition, y+height-1, x); - write_strings( - tmpBL, - Drawings.CornerBL); - for 1..width-2 { - write_string(Drawings.LineH); + if x <= 0 || y <= 0 || width <= 1 || height <= 1 { + log_error("Invalid arguments passed to draw_box(): (%, %, %, %).\n", x, y, width, height); // TODO Improve error message. + return; } - write_string(Drawings.CornerBR); - write_string(Commands.TextMode); - - t1 := current_time_monotonic(); - set_cursor_position(2, 47); - sample := cast(float64)to_nanoseconds(t1-t0)/1000; - average = (sample + counter * average) / (counter + 1); - counter += 1; - print(">%<", average); -} - -} else { - - -// TODO Not sure how... but this seems to be leaking memory... maybe it's the String_Builder!? -draw_box :: (x: int, y: int, width: int, height: int) { - t0 := current_time_monotonic(); - - assert_is_active(); - - // TODO Check if using a String_Builder improves performance (measure it)! - // TODO Validate input parameters against the terminal size. - assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments."); - auto_release_temp(); - builder := temp_builder; - // init_string_builder(*builder); - + builder := String_Builder.{ allocator = temporary_allocator }; + append(*builder, Commands.DrawingMode); + + // Draw top line append(*builder, tprint(Commands.SetCursorPosition, y, x)); append(*builder, Drawings.CornerTL); - for 1..width-2 { append(*builder, Drawings.LineH); } append(*builder, Drawings.CornerTR); + // Draw left and right sides. for idx: y+1..y+height-2 { - tmpL := tprint(Commands.SetCursorPosition, idx, x); - tmpR := tprint(Commands.SetCursorPosition, idx, x+width-1); - append(*builder, tmpL); + append(*builder, tprint(Commands.SetCursorPosition, idx, x)); append(*builder, Drawings.LineV); - append(*builder, tmpR); + append(*builder, tprint(Commands.SetCursorPosition, idx, x+width-1)); append(*builder, Drawings.LineV); } + // Draw bottom line. append(*builder, tprint(Commands.SetCursorPosition, y+height-1, x)); append(*builder, Drawings.CornerBL); for 1..width-2 { append(*builder, Drawings.LineH); } append(*builder, Drawings.CornerBR); - + append(*builder, Commands.TextMode); write_string(builder_to_string(*builder)); - - t1 := current_time_monotonic(); - set_cursor_position(2, 47); - sample := cast(float64)to_nanoseconds(t1-t0)/1000; - average = (sample + counter * average) / (counter + 1); - counter += 1; - print(">%<", average); -} } -// TODO Maybe rename to "clear()" clear_terminal :: inline () { assert_is_active(); write_string(Commands.ClearScreen); } -// TODO Maybe rename to "get_size()" get_terminal_size :: () -> width: int, height: int { assert_is_active(); @@ -735,7 +657,7 @@ get_terminal_size :: () -> width: int, height: int { while input.count >= 3 && input[input.count-1] != FORMAT[FORMAT.count-1] { input.count -= 1; } - + assert(input.count >= 3 && input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[2] == FORMAT[2] && input[input.count-1] == FORMAT[FORMAT.count-1], "Query window size in chars returned invalid response."); @@ -757,7 +679,7 @@ get_terminal_size :: () -> width: int, height: int { return columns, rows; } -set_cursor_position :: (x: int, y: int) { +set_cursor_position :: inline (x: int, y: int) { assert_is_active(); print(Commands.SetCursorPosition, y, x); } @@ -784,7 +706,7 @@ get_cursor_position :: () -> x: int, y: int { while input.count >= 2 && input[input.count-1] != FORMAT[FORMAT.count-1] { input.count -= 1; } - + assert(input.count >= 2 && input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[input.count-1] == FORMAT[FORMAT.count-1], "Query cursor position returned invalid response."); @@ -796,7 +718,7 @@ get_cursor_position :: () -> x: int, y: int { return column, row; } -set_terminal_title :: (title: string) { +set_terminal_title :: inline (title: string) { assert_is_active(); print(Commands.SetWindowTitle, title); } diff --git a/modules/TUI/palette_24b.jai b/modules/TUI/palette_24b.jai index 7a263a0..ea0191c 100644 --- a/modules/TUI/palette_24b.jai +++ b/modules/TUI/palette_24b.jai @@ -1,5 +1,5 @@ // https://www.ditig.com/publications/256-colors-cheat-sheet -Palette :: struct { +Palette :: struct #type_info_none { BLACK :: Color_24b.{0x00, 0x00, 0x00}; MAROON :: Color_24b.{0x80, 0x00, 0x00}; GREEN :: Color_24b.{0x00, 0x80, 0x00}; diff --git a/snake.jai b/snake.jai index 465ae4d..b35c0fb 100644 --- a/snake.jai +++ b/snake.jai @@ -1,6 +1,6 @@ #import "Basic"; #import "Random"; -TUI :: #import "TUI"(COLOR_MODE = 8); +TUI :: #import "TUI"(COLOR_MODE_BITS = 8); Vec2D :: struct { x: int; @@ -140,7 +140,7 @@ main :: () { seed: u64 = xx to_milliseconds(current_time_monotonic()) | 0x01; // Seed must be odd. random_seed(seed); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_cursor_position(1, 1); write_string("Please enter player name: "); @@ -161,5 +161,5 @@ main :: () { if TUI.get_key() == TUI.Keys.Escape then break; } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); } diff --git a/ttt.jai b/ttt.jai index 50fa198..3512d20 100644 --- a/ttt.jai +++ b/ttt.jai @@ -17,7 +17,8 @@ // - release : jai ttt.jai -quiet -x64 -release // - debug : jai ttt.jai -quiet -x64 -#import "Basic"()(MEMORY_DEBUGGER=true); // TODO Remove after final debug sessions. This takes up ~30MB of RAM. +// #import "Basic"()(MEMORY_DEBUGGER=true); // TODO Remove after final debug sessions. This takes up ~30MB of RAM. +#import "Basic"; #import "System"; #import "Sort"; #import "Math"; @@ -26,7 +27,7 @@ #import "String"; #import "Integer_Saturating_Arithmetic"; #import "UTF8"; -TUI :: #import "TUI"(COLOR_MODE=4); +TUI :: #import "TUI"(COLOR_MODE_BITS=4); // - fix/implement/finish TODO : use `dirty_bit_flag` to only update ehat has been changed @@ -868,7 +869,7 @@ initialize_tui :: () { } } - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); } update_layout :: () { @@ -1167,7 +1168,7 @@ main :: () { print("- success\n", to_standard_error = true); } else { - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); print("- ERROR: %", error_message, to_standard_error = true); exit(1); } @@ -1180,19 +1181,19 @@ main :: () { if perform_test && 1 { print("TEST : set and get cursor position\n", to_standard_error = true); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); X :: 2; Y :: 3; TUI.set_cursor_position(X, Y); x, y := TUI.get_cursor_position(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(x == X && y == Y, "Failed set/get cursor position.\n"); } if perform_test && 1 { print("TEST : module logger\n", to_standard_error = true); log("- log: before module start."); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_cursor_position(3, 3); print("wait"); sleep_milliseconds(1000); @@ -1200,14 +1201,14 @@ main :: () { sleep_milliseconds(1000); print(" a bit"); sleep_milliseconds(1000); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); log("- log: after module stop."); } if perform_test && 1 { print("TEST : test key input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); write_string("Press q to exit, other key to print it to screen, wait 1s to see animation."); @@ -1226,28 +1227,28 @@ main :: () { write_string(TUI.to_string(key)); } } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); print("- success\n", to_standard_error = true); } if perform_test && 1 { print("TEST : draw box\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.flush_input(); TUI.clear_terminal(); TUI.draw_box(1, 2, 5, 3); TUI.set_cursor_position(1, 1); print("Can you see the box below? (y/n)"); key := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to draw box.\n"); } if perform_test && 1 { print("TEST : get terminal size\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); width, height := TUI.get_terminal_size(); TUI.set_cursor_position(1, 1); @@ -1256,13 +1257,13 @@ main :: () { while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { key = TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to get terminal size.\n"); } if perform_test && 1 { print("TEST : set terminal title\n", to_standard_error = true); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); title := "BAZINGA"; TUI.set_terminal_title(title); TUI.set_cursor_position(1, 1); @@ -1271,14 +1272,14 @@ main :: () { while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { key = TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(key == #char "y", "Failed to set terminal title.\n"); } if perform_test && 1 { print("TEST : print keys and set terminal title\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.set_terminal_title("bazinga"); key: TUI.Key = #char "d"; last_none_char := "X"; @@ -1337,13 +1338,13 @@ main :: () { // set_temporary_storage_mark(__mark); } print("- success"); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); } if perform_test && 1 { print("TEST : user input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); print("Enter some text (use Enter to finish, Esc to cancel, or resize to abort):"); @@ -1368,14 +1369,14 @@ main :: () { } } answer := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(answer == #char "y", error_message); } if perform_test && 1 { print("TEST : hidden user input\n", to_standard_error = true); auto_release_temp(); - assert(TUI.start(), "Failed to start TUI."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); TUI.clear_terminal(); TUI.set_cursor_position(1, 1); print("Enter some secret (use Enter to finish, Esc to cancel, or resize to abort):"); @@ -1399,14 +1400,14 @@ main :: () { } } answer := TUI.get_key(); - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); assert_result(answer == #char "y", error_message); } // -- -- -- Testing TUI -- STOP - defer report_memory_leaks(); // TODO Remove after final debug sessions. + // defer report_memory_leaks(); // TODO Remove after final debug sessions. defer free_memory(); @@ -1833,7 +1834,7 @@ main :: () { if (active_task == null) continue; select_task(db, db.active_idx); - // Start/Stop + // Start and stop. case TUI.Keys.Enter; #through; case TUI.Keys.Space; if (db != *database || selected_task == null) continue; @@ -2021,7 +2022,7 @@ main :: () { TUI.get_key(); } - assert(TUI.stop(), "Failed to stop TUI."); + assert(TUI.reset_terminal(), "Failed to reset TUI."); exit(xx ifx error_saving then 1 else 0); } diff --git a/unused.jai b/unused.jai index 0425f8d..a8dddfc 100644 --- a/unused.jai +++ b/unused.jai @@ -38,6 +38,21 @@ print_database :: (db: Database) { } } +// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // + + +// Average cumulative calculation. +average: float64 = 0; +counter: float64 = 0; +t0 := current_time_monotonic(); +t1 := current_time_monotonic(); +set_cursor_position(2, 47); +sample := cast(float64)to_nanoseconds(t1-t0)/1000; +average = (sample + counter * average) / (counter + 1); +counter += 1; +print(">%us<", average); + + // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- // // Implementation of tcsetattr, tcgetattr, and tcflush using only 'ioctl' which is provided by jai. -- cgit v1.2.3 From f024b0afab602df742284b86396325ce93ce6d98 Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 6 May 2024 12:48:36 +0100 Subject: Removed TODO keyword from secundary files. --- readme.md | 2 +- unused.c | 3 +-- unused.jai | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'unused.jai') diff --git a/readme.md b/readme.md index c54ecd7..2df87b7 100644 --- a/readme.md +++ b/readme.md @@ -72,6 +72,6 @@ Task Time Tracker - By having each column-print job decoulpled, we avoid havint to measure and compensate lengths of UTF8 strings; - [x] Review all code for bugs related to auto-cast on ptrdiff_t (signed/unsigned); - [x] Review all code for bugs related to auto-cast on size_t (signed/unsigned); -- [x] Go over all `TODO` items; +- [x] Go over all to-do items; - [x] Hide stderr messages from app screen. - [x] Improve error detection/messages. diff --git a/unused.c b/unused.c index 718fa71..48d23c7 100644 --- a/unused.c +++ b/unused.c @@ -160,7 +160,6 @@ bool add_task(database_t* db, const task_t* task) { return true; } -// TODO Remove this task and move code to delete_task. // Removes task by index from database. If possible, shrinks database capacity. // Returns success. bool remove_task(database_t* db, uint32_t index) { @@ -189,7 +188,7 @@ bool remove_task(database_t* db, uint32_t index) { db->capacity = new_capacity; db->tasks = new_tasks; db->active_task = db->tasks + selected_task_offset; - // TODO Validate selected_task (may now be above count. + // Validate selected_task (may now be above count. } return true; diff --git a/unused.jai b/unused.jai index a8dddfc..ab4f667 100644 --- a/unused.jai +++ b/unused.jai @@ -1,4 +1,4 @@ -auto_release_temp(); // TODO Needs to be tested. +auto_release_temp(); // automatically release temporary memory. print(">%<", S64_MIN + delta, to_standard_error = true); // MEASURE PERFORMANCE @@ -13,7 +13,7 @@ print(">%<", S64_MIN + delta, to_standard_error = true); CA_n+1 = (x_n+1 + n*CA_n ) / (n + 1) } -// TODO DEBUG +// Memory allocator debugging. print_owner_allocator :: (tag: string, memory: *void) { owner := "unkown"; @@ -23,7 +23,7 @@ print_owner_allocator :: (tag: string, memory: *void) { print("'%' belongs to '%'\n", tag, owner); } -// TODO DEBUG +// ttt's database debugging. print_database :: (db: Database) { for db.tasks { print("% | % : % : % : % : % : % : %\n", cast(string)it.name, -- cgit v1.2.3 From 27e3e029448cf2a80b24e6e212dee8cccda06987 Mon Sep 17 00:00:00 2001 From: dam Date: Mon, 6 May 2024 13:05:12 +0100 Subject: WIP : Improve performance of draw_user_interface. --- ttt.jai | 18 ++++++++++++++++-- unused.jai | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'unused.jai') diff --git a/ttt.jai b/ttt.jai index 0802220..d8233f9 100644 --- a/ttt.jai +++ b/ttt.jai @@ -895,7 +895,14 @@ update_layout :: () { } } -draw_tui :: (db: *Database, layout: *Layout) { +dbg_average := 0; // DEBUG +dbg_count := 0; // DEBUG +draw_user_interface :: (db: *Database, layout: *Layout) { + + start := current_time_monotonic(); // DEBUG + + + // TODO During dirty_flag revamp...we may also start using the String_Builder. @@ -1094,6 +1101,13 @@ draw_tui :: (db: *Database, layout: *Layout) { TUI.set_style(style_default); x += 1; print_time(y, x, total_time, layout.columns[L_TOTAL_IDX].width); + + stop := current_time_monotonic(); // DEBUG + dbg_sample := to_nanoseconds(stop-start); // DEBUG + dbg_average = (dbg_sample + dbg_count * dbg_average) / (dbg_count + 1); // DEBUG + dbg_count += 1; // DEBUG + TUI.set_cursor_position(3, 1); + print("Average % us ---------", dbg_average/1000); // DEBUG } free_memory :: () { @@ -1371,7 +1385,7 @@ main :: () { write_strings(INVALID_WINDOW_MESSAGE); } else { - draw_tui(db, layout); + draw_user_interface(db, layout); draw_error_window(); } diff --git a/unused.jai b/unused.jai index ab4f667..d016cb7 100644 --- a/unused.jai +++ b/unused.jai @@ -3,14 +3,19 @@ print(">%<", S64_MIN + delta, to_standard_error = true); // MEASURE PERFORMANCE { - #import "Basic"; - start := current_time_monotonic(); - // Code to measure. - stop := current_time_monotonic(); - print("Measured % ns.\n", to_nanoseconds(stop-start)); + dbg_average := 0; // DEBUG + dbg_count := 0; // DEBUG - // Cumulative average: - CA_n+1 = (x_n+1 + n*CA_n ) / (n + 1) + #import "Basic"; + + // Cumulative average: CA_n+1 = (x_n+1 + n*CA_n ) / (n + 1) + start := current_time_monotonic(); // DEBUG + // ...code to be measured... + stop := current_time_monotonic(); // DEBUG + dbg_sample := to_nanoseconds(stop-start); // DEBUG + dbg_average = (dbg_sample + dbg_count * dbg_average) / (dbg_count + 1); // DEBUG + dbg_count += 1; // DEBUG + print("Average % ns.\n", dbg_average); // DEBUG } // Memory allocator debugging. -- cgit v1.2.3 From 0985900775b41c796f6b74ab4c8cc07b71506787 Mon Sep 17 00:00:00 2001 From: dam Date: Tue, 7 May 2024 15:22:56 +0100 Subject: Keep some unused code. --- unused.jai | 2 ++ 1 file changed, 2 insertions(+) (limited to 'unused.jai') diff --git a/unused.jai b/unused.jai index d016cb7..f9bb2f9 100644 --- a/unused.jai +++ b/unused.jai @@ -1,5 +1,7 @@ auto_release_temp(); // automatically release temporary memory. print(">%<", S64_MIN + delta, to_standard_error = true); +print_to_builder(*builder, "Average % us (% / % bytes) ---------", dbg_average/1000, context.temporary_storage.total_bytes_occupied, context.temporary_storage.high_water_mark); // DEBUG + // MEASURE PERFORMANCE { -- cgit v1.2.3 From f5fc7f6e700d272c2d60e4a1da219c993f01cad3 Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 9 May 2024 11:43:31 +0100 Subject: Fixed TUI module to allow returning results in temporary memory. --- modules/TUI/module.jai | 58 +++++++++++++++++++++++++++++--------------------- ttt.jai | 20 +++++++---------- unused.jai | 1 + 3 files changed, 43 insertions(+), 36 deletions(-) (limited to 'unused.jai') diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai index 112c666..f8e745e 100644 --- a/modules/TUI/module.jai +++ b/modules/TUI/module.jai @@ -33,7 +33,7 @@ #load "key_map.jai"; #add_context tui_style : Style; // This contains the last style applied by the module. -#add_context tui_buffer : *String_Builder; // If set, this buffer will be used as output target of module procedures. +#add_context tui_builder : *String_Builder; // If set, this will serve as an output buffer for this module procedures. KEY_SIZE :: #run type_info(Key).runtime_size; #assert(input_buffer.count >= KEY_SIZE); // The input buffer size must be capable to hold an entire Key. @@ -42,7 +42,7 @@ active := false; input_override : Key; input_string : string; input_buffer : [1024] u8; -temp_buffer := String_Builder.{ allocator = temporary_allocator }; +temp_builder := String_Builder.{ allocator = temporary_allocator }; #scope_module @@ -176,11 +176,13 @@ Style :: struct { } set_style :: (style: Style) { - - auto_release_temp(); // TODO Does this breaks if the context.tui_buffer is a temporary buffer... and we're writting to it? - - // TODO If context.tui_buffer is temporary... this will fail.... right? - builder := ifx context.tui_buffer != null then context.tui_buffer else *temp_buffer; + // If no tui_builder is provided, use a temporary one and discard it afterwards. + builder := context.tui_builder; + temp_mark: Temporary_Storage_State = ---; + if context.tui_builder == null { + builder = *temp_builder; + temp_mark = get_temporary_storage_mark(); + } #if COLOR_MODE_BITS == { case 4; @@ -211,8 +213,9 @@ set_style :: (style: Style) { append(builder, #run sprint(Commands.SetGraphicsRendition, "49")); } - if context.tui_buffer == null { + if context.tui_builder == null { write_builder(builder); + set_temporary_storage_mark(temp_mark); } context.tui_style = style; @@ -497,9 +500,9 @@ read_input :: (count_limit: int = -1, terminators: .. u8) -> string { read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { assert_is_active(); assert(count_limit >= 0, "Invalid arguments passed to read_input_line(): 'count_limit' must be greater-than or equal to 0."); - - builder := temp_buffer; - + + // The returned memory must be allocated before we start to use temporary memory. + // Otherwise, the returned memory would be invalid on calls of type (,, temporary_allocator). str := alloc_string(count_limit); str.count = 0; idx := 0; @@ -510,6 +513,8 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key { write_strings(Commands.ShowCursor, Commands.StartBlinking, Commands.BlinkingBarShape); while true { + builder := temp_builder; + auto_release_temp(); chars_count := count_characters(str); @@ -601,9 +606,13 @@ draw_box :: (x: int, y: int, width: int, height: int) { assert_is_active(); assert(x > 0 && y > 0 && width > 1 && height > 1, "Invalid arguments passed to draw_box(): 'x' and 'y' must be greater-than 0; 'width' and 'height' must be greater-than 1."); - auto_release_temp(); // TODO Does this breaks if the context.tui_buffer is a temporary buffer... and we're writting to it? - - builder := ifx context.tui_buffer != null then context.tui_buffer else *temp_buffer; + // If no tui_builder is provided, use a temporary one and discard it afterwards. + builder := context.tui_builder; + temp_mark: Temporary_Storage_State = ---; + if context.tui_builder == null { + builder = *temp_builder; + temp_mark = get_temporary_storage_mark(); + } append(builder, Commands.DrawingMode); @@ -633,8 +642,9 @@ draw_box :: (x: int, y: int, width: int, height: int) { append(builder, Commands.TextMode); - if context.tui_buffer == null { + if context.tui_builder == null { write_builder(builder); + set_temporary_storage_mark(temp_mark); } } @@ -692,11 +702,11 @@ get_terminal_size :: () -> width: int, height: int { set_cursor_position :: inline (x: int, y: int) { assert_is_active(); - if context.tui_buffer == null { + if context.tui_builder == null { print(Commands.SetCursorPosition, y, x); } else { - print_to_builder(context.tui_buffer, Commands.SetCursorPosition, y, x); + print_to_builder(context.tui_builder, Commands.SetCursorPosition, y, x); } } @@ -740,27 +750,27 @@ set_terminal_title :: inline (title: string) { } using_buffer :: (buffer: *String_Builder) #expand { - __buffer := context.tui_buffer; - context.tui_buffer = buffer; - `defer context.tui_buffer = __buffer; + __buffer := context.tui_builder; + context.tui_builder = buffer; + `defer context.tui_builder = __buffer; } // TODO Maybe we should have a different name for this...? tui_print :: inline (format_string: string, args: .. Any) { - if context.tui_buffer == null { + if context.tui_builder == null { print(format_string, ..args, to_standard_error = false); } else { - print_to_builder(context.tui_buffer, format_string, ..args); + print_to_builder(context.tui_builder, format_string, ..args); } } // TODO Maybe we should have a different name for this...? tui_write :: inline (format_string: string) { - if context.tui_buffer == null { + if context.tui_builder == null { write_string(format_string); } else { - append(context.tui_buffer, format_string); + append(context.tui_builder, format_string); } } diff --git a/ttt.jai b/ttt.jai index 2dbc5eb..efafb00 100644 --- a/ttt.jai +++ b/ttt.jai @@ -214,13 +214,12 @@ print_time :: (y: int, x: int, time: s64, space: int) -> int { left_padding := (space - TIME_CHARS) / 2; right_padding := space - TIME_CHARS - left_padding; - print_padding :: (size: int, char: u8 = #char " ") { + print_padding :: (size: int) { assert(size >= 0, "Cannot print negative padding values. The procedure accepts signed values just for convenience."); - auto_release_temp(); - padding := talloc_string(size); - padding.count = size; - memset(padding.data, char, size); - TUI.tui_write(padding); + while size > 0 { + TUI.tui_write(" "); + size -= 1; + } } TUI.set_cursor_position(x, y); @@ -908,9 +907,9 @@ draw_user_interface :: (db: *Database, layout: *Layout) { empty_line := talloc_string(size_x); memset(empty_line.data, #char " ", size_x); - init_string_builder(*buffer, 100000); - // builder := String_Builder.{ allocator = temporary_allocator }; - builder := buffer; + // init_string_builder(*buffer, 100000); + // builder := buffer; + builder := String_Builder.{ allocator = temporary_allocator }; TUI.using_buffer(*builder); adjust_first_day_of_week := int.[ @@ -1194,9 +1193,6 @@ prompt_user_key :: (y: int, message: string) -> TUI.Key { main :: () { -// TODO WIP WIP WIP WIP WIP WIP WIP WIP - make some tests to see how auto_release_temporary works... check if something allocated right before are kept or discarddled... and how temp allocated StringBuilder behaves... - - #if DEBUG_MEMORY { defer report_memory_leaks(); // TODO Remove after final debug sessions. } diff --git a/unused.jai b/unused.jai index f9bb2f9..1f71b37 100644 --- a/unused.jai +++ b/unused.jai @@ -1,6 +1,7 @@ auto_release_temp(); // automatically release temporary memory. print(">%<", S64_MIN + delta, to_standard_error = true); print_to_builder(*builder, "Average % us (% / % bytes) ---------", dbg_average/1000, context.temporary_storage.total_bytes_occupied, context.temporary_storage.high_water_mark); // DEBUG +print("temp [% .. % .. %] \n", context.temporary_storage.data, get_temporary_storage_mark(), context.temporary_storage.data + context.temporary_storage.size-1); // MEASURE PERFORMANCE -- cgit v1.2.3