From 675b0a5fea60dc33a97b0dc1871bb9a0b61818cc Mon Sep 17 00:00:00 2001 From: dam Date: Sat, 4 May 2024 01:43:34 +0100 Subject: WIP : Cleanup TUI module. Finally decided to go with hard-asserts (vs soft-errors/logs). --- modules/TUI/tests.jai | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 modules/TUI/tests.jai (limited to 'modules/TUI/tests.jai') diff --git a/modules/TUI/tests.jai b/modules/TUI/tests.jai new file mode 100644 index 0000000..fe213cb --- /dev/null +++ b/modules/TUI/tests.jai @@ -0,0 +1,247 @@ +// build: jai -import_dir ../ tests.jai +#import "Basic"; +TUI :: #import "TUI"; + +main :: () { + + assert_result :: (result: bool, error_message: string) { + if result == true { + print("- success\n", to_standard_error = true); + } + else { + assert(TUI.reset_terminal(), "Failed to reset TUI."); + print("- ERROR: %", error_message, to_standard_error = true); + exit(1); + } + } + + next_line :: inline () { + x, y := TUI.get_cursor_position(); + TUI.set_cursor_position(1, y+1); + } + + if 1 { + print("TEST : set and get cursor position\n", to_standard_error = true); + 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.reset_terminal(), "Failed to reset TUI."); + assert_result(x == X && y == Y, "Failed set/get cursor position.\n"); + } + + if 1 { + print("TEST : module logger\n", to_standard_error = true); + log("- log: before module start."); + assert(TUI.setup_terminal(), "Failed to setup TUI."); + TUI.set_cursor_position(3, 3); + print("wait"); + sleep_milliseconds(500); + log("- log: while module is active."); + sleep_milliseconds(500); + print(" a bit"); + sleep_milliseconds(1000); + assert(TUI.reset_terminal(), "Failed to reset TUI."); + log("- log: after module stop."); + } + + if 1 { + print("TEST : test key input\n", to_standard_error = true); + auto_release_temp(); + 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."); + next_line(); + key: TUI.Key; + while(key != #char "q") { + key = TUI.get_key(1000); + if key == TUI.Keys.None { + write_string("-"); + } + else if key == TUI.Keys.Resize { + write_string("#"); + } + else { + // else if key >= 32 && key <= 128 then print_character(cast,force(u8)key) + write_string(TUI.to_string(key)); + } + } + assert(TUI.reset_terminal(), "Failed to reset TUI."); + print("- success\n", to_standard_error = true); + } + + if 1 { + print("TEST : draw box\n", to_standard_error = true); + auto_release_temp(); + 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.reset_terminal(), "Failed to reset TUI."); + assert_result(key == #char "y", "Failed to draw box.\n"); + } + + if 1 { + print("TEST : get terminal size\n", to_standard_error = true); + auto_release_temp(); + assert(TUI.setup_terminal(), "Failed to setup TUI."); + TUI.clear_terminal(); + width, height := TUI.get_terminal_size(); + TUI.set_cursor_position(1, 1); + print("Is terminal size %x%? (y/n)", width, height); + key: TUI.Key = xx TUI.Keys.None; + while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { + key = TUI.get_key(); + } + assert(TUI.reset_terminal(), "Failed to reset TUI."); + assert_result(key == #char "y", "Failed to get terminal size.\n"); + } + + if 1 { + print("TEST : set terminal title\n", to_standard_error = true); + assert(TUI.setup_terminal(), "Failed to setup TUI."); + title := "BAZINGA"; + TUI.set_terminal_title(title); + TUI.set_cursor_position(1, 1); + print("Is terminal title '%'? (y/n)", title); + key: TUI.Key = xx TUI.Keys.None; + while (key == xx TUI.Keys.None || key == xx TUI.Keys.Resize) { + key = TUI.get_key(); + } + assert(TUI.reset_terminal(), "Failed to reset TUI."); + assert_result(key == #char "y", "Failed to set terminal title.\n"); + } + + if 1 { + print("TEST : print keys\n", to_standard_error = true); + auto_release_temp(); + assert(TUI.setup_terminal(), "Failed to setup TUI."); + key: TUI.Key = #char "d"; + last_none_char := "X"; + + width, height := TUI.get_terminal_size(); + TUI.clear_terminal(); + TUI.draw_box(1, 1, width, height); + drop_down := 0; + + while(key != #char "q") { + + if key == { + case TUI.Keys.None; { + TUI.set_cursor_position(2, 2); + last_none_char = ifx last_none_char == "X" then "+" else "X"; + write_strings(last_none_char, " (press q to exit)"); + } + + case TUI.Keys.Resize; #through; + case #char "c"; { + width, height = TUI.get_terminal_size(); + TUI.clear_terminal(); + TUI.draw_box(1, 1, width, height); + drop_down = 0; + } + + case; { + TUI.set_cursor_position(2, 3+drop_down); + str := TUI.to_string(key); + array_to_print: [..] string; + write_string(": "); + for 0..str.count-1 { + print("% ", FormatInt.{value = cast(u8)str[it], base=16}); + } + write_string(": "); + for 0..str.count-1 { + if str[it] == #char "\e" { + str[it] = #char "#"; + } + } + write_string(str); + write_string(" :"); + drop_down += 1; + } + } + + x := ifx width > 24 then width-24 else 1; + y := ifx height > 1 then height-1 else 1; + + TUI.set_cursor_position(x, y); + print("size = %x%\n", width, height); + key = TUI.get_key(1000); + + // __mark := get_temporary_storage_mark(); + // set_temporary_storage_mark(__mark); + } + print("- success"); + assert(TUI.reset_terminal(), "Failed to reset TUI."); + } + + if 1 { + print("TEST : user input\n", to_standard_error = true); + auto_release_temp(); + 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):"); + next_line(); + str, key := TUI.read_input_line(15); + TUI.set_cursor_position(1, 3); + error_message: string; + if key == { + case TUI.Keys.Escape; { + print("Have you pressed Esc? (y/n)"); + error_message = "Failed to read line on Esc."; + } + + case TUI.Keys.Resize; { + print("Have you resized the terminal? (y/n)"); + error_message = "Failed to read line on resize."; + + } + case; { + print("Have you entered '%'? (y/n)", str); + error_message = "Failed to read line."; + } + } + answer := TUI.get_key(); + assert(TUI.reset_terminal(), "Failed to reset TUI."); + assert_result(answer == #char "y", error_message); + } + + if 1 { + print("TEST : hidden user input\n", to_standard_error = true); + auto_release_temp(); + 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):"); + next_line(); + str, key := TUI.read_input_line(15, false); + TUI.set_cursor_position(1, 3); + error_message: string; + if key == { + case TUI.Keys.Escape; { + print("Have you pressed Esc? (y/n)"); + error_message = "Failed to read line on Esc."; + } + + case TUI.Keys.Resize; { + print("Have you resized the terminal? (y/n)"); + error_message = "Failed to read line on resize."; + } + case; { + print("Have you entered '%'? (y/n)", str); + error_message = "Failed to read line."; + } + } + answer := TUI.get_key(); + assert(TUI.reset_terminal(), "Failed to reset TUI."); + assert_result(answer == #char "y", error_message); + } + + // -- -- -- Testing TUI -- STOP +} -- cgit v1.2.3 From bdea73c8349c2c918befad4120f49f9826d270dc Mon Sep 17 00:00:00 2001 From: dam Date: Thu, 16 May 2024 02:42:04 +0100 Subject: Imported new module structure. --- Test_IntSatArith.jai | 304 ---------------------- modules/Integer_Saturating_Arithmetic.jai | 416 ------------------------------ modules/Saturation/module.jai | 416 ++++++++++++++++++++++++++++++ modules/Saturation/tests.jai | 304 ++++++++++++++++++++++ modules/TUI/key_map.jai | 401 ++++++++++++++-------------- modules/TUI/tests.jai | 19 +- modules/UTF8.jai | 128 --------- modules/UTF8/module.jai | 128 +++++++++ modules/UTF8/tests.jai | 5 + sizeof.c | 50 ---- ttt.jai | 2 +- 11 files changed, 1057 insertions(+), 1116 deletions(-) delete mode 100644 Test_IntSatArith.jai delete mode 100644 modules/Integer_Saturating_Arithmetic.jai create mode 100644 modules/Saturation/module.jai create mode 100644 modules/Saturation/tests.jai delete mode 100644 modules/UTF8.jai create mode 100644 modules/UTF8/module.jai create mode 100644 modules/UTF8/tests.jai delete mode 100644 sizeof.c (limited to 'modules/TUI/tests.jai') diff --git a/Test_IntSatArith.jai b/Test_IntSatArith.jai deleted file mode 100644 index 2128f7b..0000000 --- a/Test_IntSatArith.jai +++ /dev/null @@ -1,304 +0,0 @@ -// Tests for integer saturating arighmetic procedures. - -#import "Basic"; -#import "Compiler"; -#import "Math"; -#import "String"; -#import "Integer_Saturating_Arithmetic"; - -main :: () { - - write_strings( - "#=======================#\n", - "# Basic tests #\n" - ); - - test_op :: (operation: string, x: $Tx, y: $Ty, result: $Tr, type: Type, saturated: bool, remainder: Tr = 0) -> errors_found: int #expand { - - print_test_call :: (operation: string) -> string { - str: string = ---; - if operation != "div" { - TEST_CALL :: #string DONE - t_result, t_saturated := OP(cast(Tx)x, cast(Ty)y); - if result != t_result print("%_%(%, %) = %0%0\n", operation, type, x, y, result, ifx saturated then " : saturated"); - DONE - str = replace(TEST_CALL, "OP", operation); - } else { - TEST_CALL :: #string DONE - t_result, t_remainder, t_saturated := OP(cast(Tx)x, cast(Ty)y); - if result != t_result print("%_%(%, %) = % + %0%0\n", operation, type, x, y, result, remainder, ifx saturated then " : saturated"); - DONE - str = replace(TEST_CALL, "OP", operation); - } - return str; - } - - #insert #run print_test_call(operation); - - errors := 0; - if result != t_result { errors += 1; print(" > incorrect result value: got % expected %\n", t_result, result); }; - if type != type_of(t_result) { errors += 1; print(" > incorrect result type: got % expected %\n", type_of(t_result), type); }; - if saturated != t_saturated { errors += 1; print(" > incorrect saturated flag: got % expected %\n", t_saturated, saturated); }; - #if operation == "div" { - if remainder != t_remainder { errors += 1; print(" > incorrect remainder value: got % expected %\n", t_remainder, remainder); }; - } - return errors; - } - - errors := 0; - - // Test signed add. - errors += test_op("add", cast( s8) S8_MAX, cast( s8)1, S8_MAX, s8, true); - errors += test_op("add", cast(s16)S16_MAX, cast( u8)1, S16_MAX, s16, true); - errors += test_op("add", cast(s32)S32_MAX, cast(s32)1, S32_MAX, s32, true); - errors += test_op("add", cast(s64)S64_MAX, cast(u32)1, S64_MAX, s64, true); - - errors += test_op("add", cast( s8) S8_MAX, cast( s8) S8_MIN, -1, s8, false); - errors += test_op("add", cast(s16)S16_MAX, cast(s16)S16_MIN, -1, s16, false); - errors += test_op("add", cast(s32)S32_MAX, cast(s32)S32_MIN, -1, s32, false); - errors += test_op("add", cast(s64)S64_MAX, cast(s64)S64_MIN, -1, s64, false); - - // Test unsigned add. - errors += test_op("add", cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, true); - errors += test_op("add", cast(u16)U16_MAX, cast(u16)1, U16_MAX, u16, true); - errors += test_op("add", cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, true); - errors += test_op("add", cast(u64)U64_MAX, cast(u64)1, U64_MAX, u64, true); - - errors += test_op("add", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); - errors += test_op("add", cast(u16)U16_MAX, cast(u16)0, U16_MAX, u16, false); - errors += test_op("add", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); - errors += test_op("add", cast(u64)U64_MAX, cast(u64)0, U64_MAX, u64, false); - - // Test signed sub. - errors += test_op("sub", cast( s8) S8_MIN, cast( s8)1, S8_MIN, s8, true); - errors += test_op("sub", cast(s16)S16_MIN, cast( u8)1, S16_MIN, s16, true); - errors += test_op("sub", cast(s32)S32_MIN, cast(s32)1, S32_MIN, s32, true); - errors += test_op("sub", cast(s64)S64_MIN, cast(u32)1, S64_MIN, s64, true); - - errors += test_op("sub", cast( s8)-1, cast( s8) S8_MAX, S8_MIN, s8, false); - errors += test_op("sub", cast(s16)-1, cast(s16)S16_MAX, S16_MIN, s16, false); - errors += test_op("sub", cast(s32)-1, cast(s32)S32_MAX, S32_MIN, s32, false); - errors += test_op("sub", cast(s64)-1, cast(s64)S64_MAX, S64_MIN, s64, false); - - // Test unsigned sub. - errors += test_op("sub", cast( u8)1, cast( u8) U8_MAX, 0, u8, true); - errors += test_op("sub", cast( u8)1, cast(u16)U16_MAX, 0, u16, true); - errors += test_op("sub", cast(u32)1, cast(u32)U32_MAX, 0, u32, true); - errors += test_op("sub", cast(u32)1, cast(u64)U64_MAX, 0, u64, true); - - errors += test_op("sub", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); - errors += test_op("sub", cast(u16)U16_MAX, cast( u8)0, U16_MAX, u16, false); - errors += test_op("sub", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); - errors += test_op("sub", cast(u64)U64_MAX, cast(u32)0, U64_MAX, u64, false); - - // Test signed mul. - errors += test_op("mul", cast( s8) S8_MIN, cast( s8)-1, S8_MAX, s8, true); - errors += test_op("mul", cast(s16)S16_MIN, cast( s8)-1, S16_MAX, s16, true); - errors += test_op("mul", cast(s32)S32_MIN, cast(s32)-1, S32_MAX, s32, true); - errors += test_op("mul", cast(s64)S64_MIN, cast(s32)-1, S64_MAX, s64, true); - - errors += test_op("mul", cast( s8) S8_MAX, cast( s8)-2, S8_MIN, s8, true); - errors += test_op("mul", cast(s16)S16_MAX, cast( s8)-2, S16_MIN, s16, true); - errors += test_op("mul", cast(s32)S32_MAX, cast(s32)-2, S32_MIN, s32, true); - errors += test_op("mul", cast(s64)S64_MAX, cast(s32)-2, S64_MIN, s64, true); - - errors += test_op("mul", cast( s8)-2, cast( s8) S8_MAX, S8_MIN, s8, true); - errors += test_op("mul", cast( s8)-2, cast(s16)S16_MAX, S16_MIN, s16, true); - errors += test_op("mul", cast(s32)-2, cast(s32)S32_MAX, S32_MIN, s32, true); - errors += test_op("mul", cast(s32)-2, cast(s64)S64_MAX, S64_MIN, s64, true); - - errors += test_op("mul", cast( s8) S8_MAX, cast( s8)2, S8_MAX, s8, true); - errors += test_op("mul", cast(s16)S16_MAX, cast( s8)2, S16_MAX, s16, true); - errors += test_op("mul", cast(s32)S32_MAX, cast(s32)2, S32_MAX, s32, true); - errors += test_op("mul", cast(s64)S64_MAX, cast(s32)2, S64_MAX, s64, true); - - errors += test_op("mul", cast( s8) S8_MAX, cast( s8)-1, -S8_MAX, s8, false); - errors += test_op("mul", cast(s16)S16_MAX, cast( s8)-1, -S16_MAX, s16, false); - errors += test_op("mul", cast(s32)S32_MAX, cast(s32)-1, -S32_MAX, s32, false); - errors += test_op("mul", cast(s64)S64_MAX, cast(s32)-1, -S64_MAX, s64, false); - - errors += test_op("mul", cast( s8) S8_MAX, cast( s8)0, 0, s8, false); - errors += test_op("mul", cast(s16)S16_MAX, cast( u8)0, 0, s16, false); - errors += test_op("mul", cast(s32)S32_MAX, cast(s32)0, 0, s32, false); - errors += test_op("mul", cast(s64)S64_MAX, cast(u32)0, 0, s64, false); - - // Test unsigned mul. - errors += test_op("mul", cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, false); - errors += test_op("mul", cast(u16)U16_MAX, cast( u8)1, U16_MAX, u16, false); - errors += test_op("mul", cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, false); - errors += test_op("mul", cast(u64)U64_MAX, cast(u32)1, U64_MAX, u64, false); - - errors += test_op("mul", cast( u8) U8_MAX, cast( u8)2, U8_MAX, u8, true); - errors += test_op("mul", cast(u16)U16_MAX, cast( u8)2, U16_MAX, u16, true); - errors += test_op("mul", cast(u32)U32_MAX, cast(u32)2, U32_MAX, u32, true); - errors += test_op("mul", cast(u64)U64_MAX, cast(u32)2, U64_MAX, u64, true); - - // Test signed div. - errors += test_op("div", cast( s8) S8_MIN, cast( s8)-1, S8_MAX, s8, true, -1); - errors += test_op("div", cast(s16)S16_MIN, cast( s8)-1, S16_MAX, s16, true, -1); - errors += test_op("div", cast(s32)S32_MIN, cast(s32)-1, S32_MAX, s32, true, -1); - errors += test_op("div", cast(s64)S64_MIN, cast(s32)-1, S64_MAX, s64, true, -1); - - errors += test_op("div", cast( s8) S8_MAX, cast( s8)-2, - S8_MAX/2, s8, false, 1); - errors += test_op("div", cast(s16)S16_MAX, cast( s8)-2, -S16_MAX/2, s16, false, 1); - errors += test_op("div", cast(s32)S32_MAX, cast(s32)-2, -S32_MAX/2, s32, false, 1); - errors += test_op("div", cast(s64)S64_MAX, cast(s32)-2, -S64_MAX/2, s64, false, 1); - - errors += test_op("div", cast( s8)15, cast( s8)5, 3, s8, false, 0); - errors += test_op("div", cast( u8)15, cast(s16)7, 2, s16, false, 1); - errors += test_op("div", cast(s16)15, cast(s32)13, 1, s32, false, 2); - errors += test_op("div", cast(u16)100, cast(s64)3, 33, s64, false, 1); - - // Test unsigned div. - errors += test_op("div", cast( u8) U8_MAX, cast( u8)2, U8_MAX/2, u8, false, 1); - errors += test_op("div", cast(u16)U16_MAX, cast( u8)2, U16_MAX/2, u16, false, 1); - errors += test_op("div", cast(u32)U32_MAX, cast(u32)2, U32_MAX/2, u32, false, 1); - errors += test_op("div", cast(u64)U64_MAX, cast(u32)2, U64_MAX/2, u64, false, 1); - - if errors > 0 print("# Found % %!\n", errors, ifx errors == 1 then "error" else "errors"); else print(" No errors found.\n"); - - write_strings( - "#=======================#\n", - "# Benchmarks #\n" - ); - - #import "Random"; - - performance_test :: ($operation: string, $type: Type, print_result: bool = true) -> ops_per_us_gen: float, ops_per_us_asm: float { - - #if type == u8 { MIN :: 0; MAX :: U8_MAX; } - #if type == u16 { MIN :: 0; MAX :: U16_MAX; } - #if type == u32 { MIN :: 0; MAX :: U32_MAX; } - #if type == u64 { MIN :: 0; MAX :: U64_MAX; } - #if type == s8 { MIN :: S8_MIN; MAX :: S8_MAX; } - #if type == s16 { MIN :: S16_MIN; MAX :: S16_MAX; } - #if type == s32 { MIN :: S32_MIN; MAX :: S32_MAX; } - #if type == s64 { MIN :: S64_MIN; MAX :: S64_MAX; } - - NUM_TESTS :: 50000; - DATA_SIZE_BITS :: 64*1024*8; - #if type == s8 || type == u8 then - DATA_SIZE :: DATA_SIZE_BITS/8; - else #if type == s16 || type == u16 then - DATA_SIZE :: DATA_SIZE_BITS/16; - else #if type == s32 || type == u32 then - DATA_SIZE :: DATA_SIZE_BITS/32; - else #if type == s64 || type == u64 then - DATA_SIZE :: DATA_SIZE_BITS/64; - - best_gen := 0.0; - best_asm := 0.0; - numbers_x: [..] type; - numbers_y: [..] type; - array_reserve(*numbers_x, DATA_SIZE); - array_reserve(*numbers_y, DATA_SIZE); - - // Comment the line bellow to use the same "random" values. - random_seed(cast(u64)to_nanoseconds(current_time_monotonic())); - - for 0..DATA_SIZE-1 { - x := cast(type) random_get_within_range(xx MIN, xx MAX); - y := cast(type) random_get_within_range(xx MIN, xx MAX); - if y == 0 && operation == "div" { - y = 1; - } - array_add(*numbers_x, x); - array_add(*numbers_y, y); - } - - for 0..NUM_TESTS-1 { - - r_gen: type = 0; - r_asm: type = 0; - - time_gen := current_time_monotonic(); - for idx: 0..DATA_SIZE-1 #insert #run replace("r_gen ^= OP(numbers_x[idx], numbers_y[idx], true);", "OP", operation); - time_gen = current_time_monotonic() - time_gen; - - time_asm := current_time_monotonic(); - for idx: 0..DATA_SIZE-1 #insert #run replace("r_asm ^= OP(numbers_x[idx], numbers_y[idx]);", "OP", operation); - time_asm = current_time_monotonic() - time_asm; - - assert(r_gen == r_asm); - - perf_gen := cast(float)DATA_SIZE/cast(float)to_nanoseconds(time_gen); - perf_asm := cast(float)DATA_SIZE/cast(float)to_nanoseconds(time_asm); - best_gen = max(best_gen, perf_gen); - best_asm = max(best_asm, perf_asm); - } - - tmp_context := context; - push_context tmp_context { - ff := *context.print_style.default_format_float; - ff.zero_removal = .NO; - ff.width = 7; - ff.trailing_width = 2; - - fi := *context.print_style.default_format_int; - fi.minimum_digits = 3; - - if print_result { - if type == s8 || type == u8 write_string(" "); - print("% | % | % | %\n", type, best_gen, best_asm, cast(int)(100*best_asm/best_gen)); - } - } - return best_gen, best_asm; - } - - write_strings( - " | (ops / nsec) |\n", - " T | generic | x64 asm | %\n" - ); - - write_strings( - "--- | ----------------- | ---\n", - " | add |\n" - ); - performance_test("add", u8); - performance_test("add", u16); - performance_test("add", u32); - performance_test("add", u64); - performance_test("add", s8); - performance_test("add", s16); - performance_test("add", s32); - performance_test("add", s64); - - write_strings( - "--- | ----------------- | ---\n", - " | sub |\n" - ); - performance_test("sub", u8); - performance_test("sub", u16); - performance_test("sub", u32); - performance_test("sub", u64); - performance_test("sub", s8); - performance_test("sub", s16); - performance_test("sub", s32); - performance_test("sub", s64); - - write_strings( - "--- | ----------------- | ---\n", - " | mul |\n" - ); - performance_test("mul", u8); - performance_test("mul", u16); - performance_test("mul", u32); - performance_test("mul", u64); - performance_test("mul", s8); - performance_test("mul", s16); - performance_test("mul", s32); - performance_test("mul", s64); - - write_strings( - "--- | ----------------- | ---\n", - " | div |\n" - ); - performance_test("div", u8); - performance_test("div", u16); - performance_test("div", u32); - performance_test("div", u64); - performance_test("div", s8); - performance_test("div", s16); - performance_test("div", s32); - performance_test("div", s64); -} diff --git a/modules/Integer_Saturating_Arithmetic.jai b/modules/Integer_Saturating_Arithmetic.jai deleted file mode 100644 index 74643e0..0000000 --- a/modules/Integer_Saturating_Arithmetic.jai +++ /dev/null @@ -1,416 +0,0 @@ -// Integer saturating arighmetic (with assembly branch-free procedures for x64 - expecting signed values in two's complement). - -#import "Basic"; -#import "Math"; -#import "String"; - - -INTEGER_ARITHMETIC_TYPES_CHECK :: #string DONE - type_info_x := cast(*Type_Info)Tx; - type_info_y := cast(*Type_Info)Ty; - if type_info_x.type != .INTEGER || type_info_y.type != .INTEGER return false, "Non integers values passed."; - tx := cast(*Type_Info_Integer)type_info_x; - ty := cast(*Type_Info_Integer)type_info_y; - - largest_type := - ifx tx.runtime_size > ty.runtime_size then Tx else - ifx ty.runtime_size > tx.runtime_size then Ty else - ifx tx.signed == ty.signed then Tx else - void; - - // Only allow to add different signedness values if largest type is the signed one (as in JAI). - if tx.signed == ty.signed { - Tx = largest_type; - Ty = largest_type; - Tr = largest_type; - } - else if tx.signed && Tx == largest_type { - Ty = largest_type; - Tr = largest_type; - } - else if ty.signed && Ty == largest_type { - Tx = largest_type; - Tr = largest_type; - } - else return false, "Number signedness mismatch."; - - return true; -DONE - -add :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } -{ - - #if USE_GENERIC || CPU != .X64 { - - #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { - - #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } - #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } - #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } - #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } - - if (y > 0 && x > MAX - y) then return MAX, true; - if (y < 0 && x < MIN - y) then return MIN, true; - - } else { - - #if Tr == u8 { MAX :: U8_MAX; } - #if Tr == u16 { MAX :: U16_MAX; } - #if Tr == u32 { MAX :: U32_MAX; } - #if Tr == u64 { MAX :: U64_MAX; } - - if (x > MAX - y) then return MAX, true; - - } - - return x + y, false; - - } else { - - result: Tr = ---; - saturated: bool = ---; - - - ADD_SIGNED_ASM :: #string DONE - #asm { - mov result, -1; // Pre-set result with signed maximum (set all bits... - shr.SIZE result, 1; // ...then, clear MSB). - bt x, SIGN_BIT; // Test sign bit (affect CF). - adc result, 0; // Overflow signed maximum to signed minimum if CF is set. - - add.SIZE x, y; // Add values (affect OF). - seto saturated; // Set saturated flag if OF. - cmovno result, x; // Move add-result to result if NOT OF. - } - DONE - - #if Tr == s8 - #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); - #if Tr == s16 - #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); - #if Tr == s32 - #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); - #if Tr == s64 - #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); - - - ADD_UNSIGNED_ASM :: #string DONE - #asm { - mov result, -1; // Pre-set result with unsigned maximum. - add.SIZE x, y; // Add values (affect CF). - setc saturated; // Set saturated flag if CF. - cmovnc result, x; // Move add-result to result if NOT CF. - } - DONE - - #if Tr == u8 - #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".b"); - #if Tr == u16 - #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".w"); - #if Tr == u32 - #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".d"); - #if Tr == u64 - #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".q"); - - - return result, saturated; - - } -} - -sub :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } -{ - - #if USE_GENERIC || CPU != .X64 { - - #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { - - #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } - #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } - #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } - #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } - - if (y < 0 && x > MAX + y) then return MAX, true; - if (y > 0 && x < MIN + y) then return MIN, true; - - } else { - - if (y > x) then return 0, true; - - } - - return x - y, false; - - } else { - - result: Tr = ---; - saturated: bool = ---; - - - SUB_SIGNED_ASM :: #string DONE - #asm { - mov result, -1; // Pre-set result with signed maximum (set all bits... - shr.SIZE result, 1; // ...then, clear MSB). - bt x, SIGN_BIT; // Test signal bit (affect CF). - adc result, 0; // Overflow signed maximum to signed minimum if CF is set. - - sub.SIZE x, y; // Subtract values (affect OF). - seto saturated; // Set saturated flag if OF. - cmovno result, x; // Move subtract-result to result if NOT OF. - } - DONE - - #if Tr == s8 - #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); - #if Tr == s16 - #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); - #if Tr == s32 - #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); - #if Tr == s64 - #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); - - - SUB_UNSIGNED_ASM :: #string DONE - #asm { - xor result, result; // Pre-set result with usigned minimum (zero). - sub.SIZE x, y; // Subtract values (affect CF). - setc saturated; // Set saturated flag if CF. - cmovnc result, x; // Move subtract-result to result if NOT CF. - } - DONE - - #if Tr == u8 - #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".b"); - #if Tr == u16 - #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".w"); - #if Tr == u32 - #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".d"); - #if Tr == u64 - #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".q"); - - - return result, saturated; - - } - -} - -mul :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } -{ - - #if USE_GENERIC || CPU != .X64 { - - #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { - - #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } - #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } - #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } - #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } - - if x == 0 || y == 0 then return 0, false; - if x > 0 && y > 0 && x > MAX / y then return MAX, true; - if x < 0 && y < 0 && x < MAX / y then return MAX, true; - if (y < 0 && x > 0 && y < MIN / x) || (x < 0 && y > 0 && x < MIN / y) then return MIN, true; - - } else { - - #if Tr == u8 { MAX :: U8_MAX; } - #if Tr == u16 { MAX :: U16_MAX; } - #if Tr == u32 { MAX :: U32_MAX; } - #if Tr == u64 { MAX :: U64_MAX; } - - if x == 0 || y == 0 then return 0, false; - if x > MAX / y then return MAX, true; - - } - - return x * y, false; - - } else { - - result: Tr = ---; - saturated: bool = ---; - - MUL_SIGNED_ASM :: #string DONE - #asm { - // Using two copies of the x value (x_, sign) seems to be a bit faster (not sure why). - mov x_: gpr === a, x; // Pin copy of x value to register A. - - mov result, -1; // Pre-set result with signed maximum (set all bits... - shr.SIZE result, 1; // ...then, clear MSB). - mov sign:, x; // Use copy of x value. - xor sign, y; // Calculate result signal bit using xor. - bt sign, SIGN_BIT; // Test signal bit (affect CF). - adc result, 0; // Overflow signed maximum to signed minimum if CF is set. - - imul.SIZE x_, y; // Multiply values (affect OF). - seto saturated; // Set saturated flag if OF. - cmovno result, x_; // Move multiply-result to result if NOT OF. - } - DONE - - #if Tr == s8 - #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); - #if Tr == s16 - #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); - #if Tr == s32 - #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); - #if Tr == s64 - #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); - - - MUL_UNSIGNED_ASM :: #string DONE - #asm { - result === a; // Pin result to register A. - - mov result, x; // Move value x to result. - mul.SIZE reg_d:, result, y; // Multiply values (affect CF). - setc saturated; // Set saturated flag if CF. - sbb mask:, mask; // If CF: mask = -1 (all bits set); else: mask = 0. - or result, mask; // If CF was set, then result will be set to unsigned maximum (all bits set). - } - DONE - - #if Tr == u8 - #insert #run replace(replace(MUL_UNSIGNED_ASM, ".SIZE", ".b"), "reg_d:,", ""); // For 8bits mul, we do not need D register. - #if Tr == u16 - #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".w"); - #if Tr == u32 - #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".d"); - #if Tr == u64 - #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".q"); - - - return result, saturated; - - } -} - -div :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, remainder: Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } -{ - - #if USE_GENERIC || CPU != .X64 { - - #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { - - #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } - #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } - #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } - #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } - - if x == MIN && y == -1 then return MAX, -1, true; - - } - - result := x / y; - remainder := x - (y * result); - return result, remainder, false; - - } else { - - result: Tr = ---; - remainder: Tr = ---; - saturated: bool = ---; - - DIV_SIGNED_ASM :: #string DONE - #asm { - result === a; // Pin result to register A (to be used as dividend on idiv). - remainder === d; // Pin remainder to register D. - - xor saturated, saturated; // Clear saturated. - - // Detect div(MIN/-1) and flag it on ZF. - mov t_dividend:, -1; // Pre-set t_dividend with signed minimum (set all bits... - shr.SIZE t_dividend, 1; // ...then, clear MSB... - not t_dividend; // ...then, negate to obtain MSB set and all other bits cleared). - // - mov limit:, t_dividend; // Keep copy of signed minimum on limit. - add limit, 1; // Set limit as signed minimum + 1. - // - xor.SIZE t_dividend, x; // Clear dividend if x value is equal to signed minimum. - // - mov t_divisor:, -1; // Pre-set test_divisor with -1. - xor.SIZE t_divisor, y; // Clear test_divisor if y value is equal to -1. - // - or.SIZE t_dividend, t_divisor; // Or t_dividend with t_divisor (affect ZF). - - setz saturated; // Set saturated flag if ZF. - mov result, x; // Copy x value to result (dividend). - cmovz result, limit; // If ZF: copy limit (signed minimum + 1) to result (dividend). - - DIVIDE_PLACEHOLDER - - sub.SIZE remainder, saturated; // If saturated: remainder = 0 - 1; otherwise: remainder = x - 0. - } - DONE - - DIV_SIGNED_CALC_8BITS :: #string DONE - cbw result; // Prepare dividend high bits (sign-extend). - idiv.SIZE result, y; // Divide values. - mov remainder, result; // Extract remainder from result's high bits. - sar remainder, 8; // Shift remainder from high to low bits. - DONE - - DIV_SIGNED_CALC_16BITS :: #string DONE - cwd remainder, result; // Prepare dividend high bits (sign-extend). - idiv.SIZE remainder, result, y; // Divide values. - DONE - - DIV_SIGNED_CALC_32BITS :: #string DONE - cdq remainder, result; // Prepare dividend high bits (sign-extend). - idiv.SIZE remainder, result, y; // Divide values. - DONE - - DIV_SIGNED_CALC_64BITS :: #string DONE - cqo remainder, result; // Prepare dividend high bits (sign-extend). - idiv.SIZE remainder, result, y; // Divide values. - DONE - - #if Tr == s8 - #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_8BITS), ".SIZE", ".b"); - #if Tr == s16 - #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_16BITS), ".SIZE", ".w"); - #if Tr == s32 - #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_32BITS), ".SIZE", ".d"); - #if Tr == s64 - #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_64BITS), ".SIZE", ".q"); - - - DIV_UNSIGNED_ASM :: #string DONE - #asm { - result === a; // Pin result to register A. - remainder === d; // Pin remainder to register D. - - xor result, result; // Clear result. - xor remainder, remainder; // Clear remainder (required when used as dividend's high bits). - xor saturated, saturated; // Clear saturated (unsigned division never saturates). - mov result, x; // Copy x value to result. - - DIVIDE_PLACEHOLDER - } - DONE - - DIV_UNSIGNED_CALC_8BITS :: #string DONE - div.SIZE result, y; // Divide values. - mov remainder, result; // Extract remainder from result's high bits. - sar remainder, 8; // Shift remainder from high to low bits. - DONE - - DIV_UNSIGNED_CALC :: #string DONE - div.SIZE remainder, result, y; // Divide values. - DONE - - #if Tr == u8 - #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC_8BITS), ".SIZE", ".b"); - #if Tr == u16 - #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".w"); - #if Tr == u32 - #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".d"); - #if Tr == u64 - #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".q"); - - - return result, remainder, saturated; - - } -} diff --git a/modules/Saturation/module.jai b/modules/Saturation/module.jai new file mode 100644 index 0000000..74643e0 --- /dev/null +++ b/modules/Saturation/module.jai @@ -0,0 +1,416 @@ +// Integer saturating arighmetic (with assembly branch-free procedures for x64 - expecting signed values in two's complement). + +#import "Basic"; +#import "Math"; +#import "String"; + + +INTEGER_ARITHMETIC_TYPES_CHECK :: #string DONE + type_info_x := cast(*Type_Info)Tx; + type_info_y := cast(*Type_Info)Ty; + if type_info_x.type != .INTEGER || type_info_y.type != .INTEGER return false, "Non integers values passed."; + tx := cast(*Type_Info_Integer)type_info_x; + ty := cast(*Type_Info_Integer)type_info_y; + + largest_type := + ifx tx.runtime_size > ty.runtime_size then Tx else + ifx ty.runtime_size > tx.runtime_size then Ty else + ifx tx.signed == ty.signed then Tx else + void; + + // Only allow to add different signedness values if largest type is the signed one (as in JAI). + if tx.signed == ty.signed { + Tx = largest_type; + Ty = largest_type; + Tr = largest_type; + } + else if tx.signed && Tx == largest_type { + Ty = largest_type; + Tr = largest_type; + } + else if ty.signed && Ty == largest_type { + Tx = largest_type; + Tr = largest_type; + } + else return false, "Number signedness mismatch."; + + return true; +DONE + +add :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } +{ + + #if USE_GENERIC || CPU != .X64 { + + #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { + + #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } + #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } + #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } + #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } + + if (y > 0 && x > MAX - y) then return MAX, true; + if (y < 0 && x < MIN - y) then return MIN, true; + + } else { + + #if Tr == u8 { MAX :: U8_MAX; } + #if Tr == u16 { MAX :: U16_MAX; } + #if Tr == u32 { MAX :: U32_MAX; } + #if Tr == u64 { MAX :: U64_MAX; } + + if (x > MAX - y) then return MAX, true; + + } + + return x + y, false; + + } else { + + result: Tr = ---; + saturated: bool = ---; + + + ADD_SIGNED_ASM :: #string DONE + #asm { + mov result, -1; // Pre-set result with signed maximum (set all bits... + shr.SIZE result, 1; // ...then, clear MSB). + bt x, SIGN_BIT; // Test sign bit (affect CF). + adc result, 0; // Overflow signed maximum to signed minimum if CF is set. + + add.SIZE x, y; // Add values (affect OF). + seto saturated; // Set saturated flag if OF. + cmovno result, x; // Move add-result to result if NOT OF. + } + DONE + + #if Tr == s8 + #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); + #if Tr == s16 + #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); + #if Tr == s32 + #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); + #if Tr == s64 + #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); + + + ADD_UNSIGNED_ASM :: #string DONE + #asm { + mov result, -1; // Pre-set result with unsigned maximum. + add.SIZE x, y; // Add values (affect CF). + setc saturated; // Set saturated flag if CF. + cmovnc result, x; // Move add-result to result if NOT CF. + } + DONE + + #if Tr == u8 + #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".b"); + #if Tr == u16 + #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".w"); + #if Tr == u32 + #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".d"); + #if Tr == u64 + #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".q"); + + + return result, saturated; + + } +} + +sub :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } +{ + + #if USE_GENERIC || CPU != .X64 { + + #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { + + #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } + #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } + #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } + #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } + + if (y < 0 && x > MAX + y) then return MAX, true; + if (y > 0 && x < MIN + y) then return MIN, true; + + } else { + + if (y > x) then return 0, true; + + } + + return x - y, false; + + } else { + + result: Tr = ---; + saturated: bool = ---; + + + SUB_SIGNED_ASM :: #string DONE + #asm { + mov result, -1; // Pre-set result with signed maximum (set all bits... + shr.SIZE result, 1; // ...then, clear MSB). + bt x, SIGN_BIT; // Test signal bit (affect CF). + adc result, 0; // Overflow signed maximum to signed minimum if CF is set. + + sub.SIZE x, y; // Subtract values (affect OF). + seto saturated; // Set saturated flag if OF. + cmovno result, x; // Move subtract-result to result if NOT OF. + } + DONE + + #if Tr == s8 + #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); + #if Tr == s16 + #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); + #if Tr == s32 + #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); + #if Tr == s64 + #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); + + + SUB_UNSIGNED_ASM :: #string DONE + #asm { + xor result, result; // Pre-set result with usigned minimum (zero). + sub.SIZE x, y; // Subtract values (affect CF). + setc saturated; // Set saturated flag if CF. + cmovnc result, x; // Move subtract-result to result if NOT CF. + } + DONE + + #if Tr == u8 + #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".b"); + #if Tr == u16 + #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".w"); + #if Tr == u32 + #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".d"); + #if Tr == u64 + #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".q"); + + + return result, saturated; + + } + +} + +mul :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } +{ + + #if USE_GENERIC || CPU != .X64 { + + #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { + + #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } + #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } + #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } + #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } + + if x == 0 || y == 0 then return 0, false; + if x > 0 && y > 0 && x > MAX / y then return MAX, true; + if x < 0 && y < 0 && x < MAX / y then return MAX, true; + if (y < 0 && x > 0 && y < MIN / x) || (x < 0 && y > 0 && x < MIN / y) then return MIN, true; + + } else { + + #if Tr == u8 { MAX :: U8_MAX; } + #if Tr == u16 { MAX :: U16_MAX; } + #if Tr == u32 { MAX :: U32_MAX; } + #if Tr == u64 { MAX :: U64_MAX; } + + if x == 0 || y == 0 then return 0, false; + if x > MAX / y then return MAX, true; + + } + + return x * y, false; + + } else { + + result: Tr = ---; + saturated: bool = ---; + + MUL_SIGNED_ASM :: #string DONE + #asm { + // Using two copies of the x value (x_, sign) seems to be a bit faster (not sure why). + mov x_: gpr === a, x; // Pin copy of x value to register A. + + mov result, -1; // Pre-set result with signed maximum (set all bits... + shr.SIZE result, 1; // ...then, clear MSB). + mov sign:, x; // Use copy of x value. + xor sign, y; // Calculate result signal bit using xor. + bt sign, SIGN_BIT; // Test signal bit (affect CF). + adc result, 0; // Overflow signed maximum to signed minimum if CF is set. + + imul.SIZE x_, y; // Multiply values (affect OF). + seto saturated; // Set saturated flag if OF. + cmovno result, x_; // Move multiply-result to result if NOT OF. + } + DONE + + #if Tr == s8 + #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7"); + #if Tr == s16 + #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15"); + #if Tr == s32 + #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31"); + #if Tr == s64 + #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63"); + + + MUL_UNSIGNED_ASM :: #string DONE + #asm { + result === a; // Pin result to register A. + + mov result, x; // Move value x to result. + mul.SIZE reg_d:, result, y; // Multiply values (affect CF). + setc saturated; // Set saturated flag if CF. + sbb mask:, mask; // If CF: mask = -1 (all bits set); else: mask = 0. + or result, mask; // If CF was set, then result will be set to unsigned maximum (all bits set). + } + DONE + + #if Tr == u8 + #insert #run replace(replace(MUL_UNSIGNED_ASM, ".SIZE", ".b"), "reg_d:,", ""); // For 8bits mul, we do not need D register. + #if Tr == u16 + #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".w"); + #if Tr == u32 + #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".d"); + #if Tr == u64 + #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".q"); + + + return result, saturated; + + } +} + +div :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, remainder: Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } +{ + + #if USE_GENERIC || CPU != .X64 { + + #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { + + #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } + #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } + #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } + #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } + + if x == MIN && y == -1 then return MAX, -1, true; + + } + + result := x / y; + remainder := x - (y * result); + return result, remainder, false; + + } else { + + result: Tr = ---; + remainder: Tr = ---; + saturated: bool = ---; + + DIV_SIGNED_ASM :: #string DONE + #asm { + result === a; // Pin result to register A (to be used as dividend on idiv). + remainder === d; // Pin remainder to register D. + + xor saturated, saturated; // Clear saturated. + + // Detect div(MIN/-1) and flag it on ZF. + mov t_dividend:, -1; // Pre-set t_dividend with signed minimum (set all bits... + shr.SIZE t_dividend, 1; // ...then, clear MSB... + not t_dividend; // ...then, negate to obtain MSB set and all other bits cleared). + // + mov limit:, t_dividend; // Keep copy of signed minimum on limit. + add limit, 1; // Set limit as signed minimum + 1. + // + xor.SIZE t_dividend, x; // Clear dividend if x value is equal to signed minimum. + // + mov t_divisor:, -1; // Pre-set test_divisor with -1. + xor.SIZE t_divisor, y; // Clear test_divisor if y value is equal to -1. + // + or.SIZE t_dividend, t_divisor; // Or t_dividend with t_divisor (affect ZF). + + setz saturated; // Set saturated flag if ZF. + mov result, x; // Copy x value to result (dividend). + cmovz result, limit; // If ZF: copy limit (signed minimum + 1) to result (dividend). + + DIVIDE_PLACEHOLDER + + sub.SIZE remainder, saturated; // If saturated: remainder = 0 - 1; otherwise: remainder = x - 0. + } + DONE + + DIV_SIGNED_CALC_8BITS :: #string DONE + cbw result; // Prepare dividend high bits (sign-extend). + idiv.SIZE result, y; // Divide values. + mov remainder, result; // Extract remainder from result's high bits. + sar remainder, 8; // Shift remainder from high to low bits. + DONE + + DIV_SIGNED_CALC_16BITS :: #string DONE + cwd remainder, result; // Prepare dividend high bits (sign-extend). + idiv.SIZE remainder, result, y; // Divide values. + DONE + + DIV_SIGNED_CALC_32BITS :: #string DONE + cdq remainder, result; // Prepare dividend high bits (sign-extend). + idiv.SIZE remainder, result, y; // Divide values. + DONE + + DIV_SIGNED_CALC_64BITS :: #string DONE + cqo remainder, result; // Prepare dividend high bits (sign-extend). + idiv.SIZE remainder, result, y; // Divide values. + DONE + + #if Tr == s8 + #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_8BITS), ".SIZE", ".b"); + #if Tr == s16 + #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_16BITS), ".SIZE", ".w"); + #if Tr == s32 + #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_32BITS), ".SIZE", ".d"); + #if Tr == s64 + #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_64BITS), ".SIZE", ".q"); + + + DIV_UNSIGNED_ASM :: #string DONE + #asm { + result === a; // Pin result to register A. + remainder === d; // Pin remainder to register D. + + xor result, result; // Clear result. + xor remainder, remainder; // Clear remainder (required when used as dividend's high bits). + xor saturated, saturated; // Clear saturated (unsigned division never saturates). + mov result, x; // Copy x value to result. + + DIVIDE_PLACEHOLDER + } + DONE + + DIV_UNSIGNED_CALC_8BITS :: #string DONE + div.SIZE result, y; // Divide values. + mov remainder, result; // Extract remainder from result's high bits. + sar remainder, 8; // Shift remainder from high to low bits. + DONE + + DIV_UNSIGNED_CALC :: #string DONE + div.SIZE remainder, result, y; // Divide values. + DONE + + #if Tr == u8 + #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC_8BITS), ".SIZE", ".b"); + #if Tr == u16 + #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".w"); + #if Tr == u32 + #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".d"); + #if Tr == u64 + #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".q"); + + + return result, remainder, saturated; + + } +} diff --git a/modules/Saturation/tests.jai b/modules/Saturation/tests.jai new file mode 100644 index 0000000..372d66d --- /dev/null +++ b/modules/Saturation/tests.jai @@ -0,0 +1,304 @@ +// Tests for integer saturating arighmetic procedures. + +#import "Basic"; +#import "Compiler"; +#import "Math"; +#import "String"; +#import "Saturation"; + +main :: () { + + write_strings( + "#=======================#\n", + "# Basic tests #\n" + ); + + test_op :: (operation: string, x: $Tx, y: $Ty, result: $Tr, type: Type, saturated: bool, remainder: Tr = 0) -> errors_found: int #expand { + + print_test_call :: (operation: string) -> string { + str: string = ---; + if operation != "div" { + TEST_CALL :: #string DONE + t_result, t_saturated := OP(cast(Tx)x, cast(Ty)y); + if result != t_result print("%_%(%, %) = %0%0\n", operation, type, x, y, result, ifx saturated then " : saturated"); + DONE + str = replace(TEST_CALL, "OP", operation); + } else { + TEST_CALL :: #string DONE + t_result, t_remainder, t_saturated := OP(cast(Tx)x, cast(Ty)y); + if result != t_result print("%_%(%, %) = % + %0%0\n", operation, type, x, y, result, remainder, ifx saturated then " : saturated"); + DONE + str = replace(TEST_CALL, "OP", operation); + } + return str; + } + + #insert #run print_test_call(operation); + + errors := 0; + if result != t_result { errors += 1; print(" > incorrect result value: got % expected %\n", t_result, result); }; + if type != type_of(t_result) { errors += 1; print(" > incorrect result type: got % expected %\n", type_of(t_result), type); }; + if saturated != t_saturated { errors += 1; print(" > incorrect saturated flag: got % expected %\n", t_saturated, saturated); }; + #if operation == "div" { + if remainder != t_remainder { errors += 1; print(" > incorrect remainder value: got % expected %\n", t_remainder, remainder); }; + } + return errors; + } + + errors := 0; + + // Test signed add. + errors += test_op("add", cast( s8) S8_MAX, cast( s8)1, S8_MAX, s8, true); + errors += test_op("add", cast(s16)S16_MAX, cast( u8)1, S16_MAX, s16, true); + errors += test_op("add", cast(s32)S32_MAX, cast(s32)1, S32_MAX, s32, true); + errors += test_op("add", cast(s64)S64_MAX, cast(u32)1, S64_MAX, s64, true); + + errors += test_op("add", cast( s8) S8_MAX, cast( s8) S8_MIN, -1, s8, false); + errors += test_op("add", cast(s16)S16_MAX, cast(s16)S16_MIN, -1, s16, false); + errors += test_op("add", cast(s32)S32_MAX, cast(s32)S32_MIN, -1, s32, false); + errors += test_op("add", cast(s64)S64_MAX, cast(s64)S64_MIN, -1, s64, false); + + // Test unsigned add. + errors += test_op("add", cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, true); + errors += test_op("add", cast(u16)U16_MAX, cast(u16)1, U16_MAX, u16, true); + errors += test_op("add", cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, true); + errors += test_op("add", cast(u64)U64_MAX, cast(u64)1, U64_MAX, u64, true); + + errors += test_op("add", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); + errors += test_op("add", cast(u16)U16_MAX, cast(u16)0, U16_MAX, u16, false); + errors += test_op("add", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); + errors += test_op("add", cast(u64)U64_MAX, cast(u64)0, U64_MAX, u64, false); + + // Test signed sub. + errors += test_op("sub", cast( s8) S8_MIN, cast( s8)1, S8_MIN, s8, true); + errors += test_op("sub", cast(s16)S16_MIN, cast( u8)1, S16_MIN, s16, true); + errors += test_op("sub", cast(s32)S32_MIN, cast(s32)1, S32_MIN, s32, true); + errors += test_op("sub", cast(s64)S64_MIN, cast(u32)1, S64_MIN, s64, true); + + errors += test_op("sub", cast( s8)-1, cast( s8) S8_MAX, S8_MIN, s8, false); + errors += test_op("sub", cast(s16)-1, cast(s16)S16_MAX, S16_MIN, s16, false); + errors += test_op("sub", cast(s32)-1, cast(s32)S32_MAX, S32_MIN, s32, false); + errors += test_op("sub", cast(s64)-1, cast(s64)S64_MAX, S64_MIN, s64, false); + + // Test unsigned sub. + errors += test_op("sub", cast( u8)1, cast( u8) U8_MAX, 0, u8, true); + errors += test_op("sub", cast( u8)1, cast(u16)U16_MAX, 0, u16, true); + errors += test_op("sub", cast(u32)1, cast(u32)U32_MAX, 0, u32, true); + errors += test_op("sub", cast(u32)1, cast(u64)U64_MAX, 0, u64, true); + + errors += test_op("sub", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); + errors += test_op("sub", cast(u16)U16_MAX, cast( u8)0, U16_MAX, u16, false); + errors += test_op("sub", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); + errors += test_op("sub", cast(u64)U64_MAX, cast(u32)0, U64_MAX, u64, false); + + // Test signed mul. + errors += test_op("mul", cast( s8) S8_MIN, cast( s8)-1, S8_MAX, s8, true); + errors += test_op("mul", cast(s16)S16_MIN, cast( s8)-1, S16_MAX, s16, true); + errors += test_op("mul", cast(s32)S32_MIN, cast(s32)-1, S32_MAX, s32, true); + errors += test_op("mul", cast(s64)S64_MIN, cast(s32)-1, S64_MAX, s64, true); + + errors += test_op("mul", cast( s8) S8_MAX, cast( s8)-2, S8_MIN, s8, true); + errors += test_op("mul", cast(s16)S16_MAX, cast( s8)-2, S16_MIN, s16, true); + errors += test_op("mul", cast(s32)S32_MAX, cast(s32)-2, S32_MIN, s32, true); + errors += test_op("mul", cast(s64)S64_MAX, cast(s32)-2, S64_MIN, s64, true); + + errors += test_op("mul", cast( s8)-2, cast( s8) S8_MAX, S8_MIN, s8, true); + errors += test_op("mul", cast( s8)-2, cast(s16)S16_MAX, S16_MIN, s16, true); + errors += test_op("mul", cast(s32)-2, cast(s32)S32_MAX, S32_MIN, s32, true); + errors += test_op("mul", cast(s32)-2, cast(s64)S64_MAX, S64_MIN, s64, true); + + errors += test_op("mul", cast( s8) S8_MAX, cast( s8)2, S8_MAX, s8, true); + errors += test_op("mul", cast(s16)S16_MAX, cast( s8)2, S16_MAX, s16, true); + errors += test_op("mul", cast(s32)S32_MAX, cast(s32)2, S32_MAX, s32, true); + errors += test_op("mul", cast(s64)S64_MAX, cast(s32)2, S64_MAX, s64, true); + + errors += test_op("mul", cast( s8) S8_MAX, cast( s8)-1, -S8_MAX, s8, false); + errors += test_op("mul", cast(s16)S16_MAX, cast( s8)-1, -S16_MAX, s16, false); + errors += test_op("mul", cast(s32)S32_MAX, cast(s32)-1, -S32_MAX, s32, false); + errors += test_op("mul", cast(s64)S64_MAX, cast(s32)-1, -S64_MAX, s64, false); + + errors += test_op("mul", cast( s8) S8_MAX, cast( s8)0, 0, s8, false); + errors += test_op("mul", cast(s16)S16_MAX, cast( u8)0, 0, s16, false); + errors += test_op("mul", cast(s32)S32_MAX, cast(s32)0, 0, s32, false); + errors += test_op("mul", cast(s64)S64_MAX, cast(u32)0, 0, s64, false); + + // Test unsigned mul. + errors += test_op("mul", cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, false); + errors += test_op("mul", cast(u16)U16_MAX, cast( u8)1, U16_MAX, u16, false); + errors += test_op("mul", cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, false); + errors += test_op("mul", cast(u64)U64_MAX, cast(u32)1, U64_MAX, u64, false); + + errors += test_op("mul", cast( u8) U8_MAX, cast( u8)2, U8_MAX, u8, true); + errors += test_op("mul", cast(u16)U16_MAX, cast( u8)2, U16_MAX, u16, true); + errors += test_op("mul", cast(u32)U32_MAX, cast(u32)2, U32_MAX, u32, true); + errors += test_op("mul", cast(u64)U64_MAX, cast(u32)2, U64_MAX, u64, true); + + // Test signed div. + errors += test_op("div", cast( s8) S8_MIN, cast( s8)-1, S8_MAX, s8, true, -1); + errors += test_op("div", cast(s16)S16_MIN, cast( s8)-1, S16_MAX, s16, true, -1); + errors += test_op("div", cast(s32)S32_MIN, cast(s32)-1, S32_MAX, s32, true, -1); + errors += test_op("div", cast(s64)S64_MIN, cast(s32)-1, S64_MAX, s64, true, -1); + + errors += test_op("div", cast( s8) S8_MAX, cast( s8)-2, - S8_MAX/2, s8, false, 1); + errors += test_op("div", cast(s16)S16_MAX, cast( s8)-2, -S16_MAX/2, s16, false, 1); + errors += test_op("div", cast(s32)S32_MAX, cast(s32)-2, -S32_MAX/2, s32, false, 1); + errors += test_op("div", cast(s64)S64_MAX, cast(s32)-2, -S64_MAX/2, s64, false, 1); + + errors += test_op("div", cast( s8)15, cast( s8)5, 3, s8, false, 0); + errors += test_op("div", cast( u8)15, cast(s16)7, 2, s16, false, 1); + errors += test_op("div", cast(s16)15, cast(s32)13, 1, s32, false, 2); + errors += test_op("div", cast(u16)100, cast(s64)3, 33, s64, false, 1); + + // Test unsigned div. + errors += test_op("div", cast( u8) U8_MAX, cast( u8)2, U8_MAX/2, u8, false, 1); + errors += test_op("div", cast(u16)U16_MAX, cast( u8)2, U16_MAX/2, u16, false, 1); + errors += test_op("div", cast(u32)U32_MAX, cast(u32)2, U32_MAX/2, u32, false, 1); + errors += test_op("div", cast(u64)U64_MAX, cast(u32)2, U64_MAX/2, u64, false, 1); + + if errors > 0 print("# Found % %!\n", errors, ifx errors == 1 then "error" else "errors"); else print(" No errors found.\n"); + + write_strings( + "#=======================#\n", + "# Benchmarks #\n" + ); + + #import "Random"; + + performance_test :: ($operation: string, $type: Type, print_result: bool = true) -> ops_per_us_gen: float, ops_per_us_asm: float { + + #if type == u8 { MIN :: 0; MAX :: U8_MAX; } + #if type == u16 { MIN :: 0; MAX :: U16_MAX; } + #if type == u32 { MIN :: 0; MAX :: U32_MAX; } + #if type == u64 { MIN :: 0; MAX :: U64_MAX; } + #if type == s8 { MIN :: S8_MIN; MAX :: S8_MAX; } + #if type == s16 { MIN :: S16_MIN; MAX :: S16_MAX; } + #if type == s32 { MIN :: S32_MIN; MAX :: S32_MAX; } + #if type == s64 { MIN :: S64_MIN; MAX :: S64_MAX; } + + NUM_TESTS :: 50000; + DATA_SIZE_BITS :: 64*1024*8; + #if type == s8 || type == u8 then + DATA_SIZE :: DATA_SIZE_BITS/8; + else #if type == s16 || type == u16 then + DATA_SIZE :: DATA_SIZE_BITS/16; + else #if type == s32 || type == u32 then + DATA_SIZE :: DATA_SIZE_BITS/32; + else #if type == s64 || type == u64 then + DATA_SIZE :: DATA_SIZE_BITS/64; + + best_gen := 0.0; + best_asm := 0.0; + numbers_x: [..] type; + numbers_y: [..] type; + array_reserve(*numbers_x, DATA_SIZE); + array_reserve(*numbers_y, DATA_SIZE); + + // Comment the line bellow to use the same "random" values. + random_seed(cast(u64)to_nanoseconds(current_time_monotonic())); + + for 0..DATA_SIZE-1 { + x := cast(type) random_get_within_range(xx MIN, xx MAX); + y := cast(type) random_get_within_range(xx MIN, xx MAX); + if y == 0 && operation == "div" { + y = 1; + } + array_add(*numbers_x, x); + array_add(*numbers_y, y); + } + + for 0..NUM_TESTS-1 { + + r_gen: type = 0; + r_asm: type = 0; + + time_gen := current_time_monotonic(); + for idx: 0..DATA_SIZE-1 #insert #run replace("r_gen ^= OP(numbers_x[idx], numbers_y[idx], true);", "OP", operation); + time_gen = current_time_monotonic() - time_gen; + + time_asm := current_time_monotonic(); + for idx: 0..DATA_SIZE-1 #insert #run replace("r_asm ^= OP(numbers_x[idx], numbers_y[idx]);", "OP", operation); + time_asm = current_time_monotonic() - time_asm; + + assert(r_gen == r_asm); + + perf_gen := cast(float)DATA_SIZE/cast(float)to_nanoseconds(time_gen); + perf_asm := cast(float)DATA_SIZE/cast(float)to_nanoseconds(time_asm); + best_gen = max(best_gen, perf_gen); + best_asm = max(best_asm, perf_asm); + } + + tmp_context := context; + push_context tmp_context { + ff := *context.print_style.default_format_float; + ff.zero_removal = .NO; + ff.width = 7; + ff.trailing_width = 2; + + fi := *context.print_style.default_format_int; + fi.minimum_digits = 3; + + if print_result { + if type == s8 || type == u8 write_string(" "); + print("% | % | % | %\n", type, best_gen, best_asm, cast(int)(100*best_asm/best_gen)); + } + } + return best_gen, best_asm; + } + + write_strings( + " | (ops / nsec) |\n", + " T | generic | x64 asm | %\n" + ); + + write_strings( + "--- | ----------------- | ---\n", + " | add |\n" + ); + performance_test("add", u8); + performance_test("add", u16); + performance_test("add", u32); + performance_test("add", u64); + performance_test("add", s8); + performance_test("add", s16); + performance_test("add", s32); + performance_test("add", s64); + + write_strings( + "--- | ----------------- | ---\n", + " | sub |\n" + ); + performance_test("sub", u8); + performance_test("sub", u16); + performance_test("sub", u32); + performance_test("sub", u64); + performance_test("sub", s8); + performance_test("sub", s16); + performance_test("sub", s32); + performance_test("sub", s64); + + write_strings( + "--- | ----------------- | ---\n", + " | mul |\n" + ); + performance_test("mul", u8); + performance_test("mul", u16); + performance_test("mul", u32); + performance_test("mul", u64); + performance_test("mul", s8); + performance_test("mul", s16); + performance_test("mul", s32); + performance_test("mul", s64); + + write_strings( + "--- | ----------------- | ---\n", + " | div |\n" + ); + performance_test("div", u8); + performance_test("div", u16); + performance_test("div", u32); + performance_test("div", u64); + performance_test("div", s8); + performance_test("div", s16); + performance_test("div", s32); + performance_test("div", s64); +} diff --git a/modules/TUI/key_map.jai b/modules/TUI/key_map.jai index 7a074b2..f0754a7 100644 --- a/modules/TUI/key_map.jai +++ b/modules/TUI/key_map.jai @@ -8,10 +8,11 @@ setup_key_map :: () { /* This table was created/tested using the following terminals: - - g: gnome (terminal) + - g: gnome terminal - i: kitty - k: konsole - - l: linux (console) + - l: linux console + - w: windows terminal - x: xterm To signal modifier keys, a letter is appended after a + (plus sign): @@ -33,16 +34,16 @@ setup_key_map :: () { "#f1+Z" -> F1 + Shift + Alt + Ctrl + Super */ - // Up // g i k l x - table_set(*key_map, "\e[A", to_key("#up")); // + + + + + + // Up // g i k l w x + table_set(*key_map, "\e[A", to_key("#up")); // + + + + + + table_set(*key_map, "\e[1;1A", to_key("#up")); // - table_set(*key_map, "\e[1;2A", to_key("#up+$")); // + + + + - table_set(*key_map, "\e[1;3A", to_key("#up+a")); // + + + + - table_set(*key_map, "\e[1;4A", to_key("#up+A")); // + + + + - table_set(*key_map, "\e[1;5A", to_key("#up+c")); // + + + + - table_set(*key_map, "\e[1;6A", to_key("#up+C")); // + + + + - table_set(*key_map, "\e[1;7A", to_key("#up+w")); // + + + + - table_set(*key_map, "\e[1;8A", to_key("#up+W")); // + + + + + table_set(*key_map, "\e[1;2A", to_key("#up+$")); // + + + + + + table_set(*key_map, "\e[1;3A", to_key("#up+a")); // + + + + + + table_set(*key_map, "\e[1;4A", to_key("#up+A")); // + + + + + table_set(*key_map, "\e[1;5A", to_key("#up+c")); // + + + + + + table_set(*key_map, "\e[1;6A", to_key("#up+C")); // + + + + + table_set(*key_map, "\e[1;7A", to_key("#up+w")); // + + + + + + table_set(*key_map, "\e[1;8A", to_key("#up+W")); // + + + + + table_set(*key_map, "\e[1;9A", to_key("#up+s")); // + table_set(*key_map, "\e[1;10A", to_key("#up+S")); // + table_set(*key_map, "\e[1;11A", to_key("#up+x")); // + @@ -52,16 +53,16 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15A", to_key("#up+z")); // + table_set(*key_map, "\e[1;16A", to_key("#up+Z")); // + - // Down // g i k l x - table_set(*key_map, "\e[B", to_key("#down")); // + + + + + + // Down // g i k l w x + table_set(*key_map, "\e[B", to_key("#down")); // + + + + + + table_set(*key_map, "\e[1;1B", to_key("#down")); // - table_set(*key_map, "\e[1;2B", to_key("#down+$")); // + + + + - table_set(*key_map, "\e[1;3B", to_key("#down+a")); // + + + + - table_set(*key_map, "\e[1;4B", to_key("#down+A")); // + + + + - table_set(*key_map, "\e[1;5B", to_key("#down+c")); // + + + + - table_set(*key_map, "\e[1;6B", to_key("#down+C")); // + + + + - table_set(*key_map, "\e[1;7B", to_key("#down+w")); // + + + + - table_set(*key_map, "\e[1;8B", to_key("#down+W")); // + + + + + table_set(*key_map, "\e[1;2B", to_key("#down+$")); // + + + + + + table_set(*key_map, "\e[1;3B", to_key("#down+a")); // + + + + + + table_set(*key_map, "\e[1;4B", to_key("#down+A")); // + + + + + table_set(*key_map, "\e[1;5B", to_key("#down+c")); // + + + + + + table_set(*key_map, "\e[1;6B", to_key("#down+C")); // + + + + + table_set(*key_map, "\e[1;7B", to_key("#down+w")); // + + + + + + table_set(*key_map, "\e[1;8B", to_key("#down+W")); // + + + + + table_set(*key_map, "\e[1;9B", to_key("#down+s")); // + table_set(*key_map, "\e[1;10B", to_key("#down+S")); // + table_set(*key_map, "\e[1;11B", to_key("#down+x")); // + @@ -71,16 +72,16 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15B", to_key("#down+z")); // + table_set(*key_map, "\e[1;16B", to_key("#down+Z")); // + - // Right // g i k l x - table_set(*key_map, "\e[C", to_key("#right")); // + + + + + + // Right // g i k l w x + table_set(*key_map, "\e[C", to_key("#right")); // + + + + + + table_set(*key_map, "\e[1;1C", to_key("#right")); // - table_set(*key_map, "\e[1;2C", to_key("#right+$")); // + + + + - table_set(*key_map, "\e[1;3C", to_key("#right+a")); // + + + + - table_set(*key_map, "\e[1;4C", to_key("#right+A")); // + + + + - table_set(*key_map, "\e[1;5C", to_key("#right+c")); // + + + + - table_set(*key_map, "\e[1;6C", to_key("#right+C")); // + + + + - table_set(*key_map, "\e[1;7C", to_key("#right+w")); // + + + + - table_set(*key_map, "\e[1;8C", to_key("#right+W")); // + + + + + table_set(*key_map, "\e[1;2C", to_key("#right+$")); // + + + + + + table_set(*key_map, "\e[1;3C", to_key("#right+a")); // + + + + + + table_set(*key_map, "\e[1;4C", to_key("#right+A")); // + + + + + table_set(*key_map, "\e[1;5C", to_key("#right+c")); // + + + + + + table_set(*key_map, "\e[1;6C", to_key("#right+C")); // + + + + + + table_set(*key_map, "\e[1;7C", to_key("#right+w")); // + + + + + + table_set(*key_map, "\e[1;8C", to_key("#right+W")); // + + + + + table_set(*key_map, "\e[1;9C", to_key("#right+s")); // + table_set(*key_map, "\e[1;10C", to_key("#right+S")); // + table_set(*key_map, "\e[1;11C", to_key("#right+x")); // + @@ -90,16 +91,16 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15C", to_key("#right+z")); // + table_set(*key_map, "\e[1;16C", to_key("#right+Z")); // + - // Left // g i k l x - table_set(*key_map, "\e[D", to_key("#left")); // + + + + + + // Left // g i k l w x + table_set(*key_map, "\e[D", to_key("#left")); // + + + + + + table_set(*key_map, "\e[1;1D", to_key("#left")); // - table_set(*key_map, "\e[1;2D", to_key("#left+$")); // + + + + - table_set(*key_map, "\e[1;3D", to_key("#left+a")); // + + + + - table_set(*key_map, "\e[1;4D", to_key("#left+A")); // + + + + - table_set(*key_map, "\e[1;5D", to_key("#left+c")); // + + + + - table_set(*key_map, "\e[1;6D", to_key("#left+C")); // + + + + - table_set(*key_map, "\e[1;7D", to_key("#left+w")); // + + + + - table_set(*key_map, "\e[1;8D", to_key("#left+W")); // + + + + + table_set(*key_map, "\e[1;2D", to_key("#left+$")); // + + + + + + table_set(*key_map, "\e[1;3D", to_key("#left+a")); // + + + + + + table_set(*key_map, "\e[1;4D", to_key("#left+A")); // + + + + + + table_set(*key_map, "\e[1;5D", to_key("#left+c")); // + + + + + + table_set(*key_map, "\e[1;6D", to_key("#left+C")); // + + + + + + table_set(*key_map, "\e[1;7D", to_key("#left+w")); // + + + + + + table_set(*key_map, "\e[1;8D", to_key("#left+W")); // + + + + + table_set(*key_map, "\e[1;9D", to_key("#left+s")); // + table_set(*key_map, "\e[1;10D", to_key("#left+S")); // + table_set(*key_map, "\e[1;11D", to_key("#left+x")); // + @@ -109,17 +110,17 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15D", to_key("#left+z")); // + table_set(*key_map, "\e[1;16D", to_key("#left+Z")); // + - // Home // g i k l x + // Home // g i k l w x table_set(*key_map, "\e[1~", to_key("#home")); // + - table_set(*key_map, "\e[H", to_key("#home")); // + + + + + table_set(*key_map, "\e[H", to_key("#home")); // + + + + + table_set(*key_map, "\e[1;1H", to_key("#home")); // - table_set(*key_map, "\e[1;2H", to_key("#home+$")); // + + + + - table_set(*key_map, "\e[1;3H", to_key("#home+a")); // + + + + - table_set(*key_map, "\e[1;4H", to_key("#home+A")); // + + + + - table_set(*key_map, "\e[1;5H", to_key("#home+c")); // + + + + - table_set(*key_map, "\e[1;6H", to_key("#home+C")); // + + + + - table_set(*key_map, "\e[1;7H", to_key("#home+w")); // + + + + - table_set(*key_map, "\e[1;8H", to_key("#home+W")); // + + + + + table_set(*key_map, "\e[1;2H", to_key("#home+$")); // + + + + + + table_set(*key_map, "\e[1;3H", to_key("#home+a")); // + + + + + + table_set(*key_map, "\e[1;4H", to_key("#home+A")); // + + + + + + table_set(*key_map, "\e[1;5H", to_key("#home+c")); // + + + + + + table_set(*key_map, "\e[1;6H", to_key("#home+C")); // + + + + + table_set(*key_map, "\e[1;7H", to_key("#home+w")); // + + + + + + table_set(*key_map, "\e[1;8H", to_key("#home+W")); // + + + + + table_set(*key_map, "\e[1;9H", to_key("#home+s")); // + table_set(*key_map, "\e[1;10H", to_key("#home+S")); // + table_set(*key_map, "\e[1;11H", to_key("#home+x")); // + @@ -129,17 +130,17 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15H", to_key("#home+z")); // + table_set(*key_map, "\e[1;16H", to_key("#home+Z")); // + - // End // g i k l x + // End // g i k l w x table_set(*key_map, "\e[4~", to_key("#end")); // + - table_set(*key_map, "\e[F", to_key("#end")); // + + + + + table_set(*key_map, "\e[F", to_key("#end")); // + + + + + table_set(*key_map, "\e[1;1F", to_key("#end")); // - table_set(*key_map, "\e[1;2F", to_key("#end+$")); // + + + + - table_set(*key_map, "\e[1;3F", to_key("#end+a")); // + + + + - table_set(*key_map, "\e[1;4F", to_key("#end+A")); // + + + + - table_set(*key_map, "\e[1;5F", to_key("#end+c")); // + + + + - table_set(*key_map, "\e[1;6F", to_key("#end+C")); // + + + + - table_set(*key_map, "\e[1;7F", to_key("#end+w")); // + + + + - table_set(*key_map, "\e[1;8F", to_key("#end+W")); // + + + + + table_set(*key_map, "\e[1;2F", to_key("#end+$")); // + + + + + + table_set(*key_map, "\e[1;3F", to_key("#end+a")); // + + + + + + table_set(*key_map, "\e[1;4F", to_key("#end+A")); // + + + + + + table_set(*key_map, "\e[1;5F", to_key("#end+c")); // + + + + + table_set(*key_map, "\e[1;6F", to_key("#end+C")); // + + + + + table_set(*key_map, "\e[1;7F", to_key("#end+w")); // + + + + + + table_set(*key_map, "\e[1;8F", to_key("#end+W")); // + + + + + table_set(*key_map, "\e[1;9F", to_key("#end+s")); // + table_set(*key_map, "\e[1;10F", to_key("#end+S")); // + table_set(*key_map, "\e[1;11F", to_key("#end+x")); // + @@ -149,16 +150,16 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15F", to_key("#end+z")); // + table_set(*key_map, "\e[1;16F", to_key("#end+Z")); // + - // Insert // g i k l x - table_set(*key_map, "\e[2~", to_key("#ins")); // + + + + + + // Insert // g i k l w x + table_set(*key_map, "\e[2~", to_key("#ins")); // + + + + + + table_set(*key_map, "\e[2;1~", to_key("#ins")); // - table_set(*key_map, "\e[2;2~", to_key("#ins+$")); // + + + + - table_set(*key_map, "\e[2;3~", to_key("#ins+a")); // + + + + - table_set(*key_map, "\e[2;4~", to_key("#ins+A")); // + + + + - table_set(*key_map, "\e[2;5~", to_key("#ins+c")); // + + + + - table_set(*key_map, "\e[2;6~", to_key("#ins+C")); // + + + + - table_set(*key_map, "\e[2;7~", to_key("#ins+w")); // + + + + - table_set(*key_map, "\e[2;8~", to_key("#ins+W")); // + + + + + table_set(*key_map, "\e[2;2~", to_key("#ins+$")); // + + + + + + table_set(*key_map, "\e[2;3~", to_key("#ins+a")); // + + + + + + table_set(*key_map, "\e[2;4~", to_key("#ins+A")); // + + + + + + table_set(*key_map, "\e[2;5~", to_key("#ins+c")); // + + + + + + table_set(*key_map, "\e[2;6~", to_key("#ins+C")); // + + + + + + table_set(*key_map, "\e[2;7~", to_key("#ins+w")); // + + + + + + table_set(*key_map, "\e[2;8~", to_key("#ins+W")); // + + + + + table_set(*key_map, "\e[2;9~", to_key("#ins+s")); // + table_set(*key_map, "\e[2;10~", to_key("#ins+S")); // + table_set(*key_map, "\e[2;11~", to_key("#ins+x")); // + @@ -168,16 +169,16 @@ setup_key_map :: () { table_set(*key_map, "\e[2;15~", to_key("#ins+z")); // + table_set(*key_map, "\e[2;16~", to_key("#ins+Z")); // + - // Delete // g i k l x - table_set(*key_map, "\e[3~", to_key("#del")); // + + + + + + // Delete // g i k l w x + table_set(*key_map, "\e[3~", to_key("#del")); // + + + + + + table_set(*key_map, "\e[3;1~", to_key("#del")); // - table_set(*key_map, "\e[3;2~", to_key("#del+$")); // + + + + - table_set(*key_map, "\e[3;3~", to_key("#del+a")); // + + + + - table_set(*key_map, "\e[3;4~", to_key("#del+A")); // + + + + - table_set(*key_map, "\e[3;5~", to_key("#del+c")); // + + + + - table_set(*key_map, "\e[3;6~", to_key("#del+C")); // + + + + - table_set(*key_map, "\e[3;7~", to_key("#del+w")); // + + + + - table_set(*key_map, "\e[3;8~", to_key("#del+W")); // + + + + + table_set(*key_map, "\e[3;2~", to_key("#del+$")); // + + + + + + table_set(*key_map, "\e[3;3~", to_key("#del+a")); // + + + + + + table_set(*key_map, "\e[3;4~", to_key("#del+A")); // + + + + + + table_set(*key_map, "\e[3;5~", to_key("#del+c")); // + + + + + + table_set(*key_map, "\e[3;6~", to_key("#del+C")); // + + + + + + table_set(*key_map, "\e[3;7~", to_key("#del+w")); // + + + + + + table_set(*key_map, "\e[3;8~", to_key("#del+W")); // + + + + + table_set(*key_map, "\e[3;9~", to_key("#del+s")); // + table_set(*key_map, "\e[3;10~", to_key("#del+S")); // + table_set(*key_map, "\e[3;11~", to_key("#del+x")); // + @@ -187,16 +188,16 @@ setup_key_map :: () { table_set(*key_map, "\e[3;15~", to_key("#del+z")); // + table_set(*key_map, "\e[3;16~", to_key("#del+Z")); // + - // Page Up // g i k l x - table_set(*key_map, "\e[5~", to_key("#pup")); // + + + + + + // Page Up // g i k l w x + table_set(*key_map, "\e[5~", to_key("#pup")); // + + + + + + table_set(*key_map, "\e[5;1~", to_key("#pup")); // - table_set(*key_map, "\e[5;2~", to_key("#pup+$")); // + + + + - table_set(*key_map, "\e[5;3~", to_key("#pup+a")); // + + + + - table_set(*key_map, "\e[5;4~", to_key("#pup+A")); // + + + + - table_set(*key_map, "\e[5;5~", to_key("#pup+c")); // + + + + - table_set(*key_map, "\e[5;6~", to_key("#pup+C")); // + + + + - table_set(*key_map, "\e[5;7~", to_key("#pup+w")); // + + + + - table_set(*key_map, "\e[5;8~", to_key("#pup+W")); // + + + + + table_set(*key_map, "\e[5;2~", to_key("#pup+$")); // + + + + + + table_set(*key_map, "\e[5;3~", to_key("#pup+a")); // + + + + + + table_set(*key_map, "\e[5;4~", to_key("#pup+A")); // + + + + + + table_set(*key_map, "\e[5;5~", to_key("#pup+c")); // + + + + + + table_set(*key_map, "\e[5;6~", to_key("#pup+C")); // + + + + + table_set(*key_map, "\e[5;7~", to_key("#pup+w")); // + + + + + + table_set(*key_map, "\e[5;8~", to_key("#pup+W")); // + + + + + table_set(*key_map, "\e[5;9~", to_key("#pup+s")); // + table_set(*key_map, "\e[5;10~", to_key("#pup+S")); // + table_set(*key_map, "\e[5;11~", to_key("#pup+x")); // + @@ -206,16 +207,16 @@ setup_key_map :: () { table_set(*key_map, "\e[5;15~", to_key("#pup+z")); // + table_set(*key_map, "\e[5;16~", to_key("#pup+Z")); // + - // Page Down // g i k l x - table_set(*key_map, "\e[6~", to_key("#pdown")); // + + + + + + // Page Down // g i k l w x + table_set(*key_map, "\e[6~", to_key("#pdown")); // + + + + + + table_set(*key_map, "\e[6;1~", to_key("#pdown")); // - table_set(*key_map, "\e[6;2~", to_key("#pdown+$")); // + + + + - table_set(*key_map, "\e[6;3~", to_key("#pdown+a")); // + + + + - table_set(*key_map, "\e[6;4~", to_key("#pdown+A")); // + + + + - table_set(*key_map, "\e[6;5~", to_key("#pdown+c")); // + + + + - table_set(*key_map, "\e[6;6~", to_key("#pdown+C")); // + + + + - table_set(*key_map, "\e[6;7~", to_key("#pdown+w")); // + + + + - table_set(*key_map, "\e[6;8~", to_key("#pdown+W")); // + + + + + table_set(*key_map, "\e[6;2~", to_key("#pdown+$")); // + + + + + + table_set(*key_map, "\e[6;3~", to_key("#pdown+a")); // + + + + + + table_set(*key_map, "\e[6;4~", to_key("#pdown+A")); // + + + + + + table_set(*key_map, "\e[6;5~", to_key("#pdown+c")); // + + + + + + table_set(*key_map, "\e[6;6~", to_key("#pdown+C")); // + + + + + table_set(*key_map, "\e[6;7~", to_key("#pdown+w")); // + + + + + + table_set(*key_map, "\e[6;8~", to_key("#pdown+W")); // + + + + + table_set(*key_map, "\e[6;9~", to_key("#pdown+s")); // + table_set(*key_map, "\e[6;10~", to_key("#pdown+S")); // + table_set(*key_map, "\e[6;11~", to_key("#pdown+x")); // + @@ -225,10 +226,10 @@ setup_key_map :: () { table_set(*key_map, "\e[6;15~", to_key("#pdown+z")); // + table_set(*key_map, "\e[6;16~", to_key("#pdown+Z")); // + - // F1 // g i k l x + // F1 // g i k l w x table_set(*key_map, "\e[[A", to_key("#f1")); // + table_set(*key_map, "\e[25~", to_key("#f1+$")); // + - table_set(*key_map, "\eOP", to_key("#f1")); // + + + + + table_set(*key_map, "\eOP", to_key("#f1")); // + + + + + table_set(*key_map, "\eO1P", to_key("#f1+s")); // + table_set(*key_map, "\eO2P", to_key("#f1+$")); // + table_set(*key_map, "\eO3P", to_key("#f1+a")); // + @@ -239,13 +240,13 @@ setup_key_map :: () { table_set(*key_map, "\eO8P", to_key("#f1+W")); // + table_set(*key_map, "\e[1P", to_key("#f1")); // table_set(*key_map, "\e[1;1P", to_key("#f1")); // - table_set(*key_map, "\e[1;2P", to_key("#f1+$")); // + + + - table_set(*key_map, "\e[1;3P", to_key("#f1+a")); // + + + - table_set(*key_map, "\e[1;4P", to_key("#f1+A")); // + + + - table_set(*key_map, "\e[1;5P", to_key("#f1+c")); // + + + - table_set(*key_map, "\e[1;6P", to_key("#f1+C")); // + + + - table_set(*key_map, "\e[1;7P", to_key("#f1+w")); // + + + - table_set(*key_map, "\e[1;8P", to_key("#f1+W")); // + + + + table_set(*key_map, "\e[1;2P", to_key("#f1+$")); // + + + + + table_set(*key_map, "\e[1;3P", to_key("#f1+a")); // + + + + + table_set(*key_map, "\e[1;4P", to_key("#f1+A")); // + + + + + table_set(*key_map, "\e[1;5P", to_key("#f1+c")); // + + + + + table_set(*key_map, "\e[1;6P", to_key("#f1+C")); // + + + + + table_set(*key_map, "\e[1;7P", to_key("#f1+w")); // + + + + + table_set(*key_map, "\e[1;8P", to_key("#f1+W")); // + + + + table_set(*key_map, "\e[1;9P", to_key("#f1+s")); // + table_set(*key_map, "\e[1;10P", to_key("#f1+S")); // + table_set(*key_map, "\e[1;11P", to_key("#f1+x")); // + @@ -255,10 +256,10 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15P", to_key("#f1+z")); // + table_set(*key_map, "\e[1;16P", to_key("#f1+Z")); // + - // F2 // g i k l x + // F2 // g i k l w x table_set(*key_map, "\e[[B", to_key("#f2")); // + table_set(*key_map, "\e[26~", to_key("#f2+$")); // + - table_set(*key_map, "\eOQ", to_key("#f2")); // + + + + + table_set(*key_map, "\eOQ", to_key("#f2")); // + + + + + table_set(*key_map, "\eO1Q", to_key("#f2+s")); // + table_set(*key_map, "\eO2Q", to_key("#f2+$")); // + table_set(*key_map, "\eO3Q", to_key("#f2+a")); // + @@ -269,13 +270,13 @@ setup_key_map :: () { table_set(*key_map, "\eO8Q", to_key("#f2+W")); // + table_set(*key_map, "\e[1Q", to_key("#f2")); // table_set(*key_map, "\e[1;1Q", to_key("#f2")); // - table_set(*key_map, "\e[1;2Q", to_key("#f2+$")); // + + + - table_set(*key_map, "\e[1;3Q", to_key("#f2+a")); // + + + - table_set(*key_map, "\e[1;4Q", to_key("#f2+A")); // + + + - table_set(*key_map, "\e[1;5Q", to_key("#f2+c")); // + + + - table_set(*key_map, "\e[1;6Q", to_key("#f2+C")); // + + + - table_set(*key_map, "\e[1;7Q", to_key("#f2+w")); // + + + - table_set(*key_map, "\e[1;8Q", to_key("#f2+W")); // + + + + table_set(*key_map, "\e[1;2Q", to_key("#f2+$")); // + + + + + table_set(*key_map, "\e[1;3Q", to_key("#f2+a")); // + + + + + table_set(*key_map, "\e[1;4Q", to_key("#f2+A")); // + + + + + table_set(*key_map, "\e[1;5Q", to_key("#f2+c")); // + + + + + table_set(*key_map, "\e[1;6Q", to_key("#f2+C")); // + + + + + table_set(*key_map, "\e[1;7Q", to_key("#f2+w")); // + + + + + table_set(*key_map, "\e[1;8Q", to_key("#f2+W")); // + + + + table_set(*key_map, "\e[1;9Q", to_key("#f2+s")); // + table_set(*key_map, "\e[1;10Q", to_key("#f2+S")); // + table_set(*key_map, "\e[1;11Q", to_key("#f2+x")); // + @@ -285,10 +286,10 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15Q", to_key("#f2+z")); // + table_set(*key_map, "\e[1;16Q", to_key("#f2+Z")); // + - // F3 // g i k l x + // F3 // g i k l w x table_set(*key_map, "\e[[C", to_key("#f3")); // + table_set(*key_map, "\e[28~", to_key("#f3+$")); // + - table_set(*key_map, "\eOR", to_key("#f3")); // + + + + + table_set(*key_map, "\eOR", to_key("#f3")); // + + + + + table_set(*key_map, "\eO1R", to_key("#f3+s")); // + table_set(*key_map, "\eO2R", to_key("#f3+$")); // + table_set(*key_map, "\eO3R", to_key("#f3+a")); // + @@ -299,13 +300,13 @@ setup_key_map :: () { table_set(*key_map, "\eO8R", to_key("#f3+W")); // + table_set(*key_map, "\e[1R", to_key("#f3")); // table_set(*key_map, "\e[1;1R", to_key("#f3")); // - table_set(*key_map, "\e[1;2R", to_key("#f3+$")); // + + + - table_set(*key_map, "\e[1;3R", to_key("#f3+a")); // + + + - table_set(*key_map, "\e[1;4R", to_key("#f3+A")); // + + + - table_set(*key_map, "\e[1;5R", to_key("#f3+c")); // + + + - table_set(*key_map, "\e[1;6R", to_key("#f3+C")); // + + + - table_set(*key_map, "\e[1;7R", to_key("#f3+w")); // + + + - table_set(*key_map, "\e[1;8R", to_key("#f3+W")); // + + + + table_set(*key_map, "\e[1;2R", to_key("#f3+$")); // + + + + + table_set(*key_map, "\e[1;3R", to_key("#f3+a")); // + + + + + table_set(*key_map, "\e[1;4R", to_key("#f3+A")); // + + + + + table_set(*key_map, "\e[1;5R", to_key("#f3+c")); // + + + + + table_set(*key_map, "\e[1;6R", to_key("#f3+C")); // + + + + + table_set(*key_map, "\e[1;7R", to_key("#f3+w")); // + + + + + table_set(*key_map, "\e[1;8R", to_key("#f3+W")); // + + + + table_set(*key_map, "\e[1;9R", to_key("#f3+s")); // + table_set(*key_map, "\e[1;10R", to_key("#f3+S")); // + table_set(*key_map, "\e[1;11R", to_key("#f3+x")); // + @@ -315,10 +316,10 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15R", to_key("#f3+z")); // + table_set(*key_map, "\e[1;16R", to_key("#f3+Z")); // + - // F4 // g i k l x + // F4 // g i k l w x table_set(*key_map, "\e[[D", to_key("#f4")); // + table_set(*key_map, "\e[29~", to_key("#f4+$")); // + - table_set(*key_map, "\eOS", to_key("#f4")); // + + + + + table_set(*key_map, "\eOS", to_key("#f4")); // + + + + + table_set(*key_map, "\eO1S", to_key("#f4+s")); // + table_set(*key_map, "\eO2S", to_key("#f4+$")); // + table_set(*key_map, "\eO3S", to_key("#f4+a")); // + @@ -329,13 +330,13 @@ setup_key_map :: () { table_set(*key_map, "\eO8S", to_key("#f4+W")); // + table_set(*key_map, "\e[1S", to_key("#f4")); // table_set(*key_map, "\e[1;1S", to_key("#f4")); // - table_set(*key_map, "\e[1;2S", to_key("#f4+$")); // + + + - table_set(*key_map, "\e[1;3S", to_key("#f4+a")); // + + + - table_set(*key_map, "\e[1;4S", to_key("#f4+A")); // + + + - table_set(*key_map, "\e[1;5S", to_key("#f4+c")); // + + + - table_set(*key_map, "\e[1;6S", to_key("#f4+C")); // + + + - table_set(*key_map, "\e[1;7S", to_key("#f4+w")); // + + + - table_set(*key_map, "\e[1;8S", to_key("#f4+W")); // + + + + table_set(*key_map, "\e[1;2S", to_key("#f4+$")); // + + + + + table_set(*key_map, "\e[1;3S", to_key("#f4+a")); // + + + + + table_set(*key_map, "\e[1;4S", to_key("#f4+A")); // + + + + + table_set(*key_map, "\e[1;5S", to_key("#f4+c")); // + + + + + table_set(*key_map, "\e[1;6S", to_key("#f4+C")); // + + + + + table_set(*key_map, "\e[1;7S", to_key("#f4+w")); // + + + + + table_set(*key_map, "\e[1;8S", to_key("#f4+W")); // + + + + table_set(*key_map, "\e[1;9S", to_key("#f4+s")); // + table_set(*key_map, "\e[1;10S", to_key("#f4+S")); // + table_set(*key_map, "\e[1;11S", to_key("#f4+x")); // + @@ -345,18 +346,18 @@ setup_key_map :: () { table_set(*key_map, "\e[1;15S", to_key("#f4+z")); // + table_set(*key_map, "\e[1;16S", to_key("#f4+Z")); // + - // F5 // g i k l x + // F5 // g i k l w x table_set(*key_map, "\e[[E", to_key("#f5")); // + table_set(*key_map, "\e[31~", to_key("#f5+$")); // + - table_set(*key_map, "\e[15~", to_key("#f5")); // + + + + + table_set(*key_map, "\e[15~", to_key("#f5")); // + + + + + table_set(*key_map, "\e[15;1~", to_key("#f5")); // - table_set(*key_map, "\e[15;2~", to_key("#f5+$")); // + + + + - table_set(*key_map, "\e[15;3~", to_key("#f5+a")); // + + + + - table_set(*key_map, "\e[15;4~", to_key("#f5+A")); // + + + + - table_set(*key_map, "\e[15;5~", to_key("#f5+c")); // + + + + - table_set(*key_map, "\e[15;6~", to_key("#f5+C")); // + + + + - table_set(*key_map, "\e[15;7~", to_key("#f5+w")); // + + + + - table_set(*key_map, "\e[15;8~", to_key("#f5+W")); // + + + + + table_set(*key_map, "\e[15;2~", to_key("#f5+$")); // + + + + + + table_set(*key_map, "\e[15;3~", to_key("#f5+a")); // + + + + + + table_set(*key_map, "\e[15;4~", to_key("#f5+A")); // + + + + + + table_set(*key_map, "\e[15;5~", to_key("#f5+c")); // + + + + + + table_set(*key_map, "\e[15;6~", to_key("#f5+C")); // + + + + + + table_set(*key_map, "\e[15;7~", to_key("#f5+w")); // + + + + + + table_set(*key_map, "\e[15;8~", to_key("#f5+W")); // + + + + + table_set(*key_map, "\e[15;9~", to_key("#f5+s")); // + table_set(*key_map, "\e[15;10~",to_key("#f5+S")); // + table_set(*key_map, "\e[15;11~",to_key("#f5+x")); // + @@ -366,17 +367,17 @@ setup_key_map :: () { table_set(*key_map, "\e[15;15~",to_key("#f5+z")); // + table_set(*key_map, "\e[15;16~",to_key("#f5+Z")); // + - // F6 // g i k l x + // F6 // g i k l w x table_set(*key_map, "\e[32~", to_key("#f6+$")); // + - table_set(*key_map, "\e[17~", to_key("#f6")); // + + + + + + table_set(*key_map, "\e[17~", to_key("#f6")); // + + + + + + table_set(*key_map, "\e[17;1~", to_key("#f6")); // - table_set(*key_map, "\e[17;2~", to_key("#f6+$")); // + + + + - table_set(*key_map, "\e[17;3~", to_key("#f6+a")); // + + + + - table_set(*key_map, "\e[17;4~", to_key("#f6+A")); // + + + + - table_set(*key_map, "\e[17;5~", to_key("#f6+c")); // + + + + - table_set(*key_map, "\e[17;6~", to_key("#f6+C")); // + + + + - table_set(*key_map, "\e[17;7~", to_key("#f6+w")); // + + + + - table_set(*key_map, "\e[17;8~", to_key("#f6+W")); // + + + + + table_set(*key_map, "\e[17;2~", to_key("#f6+$")); // + + + + + + table_set(*key_map, "\e[17;3~", to_key("#f6+a")); // + + + + + + table_set(*key_map, "\e[17;4~", to_key("#f6+A")); // + + + + + + table_set(*key_map, "\e[17;5~", to_key("#f6+c")); // + + + + + + table_set(*key_map, "\e[17;6~", to_key("#f6+C")); // + + + + + + table_set(*key_map, "\e[17;7~", to_key("#f6+w")); // + + + + + + table_set(*key_map, "\e[17;8~", to_key("#f6+W")); // + + + + + table_set(*key_map, "\e[17;9~", to_key("#f6+s")); // + table_set(*key_map, "\e[17;10~",to_key("#f6+S")); // + table_set(*key_map, "\e[17;11~",to_key("#f6+x")); // + @@ -386,17 +387,17 @@ setup_key_map :: () { table_set(*key_map, "\e[17;15~",to_key("#f6+z")); // + table_set(*key_map, "\e[17;16~",to_key("#f6+Z")); // + - // F7 // g i k l x + // F7 // g i k l w x table_set(*key_map, "\e[33~", to_key("#f7+$")); // + - table_set(*key_map, "\e[18~", to_key("#f7")); // + + + + + + table_set(*key_map, "\e[18~", to_key("#f7")); // + + + + + + table_set(*key_map, "\e[18;1~", to_key("#f7")); // - table_set(*key_map, "\e[18;2~", to_key("#f7+$")); // + + + + - table_set(*key_map, "\e[18;3~", to_key("#f7+a")); // + + + + - table_set(*key_map, "\e[18;4~", to_key("#f7+A")); // + + + + - table_set(*key_map, "\e[18;5~", to_key("#f7+c")); // + + + + - table_set(*key_map, "\e[18;6~", to_key("#f7+C")); // + + + + - table_set(*key_map, "\e[18;7~", to_key("#f7+w")); // + + + + - table_set(*key_map, "\e[18;8~", to_key("#f7+W")); // + + + + + table_set(*key_map, "\e[18;2~", to_key("#f7+$")); // + + + + + + table_set(*key_map, "\e[18;3~", to_key("#f7+a")); // + + + + + + table_set(*key_map, "\e[18;4~", to_key("#f7+A")); // + + + + + + table_set(*key_map, "\e[18;5~", to_key("#f7+c")); // + + + + + + table_set(*key_map, "\e[18;6~", to_key("#f7+C")); // + + + + + + table_set(*key_map, "\e[18;7~", to_key("#f7+w")); // + + + + + + table_set(*key_map, "\e[18;8~", to_key("#f7+W")); // + + + + + table_set(*key_map, "\e[18;9~", to_key("#f7+s")); // + table_set(*key_map, "\e[18;10~",to_key("#f7+S")); // + table_set(*key_map, "\e[18;11~",to_key("#f7+x")); // + @@ -406,17 +407,17 @@ setup_key_map :: () { table_set(*key_map, "\e[18;15~",to_key("#f7+z")); // + table_set(*key_map, "\e[18;16~",to_key("#f7+Z")); // + - // F8 // g i k l x + // F8 // g i k l w x table_set(*key_map, "\e[34~", to_key("#f8+$")); // + - table_set(*key_map, "\e[19~", to_key("#f8")); // + + + + + + table_set(*key_map, "\e[19~", to_key("#f8")); // + + + + + + table_set(*key_map, "\e[19;1~", to_key("#f8")); // - table_set(*key_map, "\e[19;2~", to_key("#f8+$")); // + + + + - table_set(*key_map, "\e[19;3~", to_key("#f8+a")); // + + + + - table_set(*key_map, "\e[19;4~", to_key("#f8+A")); // + + + + - table_set(*key_map, "\e[19;5~", to_key("#f8+c")); // + + + + - table_set(*key_map, "\e[19;6~", to_key("#f8+C")); // + + + + - table_set(*key_map, "\e[19;7~", to_key("#f8+w")); // + + + + - table_set(*key_map, "\e[19;8~", to_key("#f8+W")); // + + + + + table_set(*key_map, "\e[19;2~", to_key("#f8+$")); // + + + + + + table_set(*key_map, "\e[19;3~", to_key("#f8+a")); // + + + + + + table_set(*key_map, "\e[19;4~", to_key("#f8+A")); // + + + + + + table_set(*key_map, "\e[19;5~", to_key("#f8+c")); // + + + + + + table_set(*key_map, "\e[19;6~", to_key("#f8+C")); // + + + + + + table_set(*key_map, "\e[19;7~", to_key("#f8+w")); // + + + + + + table_set(*key_map, "\e[19;8~", to_key("#f8+W")); // + + + + + table_set(*key_map, "\e[19;9~", to_key("#f8+s")); // + table_set(*key_map, "\e[19;10~",to_key("#f8+S")); // + table_set(*key_map, "\e[19;11~",to_key("#f8+x")); // + @@ -426,16 +427,16 @@ setup_key_map :: () { table_set(*key_map, "\e[19;15~",to_key("#f8+z")); // + table_set(*key_map, "\e[19;16~",to_key("#f8+Z")); // + - // F9 // g i k l x - table_set(*key_map, "\e[20~", to_key("#f9")); // + + + + + + // F9 // g i k l w x + table_set(*key_map, "\e[20~", to_key("#f9")); // + + + + + + table_set(*key_map, "\e[20;1~", to_key("#f9")); // - table_set(*key_map, "\e[20;2~", to_key("#f9+$")); // + + + + - table_set(*key_map, "\e[20;3~", to_key("#f9+a")); // + + + + - table_set(*key_map, "\e[20;4~", to_key("#f9+A")); // + + + + - table_set(*key_map, "\e[20;5~", to_key("#f9+c")); // + + + + - table_set(*key_map, "\e[20;6~", to_key("#f9+C")); // + + + + - table_set(*key_map, "\e[20;7~", to_key("#f9+w")); // + + + + - table_set(*key_map, "\e[20;8~", to_key("#f9+W")); // + + + + + table_set(*key_map, "\e[20;2~", to_key("#f9+$")); // + + + + + + table_set(*key_map, "\e[20;3~", to_key("#f9+a")); // + + + + + + table_set(*key_map, "\e[20;4~", to_key("#f9+A")); // + + + + + + table_set(*key_map, "\e[20;5~", to_key("#f9+c")); // + + + + + + table_set(*key_map, "\e[20;6~", to_key("#f9+C")); // + + + + + + table_set(*key_map, "\e[20;7~", to_key("#f9+w")); // + + + + + + table_set(*key_map, "\e[20;8~", to_key("#f9+W")); // + + + + + table_set(*key_map, "\e[20;9~", to_key("#f9+s")); // + table_set(*key_map, "\e[20;10~",to_key("#f9+S")); // + table_set(*key_map, "\e[20;11~",to_key("#f9+x")); // + @@ -445,16 +446,16 @@ setup_key_map :: () { table_set(*key_map, "\e[20;15~",to_key("#f9+z")); // + table_set(*key_map, "\e[20;16~",to_key("#f9+Z")); // + - // F10 // g i k l x - table_set(*key_map, "\e[21~", to_key("#f10")); // + + + + + + // F10 // g i k l w x + table_set(*key_map, "\e[21~", to_key("#f10")); // + + + + + + table_set(*key_map, "\e[21;1~", to_key("#f10")); // - table_set(*key_map, "\e[21;2~", to_key("#f10+$")); // + + + + - table_set(*key_map, "\e[21;3~", to_key("#f10+a")); // + + + + - table_set(*key_map, "\e[21;4~", to_key("#f10+A")); // + + + + - table_set(*key_map, "\e[21;5~", to_key("#f10+c")); // + + + + - table_set(*key_map, "\e[21;6~", to_key("#f10+C")); // + + + + - table_set(*key_map, "\e[21;7~", to_key("#f10+w")); // + + + + - table_set(*key_map, "\e[21;8~", to_key("#f10+W")); // + + + + + table_set(*key_map, "\e[21;2~", to_key("#f10+$")); // + + + + + + table_set(*key_map, "\e[21;3~", to_key("#f10+a")); // + + + + + + table_set(*key_map, "\e[21;4~", to_key("#f10+A")); // + + + + + + table_set(*key_map, "\e[21;5~", to_key("#f10+c")); // + + + + + + table_set(*key_map, "\e[21;6~", to_key("#f10+C")); // + + + + + + table_set(*key_map, "\e[21;7~", to_key("#f10+w")); // + + + + + + table_set(*key_map, "\e[21;8~", to_key("#f10+W")); // + + + + + table_set(*key_map, "\e[21;9~", to_key("#f10+s")); // + table_set(*key_map, "\e[21;10~",to_key("#f10+S")); // + table_set(*key_map, "\e[21;11~",to_key("#f10+x")); // + @@ -464,16 +465,16 @@ setup_key_map :: () { table_set(*key_map, "\e[21;15~",to_key("#f10+z")); // + table_set(*key_map, "\e[21;16~",to_key("#f10+Z")); // + - // F11 // g i k l x - table_set(*key_map, "\e[23~", to_key("#f11")); // + + + + + + // F11 // g i k l w x + table_set(*key_map, "\e[23~", to_key("#f11")); // + + + + + + table_set(*key_map, "\e[23;1~", to_key("#f11")); // - table_set(*key_map, "\e[23;2~", to_key("#f11+$")); // + + + + - table_set(*key_map, "\e[23;3~", to_key("#f11+a")); // + + + + - table_set(*key_map, "\e[23;4~", to_key("#f11+A")); // + + + + - table_set(*key_map, "\e[23;5~", to_key("#f11+c")); // + + + + - table_set(*key_map, "\e[23;6~", to_key("#f11+C")); // + + + + - table_set(*key_map, "\e[23;7~", to_key("#f11+w")); // + + + + - table_set(*key_map, "\e[23;8~", to_key("#f11+W")); // + + + + + table_set(*key_map, "\e[23;2~", to_key("#f11+$")); // + + + + + + table_set(*key_map, "\e[23;3~", to_key("#f11+a")); // + + + + + + table_set(*key_map, "\e[23;4~", to_key("#f11+A")); // + + + + + + table_set(*key_map, "\e[23;5~", to_key("#f11+c")); // + + + + + + table_set(*key_map, "\e[23;6~", to_key("#f11+C")); // + + + + + + table_set(*key_map, "\e[23;7~", to_key("#f11+w")); // + + + + + + table_set(*key_map, "\e[23;8~", to_key("#f11+W")); // + + + + + table_set(*key_map, "\e[23;9~", to_key("#f11+s")); // + table_set(*key_map, "\e[23;10~",to_key("#f11+S")); // + table_set(*key_map, "\e[23;11~",to_key("#f11+x")); // + @@ -483,16 +484,16 @@ setup_key_map :: () { table_set(*key_map, "\e[23;15~",to_key("#f11+z")); // + table_set(*key_map, "\e[23;16~",to_key("#f11+Z")); // + - // F12 // g i k l x - table_set(*key_map, "\e[24~", to_key("#f12")); // + + + + + + // F12 // g i k l w x + table_set(*key_map, "\e[24~", to_key("#f12")); // + + + + + + table_set(*key_map, "\e[24;1~", to_key("#f12")); // - table_set(*key_map, "\e[24;2~", to_key("#f12+$")); // + + + + - table_set(*key_map, "\e[24;3~", to_key("#f12+a")); // + + + + - table_set(*key_map, "\e[24;4~", to_key("#f12+A")); // + + + + - table_set(*key_map, "\e[24;5~", to_key("#f12+c")); // + + + + - table_set(*key_map, "\e[24;6~", to_key("#f12+C")); // + + + + - table_set(*key_map, "\e[24;7~", to_key("#f12+w")); // + + + + - table_set(*key_map, "\e[24;8~", to_key("#f12+W")); // + + + + + table_set(*key_map, "\e[24;2~", to_key("#f12+$")); // + + + + + + table_set(*key_map, "\e[24;3~", to_key("#f12+a")); // + + + + + + table_set(*key_map, "\e[24;4~", to_key("#f12+A")); // + + + + + + table_set(*key_map, "\e[24;5~", to_key("#f12+c")); // + + + + + + table_set(*key_map, "\e[24;6~", to_key("#f12+C")); // + + + + + + table_set(*key_map, "\e[24;7~", to_key("#f12+w")); // + + + + + + table_set(*key_map, "\e[24;8~", to_key("#f12+W")); // + + + + + table_set(*key_map, "\e[24;9~", to_key("#f12+s")); // + table_set(*key_map, "\e[24;10~",to_key("#f12+S")); // + table_set(*key_map, "\e[24;11~",to_key("#f12+x")); // + diff --git a/modules/TUI/tests.jai b/modules/TUI/tests.jai index fe213cb..a740e6b 100644 --- a/modules/TUI/tests.jai +++ b/modules/TUI/tests.jai @@ -31,21 +31,6 @@ main :: () { assert_result(x == X && y == Y, "Failed set/get cursor position.\n"); } - if 1 { - print("TEST : module logger\n", to_standard_error = true); - log("- log: before module start."); - assert(TUI.setup_terminal(), "Failed to setup TUI."); - TUI.set_cursor_position(3, 3); - print("wait"); - sleep_milliseconds(500); - log("- log: while module is active."); - sleep_milliseconds(500); - print(" a bit"); - sleep_milliseconds(1000); - assert(TUI.reset_terminal(), "Failed to reset TUI."); - log("- log: after module stop."); - } - if 1 { print("TEST : test key input\n", to_standard_error = true); auto_release_temp(); @@ -121,7 +106,7 @@ main :: () { print("TEST : print keys\n", to_standard_error = true); auto_release_temp(); assert(TUI.setup_terminal(), "Failed to setup TUI."); - key: TUI.Key = #char "d"; + key: TUI.Key = TUI.Keys.None; last_none_char := "X"; width, height := TUI.get_terminal_size(); @@ -135,7 +120,7 @@ main :: () { case TUI.Keys.None; { TUI.set_cursor_position(2, 2); last_none_char = ifx last_none_char == "X" then "+" else "X"; - write_strings(last_none_char, " (press q to exit)"); + write_strings(last_none_char, " (press: q to exit, c to clear, and any other key to print it's value)"); } case TUI.Keys.Resize; #through; diff --git a/modules/UTF8.jai b/modules/UTF8.jai deleted file mode 100644 index 72d3d75..0000000 --- a/modules/UTF8.jai +++ /dev/null @@ -1,128 +0,0 @@ -// Some procedures to help working with UTF8 strings. -// https://en.wikipedia.org/wiki/UTF-8 - -// Returns true if argument is a continuation byte. -is_continuation_byte :: inline (byte: u8) -> bool { - // BBBB BBBB & 1100 0000 == 10XX XXXX -> is continuation byte - return (byte & 0xC0) == 0x80; -} - -// Given a leading_byte, returns the number of bytes on the character. -count_character_bytes :: inline (leading_byte: u8) -> int { - // BBBB BBBB & 1110 0000 == 110X XXXX -> 1 initial + 1 continuation byte - if (leading_byte & 0xE0) == 0xC0 return 1+1; - - // BBBB BBBB & 1111 0000 == 1110 XXXX -> 1 initial + 2 continuation byte - if (leading_byte & 0xF0) == 0xE0 return 1+2; - - // BBBB BBBB & 1111 1000 == 1111 0XXX -> 1 initial + 3 continuation byte - if (leading_byte & 0xF8) == 0xF0 return 1+3; - - return 1; -} - -// Returns the string (using same str.data) truncated to the provided length. -truncate :: (str: string, length: int) -> string { - - if str.data == null then return ""; - - if str.count < length then return .{str.count, str.data}; - - data := str.data; - count := str.count; - - // Find index of first continuation byte. - idx := length; - while (idx > 0 && is_continuation_byte(data[idx - 1])) { - idx -= 1; - } - continuation_bytes := length - idx; - - // If string starts with continuation bytes, it's an invalid UTF8 string. - if (idx == 0 && continuation_bytes > 0) { - length = 0; - } - // If length truncates some continuation bytes, remove incomplete UTF8 character. - else if (idx > 0 // string is not empty - // continuation bytes are not complete - && !(continuation_bytes == 0 && (data[idx - 1] & 0x80) == 0x00) - && !(continuation_bytes == 1 && (data[idx - 1] & 0xE0) == 0xC0) - && !(continuation_bytes == 2 && (data[idx - 1] & 0xF0) == 0xE0) - && !(continuation_bytes == 3 && (data[idx - 1] & 0xF8) == 0xF0) - ) { - length -= (continuation_bytes + 1); // Remove start byte, hence '+ 1'. - } - - return .{length, str.data}; -} - -// Returns true when the string is empty or consists of space characters. -is_empty :: (str: string) -> bool { - for 0..str.count-1 { - if str[it] == { - case #char "\0"; #through; - case #char "\t"; #through; // horizontal tab - case #char "\n"; #through; // line feed - case #char "\x0B"; #through; // vertical tabulation - case #char "\x0C"; #through; // form feed - case #char "\r"; #through; // carriage return - case #char " "; - continue; - case; - return false; - } - } - return true; -} - -// Counts the number of characters. -count_characters :: (str: string, $is_null_terminated := false) -> int { - characters := 0; - idx := 0; - - #if is_null_terminated { - while idx < str.count && str[idx] != 0 { - idx += count_character_bytes(str[idx]); - characters += 1; - } - } - else { - while idx < str.count { - idx += count_character_bytes(str[idx]); - characters += 1; - } - } - - return characters; -} - -// Deletes character by it's index, and moves tail data to take its place. -delete_character :: (str: *string, character_idx: int) -> success := true { - buffer_idx := get_byte_index(str.*, character_idx); - - if buffer_idx < 0 return false; - - bytes_to_delete := count_character_bytes(str.data[buffer_idx]); - - for buffer_idx..str.count-1-bytes_to_delete { - str.data[it] = str.data[it+bytes_to_delete]; - } - for str.count-bytes_to_delete..str.count-1 { - str.data[it] = 0; - } - - str.count -= bytes_to_delete; - return; -} - -// Searches for the given character index and returns its byte index on the string. -get_byte_index :: (str: string, character_index: int) -> buffer_index: int, success := true { - buff_idx := 0; - char_idx := 0; - while buff_idx < str.count { - if char_idx == character_index return buff_idx; - buff_idx += count_character_bytes(str[buff_idx]); - char_idx += 1; - } - return -1, false; -} diff --git a/modules/UTF8/module.jai b/modules/UTF8/module.jai new file mode 100644 index 0000000..72d3d75 --- /dev/null +++ b/modules/UTF8/module.jai @@ -0,0 +1,128 @@ +// Some procedures to help working with UTF8 strings. +// https://en.wikipedia.org/wiki/UTF-8 + +// Returns true if argument is a continuation byte. +is_continuation_byte :: inline (byte: u8) -> bool { + // BBBB BBBB & 1100 0000 == 10XX XXXX -> is continuation byte + return (byte & 0xC0) == 0x80; +} + +// Given a leading_byte, returns the number of bytes on the character. +count_character_bytes :: inline (leading_byte: u8) -> int { + // BBBB BBBB & 1110 0000 == 110X XXXX -> 1 initial + 1 continuation byte + if (leading_byte & 0xE0) == 0xC0 return 1+1; + + // BBBB BBBB & 1111 0000 == 1110 XXXX -> 1 initial + 2 continuation byte + if (leading_byte & 0xF0) == 0xE0 return 1+2; + + // BBBB BBBB & 1111 1000 == 1111 0XXX -> 1 initial + 3 continuation byte + if (leading_byte & 0xF8) == 0xF0 return 1+3; + + return 1; +} + +// Returns the string (using same str.data) truncated to the provided length. +truncate :: (str: string, length: int) -> string { + + if str.data == null then return ""; + + if str.count < length then return .{str.count, str.data}; + + data := str.data; + count := str.count; + + // Find index of first continuation byte. + idx := length; + while (idx > 0 && is_continuation_byte(data[idx - 1])) { + idx -= 1; + } + continuation_bytes := length - idx; + + // If string starts with continuation bytes, it's an invalid UTF8 string. + if (idx == 0 && continuation_bytes > 0) { + length = 0; + } + // If length truncates some continuation bytes, remove incomplete UTF8 character. + else if (idx > 0 // string is not empty + // continuation bytes are not complete + && !(continuation_bytes == 0 && (data[idx - 1] & 0x80) == 0x00) + && !(continuation_bytes == 1 && (data[idx - 1] & 0xE0) == 0xC0) + && !(continuation_bytes == 2 && (data[idx - 1] & 0xF0) == 0xE0) + && !(continuation_bytes == 3 && (data[idx - 1] & 0xF8) == 0xF0) + ) { + length -= (continuation_bytes + 1); // Remove start byte, hence '+ 1'. + } + + return .{length, str.data}; +} + +// Returns true when the string is empty or consists of space characters. +is_empty :: (str: string) -> bool { + for 0..str.count-1 { + if str[it] == { + case #char "\0"; #through; + case #char "\t"; #through; // horizontal tab + case #char "\n"; #through; // line feed + case #char "\x0B"; #through; // vertical tabulation + case #char "\x0C"; #through; // form feed + case #char "\r"; #through; // carriage return + case #char " "; + continue; + case; + return false; + } + } + return true; +} + +// Counts the number of characters. +count_characters :: (str: string, $is_null_terminated := false) -> int { + characters := 0; + idx := 0; + + #if is_null_terminated { + while idx < str.count && str[idx] != 0 { + idx += count_character_bytes(str[idx]); + characters += 1; + } + } + else { + while idx < str.count { + idx += count_character_bytes(str[idx]); + characters += 1; + } + } + + return characters; +} + +// Deletes character by it's index, and moves tail data to take its place. +delete_character :: (str: *string, character_idx: int) -> success := true { + buffer_idx := get_byte_index(str.*, character_idx); + + if buffer_idx < 0 return false; + + bytes_to_delete := count_character_bytes(str.data[buffer_idx]); + + for buffer_idx..str.count-1-bytes_to_delete { + str.data[it] = str.data[it+bytes_to_delete]; + } + for str.count-bytes_to_delete..str.count-1 { + str.data[it] = 0; + } + + str.count -= bytes_to_delete; + return; +} + +// Searches for the given character index and returns its byte index on the string. +get_byte_index :: (str: string, character_index: int) -> buffer_index: int, success := true { + buff_idx := 0; + char_idx := 0; + while buff_idx < str.count { + if char_idx == character_index return buff_idx; + buff_idx += count_character_bytes(str[buff_idx]); + char_idx += 1; + } + return -1, false; +} diff --git a/modules/UTF8/tests.jai b/modules/UTF8/tests.jai new file mode 100644 index 0000000..aff1d36 --- /dev/null +++ b/modules/UTF8/tests.jai @@ -0,0 +1,5 @@ +#import "Basic"; + +main :: () { + assert(false, "TODO"); // TODO +} diff --git a/sizeof.c b/sizeof.c deleted file mode 100644 index c260c31..0000000 --- a/sizeof.c +++ /dev/null @@ -1,50 +0,0 @@ -// compile with : gcc sizeof.c -lncurses - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char **argv) { - // initscr(); - - fprintf(stderr, "sizeof char: %d\n", sizeof(char)); - fprintf(stderr, "sizeof short: %d\n", sizeof(short)); - fprintf(stderr, "sizeof int: %d\n", sizeof(int)); - fprintf(stderr, "sizeof long: %d\n", sizeof(long)); - fprintf(stderr, "sizeof unsigned: %d\n", sizeof(unsigned)); - fprintf(stderr, "sizeof chtype: %d\n", sizeof(chtype)); - // int w_size_x, w_size_y; - // getmaxyx(stdscr, w_size_y, w_size_x); - // char str[64]; - // memset(str, 0, 64); - // sprintf(str, "x,y : %dx%d\n", w_size_x, w_size_y); - // mvaddstr(2, 2, str); - // sprintf(str, "resize:%d\n", KEY_RESIZE); - // mvaddstr(3, 2, str); - - // unsigned m = ACS_DIAMOND; - fprintf(stderr, "sizeof ACS %d\n", sizeof(ACS_DIAMOND)); - // - // if (ACS_DIAMOND != 0 || ACS_URCORNER != 0){ - // fprintf(stderr, "BAZINGA\n"); - // } -// fprintf(stderr, ">%d<\n", strlen(ACS_DIAMOND)); - - // mvaddch(0, 0, m); - // getch(); - // endwin(); -} - diff --git a/ttt.jai b/ttt.jai index bb395fa..b0d60d0 100644 --- a/ttt.jai +++ b/ttt.jai @@ -7,7 +7,7 @@ DEBUG :: false; #import "File"; #import "File_Utilities"; #import "String"; -#import "Integer_Saturating_Arithmetic"; +#import "Saturation"; #import "UTF8"; TUI :: #import "TUI"(COLOR_MODE_BITS=4); -- cgit v1.2.3