From d0fd9eb4f0f1a507fda56e900481b4ea77edd399 Mon Sep 17 00:00:00 2001 From: Anton Anikin Date: Thu, 27 Feb 2025 16:18:38 +0800 Subject: [PATCH] add array.h/cpp --- .env.bat | 8 + CMakeLists.txt | 33 ++++ cmake/check-from_chars.cmake | 7 + cmake/check-from_chars.cpp | 8 + code.bat | 3 + src/array.cpp | 318 +++++++++++++++++++++++++++++++++++ src/array.h | 33 ++++ 7 files changed, 410 insertions(+) create mode 100644 .env.bat create mode 100644 cmake/check-from_chars.cmake create mode 100644 cmake/check-from_chars.cpp create mode 100644 code.bat create mode 100644 src/array.cpp create mode 100644 src/array.h diff --git a/.env.bat b/.env.bat new file mode 100644 index 0000000..f7eaf00 --- /dev/null +++ b/.env.bat @@ -0,0 +1,8 @@ +@REM set MSYS_PATH=C:\msys64\usr\bin +set MSYS_PATH=%USERPROFILE%\scoop\apps\msys2\current + +@REM gcc +@REM set PATH=%MSYS_PATH%\mingw64\bin;%MSYS_PATH%\usr\bin;%PATH% + +@REM clang +set PATH=%MSYS_PATH%\clang64\bin;%MSYS_PATH%\usr\bin;%PATH% diff --git a/CMakeLists.txt b/CMakeLists.txt index bd7ebc0..3d4898e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,42 @@ +cmake_minimum_required(VERSION 3.11) # FetchContent + project(keycode) +include(FetchContent) +include("cmake/Check-from_chars.cmake") + +set (LIBS fmt) + +if (NOT DEFINED OPTCON_LOCAL_BUILD) + FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt + GIT_TAG 123913715afeb8a437e6388b4473fcc4753e1c9a # 11.1.4 + ) + + FetchContent_MakeAvailable(fmt) +endif() + +if (NOT FROM_CHARS_WORKS) + FetchContent_Declare( + fast_float + GIT_REPOSITORY https://github.com/fastfloat/fast_float + GIT_TAG 77cc847c842c49e7e3477c1e95da2b6540166d66 # 8.0.0 + ) + + FetchContent_MakeAvailable(fast_float) + + list(APPEND LIBS fast_float) + add_definitions(-DUSE_FAST_FLOAT) +endif() + add_library(keycode STATIC + src/array.cpp src/keycode.c ) +target_link_libraries(keycode ${LIBS}) + target_include_directories(keycode PUBLIC src ) diff --git a/cmake/check-from_chars.cmake b/cmake/check-from_chars.cmake new file mode 100644 index 0000000..b1d64d9 --- /dev/null +++ b/cmake/check-from_chars.cmake @@ -0,0 +1,7 @@ +message(STATUS "Checking that std::from_chars for floats is supported by the C++ library") +try_compile(FROM_CHARS_WORKS "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_LIST_DIR}/check-from_chars.cpp") +if(FROM_CHARS_WORKS) + message(STATUS "Checking that std::from_chars for floats is supported by the C++ library - yes") +else() + message(STATUS "Checking that std::from_chars for floats is supported by the C++ library - NO") +endif() diff --git a/cmake/check-from_chars.cpp b/cmake/check-from_chars.cpp new file mode 100644 index 0000000..5d7d0f8 --- /dev/null +++ b/cmake/check-from_chars.cpp @@ -0,0 +1,8 @@ +#include + +int main() +{ + const char str[] = "1.2345e3"; + float num; + std::from_chars(str, str + sizeof str - 1, num); +} diff --git a/code.bat b/code.bat new file mode 100644 index 0000000..f079bd3 --- /dev/null +++ b/code.bat @@ -0,0 +1,3 @@ +call .env.bat + +code.cmd . diff --git a/src/array.cpp b/src/array.cpp new file mode 100644 index 0000000..c2035dc --- /dev/null +++ b/src/array.cpp @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include + +#if USE_FAST_FLOAT +#include +using fast_float::from_chars; +using fast_float::from_chars_result; +#else +#include +using std::from_chars; +using std::from_chars_result; +#endif + +#include "array.h" + +constexpr int PRINT_OFFSET = 30; + +inline void +putchar_n(int c, int count) +{ + for (int i = 0; i < count; ++i) { + putchar(c); + } +} + +inline void +goback(int count) +{ + putchar_n('\b', count); +} + +inline void +goback(std::string_view str) +{ + goback(str.size()); +} + +template +std::tuple +from_string(std::string_view str) +{ + T value; + bool ok = false; + auto start = str.data(); + auto end = str.data() + str.size(); + auto [ptr, ec] = from_chars(start, end, value); + + if (ec == std::errc() && ptr == end) { + ok = true; + } + + return { value, ok }; +} + +template +std::string +format_value(T value) +{ + return fmt::format("{}", value); +} + +template <> +std::string +format_value(double value) +{ + return fmt::format("{:.7g}", value); +} + +template +constexpr std::string_view +allowed_symbols(); + +template <> +constexpr std::string_view +allowed_symbols() +{ + return "01234567890+-"; +} + +template <> +constexpr std::string_view +allowed_symbols() +{ + return "01234567890+-.e"; +} + +template +bool +is_allowed(int c) +{ + auto as = allowed_symbols(); + return isprint(c) && (as.find((char)c) != std::string::npos); +} + +template +void +print_value(T value, T min, T max) +{ + std::string str; + str = fmt::format(" {}", format_value(value)); + str = fmt::format("{:.>{}}", str, PRINT_OFFSET); + str = fmt::format("{} [{:g}, {:g}]", str, (double)min, (double)max); + + fmt::print(fg(fmt::color::gray), str); + goback(str); +} + +template +void +print_value(T value) +{ + T min = std::numeric_limits::lowest(); + T max = std::numeric_limits::max(); + + print_value(value, min, max); +} + +template +void +edit_value(T& value, T min, T max, int exist_key = -1) +{ + std::string str; + if (is_allowed(exist_key)) { + str += exist_key; + } + + while (true) { + while (true) { + print_value(value, min, max); + + auto [new_value, ok] = from_string(str); + if (ok) { + if (new_value != std::clamp(new_value, min, max)) { + ok = false; + } + } + + if (ok) { + fmt::print("{} ", str); + } + else { + fmt::print(fg(fmt::color::red), "{} ", str); + } + goback(1); + + int key = keycode(); + goback(str); + + if (key == KEY_ESC) { + print_value(value, min, max); + return; + } + else if (key == KEY_ENTER && ok) { + value = new_value; + str.clear(); + break; + } + else if (key == KEY_BS && !str.empty()) { + str.pop_back(); + } + else if (is_allowed(key)) { + str += key; + } + } + } +} + +template +void +edit_value(T& old_value, int exist_key = -1) +{ + T min = std::numeric_limits::lowest(); + T max = std::numeric_limits::max(); + + edit_value(old_value, min, max, exist_key); +} + +template +using limit = std::variant; + +template +T +limit_get(limit l, int index) +{ + if (l.index() == 0) { + return std::get(l); + } + + return std::get(l)[index]; +} + +template +void +edit_array( + T* array, + int len, + limit _min, + limit _max, + bool zero_based_indexing = true) +{ + auto min = [_min](int index) -> T { + return limit_get(_min, index); + }; + auto max = [_max](int index) -> T { + return limit_get(_max, index); + }; + + const int start_index = zero_based_indexing ? 0 : 1; + const int end_index = zero_based_indexing ? (len - 1) : len; + int index = start_index; + + while (1) { + if (index > end_index) { + index = start_index; + } + else if (index < start_index) { + index = end_index; + } + + auto s = fmt::format("[{:4}] ", index); + fmt::print("{}", s); + + print_value(array[index], min(index), max(index)); + + while (1) { + int key = keycode(); + if (key == KEY_ESC) { + return; + } + else if (key == KEY_LEFT) { + --index; + goback(s); + break; + } + else if (key == KEY_RIGHT) { + ++index; + goback(s); + break; + } + else if (is_allowed(key)) { + edit_value(array[index], min(index), max(index), key); + } + } + } +} + +template +void +edit_array(T* array, int len, bool zero_based_indexing = true) +{ + T min = std::numeric_limits::lowest(); + T max = std::numeric_limits::max(); + + edit_array(array, len, { min }, { max }, zero_based_indexing); +} + +// ============================================================================== +// Gornov API +// ============================================================================== + +void +taklong(long* value) +{ + printf(" "); + edit_value(*value); + printf("\b"); +} + +void +takdouble(double* value) +{ + printf(" "); + edit_value(*value); + printf("\b"); +} + +void +rtaklong(long* value, long min, long max) +{ + printf(" "); + edit_value(*value, min, max); + printf("\b"); +} + +void +rtakdouble(double* value, double min, double max) +{ + printf(" "); + edit_value(*value, min, max); + printf("\b"); +} + +void +takari(long* array, long len) +{ + edit_array(array, len, false); +} + +void +takarr(double* array, long len) +{ + edit_array(array, len, false); +} + +void +takarr_lg(double* array, long len, double* min, double* max) +{ + edit_array(array, len, { min }, { max }, false); +} + +void +takari_lg(long* array, long len, long* min, long* max) +{ + edit_array(array, len, { min }, { max }, false); +} diff --git a/src/array.h b/src/array.h new file mode 100644 index 0000000..a51c01c --- /dev/null +++ b/src/array.h @@ -0,0 +1,33 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void +taklong(long*); + +void +takdouble(double*); + +void +takarr(double*, long); + +void +takarr_lg(double*, long, double*, double*); + +void +takari(long*, long); + +void +takari_lg(long*, long, long*, long*); + +void +rtaklong(long* i, long imin, long imax); + +void +rtakdouble(double* d, double dmin, double dmax); + +#ifdef __cplusplus +} +#endif