├── examples ├── example_fmt_repl.c ├── example_cpp_str.cpp ├── example_fmt_align.c ├── example_fmt_array.c ├── example_fmt_int_index.c ├── README.md └── example_build_config.c ├── support ├── themes │ ├── doxygen │ │ ├── .gitignore │ │ ├── css │ │ │ ├── search.scss │ │ │ ├── style.scss │ │ │ ├── footer.scss │ │ │ ├── code.scss │ │ │ ├── header.scss │ │ │ └── content.scss │ │ └── html │ │ │ ├── footer.html │ │ │ └── header.html │ └── common │ │ ├── .gitignore │ │ ├── config.scss │ │ └── stylevars.scss ├── Doxyfile ├── WebAssemblyExport.mk ├── project.toml └── WebAssembly.mk ├── CHANGELOG.md ├── .gitmodules ├── tests ├── .gitignore ├── test_into_fp.cpp ├── test_basic.c ├── test_fmt_escape.c ├── test_wrap_fmt_cxx.cpp ├── test_sync_io.c ├── test_fmt_sign.c ├── test_fmt_array.c ├── test_fmt_arg.c ├── test_fmt_behav.c ├── test_fmt_fixed.c ├── test_fmt_sty.c ├── test_string_trans.cpp ├── test_cimpl_parser.c ├── test_string_append.cpp ├── test_into_fix.cpp ├── test_fmt_type_int_val.c ├── test_string_equal.cpp ├── test_fmt_type_int_arr.c ├── test_fmt_align.c ├── test_string_iterator.cpp ├── test_helper.h ├── test_string_concat.cpp ├── test_string_replace.cpp ├── test_main.h ├── test_string_basic.cpp ├── test_fmt_chrono.c ├── test_main.c ├── test_into_int.cpp ├── test_monad_oper.cpp ├── test_string_find.cpp └── test_monad_obj.cpp ├── .github ├── workflows │ ├── ssh.yml │ ├── auto-close-issues.yml │ ├── format-checker.yml │ ├── make-wasm.yml │ ├── cmsis-pack.yml │ ├── codeql.yml │ ├── sanitizer.yml │ ├── coverage.yml │ ├── tests.yml │ ├── doxygen.yml │ ├── cmake-build.yml │ ├── releases.yml │ ├── make-lib.yml │ ├── make-dylib.yml │ └── make-examples.yml └── dependabot.yml ├── src ├── mm_intofp.c ├── win_resource.rc ├── mm_cfg.c └── mm_io.c ├── inc ├── mtfmt.hpp ├── mm_parser.hpp ├── mtfmt.h ├── mm_type.h ├── mm_result.h ├── mm_heap.h ├── mm_fmt.h ├── mm_io.h ├── mm_type.hpp └── mm_parser.h ├── .gitignore ├── .clang-format ├── CMakeLists.txt └── Makefile /examples/example_fmt_repl.c: -------------------------------------------------------------------------------- 1 | 2 | int main(void) 3 | { 4 | } 5 | -------------------------------------------------------------------------------- /support/themes/doxygen/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | 4 | !.gitignore 5 | 6 | !*.scss 7 | !*.html 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | ## 0.1.1 5 | 6 | oaiqi预发布版本,hzuyaoyo。glai卡验证ci等东东 7 | 8 | ## 0.1.0 9 | 10 | 早期预发布版本,msqqq 11 | 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "thirds/unity"] 2 | path = thirds/unity 3 | url = https://github.com/ThrowTheSwitch/Unity.git 4 | [submodule "thirds/zipack"] 5 | path = thirds/zipack 6 | url = https://github.com/MtFmT-Lib/zipack.git 7 | -------------------------------------------------------------------------------- /support/themes/common/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | 4 | .idea 5 | .pyenv 6 | .vscode 7 | lib 8 | pack 9 | build 10 | target 11 | target_package 12 | target_coverage 13 | cmake-build-debug 14 | 15 | !*.scss 16 | !.gitignore 17 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | 4 | .pyenv 5 | .vscode 6 | build 7 | target 8 | lib 9 | 10 | !*.md 11 | !LICENSE 12 | !COPYING 13 | !.gitignore 14 | 15 | */internal/* 16 | 17 | !*.h 18 | !*.c 19 | !*.cpp 20 | !Makefile 21 | -------------------------------------------------------------------------------- /.github/workflows/ssh.yml: -------------------------------------------------------------------------------- 1 | name: SSH 2 | on: [workflow_dispatch] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Setup tmate session 10 | uses: mxschmitt/action-tmate@v3 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # action 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "monthly" 8 | open-pull-requests-limit: 2 9 | # 子模块 10 | - package-ecosystem: "gitsubmodule" 11 | directory: "/" 12 | schedule: 13 | interval: "monthly" 14 | open-pull-requests-limit: 2 15 | -------------------------------------------------------------------------------- /examples/example_cpp_str.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file example_cpp_str.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief C++ wrapper中字符串部分的例子 6 | * @version 1.0 7 | * @date 2023-07-23 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | 14 | int main(void) 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/mm_intofp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_intofp.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 浮点格式化的函数实现 6 | * @version 1.0 7 | * @date 2023-03-24 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | 13 | #define MSTR_IMP_SOURCES 1 14 | 15 | #include "mm_fmt.h" 16 | #include "mm_type.h" 17 | -------------------------------------------------------------------------------- /tests/test_into_fp.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_into_fp.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 浮点数格式化的测试 6 | * @version 1.0 7 | * @date 2023-06-30 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | extern "C" void ftoa_basic(void) 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /support/themes/doxygen/css/search.scss: -------------------------------------------------------------------------------- 1 | @import 'config'; 2 | 3 | // SPDX-License-Identifier: AGPL-3.0 4 | 5 | // inactive 下的样式 6 | #MSearchBox, 7 | .MSearchBoxInactive { 8 | box-shadow: none; 9 | border: 1px solid $light-line-color; 10 | border-radius: 2px; 11 | } 12 | 13 | #MSearchClose { 14 | display: none; 15 | } 16 | 17 | // 搜索结果 18 | #MSearchResultsWindow { 19 | border-radius: 2px; 20 | border: 1px solid $split-line-color; 21 | background-color: $bg-color; 22 | padding: 0.5em; 23 | } -------------------------------------------------------------------------------- /inc/mtfmt.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mtfmt.hpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 格式化 (C++ Header) 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MTFMT_HPP_) 13 | #define _INCLUDE_MTFMT_HPP_ 14 | #include "mm_parser.hpp" 15 | #include "mm_result.hpp" 16 | #include "mm_string.hpp" 17 | #include "mm_type.hpp" 18 | #include "mtfmt.h" 19 | #endif // _INCLUDE_MTFMT_H_ 20 | -------------------------------------------------------------------------------- /inc/mm_parser.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_parser.hpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 分析器 (CPP frontend) 6 | * @version 1.0 7 | * @date 2023-06-15 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_PARSER_HPP_) 13 | #define _INCLUDE_MM_PARSER_HPP_ 1 14 | #include "mm_result.hpp" 15 | 16 | namespace mtfmt 17 | { 18 | namespace details 19 | { 20 | } 21 | } // namespace mtfmt 22 | #endif // _INCLUDE_MM_PARSER_HPP_ 23 | -------------------------------------------------------------------------------- /inc/mtfmt.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mtfmt.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 格式化 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MTFMT_H_) 13 | #define _INCLUDE_MTFMT_H_ 14 | #include "mm_cfg.h" 15 | #include "mm_fmt.h" 16 | #include "mm_heap.h" 17 | #include "mm_io.h" 18 | #include "mm_result.h" 19 | #include "mm_string.h" 20 | #include "mm_type.h" 21 | #endif // _INCLUDE_MTFMT_H_ 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | 4 | .idea 5 | .pyenv 6 | .vscode 7 | lib 8 | pack 9 | build 10 | build_codedb 11 | target 12 | target_package 13 | target_coverage 14 | cmake-build-debug 15 | 16 | !*.md 17 | !LICENSE 18 | !COPYING 19 | !.gitignore 20 | !.gitmodules 21 | !.gitattributes 22 | 23 | !*.yml 24 | !*.html 25 | !*.txt 26 | 27 | !*.png 28 | !*_nlfs.jpg 29 | !*_nlfs.webp 30 | !*.svg 31 | !*.drawio 32 | 33 | !.clang-format 34 | !*.hpp 35 | !*.cpp 36 | !*.h 37 | !*.c 38 | !*.mk 39 | !*.toml 40 | !Makefile 41 | !Doxyfile 42 | !*_resource.rc 43 | 44 | */internal/* 45 | 46 | -------------------------------------------------------------------------------- /tests/test_basic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_basic.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 基本测试 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_heap.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | void allocate_then_free() 20 | { 21 | void* p = mstr_heap_alloc(256); 22 | TEST_ASSERT_TRUE(p != NULL); 23 | mstr_heap_free(p); 24 | } 25 | -------------------------------------------------------------------------------- /support/themes/doxygen/css/style.scss: -------------------------------------------------------------------------------- 1 | @import 'code'; 2 | @import 'config'; 3 | @import 'header'; 4 | @import 'search'; 5 | @import 'footer'; 6 | @import 'content'; 7 | 8 | // SPDX-License-Identifier: AGPL-3.0 9 | 10 | * { 11 | margin: 0; 12 | padding: 0; 13 | } 14 | 15 | html, 16 | body { 17 | color: $fr-color; 18 | background-color: $bg-color; 19 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 20 | } 21 | 22 | hr { 23 | height: 1px; 24 | margin-top: 1.5em; 25 | margin-bottom: 1.5em; 26 | border: 1px solid $light-line-color; 27 | background-color: $light-line-color; 28 | } -------------------------------------------------------------------------------- /tests/test_fmt_escape.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_escape.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 转义字符 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | void fmt_escape_bracket(void) 20 | { 21 | MString s; 22 | EVAL(mstr_create_empty(&s)); 23 | EVAL(mstr_format(&s, "@{{{0:i32}}}@", 1, 1234)); 24 | ASSERT_EQUAL_STRING(&s, "@{1234}@"); 25 | mstr_free(&s); 26 | } 27 | -------------------------------------------------------------------------------- /examples/example_fmt_align.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file example_fmt_align.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同对齐方式下的格式化的例子 6 | * @version 1.0 7 | * @date 2023-07-23 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | 14 | int main(void) 15 | { 16 | mstr_print( 17 | "mstr_print(\"|{{0:i32:=8}}|\\n\", 1234); // => |{0:i32:=8}|\n", 18 | 1234 19 | ); 20 | mstr_print( 21 | "mstr_print(\"|{{0:i32:<8}}|\\n\", 1234); // => |{0:i32:<8}|\n", 22 | 1234 23 | ); 24 | mstr_print( 25 | "mstr_print(\"|{{0:i32:>8}}|\\n\", 1234); // => |{0:i32:>8}|\n", 26 | 1234 27 | ); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/test_wrap_fmt_cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_wrap_fmt_cxx.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief fmt (cpp包装) 6 | * @version 1.0 7 | * @date 2023-06-05 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | #include 19 | 20 | extern "C" void cpp_wrap_fmt(void) 21 | { 22 | mtfmt::string str = mtfmt::string::format_variable("{0:i32}", 123) 23 | .or_value("error"); 24 | ASSERT_EQUAL_VALUE(str, "123"); 25 | } 26 | 27 | extern "C" void cpp_wrap_fmt_parser(void) 28 | { 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/auto-close-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues and PRs 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | workflow_dispatch: 6 | inputs: 7 | logLevel: 8 | description: "Log level" 9 | required: true 10 | default: "warning" 11 | 12 | jobs: 13 | stale: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/stale@v9 17 | with: 18 | stale-pr-message: "This pull request is stale because it has been open 30 days with no activity. It will be closed in 7 days." 19 | stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days." 20 | days-before-stale: 30 21 | days-before-close: 7 22 | -------------------------------------------------------------------------------- /examples/example_fmt_array.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file example_fmt_array.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 数组格式化的例子 6 | * @version 1.0 7 | * @date 2023-07-24 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | 14 | int main(void) 15 | { 16 | const uint32_t array[5] = {0x12, 0x34, 0x56, 0x78, 0x90}; 17 | const usize_t array_size = 5; 18 | mstr_print( 19 | "mstr_print(\"{{[0:i32]}}\\n\", array, array_size); // => " 20 | "{[0:i32]}\n", 21 | array, 22 | array_size 23 | ); 24 | mstr_print( 25 | "mstr_print(\"{{[0:i32:x]}}\\n\", array, array_size); // => " 26 | "{[0:i32:x]}\n", 27 | array, 28 | array_size 29 | ); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /support/themes/doxygen/css/footer.scss: -------------------------------------------------------------------------------- 1 | @import 'config'; 2 | 3 | // SPDX-License-Identifier: AGPL-3.0 4 | 5 | // 页脚 6 | .footer { 7 | margin-top: 1em; 8 | margin-bottom: 2em; 9 | border: none; 10 | border-top: solid 1px $light-line-color; 11 | } 12 | 13 | // footer的内容 14 | .footer-inner { 15 | margin: 0 auto; 16 | padding-top: 1em; 17 | width: $content-width; 18 | min-width: $content-min-width; 19 | max-width: $header-footer-max-width; 20 | font-style: normal; 21 | display: flex; 22 | align-items: baseline; 23 | flex-direction: row; 24 | justify-content: space-between; 25 | 26 | a { 27 | color: $hyper-link-color; 28 | text-decoration: none; 29 | font-weight: bold; 30 | 31 | &:hover { 32 | text-decoration: underline; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /support/Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.9.3 2 | DOXYFILE_ENCODING = UTF-8 3 | PROJECT_NAME = MtFmt 4 | PROJECT_NUMBER = 1.0.0 5 | PROJECT_BRIEF = MtFmt is a format library on embed. system and wrote by pure C. 6 | OUTPUT_LANGUAGE = Chinese 7 | JAVADOC_AUTOBRIEF = YES 8 | QT_AUTOBRIEF = YES 9 | OPTIMIZE_OUTPUT_FOR_C = YES 10 | INPUT = README.md src inc 11 | INPUT_ENCODING = UTF-8 12 | RECURSIVE = YES 13 | OUTPUT_DIRECTORY = ./_site 14 | GENERATE_LATEX = NO 15 | EXTRACT_ALL = YES 16 | EXTRACT_PRIVATE = YES 17 | EXTRACT_STATIC = YES 18 | USE_MDFILE_AS_MAINPAGE = README.md 19 | HTML_HEADER = ./support/themes/doxygen/html/header.html 20 | HTML_FOOTER = ./support/themes/doxygen/html/footer.html 21 | HTML_STYLESHEET = ./support/themes/doxygen/html/style.css 22 | -------------------------------------------------------------------------------- /tests/test_sync_io.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_sync_io.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 同步IO的测试 6 | * @version 1.0 7 | * @date 2023-06-14 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | #include 19 | 20 | static MStrIOCallback cb; 21 | 22 | static mstr_result_t leak_write( 23 | void* ctx, const byte_t* data, usize_t len 24 | ) 25 | { 26 | memcpy(ctx, data, len); 27 | return MStr_Ok; 28 | } 29 | 30 | void sync_io_write(void) 31 | { 32 | char buff[256]; 33 | EVAL(mstr_io_init(buff, &cb, leak_write)); 34 | EVAL(mstr_ioformat(&cb, "{0:s}", 1, "Test")); 35 | TEST_ASSERT_TRUE(memcmp(buff, "Test", 4) == 0); 36 | } 37 | -------------------------------------------------------------------------------- /support/themes/doxygen/html/footer.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /support/WebAssemblyExport.mk: -------------------------------------------------------------------------------- 1 | # 2 | EXPORTS = _mstr_append,_mstr_as_utf8,_mstr_c_str,_mstr_char_at,_mstr_char_length,_mstr_char_offset_at,_mstr_clear,_mstr_codepoint_of,_mstr_concat,_mstr_concat_cstr,_mstr_concat_cstr_slice,_mstr_configure,_mstr_contains,_mstr_context_format,_mstr_copy_from,_mstr_create,_mstr_end_with,_mstr_equal,_mstr_find,_mstr_fmt_iqtoa,_mstr_fmt_itoa,_mstr_fmt_parse_goal,_mstr_fmt_parser_end_position,_mstr_fmt_parser_init,_mstr_fmt_ttoa,_mstr_fmt_uqtoa,_mstr_fmt_utoa,_mstr_format,_mstr_free,_mstr_get_stdout,_mstr_heap_allocate_sym,_mstr_heap_free_sym,_mstr_heap_get_allocate_count,_mstr_heap_get_free_size,_mstr_heap_get_high_water_mark,_mstr_heap_init_sym,_mstr_heap_re_allocate_sym,_mstr_heap_realloc_cpimp_sym,_mstr_insert,_mstr_io_init,_mstr_ioformat,_mstr_iovformat,_mstr_iter,_mstr_lead_char_offset,_mstr_move_from,_mstr_remove,_mstr_repeat_append,_mstr_replace,_mstr_retain,_mstr_reverse_only,_mstr_reverse_self,_mstr_start_with,_mstr_vformat,_mstr_reserve 3 | # 4 | -------------------------------------------------------------------------------- /src/win_resource.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,0,0,0 3 | PRODUCTVERSION 1,0,0,0 4 | FILEFLAGSMASK 0x3fL 5 | #ifdef _DEBUG 6 | FILEFLAGS 0x21L 7 | #else 8 | FILEFLAGS 0x20L 9 | #endif 10 | FILEOS 0x40000L 11 | FILETYPE 0x1L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "080404b0" 17 | BEGIN 18 | VALUE "CompanyName", "XiangYang & HalfSweet" 19 | VALUE "FileDescription", "MtFmt" 20 | VALUE "FileVersion", "1.0.0.0" 21 | VALUE "InternalName", "mtfmt.dll" 22 | VALUE "LegalCopyright", "Copyright (C) XiangYang, HalfSweet, all rights reserved." 23 | VALUE "OriginalFilename", "mtfmt.dll" 24 | VALUE "ProductName", "MtFmt" 25 | VALUE "ProductVersion", "1.0.0.0" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x804, 1200 31 | END 32 | END 33 | -------------------------------------------------------------------------------- /examples/example_fmt_int_index.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file example_fmt_int_index.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 整数在不同进制下的格式化的例子 6 | * @version 1.0 7 | * @date 2023-07-23 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | 14 | int main(void) 15 | { 16 | mstr_print( 17 | "mstr_print(\"{{0:i32:b}}\\n\", 165); // => {0:i32:b}\n", 165 18 | ); 19 | mstr_print( 20 | "mstr_print(\"{{0:i32:o}}\\n\", 165); // => {0:i32:o}\n", 165 21 | ); 22 | mstr_print( 23 | "mstr_print(\"{{0:i32:d}}\\n\", 165); // => {0:i32:d}\n", 165 24 | ); 25 | mstr_print( 26 | "mstr_print(\"{{0:i32:h}}\\n\", 165); // => {0:i32:h}\n", 165 27 | ); 28 | mstr_print( 29 | "mstr_print(\"{{0:i32:H}}\\n\", 165); // => {0:i32:H}\n", 165 30 | ); 31 | mstr_print( 32 | "mstr_print(\"{{0:i32:x}}\\n\", 165); // => {0:i32:x}\n", 165 33 | ); 34 | mstr_print( 35 | "mstr_print(\"{{0:i32:X}}\\n\", 165); // => {0:i32:X}\n", 165 36 | ); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/format-checker.yml: -------------------------------------------------------------------------------- 1 | name: "Code style" 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/**.c" 7 | - "inc/**.h" 8 | - "inc/**.hpp" 9 | - "tests/**.c" 10 | - "tests/**.h" 11 | - "examples/**.c" 12 | - "examples/**.h" 13 | - "examples/**.hpp" 14 | - "**.clang-format" 15 | - ".github/workflows/format-checker.yml" 16 | pull_request: 17 | branches: [master] 18 | paths: 19 | - "src/**.c" 20 | - "inc/**.h" 21 | - "inc/**.hpp" 22 | - "tests/**.c" 23 | - "tests/**.h" 24 | - "examples/**.c" 25 | - "examples/**.h" 26 | - "examples/**.hpp" 27 | - "**.clang-format" 28 | - ".github/workflows/format-checker.yml" 29 | workflow_dispatch: 30 | inputs: 31 | logLevel: 32 | description: "Log level" 33 | required: true 34 | default: "warning" 35 | 36 | jobs: 37 | check: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@v4 42 | - name: Check code style 43 | uses: jidicula/clang-format-action@v4.11.0 44 | with: 45 | clang-format-version: "16" 46 | check-path: "./" 47 | fallback-style: Microsoft 48 | -------------------------------------------------------------------------------- /support/themes/common/config.scss: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | 3 | // 内容宽度 4 | $content-width: 80%; 5 | $content-min-width: 480px; 6 | $content-max-width: 960px; 7 | $header-footer-max-width: 1024px; 8 | 9 | // 基本样式的主题色 10 | $bg-color: white; 11 | $fr-color: #32393b; 12 | $text-color: #363534; 13 | 14 | // 分割线颜色 15 | $split-line-color: #ccc; 16 | 17 | // 浅色边框颜色 18 | $light-line-color: #eff0f1; 19 | 20 | // 超链接的颜色 21 | $hyper-link-color: #1e6bb8; 22 | 23 | // 首行缩进 (懒得搞) 24 | $line-ident: 0; 25 | 26 | // 段颜色 27 | $section-text-color: #2a83a2; 28 | 29 | // checkbox 选中显示的图片 30 | $checkbox-img: url('data:image/svg+xml;utf8,'); 31 | 32 | // checkbox 显示的图片颜色 33 | $checkbox-color: #333; 34 | 35 | // checkbox 边框颜色 36 | $checkbox-border-color: #c9c9c9; 37 | 38 | // 表格文字颜色 39 | $table-text-color: mix(#89c3eb, black, 75%); 40 | 41 | // 表格超链接颜色 42 | $table-hyper-color: mix(#0095d9, black, 75%); 43 | 44 | // 表格偶数项颜色 45 | $table-background-color: mix($light-line-color, white, 50%); 46 | 47 | // 程序使用的字体 48 | $fixed-font: 'Cascadia Code', monospace, fixed; -------------------------------------------------------------------------------- /.github/workflows/make-wasm.yml: -------------------------------------------------------------------------------- 1 | name: "make wasm" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "support/WebAssembly.mk" 10 | - "support/WebAssemblyExport.mk" 11 | - ".github/workflows/make-wasm.yml" 12 | pull_request: 13 | branches: [master] 14 | paths: 15 | - "src/**.c" 16 | - "inc/**.h" 17 | - "support/WebAssembly.mk" 18 | - "support/WebAssemblyExport.mk" 19 | - ".github/workflows/make-wasm.yml" 20 | workflow_dispatch: 21 | inputs: 22 | logLevel: 23 | description: "Log level" 24 | required: true 25 | default: "warning" 26 | 27 | jobs: 28 | build-wasm: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | submodules: true 35 | # WASM 36 | - name: Setup emsdk 37 | uses: mymindstorm/setup-emsdk@v14 38 | - name: WASM build 39 | run: | 40 | emmake make --makefile=support/WebAssembly.mk 41 | # 打包 42 | - name: Upload artifact 43 | if: github.ref == 'refs/heads/master' 44 | uses: actions/upload-artifact@v4 45 | with: 46 | name: wasm-artifact 47 | path: target/*.wasm 48 | -------------------------------------------------------------------------------- /tests/test_fmt_sign.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_sign.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同符号设置下的测试 6 | * @version 1.0 7 | * @date 2023-05-30 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_fmt.h" 13 | #include "mm_heap.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | #include 19 | 20 | void fmt_sign_add(void) 21 | { 22 | MString s; 23 | EVAL(mstr_create_empty(&s)); 24 | EVAL(mstr_format( 25 | &s, "@{0:i32:+}@{1:i32:+}@{2:i32:+}@", 3, 123, -123, 0 26 | )); 27 | ASSERT_EQUAL_STRING(&s, "@+123@-123@0@"); 28 | mstr_free(&s); 29 | } 30 | 31 | void fmt_sign_sub(void) 32 | { 33 | MString s; 34 | EVAL(mstr_create_empty(&s)); 35 | EVAL(mstr_format( 36 | &s, "@{0:i32:-}@{1:i32:-}@{2:i32:-}@", 3, 123, -123, 0 37 | )); 38 | ASSERT_EQUAL_STRING(&s, "@123@-123@0@"); 39 | mstr_free(&s); 40 | } 41 | 42 | void fmt_sign_space(void) 43 | { 44 | MString s; 45 | EVAL(mstr_create_empty(&s)); 46 | EVAL(mstr_format( 47 | &s, "@{0:i32: }@{1:i32: }@{2:i32: }@", 3, 123, -123, 0 48 | )); 49 | ASSERT_EQUAL_STRING(&s, "@ 123@-123@ 0@"); 50 | mstr_free(&s); 51 | } 52 | -------------------------------------------------------------------------------- /tests/test_fmt_array.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_array.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 数组格式化 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | // 数组 20 | #define ARRAY_SIZE ((size_t)6) 21 | 22 | static const int32_t array[ARRAY_SIZE] = {1, 1, 4, 5, 1, 4}; 23 | 24 | void fmt_array(void) 25 | { 26 | MString s; 27 | EVAL(mstr_create_empty(&s)); 28 | EVAL(mstr_format(&s, "@{[0:i32]}@", 2, array, ARRAY_SIZE)); 29 | ASSERT_EQUAL_STRING(&s, "@1, 1, 4, 5, 1, 4@"); 30 | mstr_free(&s); 31 | } 32 | 33 | void fmt_array_userdefind_split(void) 34 | { 35 | MString s; 36 | EVAL(mstr_create_empty(&s)); 37 | EVAL(mstr_format(&s, "@{[0:i32|:#]}@", 2, array, ARRAY_SIZE)); 38 | ASSERT_EQUAL_STRING(&s, "@1#1#4#5#1#4@"); 39 | mstr_free(&s); 40 | } 41 | 42 | void fmt_array_element_style(void) 43 | { 44 | MString s; 45 | EVAL(mstr_create_empty(&s)); 46 | EVAL(mstr_format(&s, "@{[0:i32|:#:x]}@", 2, array, ARRAY_SIZE)); 47 | ASSERT_EQUAL_STRING(&s, "@0x1#0x1#0x4#0x5#0x1#0x4@"); 48 | mstr_free(&s); 49 | } 50 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: LGPL-3.0 2 | Language: Cpp 3 | BasedOnStyle: Microsoft 4 | # 每行字符的限制 5 | ColumnLimit: 72 6 | # 缩进宽度 7 | IndentWidth: 4 8 | # 访问说明符偏移 9 | AccessModifierOffset: -4 10 | # 大括号的换行规则 11 | BreakBeforeBraces: Custom 12 | BraceWrapping: 13 | SplitEmptyFunction: true 14 | SplitEmptyRecord: true 15 | SplitEmptyNamespace: true 16 | AfterCaseLabel: true 17 | # 换行定义 18 | BeforeElse: true 19 | AfterControlStatement: Never 20 | # 缩进大括号(整个大括号框起来的部分都缩进) 21 | IndentBraces: false 22 | # 指针 23 | PointerAlignment: Left 24 | DerivePointerAlignment: false 25 | # 括号内的内容对齐方式 26 | # clang-format version >= 14.0 27 | AlignAfterOpenBracket: BlockIndent 28 | # 允许短的case标签放在同一行 29 | AllowShortCaseLabelsOnASingleLine: true 30 | # 实参要么都在同一行,要么都各自一行 31 | BinPackArguments: false 32 | # 形参要么都在同一行,要么都各自一行 33 | BinPackParameters: false 34 | # 对#include进行排序 35 | IncludeCategories: 36 | - Regex: '^(<|"(m*))' 37 | Priority: 2 38 | - Regex: ".*" 39 | Priority: 1 40 | # 连续空行的最大数量 41 | MaxEmptyLinesToKeep: 1 42 | # 函数返回类型换行时,缩进函数声明或函数定义的函数名 43 | IndentWrappedFunctionNames: true 44 | # 允许函数声明的所有参数在放在下一行 45 | AllowAllParametersOfDeclarationOnNextLine: true 46 | # 反斜杠 47 | AlignEscapedNewlines: Left 48 | # 宏 49 | AlignConsecutiveMacros: 50 | Enabled: true 51 | AcrossEmptyLines: true 52 | AcrossComments: true 53 | # ? : 不要换行 54 | BreakBeforeTernaryOperators: false 55 | -------------------------------------------------------------------------------- /.github/workflows/cmsis-pack.yml: -------------------------------------------------------------------------------- 1 | name: "CMSIS pack" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "inc/**.hpp" 10 | - "support/project.toml" 11 | - ".github/workflows/cmsis-pack.yml" 12 | pull_request: 13 | branches: [master] 14 | paths: 15 | - "src/**.c" 16 | - "inc/**.h" 17 | - "inc/**.hpp" 18 | - "support/project.toml" 19 | - ".github/workflows/cmsis-pack.yml" 20 | workflow_dispatch: 21 | inputs: 22 | logLevel: 23 | description: "Log level" 24 | required: true 25 | default: "warning" 26 | 27 | jobs: 28 | pack-file: 29 | runs-on: "ubuntu-latest" 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | submodules: true 35 | - name: Install dependencies 36 | run: | 37 | python -m pip install --upgrade pip 38 | pip install flake8 pytest 39 | if [ -f thirds/zipack/requirements.txt ]; then pip install -r thirds/zipack/requirements.txt; fi 40 | - name: Generate CMSIS pack 41 | run: | 42 | python thirds/zipack --project=./support/project.toml cmsis-pack 43 | - name: Upload artifact 44 | uses: actions/upload-artifact@v4 45 | with: 46 | name: cmsis-pack-artifact 47 | path: target_package/*.pack 48 | -------------------------------------------------------------------------------- /tests/test_fmt_arg.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_arg.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同格式化参数下的测试 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_fmt.h" 13 | #include "mm_heap.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | #include 19 | 20 | void fmt_seq_arg_id(void) 21 | { 22 | MString s; 23 | EVAL(mstr_create_empty(&s)); 24 | EVAL(mstr_format(&s, "@{0:i32}-{1:i32}-{0:i32}@", 2, 123, 456)); 25 | ASSERT_EQUAL_STRING(&s, "@123-456-123@"); 26 | mstr_free(&s); 27 | } 28 | 29 | void fmt_seq_arg_id_err(void) 30 | { 31 | MString s; 32 | mstr_result_t result = MStr_Ok; 33 | EVAL(mstr_create_empty(&s)); 34 | // ERR: 参数太少 35 | result = mstr_format(&s, "@{0:i32}-{1:i32}@", 1, 123); 36 | TEST_ASSERT_TRUE(result == MStr_Err_InvaildArgumentID); 37 | mstr_clear(&s); 38 | // ERR: 参数顺序不对 39 | result = mstr_format(&s, "@{1:i32}-{0:i32}@", 2, 123, 456); 40 | TEST_ASSERT_TRUE(result == MStr_Err_UnusedArgumentID); 41 | mstr_clear(&s); 42 | // ERR: 参数顺序类型 43 | result = mstr_format(&s, "@{0:i32}-{0:u32}@", 1, 123); 44 | TEST_ASSERT_TRUE(result == MStr_Err_InvaildArgumentType); 45 | mstr_clear(&s); 46 | // free 47 | mstr_free(&s); 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "inc/**.hpp" 10 | - ".github/workflows/codeql.yml" 11 | pull_request: 12 | branches: [master] 13 | paths: 14 | - "src/**.c" 15 | - "inc/**.h" 16 | - "inc/**.hpp" 17 | - ".github/workflows/codeql.yml" 18 | workflow_dispatch: 19 | inputs: 20 | logLevel: 21 | description: "Log level" 22 | required: true 23 | default: "warning" 24 | 25 | concurrency: 26 | group: CodeQL-${{ github.ref }} 27 | cancel-in-progress: true 28 | 29 | permissions: 30 | contents: read 31 | 32 | jobs: 33 | analyze: 34 | permissions: 35 | actions: read 36 | contents: read 37 | security-events: write 38 | name: analyze 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v4 43 | with: 44 | submodules: true 45 | 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v3 48 | with: 49 | languages: cpp 50 | 51 | - name: Build 52 | run: | 53 | make dylib 54 | env: 55 | MTFMT_BUILD_C_DEFS: >- 56 | -D_MSTR_BUILD_DYLIB=1 57 | MTFMT_BUILD_USE_LTO: "1" 58 | 59 | - name: Perform CodeQL Analysis 60 | uses: github/codeql-action/analyze@v3 61 | -------------------------------------------------------------------------------- /support/themes/doxygen/css/code.scss: -------------------------------------------------------------------------------- 1 | @import 'config'; 2 | 3 | // SPDX-License-Identifier: AGPL-3.0 4 | 5 | // 代码块样式 6 | .fragment { 7 | font-family: $fixed-font; 8 | font-size: 14px; 9 | margin-top: 0.8em; 10 | margin-bottom: 0.8em; 11 | padding-top: 0.5em; 12 | padding-bottom: 0.5em; 13 | 14 | .line { 15 | white-space: pre; 16 | } 17 | } 18 | 19 | pre.fragment { 20 | margin-top: 0.8em; 21 | margin-bottom: 0.8em; 22 | padding-top: 0.5em; 23 | padding-bottom: 0.5em; 24 | overflow: auto; 25 | word-wrap: break-word; 26 | line-height: 125%; 27 | } 28 | 29 | // 行(超链接) 30 | a.line { 31 | color: mix(#89c3eb, black, 100%); 32 | text-decoration: none; 33 | 34 | &:hover { 35 | text-decoration: underline; 36 | } 37 | 38 | &:visited { 39 | color: mix(#89c3eb, black, 75%); 40 | } 41 | } 42 | 43 | // 行号 44 | .lineno { 45 | color: grey; 46 | text-align: right; 47 | padding-right: 0.8em; 48 | } 49 | 50 | // 代码高亮样式 51 | span.preprocessor { 52 | color: rgb(200, 0, 200); 53 | } 54 | 55 | span.keyword { 56 | color: blue; 57 | } 58 | 59 | span.keywordtype { 60 | color: blue; 61 | } 62 | 63 | span.keywordflow { 64 | color: rgb(200, 0, 200); 65 | } 66 | 67 | span.comment { 68 | color: rgb(0, 150, 0); 69 | } 70 | 71 | span.stringliteral { 72 | color: rgb(168, 20, 20); 73 | } 74 | 75 | span.charliteral { 76 | color: rgb(192, 100, 40); 77 | } -------------------------------------------------------------------------------- /tests/test_fmt_behav.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_behav.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 格式化行为 6 | * @version 1.0 7 | * @date 2023-06-02 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_fmt.h" 13 | #include "mm_heap.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | #include 19 | 20 | void fmt_behav_signed_bin(void) 21 | { 22 | MString s; 23 | EVAL(mstr_create_empty(&s)); 24 | EVAL(mstr_format(&s, "@{0:i32:b}@{1:i32:b}@", 2, 10, -10)); 25 | ASSERT_EQUAL_STRING(&s, "@1010@-1010@"); 26 | mstr_free(&s); 27 | } 28 | 29 | void fmt_behav_signed_oct(void) 30 | { 31 | MString s; 32 | EVAL(mstr_create_empty(&s)); 33 | EVAL(mstr_format(&s, "@{0:i32:o}@{1:i32:o}@", 2, 10, -10)); 34 | ASSERT_EQUAL_STRING(&s, "@12@-12@"); 35 | mstr_free(&s); 36 | } 37 | 38 | void fmt_behav_signed_dec(void) 39 | { 40 | MString s; 41 | EVAL(mstr_create_empty(&s)); 42 | EVAL(mstr_format(&s, "@{0:i32:d}@{1:i32:d}@", 2, 10, -10)); 43 | ASSERT_EQUAL_STRING(&s, "@10@-10@"); 44 | mstr_free(&s); 45 | } 46 | 47 | void fmt_behav_signed_hex(void) 48 | { 49 | MString s; 50 | EVAL(mstr_create_empty(&s)); 51 | EVAL(mstr_format(&s, "@{0:i32:h}@{1:i32:h}@", 2, 10, -10)); 52 | ASSERT_EQUAL_STRING(&s, "@a@-a@"); 53 | mstr_free(&s); 54 | } 55 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | 示例程序 2 | ======== 3 | 4 | 该文件夹包括一些示例程序,可以在**根**目录下使用下面的命令构建所有的例子: 5 | 6 | ```bash 7 | make examples 8 | ``` 9 | 10 | 或者使用下面的命令构建特定的例子,以 `example_build_config.c` 在 Linux 下为例: 11 | 12 | ```bash 13 | make target/example_build_config.out 14 | ``` 15 | 16 | 大多数例子都使用 stdout 的 feature,设置如下环境变量将其启用: 17 | 18 | ```bash 19 | # Linux 20 | env MTFMT_BUILD_C_DEFS="-D_MSTR_USE_STD_IO=1" 21 | ``` 22 | 23 | 或者 24 | 25 | ```powershell 26 | # PowerShell 27 | $Env:MTFMT_BUILD_EXEFILE_EXT=".exe" 28 | $Env:MTFMT_BUILD_C_DEFS="-D_MSTR_USE_STD_IO=1" 29 | ``` 30 | 31 | 下表给出了目前可用的示例。 32 | 33 | | 文件 | 描述 | 34 | | :------------------------------------------------------------------------------------------------------ | :--------------------------------------- | 35 | | [example_build_config.c](https://github.com/MtFmT-Lib/mtfmt/blob/master/examples/example_build_config.c) | 打印构建配置 | 36 | | [example_fmt_align.c](https://github.com/MtFmT-Lib/mtfmt/blob/master/examples/example_fmt_align.c) | 格式化的对齐方式(左对齐、居中、右对齐) | 37 | | [example_fmt_int_index.c](https://github.com/MtFmT-Lib/mtfmt/blob/master/examples/example_fmt_int_index.c) | 不同进制下的整数格式化 | 38 | | [example_fmt_array.c](https://github.com/MtFmT-Lib/mtfmt/blob/master/examples/example_fmt_array.c) | 数组格式化 | 39 | -------------------------------------------------------------------------------- /tests/test_fmt_fixed.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_fixed.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 定点数相关的test 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | void fmt_quat_value_sign(void) 20 | { 21 | MString s; 22 | EVAL(mstr_create_empty(&s)); 23 | // 1/4096 .. 24 | EVAL(mstr_format(&s, "@{0:q12}@{1:q12}@", 2, 1, -1)); 25 | ASSERT_EQUAL_STRING(&s, "@0.000244140625@-0.000244140625@"); 26 | // 无符号版本 27 | // 1048575 * 4096 = 0xfffff000 28 | mstr_clear(&s); 29 | EVAL(mstr_format(&s, "@{0:q12u}@{1:q12u}@", 2, 4096, -4096)); 30 | ASSERT_EQUAL_STRING(&s, "@1@1048575@"); 31 | mstr_free(&s); 32 | } 33 | 34 | void fmt_quat_value_dualprec(void) 35 | { 36 | MString s; 37 | EVAL(mstr_create_empty(&s)); 38 | // 1/4096 .. 39 | EVAL(mstr_format(&s, "@{0:q12}@{1:q31}@", 2, 1, 1)); 40 | ASSERT_EQUAL_STRING( 41 | &s, "@0.000244140625@0.0000000000000000000002166527565@" 42 | ); 43 | mstr_free(&s); 44 | } 45 | 46 | void fmt_quat_value_singprec(void) 47 | { 48 | MString s; 49 | EVAL(mstr_create_empty(&s)); 50 | // 1/4 .. 51 | EVAL(mstr_format(&s, "@{0:q2}@", 1, 1)); 52 | ASSERT_EQUAL_STRING(&s, "@0.25@"); 53 | mstr_free(&s); 54 | } 55 | -------------------------------------------------------------------------------- /src/mm_cfg.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_cfg.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 打印编译时的环境 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | 13 | #define MSTR_IMP_SOURCES 1 14 | 15 | #include "mm_cfg.h" 16 | #include "mm_type.h" 17 | 18 | MSTR_EXPORT_API(uint32_t) mstr_configure(void) 19 | { 20 | uint32_t configure = 0x00; 21 | #if _MSTR_USE_MALLOC 22 | configure |= MSTRCFG_USE_MALLOC_BIT; 23 | #endif // _MSTR_USE_MALLOC 24 | #if _MSTR_BUILD_DLL 25 | configure |= MSTRCFG_BUILD_DLL_BIT; 26 | #endif // _MSTR_USE_MALLOC 27 | #if _MSTR_USE_HARDWARE_DIV 28 | configure |= MSTRCFG_BUILD_HARDWARE_DIV; 29 | #endif // _MSTR_USE_HARDWARE_DIV 30 | #if _MSTR_USE_STD_IO 31 | configure |= MSTRCFG_USE_STD_IO; 32 | #endif // _MSTR_USE_STD_IO 33 | #if _MSTR_USE_UTF_8 34 | configure |= MSTRCFG_USE_UTF_8; 35 | #endif // _MSTR_USE_UTF_8 36 | #if _MSTR_USE_CPP_EXCEPTION 37 | configure |= MSTRCFG_USE_CXX_EXCEPTION; 38 | #endif // _MSTR_USE_CPP_EXCEPTION 39 | #if _MSTR_USE_FP_FLOAT32 40 | configure |= MSTRCFG_USE_FLOAT32; 41 | #endif // _MSTR_USE_FP_FLOAT32 42 | #if _MSTR_USE_FP_FLOAT64 43 | configure |= MSTRCFG_USE_FLOAT64; 44 | #endif // _MSTR_USE_FP_FLOAT64 45 | #if _MSTR_USE_ALLOC 46 | configure |= MSTRCFG_USE_ALLOCATOR; 47 | #endif // _MSTR_USE_ALLOC 48 | // 使用的编译器信息 49 | configure |= MSTR_BUILD_CC << 12; 50 | // ret 51 | return configure; 52 | } 53 | -------------------------------------------------------------------------------- /support/themes/common/stylevars.scss: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | $bg-color: #fff; 3 | $bg-color-dark: #2d3138; 4 | $bg-color-warm: #f1e3d3; 5 | 6 | // 跨儿旗 7 | $transflag-blue: #5bcefa; 8 | $transflag-white: #f0f0f0; 9 | $transflag-pink: #f6a8b8; 10 | $transflag-gray: #ccc; 11 | 12 | // 尺寸范围 13 | $xs: 520px; 14 | $sm: 640px; 15 | $md: 768px; 16 | $lg: 1024px; 17 | $xl: 1280px; 18 | 19 | // 内容的最大宽度 20 | $header-max-width: 1280px; 21 | $content-max-width: 1280px; 22 | $content-toc-max-width: 360px; 23 | $content-text-max-width: $content-max-width - $content-toc-max-width; 24 | $footer-max-width: 1280px; 25 | 26 | // 边框颜色 27 | $border-color: #f0f0f0; 28 | 29 | // 表格边框颜色 30 | $table-border-color: #666; 31 | 32 | // 超链接颜色 33 | $hpyer-link-color: #198619; 34 | 35 | // 按钮颜色 36 | $button-color: #198619; 37 | $button-bg-color: $bg-color; 38 | 39 | // 中文字体 40 | $text-font-zh: "TIBch", "Diatype T", "Segoe UI", "Helvetica Neue", 41 | arial, "Hiragino Sans GB", "PingFang SC", "Heiti SC", "Noto Sans CJK SC", 42 | "Source Han Sans SC", "Microsoft YaHei UI", "Microsoft YaHei"; 43 | 44 | // 西文字体 45 | $text-font-en: "Overpass", -apple-system, 46 | blinkmacsystemfont, "Segoe UI", roboto, oxygen, 47 | ubuntu, cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 48 | 49 | // 代码块的等宽字符 50 | $mono-text-font: 'Cascadia Mono', 'Consolas', 'Courier New', monospace; 51 | 52 | // 内联代码块的字体(en) 53 | $inline-code-text-font-en: 'Consolas', 'Courier New', monospace; 54 | 55 | // 内联代码块的字体(zh) 56 | $inline-code-text-font-zh: $inline-code-text-font-en; 57 | 58 | // 文本颜色 59 | $text-color: #4f4f4f; -------------------------------------------------------------------------------- /tests/test_fmt_sty.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_sty.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 值格式化样式的测试 6 | * @version 1.0 7 | * @date 2023-05-30 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_fmt.h" 13 | #include "mm_heap.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | #include 19 | 20 | void fmt_sty_bin(void) 21 | { 22 | MString s; 23 | EVAL(mstr_create_empty(&s)); 24 | EVAL(mstr_format(&s, "@{0:i32:b}@", 1, 0xa5)); 25 | ASSERT_EQUAL_STRING(&s, "@10100101@"); 26 | mstr_free(&s); 27 | } 28 | 29 | void fmt_sty_oct(void) 30 | { 31 | MString s; 32 | EVAL(mstr_create_empty(&s)); 33 | EVAL(mstr_format(&s, "@{0:i32:o}@", 1, 0xa5)); 34 | ASSERT_EQUAL_STRING(&s, "@245@"); 35 | mstr_free(&s); 36 | } 37 | 38 | void fmt_sty_dec(void) 39 | { 40 | MString s; 41 | EVAL(mstr_create_empty(&s)); 42 | EVAL(mstr_format(&s, "@{0:i32}@{0:i32:d}@", 1, 0xa5)); 43 | ASSERT_EQUAL_STRING(&s, "@165@165@"); 44 | mstr_free(&s); 45 | } 46 | 47 | void fmt_sty_hex(void) 48 | { 49 | MString s; 50 | EVAL(mstr_create_empty(&s)); 51 | EVAL(mstr_format(&s, "@{0:i32:h}@{0:i32:H}@", 1, 0xa5)); 52 | ASSERT_EQUAL_STRING(&s, "@a5@A5@"); 53 | mstr_free(&s); 54 | } 55 | 56 | void fmt_sty_hex_prefix(void) 57 | { 58 | MString s; 59 | EVAL(mstr_create_empty(&s)); 60 | EVAL(mstr_format(&s, "@{0:i32:x}@{0:i32:X}@", 1, 0xa5)); 61 | ASSERT_EQUAL_STRING(&s, "@0xa5@0XA5@"); 62 | mstr_free(&s); 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/sanitizer.yml: -------------------------------------------------------------------------------- 1 | name: Sanitizer 2 | 3 | on: 4 | push: 5 | paths: 6 | - "inc/**.h" 7 | - "inc/**.hpp" 8 | - "src/**.c" 9 | - "src/**.cpp" 10 | - "tests/**.c" 11 | - "tests/**.cpp" 12 | - "Makefile" 13 | - ".github/workflows/sanitizer.yml" 14 | pull_request: 15 | branches: [master] 16 | paths: 17 | - "inc/**.h" 18 | - "inc/**.hpp" 19 | - "src/**.c" 20 | - "src/**.cpp" 21 | - "tests/**.c" 22 | - "tests/**.cpp" 23 | - "Makefile" 24 | - ".github/workflows/sanitizer.yml" 25 | workflow_dispatch: 26 | inputs: 27 | logLevel: 28 | description: "Log level" 29 | required: true 30 | default: "warning" 31 | 32 | jobs: 33 | sanitizer: 34 | runs-on: ubuntu-latest 35 | strategy: 36 | fail-fast: true 37 | matrix: 38 | sanitizer: [address, leak, undefined] 39 | name: >- 40 | Sanitizer ${{ matrix.sanitizer }} 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@v4 44 | with: 45 | submodules: true 46 | # 设置GCC版本 47 | - name: Set up GCC 48 | uses: egor-tensin/setup-gcc@v1 49 | with: 50 | version: latest 51 | # 运行 52 | - name: Test with sanitizer 53 | if: ${{ matrix.sanitizer }} != 'none' 54 | run: | 55 | make test 56 | env: 57 | MTFMT_BUILD_DEBUG: "1" 58 | MTFMT_BUILD_WITH_SANITIZER: "-fsanitize=${{ matrix.sanitizer }}" 59 | MTFMT_BUILD_C_DEFS: >- 60 | -D_MSTR_USE_MALLOC=1 61 | -D_MSTR_USE_HARDWARE_DIV=1 62 | -------------------------------------------------------------------------------- /tests/test_string_trans.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_trans.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 各个变换操作 6 | * @version 1.0 7 | * @date 2023-06-21 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_heap.h" 13 | #include "mm_string.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | 19 | extern "C" void string_trans_clear(void) 20 | { 21 | MString str; 22 | EVAL(mstr_create(&str, u8"Smile")); 23 | ASSERT_EQUAL_VALUE(str.count, 5); 24 | ASSERT_EQUAL_VALUE(str.length, 5); 25 | mstr_clear(&str); 26 | ASSERT_EQUAL_VALUE(str.count, 0); 27 | ASSERT_EQUAL_VALUE(str.length, 0); 28 | mstr_free(&str); 29 | } 30 | 31 | extern "C" void string_trans_reverse(void) 32 | { 33 | // 偶数长度 34 | MString src1; 35 | EVAL(mstr_create(&src1, "1234567890")); 36 | ASSERT_EQUAL_STRING(&src1, "1234567890"); 37 | mstr_reverse_self(&src1); 38 | ASSERT_EQUAL_STRING(&src1, "0987654321"); 39 | mstr_free(&src1); 40 | // 奇数长度 41 | MString src2; 42 | EVAL(mstr_create(&src2, "123456789")); 43 | ASSERT_EQUAL_STRING(&src2, "123456789"); 44 | mstr_reverse_self(&src2); 45 | ASSERT_EQUAL_STRING(&src2, "987654321"); 46 | mstr_free(&src2); 47 | // Unicode 48 | #if _MSTR_USE_UTF_8 49 | MString src_unicode; 50 | EVAL(mstr_create(&src_unicode, u8"😀😊")); 51 | ASSERT_EQUAL_STRING(&src_unicode, u8"😀😊"); 52 | mstr_reverse_self(&src_unicode); 53 | ASSERT_EQUAL_STRING(&src_unicode, u8"😊😀"); 54 | mstr_free(&src_unicode); 55 | #endif 56 | } 57 | -------------------------------------------------------------------------------- /support/project.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "向阳 ", 4 | "HalfSweet ", 5 | ] 6 | description = "A formatting library wrote by pure C language" 7 | keywords = ["formatting", "heap-manager", "string"] 8 | name = "mtfmt" 9 | organization = "mtfmt" 10 | version = "0.1.1" 11 | 12 | [package.support] 13 | homepage = "https://mtfmt.cc/" 14 | issue = "https://github.com/MtFmT-Lib/mtfmt/issues" 15 | organization = "https://github.com/MtFmT-Lib/mtfmt" 16 | 17 | [[package.file]] 18 | category = "binarys" 19 | file = ["target/*.a", "target/*.so"] 20 | output = "bin/lib" 21 | 22 | [[package.file]] 23 | category = "binarys" 24 | file = [ 25 | "build/target/bin/*.dll", 26 | "build/target/lib/*.lib", 27 | "build/target/lib/*.exp", 28 | ] 29 | output = "bin/dll" 30 | 31 | [[package.file]] 32 | category = "headers" 33 | file = ["inc/**"] 34 | output = "inc" 35 | 36 | [[package.file]] 37 | category = "sources" 38 | file = ["src/*.c"] 39 | output = "src" 40 | 41 | [[package.file]] 42 | category = "resources" 43 | file = ["src/*.rc"] 44 | output = "src" 45 | 46 | [[package.file]] 47 | file = ["LICENSE", "*.md", "version.txt"] 48 | output = "." 49 | 50 | [[action]] 51 | categories = ["default", "headers", "sources"] 52 | mode = "pack" 53 | name = "pack" 54 | target = "mtfmt_src.zip" 55 | 56 | [[action]] 57 | cmsis-Cclass = "Utility" 58 | cmsis-Cgroup = "MtFmt:Library" 59 | mode = "cmsis" 60 | name = "cmsis-pdsc" 61 | target = "{package:organization}.{package:name}.pdsc" 62 | 63 | [[action]] 64 | categories = ["headers", "sources", "@cmsis-pdsc"] 65 | mode = "pack" 66 | name = "cmsis-pack" 67 | target = "{package:organization}.{package:name}.{package:version}.pack" 68 | -------------------------------------------------------------------------------- /tests/test_cimpl_parser.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_cimpl_parser.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief parser的测试 (主要是各个err ...) 6 | * @version 1.0 7 | * @date 2023-07-16 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_parser.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | static mstr_result_t do_parse(const char*, MStrFmtParseResult*); 20 | 21 | void parser_err_invaild_begin(void) 22 | { 23 | mstr_result_t res; 24 | MStrFmtParseResult out; 25 | // 不可作为起始的字符 26 | res = do_parse("s", &out); 27 | ASSERT_EQUAL_VALUE(res, MStr_Err_MissingReplacement); 28 | } 29 | 30 | void parser_err_invaild_end(void) 31 | { 32 | mstr_result_t res; 33 | MStrFmtParseResult out; 34 | // 缺失 "}" 35 | res = do_parse("{0:i32", &out); 36 | ASSERT_EQUAL_VALUE(res, MStr_Err_MissingRightBrace); 37 | } 38 | 39 | void parser_err_invaild_fmt_spec(void) 40 | { 41 | mstr_result_t res; 42 | MStrFmtParseResult out; 43 | // 不被识别的fmt spec 44 | res = do_parse("{0:i32:v}", &out); 45 | ASSERT_EQUAL_VALUE(res, MStr_Err_UnsupportFormatType); 46 | } 47 | 48 | // 恼 也许我们应该考虑换个方式测测... 49 | // 这样子好笨蛋 50 | 51 | /** 52 | * @brief 调用parser解析输入 53 | * 54 | */ 55 | static mstr_result_t do_parse(const char* inp, MStrFmtParseResult* out) 56 | { 57 | mstr_result_t result; 58 | MStrFmtParserState* state; 59 | byte_t state_memory[MFMT_PARSER_STATE_SIZE]; 60 | mstr_fmt_parser_init(state_memory, inp, &state); 61 | result = mstr_fmt_parse_goal(state, out); 62 | return result; 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: "Code coverage" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "tests/**.c" 8 | - "src/**.c" 9 | - "inc/**.h" 10 | - "inc/**.hpp" 11 | - "Makefile" 12 | - ".github/workflows/coverage.yml" 13 | pull_request: 14 | branches: [master] 15 | paths: 16 | - "tests/**.c" 17 | - "src/**.c" 18 | - "inc/**.h" 19 | - "inc/**.hpp" 20 | - "Makefile" 21 | - ".github/workflows/coverage.yml" 22 | workflow_dispatch: 23 | inputs: 24 | logLevel: 25 | description: "Log level" 26 | required: true 27 | default: "warning" 28 | 29 | # 保证权限合适 30 | permissions: 31 | contents: read 32 | id-token: write 33 | 34 | jobs: 35 | coverage: 36 | runs-on: "ubuntu-latest" 37 | name: "code coverage" 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | with: 42 | submodules: true 43 | # 设置GCC版本 44 | - name: Set up GCC 45 | uses: egor-tensin/setup-gcc@v1 46 | with: 47 | version: latest 48 | # 构建 (code coverage) 49 | - name: Make coverage 50 | run: | 51 | make coverage 52 | env: 53 | MTFMT_BUILD_USE_LTO: "1" 54 | MTFMT_BUILD_COVERAGE: "1" 55 | MTFMT_BUILD_C_DEFS: >- 56 | -D_MSTR_USE_MALLOC=0 57 | -D_MSTR_USE_HARDWARE_DIV=0 58 | -D_MSTR_USE_UTF_8=1 59 | -D_MSTR_USE_FP_FLOAT32=1 60 | -D_MSTR_USE_FP_FLOAT64=1 61 | # 覆盖率报告 62 | - name: Code coverage report 63 | uses: threeal/gcovr-action@latest 64 | with: 65 | root: '.' 66 | coveralls-send: true 67 | github-token: ${{ secrets.COVERALLS_REPO_TOKEN }} 68 | exclude: thirds/* 69 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | paths: 6 | - "inc/**.h" 7 | - "inc/**.hpp" 8 | - "src/**.c" 9 | - "src/**.cpp" 10 | - "tests/**.c" 11 | - "tests/**.cpp" 12 | - "Makefile" 13 | - "thirds/unity" 14 | - ".github/workflows/tests.yml" 15 | pull_request: 16 | branches: [master] 17 | paths: 18 | - "inc/**.h" 19 | - "inc/**.hpp" 20 | - "src/**.c" 21 | - "src/**.cpp" 22 | - "tests/**.c" 23 | - "tests/**.cpp" 24 | - "thirds/unity" 25 | - "Makefile" 26 | - ".github/workflows/tests.yml" 27 | workflow_dispatch: 28 | inputs: 29 | logLevel: 30 | description: "Log level" 31 | required: true 32 | default: "warning" 33 | 34 | jobs: 35 | tests-pchost: 36 | strategy: 37 | fail-fast: true 38 | matrix: 39 | platform: [x64] 40 | buildin-div: ["0", "1"] 41 | utf-8-support: ["0", "1"] 42 | runs-on: ubuntu-latest 43 | name: >- 44 | Tests ${{ matrix.platform }} 45 | div: ${{ matrix.buildin-div }} 46 | utf-8: ${{ matrix.utf-8-support }} 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v4 50 | with: 51 | submodules: true 52 | # 设置GCC版本 53 | - name: Set up GCC 54 | uses: egor-tensin/setup-gcc@v1 55 | with: 56 | version: latest 57 | platform: "${{ matrix.platform }}" 58 | # 进行测试 59 | - name: Test 60 | run: | 61 | make test 62 | env: 63 | MTFMT_BUILD_USE_LTO: "1" 64 | MTFMT_BUILD_C_DEFS: >- 65 | -D_MSTR_USE_MALLOC=0 66 | -D_MSTR_USE_HARDWARE_DIV=${{ matrix.buildin-div }} 67 | -D_MSTR_USE_UTF_8=${{ matrix.utf-8-support }} 68 | -D_MSTR_USE_FP_FLOAT32=1 69 | -D_MSTR_USE_FP_FLOAT64=1 70 | -------------------------------------------------------------------------------- /tests/test_string_append.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_append.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串追加字符 6 | * @version 1.0 7 | * @date 2023-06-21 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | 18 | template 19 | constexpr mtfmt::unicode_t unicode_char(const char (&u8char)[N]) 20 | { 21 | return mtfmt::string::unicode_char(u8char); 22 | } 23 | 24 | extern "C" void string_append(void) 25 | { 26 | // @mstr_append 27 | mtfmt::string str = u8"Smile"; 28 | ASSERT_EQUAL_VALUE(str.length(), 5); 29 | ASSERT_EQUAL_VALUE(str.byte_count(), 5); 30 | str += ':'; 31 | ASSERT_EQUAL_VALUE(str.length(), 6); 32 | ASSERT_EQUAL_VALUE(str.byte_count(), 6); 33 | ASSERT_EQUAL_VALUE(str, u8"Smile:"); 34 | #if _MSTR_USE_UTF_8 35 | constexpr auto ch = unicode_char(u8"😀"); 36 | str += ch; 37 | ASSERT_EQUAL_VALUE(str.length(), 7); 38 | ASSERT_EQUAL_VALUE(str.byte_count(), 10); 39 | ASSERT_EQUAL_VALUE(str, u8"Smile:😀"); 40 | #endif // _MSTR_USE_UTF_8 41 | } 42 | 43 | extern "C" void string_repeat_append(void) 44 | { 45 | // @mstr_repeat_append 46 | mtfmt::string str = u8"Smile"; 47 | ASSERT_EQUAL_VALUE(str.length(), 5); 48 | ASSERT_EQUAL_VALUE(str.byte_count(), 5); 49 | str += mtfmt::string::repeat_char_t{':', 3}; 50 | ASSERT_EQUAL_VALUE(str, u8"Smile:::"); 51 | ASSERT_EQUAL_VALUE(str.length(), 8); 52 | ASSERT_EQUAL_VALUE(str.byte_count(), 8); 53 | #if _MSTR_USE_UTF_8 54 | constexpr auto ch = unicode_char(u8"😀"); 55 | str += mtfmt::string::repeat_char_t{ch, 3}; 56 | ASSERT_EQUAL_VALUE(str.length(), 11); 57 | ASSERT_EQUAL_VALUE(str.byte_count(), 20); 58 | ASSERT_EQUAL_VALUE(str, u8"Smile:::😀😀😀"); 59 | #endif // _MSTR_USE_UTF_8 60 | } 61 | -------------------------------------------------------------------------------- /tests/test_into_fix.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_into_fix.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 定点数格式化的测试 6 | * @version 1.0 7 | * @date 2023-06-30 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | extern "C" void qtoa_signed(void) 20 | { 21 | // @mstr_fmt_iqtoa 22 | mtfmt::string str = u8""; 23 | str.append_from(mtfmt::fixed_value(1), 2); 24 | ASSERT_EQUAL_VALUE(str, u8"0.25"); 25 | // 负数 26 | // sign的处理和整数是同一个部分, 理论上可以不管它啦 27 | mtfmt::string str_neg = u8""; 28 | str_neg.append_from(mtfmt::fixed_value(-1), 2); 29 | ASSERT_EQUAL_VALUE(str_neg, u8"-0.25"); 30 | // 零 31 | mtfmt::string str_zero = u8""; 32 | str_zero.append_from(mtfmt::fixed_value(0), 2); 33 | ASSERT_EQUAL_VALUE(str_zero, u8"0"); 34 | } 35 | 36 | extern "C" void qtoa_signed_from(void) 37 | { 38 | // @mstr_fmt_iqtoa 39 | // 这个函数的所有功能在其它地方测试过啦 40 | // 所以简单确认下wrapper是否正确 41 | mtfmt::string str = 42 | mtfmt::string::from(mtfmt::fixed_value(1), 2).or_value("error"); 43 | ASSERT_EQUAL_VALUE(str, u8"0.25"); 44 | } 45 | 46 | extern "C" void qtoa_unsigned(void) 47 | { 48 | // @mstr_fmt_uqtoa 49 | mtfmt::string str = u8""; 50 | str.append_from(mtfmt::fixed_value(1u), 2); 51 | ASSERT_EQUAL_VALUE(str, u8"0.25"); 52 | // 零 53 | mtfmt::string str_zero = u8""; 54 | str_zero.append_from(mtfmt::fixed_value(0u), 2); 55 | ASSERT_EQUAL_VALUE(str_zero, u8"0"); 56 | } 57 | 58 | extern "C" void qtoa_unsigned_from(void) 59 | { 60 | // @mstr_fmt_uqtoa 61 | // 这个函数的所有功能在其它地方测试过啦 62 | // 所以简单确认下wrapper是否正确 63 | mtfmt::string str = mtfmt::string::from(mtfmt::fixed_value(1u), 2) 64 | .or_value("error"); 65 | ASSERT_EQUAL_VALUE(str, u8"0.25"); 66 | } 67 | -------------------------------------------------------------------------------- /support/WebAssembly.mk: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: LGPL-3.0 3 | # Copyright (c) 向阳, all rights reserved. 4 | # 5 | 6 | TARGET = mtfmt 7 | 8 | # 编译时显示的内容 9 | CC_DISPLAY = CC: 10 | 11 | # 链接时显示的内容 12 | LD_DISPLAY = Linker output : 13 | 14 | # 打包时显示的内容 15 | AR_DISPLAY = AR output : 16 | 17 | # Table generator 18 | TBGEN_DISPLAY = TB: 19 | 20 | # Build path 21 | BUILD_DIR = build 22 | 23 | # 构建输出 24 | OUTPUT_DIR = target 25 | 26 | # 导出函数的文件 27 | EXPORT_FILE = support/WebAssemblyExport.mk 28 | 29 | # self 30 | THIS_FILE = support/WebAssembly.mk 31 | 32 | # C sources 33 | C_SOURCES = \ 34 | $(wildcard src/*.c) \ 35 | $(wildcard src/**/*.c) 36 | 37 | # 需要导出的函数 38 | include $(EXPORT_FILE) 39 | C_EXPORT_FUNCS = "EXPORTED_FUNCTIONS=[$(EXPORTS)]" 40 | 41 | # 编译器 42 | CC = emcc 43 | AR = emar 44 | 45 | # C defines 46 | C_DEFS = -D_MSTR_USE_MALLOC=1 47 | 48 | # C includes 49 | C_INCLUDES = \ 50 | -Iinc 51 | 52 | # Standard 53 | C_STANDARD = --std=c11 54 | 55 | ifeq ($(DEBUG), 1) 56 | CFLAGS = $(ARCH) $(C_STANDARD) $(C_DEFS) $(C_INCLUDES) $(OPT) -D_DEBUG -Wall 57 | else 58 | CFLAGS = $(ARCH) $(C_STANDARD) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall 59 | endif 60 | 61 | # 依赖文件 62 | CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" 63 | 64 | # build all 65 | all: $(OUTPUT_DIR)/$(TARGET).wasm 66 | @echo build completed. 67 | 68 | # list of objects 69 | OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) 70 | vpath %.c $(sort $(dir $(C_SOURCES))) 71 | 72 | $(BUILD_DIR)/%.o: %.c $(THIS_FILE) | $(BUILD_DIR) 73 | @echo $(CC_DISPLAY) $< 74 | @$(CC) -c $(CFLAGS) $< -o $@ 75 | 76 | $(OUTPUT_DIR)/$(TARGET).a: $(OBJECTS) $(THIS_FILE) | $(OUTPUT_DIR) 77 | @echo $(AR_DISPLAY) $@ 78 | @$(AR) rcs $@ $(OBJECTS) 79 | 80 | $(OUTPUT_DIR)/$(TARGET).wasm: $(OUTPUT_DIR)/$(TARGET).a 81 | @echo $(LD_DISPLAY) $@ 82 | @$(CC) $< -o $@ --no-entry -s STANDALONE_WASM -s WASM=1 -s $(C_EXPORT_FUNCS) 83 | 84 | $(BUILD_DIR): 85 | mkdir $@ 86 | 87 | $(OUTPUT_DIR): 88 | mkdir $@ 89 | 90 | # 依赖 91 | -include $(wildcard $(BUILD_DIR)/*.d) 92 | -------------------------------------------------------------------------------- /.github/workflows/doxygen.yml: -------------------------------------------------------------------------------- 1 | name: Doxygen 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "inc/**.hpp" 10 | - "support/Doxygen" 11 | - ".github/workflows/doxygen.yml" 12 | pull_request: 13 | branches: [master] 14 | paths: 15 | - "src/**.c" 16 | - "inc/**.h" 17 | - "inc/**.hpp" 18 | - "support/Doxygen" 19 | - ".github/workflows/doxygen.yml" 20 | workflow_dispatch: 21 | inputs: 22 | logLevel: 23 | description: "Log level" 24 | required: true 25 | default: "warning" 26 | 27 | # 保证权限合适 28 | permissions: 29 | contents: read 30 | pages: write 31 | id-token: write 32 | 33 | # 避免跳过部署 34 | concurrency: 35 | group: "pages" 36 | cancel-in-progress: false 37 | 38 | jobs: 39 | # Build github pages 40 | build: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | # Doxygen 样式 46 | - name: Doxygen scss compile 47 | uses: gha-utilities/sass-build@v0.5.1 48 | with: 49 | source: "./support/themes/doxygen/css/style.scss" 50 | destination: "./support/themes/doxygen/html/style.css" 51 | includePaths: "./support/themes/doxygen/:./support/themes/common/" 52 | outputStyle: compressed 53 | # Doxygen 54 | - name: Doxygen 55 | uses: mattnotmitt/doxygen-action@v1.9.8 56 | with: 57 | working-directory: "./" 58 | doxyfile-path: "support/Doxyfile" 59 | # 打包 60 | - name: Upload artifact 61 | uses: actions/upload-pages-artifact@v3 62 | 63 | # 进行部署 (仅master branch) 64 | deploy: 65 | if: github.ref == 'refs/heads/master' 66 | environment: 67 | name: github-pages 68 | url: ${{ steps.deployment.outputs.page_url }} 69 | runs-on: ubuntu-latest 70 | needs: [build] 71 | steps: 72 | - name: Deploy to GitHub Pages 73 | id: deployment 74 | uses: actions/deploy-pages@v4 75 | -------------------------------------------------------------------------------- /inc/mm_type.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_type.h 4 | * @author HalfSweet (HalfSweet@HalfSweet.cn), 5 | * 向阳 (hinatahoshino@foxmail.com) 6 | * @brief type alias和type def 7 | * @version 0.1 8 | * @date 2023-05-06 9 | * 10 | * @copyright Copyright (C) 2023 HalfSweet, 向阳, all rights reserved. 11 | * 12 | */ 13 | 14 | #if !defined(__INCLUDE_MM_TYPE_H__) 15 | #define __INCLUDE_MM_TYPE_H__ 1 16 | #include "mm_cfg.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /** 23 | * @brief 字节(无符号) 24 | * 25 | */ 26 | typedef uint8_t byte_t; 27 | 28 | /** 29 | * @brief 尺寸(无符号) 30 | * 31 | */ 32 | typedef size_t usize_t, uptr_t; 33 | 34 | /** 35 | * @brief 尺寸(有符号, sizeof(isize_t) == sizeof(usize_t) == 36 | * sizeof(iptr_t)) 37 | * 38 | */ 39 | typedef intptr_t isize_t, iptr_t; 40 | 41 | /** 42 | * @brief 32位浮点值 43 | * 44 | */ 45 | typedef float float32_t; 46 | 47 | /** 48 | * @brief 64位浮点值 49 | * 50 | */ 51 | typedef double float64_t; 52 | 53 | /** 54 | * @brief 布尔值 55 | * 56 | */ 57 | typedef bool mstr_bool_t; 58 | enum 59 | { 60 | True = true, 61 | False = false, 62 | }; 63 | 64 | /** 65 | * @brief 字符 66 | * 67 | */ 68 | typedef char mstr_char_t; 69 | 70 | /** 71 | * @brief unicode代码点 72 | * 73 | */ 74 | typedef uint32_t mstr_codepoint_t; 75 | 76 | /** 77 | * @brief RTC时间 78 | * 79 | */ 80 | typedef struct tagMStrTime 81 | { 82 | //! (BCD) 年份 83 | uint16_t year; 84 | 85 | //! (BCD) 月 86 | uint8_t month; 87 | 88 | //! (BCD) 日 89 | uint8_t day; 90 | 91 | //! (BCD) 时 92 | uint8_t hour; 93 | 94 | //! (BCD) 分 95 | uint8_t minute; 96 | 97 | //! (BCD) 秒 98 | uint8_t second; 99 | 100 | //! (BCD) 星期 101 | uint8_t week; 102 | 103 | //! (BCD) 亚秒值, 单位: x0.1ms 104 | uint32_t sub_second; 105 | } MStrTime; 106 | #endif // __INCLUDE_MM_TYPE_H__ 107 | -------------------------------------------------------------------------------- /tests/test_fmt_type_int_val.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_type_int_val.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 格式化的整数值类型测试 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | void fmt_integer_i8(void) 20 | { 21 | MString s; 22 | EVAL(mstr_create_empty(&s)); 23 | EVAL(mstr_format( 24 | &s, "@{0:i8}#{1:i8}@", 2, (int8_t)(123), (int8_t)(0xff) 25 | )); 26 | ASSERT_EQUAL_STRING(&s, "@123#-1@"); 27 | mstr_free(&s); 28 | } 29 | 30 | void fmt_integer_i16(void) 31 | { 32 | MString s; 33 | EVAL(mstr_create_empty(&s)); 34 | EVAL(mstr_format( 35 | &s, "@{0:i16}#{1:i16}@", 2, (int16_t)(123), (int16_t)(0xffff) 36 | )); 37 | ASSERT_EQUAL_STRING(&s, "@123#-1@"); 38 | mstr_free(&s); 39 | } 40 | 41 | void fmt_integer_i32(void) 42 | { 43 | MString s; 44 | EVAL(mstr_create_empty(&s)); 45 | EVAL(mstr_format( 46 | &s, 47 | "@{0:i32}#{1:i32}@", 48 | 2, 49 | (int32_t)(123), 50 | (int32_t)(0xffffffff) 51 | )); 52 | ASSERT_EQUAL_STRING(&s, "@123#-1@"); 53 | mstr_free(&s); 54 | } 55 | 56 | void fmt_integer_u8(void) 57 | { 58 | MString s; 59 | EVAL(mstr_create_empty(&s)); 60 | EVAL(mstr_format(&s, "@{0:u8}@", 2, (uint8_t)(0xff))); 61 | ASSERT_EQUAL_STRING(&s, "@255@"); 62 | mstr_free(&s); 63 | } 64 | 65 | void fmt_integer_u16(void) 66 | { 67 | MString s; 68 | EVAL(mstr_create_empty(&s)); 69 | EVAL(mstr_format(&s, "@{0:u16}@", 2, (uint16_t)(0xffff))); 70 | ASSERT_EQUAL_STRING(&s, "@65535@"); 71 | mstr_free(&s); 72 | } 73 | 74 | void fmt_integer_u32(void) 75 | { 76 | MString s; 77 | EVAL(mstr_create_empty(&s)); 78 | EVAL(mstr_format(&s, "@{0:u32}@", 2, (uint32_t)(0xffffffff))); 79 | ASSERT_EQUAL_STRING(&s, "@4294967295@"); 80 | mstr_free(&s); 81 | } 82 | -------------------------------------------------------------------------------- /.github/workflows/cmake-build.yml: -------------------------------------------------------------------------------- 1 | name: "CMake build" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "CMakeLists.txt" 10 | - ".github/workflows/cmake-build.yml" 11 | pull_request: 12 | branches: [master] 13 | paths: 14 | - "src/**.c" 15 | - "inc/**.h" 16 | - "CMakeLists.txt" 17 | - ".github/workflows/cmake-build.yml" 18 | workflow_dispatch: 19 | inputs: 20 | logLevel: 21 | description: "Log level" 22 | required: true 23 | default: "warning" 24 | 25 | jobs: 26 | cmake-build-linux: 27 | runs-on: ubuntu-latest 28 | name: cmake build on ubuntu-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v4 32 | with: 33 | submodules: true 34 | - name: cmake 35 | run: | 36 | mkdir build 37 | cd build 38 | cmake .. -DMTFMT_RT_USE_MALLOC=ON -DMTFMT_RT_USE_DIV=ON -DMTFMT_RT_USE_STDOUT=ON -DMTFMT_BUILD_SHARED=ON 39 | cmake --build . 40 | # 打包动态库 41 | - name: Upload artifact 42 | if: github.ref == 'refs/heads/master' 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: cmake-linux-artifact 46 | path: | 47 | ./build/*.so 48 | 49 | cmake-build-windows: 50 | runs-on: windows-latest 51 | name: cmake build on windows-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | with: 56 | submodules: true 57 | - name: cmake 58 | run: | 59 | mkdir build 60 | cd build 61 | cmake .. -DMTFMT_RT_USE_MALLOC=ON -DMTFMT_RT_USE_DIV=ON -DMTFMT_RT_USE_STDOUT=ON -DMTFMT_BUILD_SHARED=ON 62 | cmake --build . --config=Release 63 | shell: pwsh 64 | # 打包动态库 65 | - name: Upload artifact 66 | if: github.ref == 'refs/heads/master' 67 | uses: actions/upload-artifact@v4 68 | with: 69 | name: cmake-windows-artifact 70 | path: | 71 | ./build/target/* 72 | -------------------------------------------------------------------------------- /tests/test_string_equal.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_equal.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串相等, 包括equal, start_with, end_with 6 | * @version 1.0 7 | * @date 2023-06-23 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | 18 | extern "C" void string_equal(void) 19 | { 20 | mtfmt::string str1 = "String"; 21 | mtfmt::string str2 = str1; 22 | // @mstr_equal 23 | TEST_ASSERT_TRUE(str1 == "String"); 24 | TEST_ASSERT_TRUE(str2 == "String"); 25 | TEST_ASSERT_TRUE("String" == str1); 26 | TEST_ASSERT_TRUE("String" == str2); 27 | TEST_ASSERT_TRUE(str1 == str2); 28 | TEST_ASSERT_TRUE(str2 == str1); 29 | } 30 | 31 | extern "C" void string_not_equal(void) 32 | { 33 | mtfmt::string str1 = "String1"; 34 | mtfmt::string str2 = "String2"; 35 | // @mstr_equal 36 | TEST_ASSERT_TRUE(str2 != str1); 37 | TEST_ASSERT_TRUE(str1 != str2); 38 | TEST_ASSERT_TRUE(str1 != "String2"); 39 | TEST_ASSERT_TRUE(str2 != "String1"); 40 | TEST_ASSERT_TRUE("String2" != str1); 41 | TEST_ASSERT_TRUE("String1" != str2); 42 | } 43 | 44 | extern "C" void string_end_with(void) 45 | { 46 | mtfmt::string src = "Example"; 47 | // @mstr_end_with 48 | ASSERT_EQUAL_VALUE(src, "Example"); 49 | ASSERT_EQUAL_VALUE(src.end_with("ple"), True); 50 | ASSERT_EQUAL_VALUE(src.end_with("Example"), True); 51 | ASSERT_EQUAL_VALUE(src.end_with("Exa"), False); 52 | ASSERT_EQUAL_VALUE(src.end_with("ple!"), False); 53 | ASSERT_EQUAL_VALUE(src.end_with("Example!"), False); 54 | } 55 | 56 | extern "C" void string_start_with(void) 57 | { 58 | mtfmt::string src = "Example"; 59 | // @mstr_start_with 60 | ASSERT_EQUAL_VALUE(src, "Example"); 61 | ASSERT_EQUAL_VALUE(src.start_with("Exa"), True); 62 | ASSERT_EQUAL_VALUE(src.start_with("Example"), True); 63 | ASSERT_EQUAL_VALUE(src.start_with("xample"), False); 64 | ASSERT_EQUAL_VALUE(src.start_with("Example!"), False); 65 | } 66 | -------------------------------------------------------------------------------- /support/themes/doxygen/html/header.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | $projectname: $title 11 | 12 | 13 | $title 14 | 15 | $treeview 16 | $search 17 | $mathjax 18 | 19 | 20 | 21 | $extrastylesheet 22 | 23 | 24 | 25 |
26 | 27 | 28 |
29 | 30 | 33 | 34 | 35 |
36 |
$projectname 37 | $projectnumber 38 | 39 |
40 | 41 |
$projectbrief
42 | 43 |
44 | 45 | 46 | 47 |
48 |
$projectbrief
49 |
50 | 51 | 52 | 53 | 54 |
>$searchbox
55 | 56 | 57 |
58 | 59 | -------------------------------------------------------------------------------- /tests/test_fmt_type_int_arr.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_type_int_arr.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 格式化的整数数组的类型测试 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 20 | 21 | void fmt_integer_array_i8(void) 22 | { 23 | MString s; 24 | const int8_t arr[] = {123, (int8_t)(0xff)}; 25 | EVAL(mstr_create_empty(&s)); 26 | EVAL(mstr_format(&s, "@{[0:i8]}@", 2, arr, ARRAY_SIZE(arr))); 27 | ASSERT_EQUAL_STRING(&s, "@123, -1@"); 28 | mstr_free(&s); 29 | } 30 | 31 | void fmt_integer_array_i16(void) 32 | { 33 | MString s; 34 | const int16_t arr[] = {123, (int16_t)(0xffff)}; 35 | EVAL(mstr_create_empty(&s)); 36 | EVAL(mstr_format(&s, "@{[0:i16]}@", 2, arr, ARRAY_SIZE(arr))); 37 | ASSERT_EQUAL_STRING(&s, "@123, -1@"); 38 | mstr_free(&s); 39 | } 40 | 41 | void fmt_integer_array_i32(void) 42 | { 43 | MString s; 44 | const int32_t arr[] = {123, (int32_t)(0xffffffff)}; 45 | EVAL(mstr_create_empty(&s)); 46 | EVAL(mstr_format(&s, "@{[0:i32]}@", 2, arr, ARRAY_SIZE(arr))); 47 | ASSERT_EQUAL_STRING(&s, "@123, -1@"); 48 | mstr_free(&s); 49 | } 50 | 51 | void fmt_integer_array_u8(void) 52 | { 53 | MString s; 54 | const uint8_t arr[] = {123, 0xff}; 55 | EVAL(mstr_create_empty(&s)); 56 | EVAL(mstr_format(&s, "@{[0:u8]}@", 2, arr, ARRAY_SIZE(arr))); 57 | ASSERT_EQUAL_STRING(&s, "@123, 255@"); 58 | mstr_free(&s); 59 | } 60 | 61 | void fmt_integer_array_u16(void) 62 | { 63 | MString s; 64 | const uint16_t arr[] = {123, 0xffff}; 65 | EVAL(mstr_create_empty(&s)); 66 | EVAL(mstr_format(&s, "@{[0:u16]}@", 2, arr, ARRAY_SIZE(arr))); 67 | ASSERT_EQUAL_STRING(&s, "@123, 65535@"); 68 | mstr_free(&s); 69 | } 70 | 71 | void fmt_integer_array_u32(void) 72 | { 73 | MString s; 74 | const uint32_t arr[] = {123, 0xffffffff}; 75 | EVAL(mstr_create_empty(&s)); 76 | EVAL(mstr_format(&s, "@{[0:u32]}@", 2, arr, ARRAY_SIZE(arr))); 77 | ASSERT_EQUAL_STRING(&s, "@123, 4294967295@"); 78 | mstr_free(&s); 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/releases.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - "*" 5 | workflow_dispatch: 6 | inputs: 7 | logLevel: 8 | description: "Log level" 9 | required: true 10 | default: "warning" 11 | 12 | permissions: 13 | contents: write 14 | 15 | name: Release it 16 | jobs: 17 | release-to-github: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | # 源代码 25 | - name: Archive project sources 26 | id: archive_project 27 | run: | 28 | FILE_NAME=${GITHUB_REPOSITORY#*/}-${GITHUB_REF##*/} 29 | git archive ${{ github.ref }} -o ${FILE_NAME}.zip 30 | git archive ${{ github.ref }} -o ${FILE_NAME}.tar.gz 31 | echo "::set-output name=file_name::${FILE_NAME}" 32 | - name: Compute digests 33 | id: compute_digests_arch 34 | run: | 35 | echo "$(openssl dgst -sha256 -r ${{ steps.archive_project.outputs.file_name }}.tar.gz)" >>checksum.txt 36 | echo "$(openssl dgst -sha512 -r ${{ steps.archive_project.outputs.file_name }}.tar.gz)" >>checksum.txt 37 | echo "$(openssl dgst -sha256 -r ${{ steps.archive_project.outputs.file_name }}.zip)" >>checksum.txt 38 | echo "$(openssl dgst -sha512 -r ${{ steps.archive_project.outputs.file_name }}.zip)" >>checksum.txt 39 | # keil包 40 | # TODO 下面的内容都只是单纯的复制了其它的workflows, 把它们改成actions 41 | - name: Install dependencies 42 | run: | 43 | python -m pip install --upgrade pip 44 | pip install flake8 pytest 45 | if [ -f zipack/requirements.txt ]; then pip install -r zipack/requirements.txt; fi 46 | - name: Generate CMSIS pack 47 | run: | 48 | python zipack --project=./support/project.toml cmsis-pack 49 | - name: Compute digests for CMSIS pack 50 | id: compute_digests_cmsis 51 | run: | 52 | echo "$(openssl dgst -sha256 -r target_package/*.pack)" >>checksum.txt 53 | echo "$(openssl dgst -sha512 -r target_package/*.pack)" >>checksum.txt 54 | # 上传发布内容 55 | - name: Create Release 56 | id: create_release 57 | uses: ncipollo/release-action@v1 58 | with: 59 | artifacts: >- 60 | ./${{ steps.archive_project.outputs.file_name }}.zip, 61 | ./${{ steps.archive_project.outputs.file_name }}.tar.gz, 62 | ./target_package/*.pack, 63 | ./checksum.txt 64 | allowUpdates: true 65 | body: | 66 | Automated release from `releases.yml` -------------------------------------------------------------------------------- /src/mm_io.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_io.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief IO 6 | * @version 1.0 7 | * @date 2023-06-07 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | 13 | #define MSTR_IMP_SOURCES 1 14 | 15 | #include "mm_io.h" 16 | #include "mm_cfg.h" 17 | #include "mm_fmt.h" 18 | #include "mm_string.h" 19 | 20 | // 21 | // private: 22 | // 23 | 24 | static mstr_result_t stdio_callback(void*, const byte_t*, usize_t); 25 | 26 | /** 27 | * @brief stdout IO 28 | * 29 | */ 30 | static MStrIOCallback mstr_stdout = { 31 | .capture = NULL, 32 | .io_write = stdio_callback, 33 | }; 34 | 35 | // 36 | // public: 37 | // 38 | 39 | MSTR_EXPORT_API(mstr_result_t) 40 | mstr_io_init(void* context, MStrIOCallback* obj, MStrIOWrite cb_write) 41 | { 42 | obj->capture = context; 43 | obj->io_write = cb_write; 44 | return MStr_Ok; 45 | } 46 | 47 | MSTR_EXPORT_API(MStrIOCallback*) mstr_get_stdout(void) 48 | { 49 | return &mstr_stdout; 50 | } 51 | 52 | MSTR_EXPORT_API(mstr_result_t) 53 | mstr_ioformat( 54 | MStrIOCallback* io, const char* fmt, usize_t fmt_place, ... 55 | ) 56 | { 57 | va_list ap; 58 | mstr_result_t res; 59 | va_start(ap, fmt_place); 60 | res = mstr_iovformat(io, fmt, fmt_place, &ap); 61 | va_end(ap); 62 | return res; 63 | } 64 | 65 | MSTR_EXPORT_API(mstr_result_t) 66 | mstr_iovformat( 67 | MStrIOCallback* io, 68 | const char* fmt, 69 | usize_t fmt_place, 70 | va_list* ap_ptr 71 | ) 72 | { 73 | MString buff; 74 | mstr_result_t res_create; 75 | mstr_result_t res = MStr_Ok; 76 | MSTR_AND_THEN(res, mstr_create_empty(&buff)); 77 | res_create = res; 78 | // 进行格式化 79 | MSTR_AND_THEN(res, mstr_vformat(fmt, &buff, fmt_place, ap_ptr)); 80 | // 写到输出 81 | MSTR_AND_THEN( 82 | res, 83 | io->io_write(io->capture, (const byte_t*)buff.buff, buff.count) 84 | ); 85 | // 释放 86 | if (MSTR_SUCC(res_create)) { 87 | mstr_free(&buff); 88 | } 89 | return res; 90 | } 91 | 92 | static mstr_result_t stdio_callback( 93 | void* ctx, const byte_t* data, usize_t len 94 | ) 95 | { 96 | #if _MSTR_USE_STD_IO 97 | usize_t nu = sizeof(usize_t); 98 | usize_t ncnt = len / nu; 99 | usize_t nrem = len % nu; 100 | fwrite(data, nu, ncnt, stdout); 101 | fwrite(data + ncnt * nu, 1, nrem, stdout); 102 | return MStr_Ok; 103 | #endif 104 | (void)ctx; 105 | (void)data; 106 | (void)len; 107 | return MStr_Err_NoImplemention; 108 | } 109 | -------------------------------------------------------------------------------- /tests/test_fmt_align.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_align.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同格式化对齐方式下的测试 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mm_fmt.h" 13 | #include "mm_heap.h" 14 | #include "test_helper.h" 15 | #include "test_main.h" 16 | #include "unity.h" 17 | #include 18 | #include 19 | 20 | void fmt_align_left(void) 21 | { 22 | MString s; 23 | EVAL(mstr_create_empty(&s)); 24 | EVAL(mstr_format(&s, "@{0:i32:<8}@", 1, 123)); 25 | ASSERT_EQUAL_STRING(&s, "@123 @"); 26 | mstr_free(&s); 27 | } 28 | 29 | void fmt_align_left_fill(void) 30 | { 31 | MString s; 32 | EVAL(mstr_create_empty(&s)); 33 | EVAL(mstr_format(&s, "@{0:i32:#<8}@", 1, 123)); 34 | ASSERT_EQUAL_STRING(&s, "@123#####@"); 35 | mstr_free(&s); 36 | } 37 | 38 | void fmt_align_left_long(void) 39 | { 40 | MString s; 41 | EVAL(mstr_create_empty(&s)); 42 | EVAL(mstr_format(&s, "@{0:i32:#<8}@", 1, (int32_t)123456789l)); 43 | ASSERT_EQUAL_STRING(&s, "@123456789@"); 44 | mstr_free(&s); 45 | } 46 | 47 | void fmt_align_right(void) 48 | { 49 | MString s; 50 | EVAL(mstr_create_empty(&s)); 51 | EVAL(mstr_format(&s, "@{0:i32:>8}@", 1, 123)); 52 | ASSERT_EQUAL_STRING(&s, "@ 123@"); 53 | mstr_free(&s); 54 | } 55 | 56 | void fmt_align_right_fill(void) 57 | { 58 | MString s; 59 | EVAL(mstr_create_empty(&s)); 60 | EVAL(mstr_format(&s, "@{0:i32:#>8}@", 1, 123)); 61 | ASSERT_EQUAL_STRING(&s, "@#####123@"); 62 | mstr_free(&s); 63 | } 64 | 65 | void fmt_align_right_long(void) 66 | { 67 | MString s; 68 | EVAL(mstr_create_empty(&s)); 69 | EVAL(mstr_format(&s, "@{0:i32:#>8}@", 1, (int32_t)123456789l)); 70 | ASSERT_EQUAL_STRING(&s, "@123456789@"); 71 | mstr_free(&s); 72 | } 73 | 74 | void fmt_align_middle(void) 75 | { 76 | MString s; 77 | EVAL(mstr_create_empty(&s)); 78 | EVAL(mstr_format(&s, "@{0:i32:=8}@", 1, 123)); 79 | ASSERT_EQUAL_STRING(&s, "@ 123 @"); 80 | mstr_free(&s); 81 | } 82 | 83 | void fmt_align_middle_fill(void) 84 | { 85 | MString s; 86 | EVAL(mstr_create_empty(&s)); 87 | EVAL(mstr_format(&s, "@{0:i32:#=8}@", 1, 123)); 88 | ASSERT_EQUAL_STRING(&s, "@##123###@"); 89 | mstr_free(&s); 90 | } 91 | 92 | void fmt_align_middle_long(void) 93 | { 94 | MString s; 95 | EVAL(mstr_create_empty(&s)); 96 | EVAL(mstr_format(&s, "@{0:i32:#=8}@", 1, (int32_t)123456789l)); 97 | ASSERT_EQUAL_STRING(&s, "@123456789@"); 98 | mstr_free(&s); 99 | } 100 | -------------------------------------------------------------------------------- /.github/workflows/make-lib.yml: -------------------------------------------------------------------------------- 1 | name: "make lib" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "Makefile" 10 | - ".github/workflows/make-lib.yml" 11 | pull_request: 12 | branches: [master] 13 | paths: 14 | - "src/**.c" 15 | - "inc/**.h" 16 | - "Makefile" 17 | - ".github/workflows/make-lib.yml" 18 | workflow_dispatch: 19 | inputs: 20 | logLevel: 21 | description: "Log level" 22 | required: true 23 | default: "warning" 24 | 25 | jobs: 26 | # Cortex-M0, Cortex-M0+ 27 | build-lib-cortex-m: 28 | strategy: 29 | fail-fast: true 30 | matrix: 31 | target-cpu: ["m0", "m0plus"] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | with: 37 | submodules: true 38 | # 设置GCC版本 39 | - uses: fiam/arm-none-eabi-gcc@v1 40 | with: 41 | release: "10-2020-q4" 42 | # 构建 43 | - name: Make static library at cortex-${{ matrix.target-cpu }} 44 | run: | 45 | make lib 46 | env: 47 | MTFMT_BUILD_GCC_PREFIX: "arm-none-eabi-" 48 | MTFMT_BUILD_ARCH: "-mcpu=cortex-${{ matrix.target-cpu }} -mthumb" 49 | MTFMT_BUILD_TARGET_NAME: "mtfmt_${{ matrix.target-cpu }}" 50 | # 打包 51 | - name: Upload artifact 52 | if: github.ref == 'refs/heads/master' 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: "gcc-lib-${{ matrix.target-cpu }}-without-div" 56 | path: target/* 57 | 58 | # Cortex-M, 带除法指令 59 | build-lib-cortex-m-div: 60 | strategy: 61 | fail-fast: true 62 | matrix: 63 | target-cpu: ["m3", "m4", "m7", "m23", "m33", "m55"] 64 | runs-on: ubuntu-latest 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | with: 69 | submodules: true 70 | # 设置GCC版本 71 | - uses: fiam/arm-none-eabi-gcc@v1 72 | with: 73 | release: "10-2020-q4" 74 | # 构建 75 | - name: Make static library at cortex-${{ matrix.target-cpu }} 76 | run: | 77 | make lib 78 | env: 79 | MTFMT_BUILD_GCC_PREFIX: "arm-none-eabi-" 80 | MTFMT_BUILD_ARCH: "-mcpu=cortex-${{ matrix.target-cpu }} -mthumb" 81 | MTFMT_BUILD_TARGET_NAME: "mtfmt_${{ matrix.target-cpu }}" 82 | MTFMT_BUILD_C_DEFS: "-D_MSTR_USE_HARDWARE_DIV=1" 83 | # 打包 84 | - name: Upload artifact 85 | if: github.ref == 'refs/heads/master' 86 | uses: actions/upload-artifact@v4 87 | with: 88 | name: "gcc-lib-${{ matrix.target-cpu }}" 89 | path: target/* 90 | -------------------------------------------------------------------------------- /tests/test_string_iterator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_iterator.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串迭代器 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | template 20 | constexpr mtfmt::unicode_t unicode_char(const char (&u8char)[N]) 21 | { 22 | return mtfmt::string::unicode_char(u8char); 23 | } 24 | 25 | extern "C" void string_index(void) 26 | { 27 | // ascii 28 | mtfmt::string str_ascii = u8"str"; 29 | ASSERT_EQUAL_VALUE(str_ascii[0], unicode_char(u8"s")); 30 | ASSERT_EQUAL_VALUE(str_ascii[1], unicode_char(u8"t")); 31 | ASSERT_EQUAL_VALUE(str_ascii[2], unicode_char(u8"r")); 32 | // unicode 33 | #if _MSTR_USE_UTF_8 34 | mtfmt::string str_uni = u8"😊😀汉字"; 35 | ASSERT_EQUAL_VALUE(str_uni[0], unicode_char(u8"😊")); 36 | ASSERT_EQUAL_VALUE(str_uni[1], unicode_char(u8"😀")); 37 | ASSERT_EQUAL_VALUE(str_uni[2], unicode_char(u8"汉")); 38 | ASSERT_EQUAL_VALUE(str_uni[3], unicode_char(u8"字")); 39 | #endif // _MSTR_USE_UTF_8 40 | } 41 | 42 | extern "C" void string_const_iterator(void) 43 | { 44 | // ascii 45 | mtfmt::string str_ascii = u8"iterator"; 46 | mtfmt::string str_ascii_output; 47 | for (auto ch : str_ascii) { 48 | str_ascii_output.push(ch); 49 | } 50 | ASSERT_EQUAL_VALUE(str_ascii, str_ascii_output); 51 | // unicode 52 | #if _MSTR_USE_UTF_8 53 | mtfmt::string str_uni = u8"😊😀汉字"; 54 | mtfmt::string str_uni_output; 55 | for (auto ch : str_uni) { 56 | str_uni_output.push(ch); 57 | } 58 | ASSERT_EQUAL_VALUE(str_uni, str_uni_output); 59 | #endif // _MSTR_USE_UTF_8 60 | } 61 | 62 | extern "C" void string_reverse_const_iterator(void) 63 | { 64 | // ascii 65 | mtfmt::string str_ascii = u8"Example"; 66 | mtfmt::string str_ascii_output; 67 | auto it_ascii = str_ascii.rbegin(); 68 | auto it_ascii_end = str_ascii.rend(); 69 | for (; it_ascii != it_ascii_end; ++it_ascii) { 70 | str_ascii_output.push(*it_ascii); 71 | } 72 | str_ascii_output.reverse(); 73 | ASSERT_EQUAL_VALUE(str_ascii, str_ascii_output); 74 | // unicode 75 | #if _MSTR_USE_UTF_8 76 | mtfmt::string str_uni = u8"😊😀汉字"; 77 | mtfmt::string str_uni_output; 78 | auto it = str_uni.rbegin(); 79 | auto it_end = str_uni.rend(); 80 | for (; it != it_end; ++it) { 81 | str_uni_output.push(*it); 82 | } 83 | str_uni_output.reverse(); 84 | ASSERT_EQUAL_VALUE(str_uni, str_uni_output); 85 | #endif // _MSTR_USE_UTF_8 86 | } 87 | -------------------------------------------------------------------------------- /tests/test_helper.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_helper.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 测试相关的helper 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_TEST_HELPER_H_) 13 | #define _INCLUDE_TEST_HELPER_H_ 1 14 | #include "mm_cfg.h" 15 | #include "mm_string.h" 16 | #include "unity.h" 17 | 18 | /** 19 | * @brief 确定两个值相等 20 | * 21 | */ 22 | #define ASSERT_EQUAL_VALUE(val1, val2) \ 23 | do { \ 24 | TEST_ASSERT_TRUE_MESSAGE( \ 25 | (val1) == (val2), #val1 " != " #val2 \ 26 | ); \ 27 | } while (0) 28 | 29 | /** 30 | * @brief 确定一个值里面包括一个位 31 | * 32 | */ 33 | #define ASSERT_ISSET_BIT(val1, val2) \ 34 | do { \ 35 | TEST_ASSERT_TRUE_MESSAGE( \ 36 | ((val1) & (val2)) == (val2), #val1 " not set " #val2 \ 37 | ); \ 38 | } while (0) 39 | 40 | /** 41 | * @brief 确定两个值不相等 42 | * 43 | */ 44 | #define ASSERT_NOTEQUAL_VALUE(val1, val2) \ 45 | do { \ 46 | TEST_ASSERT_TRUE_MESSAGE( \ 47 | (val1) != (val2), #val1 " == " #val2 \ 48 | ); \ 49 | } while (0) 50 | 51 | /** 52 | * @brief 确定2个字符串相等 53 | * 54 | */ 55 | #define ASSERT_EQUAL_STRING(str, target_str) \ 56 | do { \ 57 | MString target; \ 58 | mstr_create(&target, (target_str)); \ 59 | mstr_bool_t res = mstr_equal(str, &target); \ 60 | mstr_free(&target); \ 61 | TEST_ASSERT_TRUE_MESSAGE(res, " str != " target_str); \ 62 | } while (0) 63 | 64 | /** 65 | * @brief 确定2个字符串不相等 66 | * 67 | */ 68 | #define ASSERT_NOT_EQUAL_STRING(str, target_str) \ 69 | do { \ 70 | MString target; \ 71 | mstr_create(&target, (target_str)); \ 72 | mstr_bool_t res = mstr_equal(str, &target); \ 73 | mstr_free(&target); \ 74 | TEST_ASSERT_FALSE(res); \ 75 | } while (0) 76 | 77 | /** 78 | * @brief 对某个表达式求值 79 | * 80 | */ 81 | #define EVAL(expr) \ 82 | TEST_ASSERT_TRUE_MESSAGE(MSTR_SUCC(expr), #expr "!= OK") 83 | 84 | #endif // _INCLUDE_TEST_HELPER_H_ 85 | -------------------------------------------------------------------------------- /tests/test_string_concat.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_concat.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串拼接 6 | * @version 1.0 7 | * @date 2023-06-21 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | 18 | template 19 | constexpr mtfmt::unicode_t unicode_char(const char (&u8char)[N]) 20 | { 21 | return mtfmt::string::unicode_char(u8char); 22 | } 23 | 24 | extern "C" void string_concat_object(void) 25 | { 26 | // @mstr_concat 27 | mtfmt::string str_lhs = u8"Smile:"; 28 | ASSERT_EQUAL_VALUE(str_lhs.length(), 6); 29 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 6); 30 | #if _MSTR_USE_UTF_8 31 | mtfmt::string str_rhs = u8"😀"; 32 | str_lhs += str_rhs; 33 | ASSERT_EQUAL_VALUE(str_lhs.length(), 7); 34 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 10); 35 | ASSERT_EQUAL_VALUE(str_lhs, u8"Smile:😀"); 36 | #else 37 | mtfmt::string str_rhs = u8"Everyday!"; 38 | str_lhs += str_rhs; 39 | ASSERT_EQUAL_VALUE(str_lhs.length(), 15); 40 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 15); 41 | ASSERT_EQUAL_VALUE(str_lhs, u8"Smile:Everyday!"); 42 | #endif // _MSTR_USE_UTF_8 43 | } 44 | 45 | extern "C" void string_concat_c_str(void) 46 | { 47 | mtfmt::string str_lhs = u8"Smile:"; 48 | ASSERT_EQUAL_VALUE(str_lhs.length(), 6); 49 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 6); 50 | str_lhs += u8"@@"; 51 | ASSERT_EQUAL_VALUE(str_lhs.length(), 8); 52 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 8); 53 | #if _MSTR_USE_UTF_8 54 | str_lhs += u8"😀"; 55 | ASSERT_EQUAL_VALUE(str_lhs.length(), 9); 56 | ASSERT_EQUAL_VALUE(str_lhs.byte_count(), 12); 57 | ASSERT_EQUAL_VALUE(str_lhs, u8"Smile:@@😀"); 58 | #endif // _MSTR_USE_UTF_8 59 | } 60 | 61 | extern "C" void string_concat_c_slice(void) 62 | { 63 | MString str_lhs; 64 | EVAL(mstr_create(&str_lhs, u8"Smile:")); 65 | ASSERT_EQUAL_VALUE(str_lhs.count, 6); 66 | ASSERT_EQUAL_VALUE(str_lhs.length, 6); 67 | #if _MSTR_USE_UTF_8 68 | const char c_str[] = {u8"😀😀😀"}; 69 | // 拼接完整的字符串 70 | EVAL(mstr_concat_cstr_slice(&str_lhs, c_str + 0, c_str + 4)); 71 | ASSERT_EQUAL_VALUE(str_lhs.count, 10); 72 | ASSERT_EQUAL_VALUE(str_lhs.length, 7); 73 | ASSERT_EQUAL_STRING(&str_lhs, u8"Smile:😀"); 74 | // 拼接不完整的字符串 75 | mstr_result_t res; 76 | res = mstr_concat_cstr_slice(&str_lhs, c_str + 0, c_str + 3); 77 | ASSERT_EQUAL_VALUE(res, MStr_Err_EncodingNotCompleted); 78 | #else 79 | const char c_str[] = {u8"Everyday"}; 80 | EVAL(mstr_concat_cstr_slice(&str_lhs, c_str + 0, c_str + 4)); 81 | ASSERT_EQUAL_VALUE(str_lhs.count, 10); 82 | ASSERT_EQUAL_VALUE(str_lhs.length, 10); 83 | ASSERT_EQUAL_STRING(&str_lhs, u8"Smile:Ever"); 84 | #endif // _MSTR_USE_UTF_8 85 | mstr_free(&str_lhs); 86 | } 87 | -------------------------------------------------------------------------------- /inc/mm_result.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_result.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 结果类型 6 | * @version 1.0 7 | * @date 2023-04-22 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_RESULT_H_) 13 | #define _INCLUDE_MM_RESULT_H_ 1 14 | 15 | /** 16 | * @brief 结果类型 17 | * 18 | */ 19 | typedef enum tagMStrResult 20 | { 21 | MStr_Ok = 0, 22 | // ERR: 内存错误: 内存分配失败 23 | MStr_Err_HeapTooSmall = -128, 24 | // ERR: 未实现 25 | MStr_Err_NoImplemention, 26 | // ERR: 编码错误 27 | MStr_Err_UnicodeEncodingError, 28 | // ERR: 源字符串不完整 29 | MStr_Err_EncodingNotCompleted, 30 | // ERR: 越界 31 | MStr_Err_IndexOutOfBound, 32 | // ERR: 模式串匹配失败 33 | MStr_Err_NoSubstrFound, 34 | // ERR: 越界(迭代器) 35 | MStr_Err_IteratorOutOfBound, 36 | // ERR: buff太短 37 | MStr_Err_BufferTooSmall, 38 | // ERR: 格式化: 索引太多 39 | MStr_Err_IndexTooLarge, 40 | // ERR: 格式化: Parser错误: 未识别的token 41 | MStr_Err_UnrecognizedToken, 42 | // ERR: 格式化: Parser错误: Missing `{` 43 | MStr_Err_MissingLeftBrace, 44 | // ERR: 格式化: Parser错误: Missing `}` 45 | MStr_Err_MissingRightBrace, 46 | // ERR: 格式化: Parser错误: Missing Formatter 47 | MStr_Err_MissingReplacement, 48 | // ERR: 格式化: Parser错误: Missing ArgID 49 | MStr_Err_MissingArgumentID, 50 | // ERR: 格式化: Parser错误: Missing ArgType 51 | MStr_Err_MissingArgumentType, 52 | // ERR: 格式化: Parser错误: 不支持的fill char 53 | MStr_Err_UnsupportFillChar, 54 | // ERR: 格式化: Parser错误: fill char后面必须要align 55 | MStr_Err_MissingAlignAfterFillChar, 56 | // ERR: 格式化: Parser错误: 不支持的格式化方式 57 | MStr_Err_UnsupportFormatType, 58 | // ERR: 格式化: Parser错误: 请求宽度太大 59 | MStr_Err_WidthTooLarge, 60 | // ERR: 格式化: Parser错误: 请求的格式化项太多(日期时间) 61 | MStr_Err_TooMoreChronoItem, 62 | // ERR: 格式化: Parser错误: 错失格式化的token(日期时间) 63 | MStr_Err_MissingChronoItemType, 64 | // ERR: 格式化: Parser错误: 未指定 65 | MStr_Err_UndefinedParserError, 66 | // ERR: 格式化: 还未使用过的参数ID 67 | MStr_Err_UnusedArgumentID, 68 | // ERR: 格式化: 参数太大 69 | MStr_Err_InvaildArgumentID, 70 | // ERR: 格式化: 类型不正确 71 | MStr_Err_InvaildArgumentType, 72 | // ERR: 格式化: 内建buffer不够大 73 | MStr_Err_InternalBufferTooSmall, 74 | // ERR: 格式化: 不支持的类型 75 | MStr_Err_UnsupportType, 76 | // ERR: 格式化: 不支持的量化精度 77 | MStr_Err_UnsupportQuantBits, 78 | // ERR: 最后一个的flag 79 | MStr_Err_Flag_LastOne, 80 | } mstr_result_t; 81 | 82 | // 判断操作是否成功 83 | #define MSTR_SUCC(s) ((s) == MStr_Ok) 84 | 85 | // 判断操作是否失败 86 | #define MSTR_FAILED(s) ((s) != MStr_Ok) 87 | 88 | // AND_THEN 89 | #define MSTR_AND_THEN(res, then) \ 90 | do { \ 91 | mstr_result_t r = (res); \ 92 | if (MSTR_SUCC(r)) { \ 93 | r = (then); \ 94 | } \ 95 | (res) = r; \ 96 | } while (0) 97 | 98 | #endif // _INCLUDE_MM_RESULT_H_ 99 | -------------------------------------------------------------------------------- /examples/example_build_config.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file example_build_config.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同对齐方式下的格式化的例子 6 | * @version 1.0 7 | * @date 2023-07-23 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include 14 | #include 15 | 16 | /** 17 | * @brief 测试1个位是否被set 18 | * 19 | */ 20 | #define BIT_TEST(v, b) (!!(((v) & (b)) == (b))) 21 | 22 | /** 23 | * @brief 把boolean变成C字符串 24 | * 25 | */ 26 | #define BOOL2STR(b) ((b) ? "true" : "false") 27 | 28 | int main(void) 29 | { 30 | uint32_t cfg = MSTR_CONFIGURE_CFG_VAL(mstr_configure()); 31 | uint32_t compiler = MSTR_CONFIGURE_CC_VAL(cfg); 32 | 33 | puts("+-----------+--------------------------+-------+"); 34 | 35 | const char* compiler_name = "unknown"; 36 | switch (compiler) { 37 | case MSTR_BUILD_CC_MSVC: compiler_name = "msvc"; break; 38 | case MSTR_BUILD_CC_GNUC: compiler_name = "gcc"; break; 39 | case MSTR_BUILD_CC_ARMCC: compiler_name = "armcc"; break; 40 | case MSTR_BUILD_CC_ARMCLANG: compiler_name = "armclang"; break; 41 | case MSTR_BUILD_CC_EMSCRIPTEN: compiler_name = "emscripten"; break; 42 | case MSTR_BUILD_CC_OTHER: compiler_name = "unknown"; break; 43 | } 44 | printf("| Compiler | %-24s | --- |\n", compiler_name); 45 | 46 | puts("+-----------+--------------------------+-------+"); 47 | 48 | printf( 49 | "| Configure | %-24s | %5s |\n", 50 | "_MSTR_USE_MALLOC", 51 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_MALLOC_BIT)) 52 | ); 53 | printf( 54 | "| | %-24s | %5s |\n", 55 | "_MSTR_BUILD_DLL", 56 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_BUILD_DLL_BIT)) 57 | ); 58 | printf( 59 | "| | %-24s | %5s |\n", 60 | "_MSTR_USE_HARDWARE_DIV", 61 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_BUILD_HARDWARE_DIV)) 62 | ); 63 | printf( 64 | "| | %-24s | %5s |\n", 65 | "_MSTR_USE_STD_IO", 66 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_STD_IO)) 67 | ); 68 | printf( 69 | "| | %-24s | %5s |\n", 70 | "_MSTR_USE_UTF_8", 71 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_UTF_8)) 72 | ); 73 | printf( 74 | "| | %-24s | %5s |\n", 75 | "_MSTR_USE_CPP_EXCEPTION", 76 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_CXX_EXCEPTION)) 77 | ); 78 | printf( 79 | "| | %-24s | %5s |\n", 80 | "_MSTR_USE_FP_FLOAT32", 81 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_FLOAT32)) 82 | ); 83 | printf( 84 | "| | %-24s | %5s |\n", 85 | "_MSTR_USE_FP_FLOAT64", 86 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_FLOAT64)) 87 | ); 88 | printf( 89 | "| | %-24s | %5s |\n", 90 | "_MSTR_USE_ALLOC", 91 | BOOL2STR(BIT_TEST(cfg, MSTRCFG_USE_ALLOCATOR)) 92 | ); 93 | 94 | puts("+-----------+--------------------------+-------+"); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_string_replace.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_replace.cpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串替换的测试 6 | * @version 1.0 7 | * @date 2023-06-24 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | template 20 | constexpr mtfmt::unicode_t unicode_char(const char (&u8char)[N]) 21 | { 22 | return mtfmt::string::unicode_char(u8char); 23 | } 24 | 25 | extern "C" void string_retain_all(void) 26 | { 27 | mtfmt::string str_ascii = "Exa#mmp#ml#ee"; 28 | auto result_ascii = str_ascii.retain("#m"); 29 | ASSERT_EQUAL_VALUE(result_ascii.is_succ(), true); 30 | ASSERT_EQUAL_VALUE(str_ascii, "Exampl#ee"); 31 | ASSERT_EQUAL_VALUE(str_ascii.length(), 9); 32 | ASSERT_EQUAL_VALUE(str_ascii.byte_count(), 9); 33 | #if _MSTR_USE_UTF_8 34 | mtfmt::string str_unicode = u8"😀😊😔🍥😊😔😀"; 35 | auto result_unicode = str_unicode.retain(u8"😔"); 36 | ASSERT_EQUAL_VALUE(result_unicode.is_succ(), true); 37 | ASSERT_EQUAL_VALUE(str_unicode, u8"😀😊🍥😊😀"); 38 | ASSERT_EQUAL_VALUE(str_unicode.length(), 5); 39 | ASSERT_EQUAL_VALUE(str_unicode.byte_count(), 20); 40 | #endif // _MSTR_USE_UTF_8 41 | } 42 | 43 | extern "C" void string_retain_endwith(void) 44 | { 45 | mtfmt::string str_ascii = "StartWithEndWith"; 46 | auto result_ascii = 47 | str_ascii.retain("StartWith", MStringReplaceOption_StartWith); 48 | ASSERT_EQUAL_VALUE(result_ascii.is_succ(), true); 49 | ASSERT_EQUAL_VALUE(str_ascii, "EndWith"); 50 | ASSERT_EQUAL_VALUE(str_ascii.length(), 7); 51 | ASSERT_EQUAL_VALUE(str_ascii.byte_count(), 7); 52 | #if _MSTR_USE_UTF_8 53 | mtfmt::string str_unicode = u8"😔🍥😀"; 54 | auto result_unicode = 55 | str_unicode.retain(u8"😔", MStringReplaceOption_StartWith); 56 | ASSERT_EQUAL_VALUE(result_unicode.is_succ(), true); 57 | ASSERT_EQUAL_VALUE(str_unicode, u8"🍥😀"); 58 | ASSERT_EQUAL_VALUE(str_unicode.length(), 2); 59 | ASSERT_EQUAL_VALUE(str_unicode.byte_count(), 8); 60 | #endif // _MSTR_USE_UTF_8 61 | } 62 | 63 | extern "C" void string_retain_startwith(void) 64 | { 65 | mtfmt::string str_ascii = "StartWithEndWith"; 66 | auto result_ascii = 67 | str_ascii.retain("EndWith", MStringReplaceOption_EndWith); 68 | ASSERT_EQUAL_VALUE(result_ascii.is_succ(), true); 69 | ASSERT_EQUAL_VALUE(str_ascii, "StartWith"); 70 | ASSERT_EQUAL_VALUE(str_ascii.length(), 9); 71 | ASSERT_EQUAL_VALUE(str_ascii.byte_count(), 9); 72 | #if _MSTR_USE_UTF_8 73 | mtfmt::string str_unicode = u8"🍥😀😔"; 74 | auto result_unicode = 75 | str_unicode.retain(u8"😔", MStringReplaceOption_EndWith); 76 | ASSERT_EQUAL_VALUE(result_unicode.is_succ(), true); 77 | ASSERT_EQUAL_VALUE(str_unicode, u8"🍥😀"); 78 | ASSERT_EQUAL_VALUE(str_unicode.length(), 2); 79 | ASSERT_EQUAL_VALUE(str_unicode.byte_count(), 8); 80 | #endif // _MSTR_USE_UTF_8 81 | } 82 | -------------------------------------------------------------------------------- /.github/workflows/make-dylib.yml: -------------------------------------------------------------------------------- 1 | name: "make dylib" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "Makefile" 10 | - ".github/workflows/make-dylib.yml" 11 | pull_request: 12 | branches: [master] 13 | paths: 14 | - "src/**.c" 15 | - "inc/**.h" 16 | - "Makefile" 17 | - ".github/workflows/make-dylib.yml" 18 | workflow_dispatch: 19 | inputs: 20 | logLevel: 21 | description: "Log level" 22 | required: true 23 | default: "warning" 24 | 25 | jobs: 26 | build-dylib-linux: 27 | strategy: 28 | fail-fast: true 29 | matrix: 30 | platform: [x86, x64] 31 | os: [ubuntu-latest] 32 | runs-on: "${{ matrix.os }}" 33 | name: "Build dylib: ${{ matrix.os }} / ${{ matrix.platform }}" 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v4 37 | with: 38 | submodules: true 39 | # 设置GCC版本 40 | - name: Set up GCC 41 | uses: egor-tensin/setup-gcc@v1 42 | with: 43 | version: latest 44 | platform: "${{ matrix.platform }}" 45 | # 构建 (linux) 46 | - name: Make dylib at ${{ matrix.os }} / ${{ matrix.platform }} 47 | run: | 48 | make dylib 49 | env: 50 | MTFMT_BUILD_C_DEFS: "-D_MSTR_BUILD_DYLIB=1 -D_MSTR_USE_MALLOC=1 -D_MSTR_USE_HARDWARE_DIV=1" 51 | MTFMT_BUILD_USE_LTO: "1" 52 | MTFMT_BUILD_TARGET_NAME: "mtfmt_${{ matrix.platform }}" 53 | # 打包 54 | - name: Upload artifact 55 | if: github.ref == 'refs/heads/master' 56 | uses: actions/upload-artifact@v4 57 | with: 58 | name: dylib-artifact-${{ matrix.os }}-${{ matrix.platform }} 59 | path: target/* 60 | 61 | build-dylib-win: 62 | strategy: 63 | fail-fast: true 64 | matrix: 65 | platform: [x86, x64] 66 | os: [windows-latest] 67 | runs-on: "${{ matrix.os }}" 68 | name: "Build dll: ${{ matrix.os }} / ${{ matrix.platform }}" 69 | steps: 70 | - name: Checkout 71 | uses: actions/checkout@v4 72 | with: 73 | submodules: true 74 | - name: Install Cygwin 75 | uses: egor-tensin/setup-cygwin@v4 76 | with: 77 | packages: make gcc-g++ 78 | - name: Make dll at ${{ matrix.os }} / ${{ matrix.platform }} 79 | run: | 80 | make dylib 81 | env: 82 | MTFMT_BUILD_C_DEFS: "-D_MSTR_BUILD_DLL=1 -D_MSTR_USE_MALLOC=1 -D_MSTR_USE_HARDWARE_DIV=1 -D_MSTR_USE_STD_IO=1" 83 | MTFMT_BUILD_USE_LTO: "1" 84 | MTFMT_BUILD_INC_ADDITIONAL_OUT: "1" 85 | MTFMT_BUILD_TARGET_NAME: "mtfmt_win_${{ matrix.platform }}" 86 | MTFMT_BUILD_LIB_EXT: ".lib" 87 | MTFMT_BUILD_DYLIB_EXT: ".dll" 88 | # 因为cygwin要1个dll...所以把它copy过去一起放到存档里 89 | - name: Copy cygwin dlls 90 | run: | 91 | copy C:\tools\cygwin\bin\cygwin1.dll target/ 92 | # 打包 93 | - name: Upload artifact 94 | if: github.ref == 'refs/heads/master' 95 | uses: actions/upload-artifact@v4 96 | with: 97 | name: dll-artifact-${{ matrix.os }}-${{ matrix.platform }} 98 | path: target/* 99 | -------------------------------------------------------------------------------- /inc/mm_heap.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_heap.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 堆内存管理 6 | * @version 1.0 7 | * @date 2023-04-22 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_HEAP_H_) 13 | #define _INCLUDE_MM_HEAP_H_ 1 14 | #include "mm_cfg.h" 15 | #include "mm_type.h" 16 | 17 | /** 18 | * @brief 初始化堆分配器 19 | * 20 | * @param[in] heap_memory: 堆内存区 21 | * @param[in] heap_size: 堆内存区的大小 22 | */ 23 | MSTR_EXPORT_API(void) 24 | mstr_heap_init_sym(iptr_t heap_memory, usize_t heap_size); 25 | 26 | /** 27 | * @brief 尝试从堆中分配size大小的内存 28 | * 29 | * @return void*: 分配结果, 如果分配失败返回NULL 30 | */ 31 | MSTR_EXPORT_API(void*) 32 | mstr_heap_allocate_sym(usize_t size, usize_t align); 33 | 34 | /** 35 | * @brief 尝试从堆中重新分配size大小的内存 36 | * 37 | * @param[in] old_ptr: 之前的ptr 38 | * @param[in] new_size: 需要分配的新大小 39 | * @param[in] old_size: 之前的ptr的大小 40 | * 41 | * @return void*: 分配结果, 如果分配失败返回NULL 42 | */ 43 | MSTR_EXPORT_API(void*) 44 | mstr_heap_re_allocate_sym( 45 | void* old_ptr, usize_t new_size, usize_t old_size 46 | ); 47 | 48 | /** 49 | * @brief 释放由 mstr_heap_allocate 分配的内存 50 | * 51 | */ 52 | MSTR_EXPORT_API(void) mstr_heap_free_sym(void* memory); 53 | 54 | /** 55 | * @brief 取得当前的空闲内存大小 56 | * 57 | */ 58 | MSTR_EXPORT_API(usize_t) mstr_heap_get_free_size(void); 59 | 60 | /** 61 | * @brief 取得自运行以来空闲内存最小的值 62 | * 63 | */ 64 | MSTR_EXPORT_API(usize_t) mstr_heap_get_high_water_mark(void); 65 | 66 | /** 67 | * @brief 由memcpy实现realloc 68 | * 69 | * @return void*: 分配结果, 如果分配失败返回NULL 70 | */ 71 | MSTR_EXPORT_API(void*) 72 | mstr_heap_realloc_cpimp_sym( 73 | void* old_ptr, usize_t new_size, usize_t old_size 74 | ); 75 | 76 | /** 77 | * @brief 取得分配器的统计数据 78 | * 79 | * @param[out] alloc_count: 分配次数 80 | * @param[out] free_count: 释放次数 81 | */ 82 | MSTR_EXPORT_API(void) 83 | mstr_heap_get_allocate_count(usize_t* alloc_count, usize_t* free_count); 84 | 85 | #if _MSTR_USE_MALLOC 86 | #define mstr_heap_init(mem, leng) ((void)mem, (void)leng) 87 | 88 | #define mstr_heap_alloc(s) _MSTR_MEM_ALLOC_FUNCTION((s)) 89 | 90 | #if MSTR_MEM_REALLOC_FUNCTION_NOT_AVAL 91 | /** 92 | * @brief 使用memcpy实现一个"realloc" 93 | * 94 | */ 95 | #define mstr_heap_realloc(p, new_s, old_s) \ 96 | mstr_heap_realloc_cpimp_sym((p), (new_s), (old_s)) 97 | #else 98 | /** 99 | * @brief 使用自定义的realloc 100 | * 101 | */ 102 | #define mstr_heap_realloc(p, s, os) _MSTR_MEM_REALLOC_FUNCTION((p), (s)) 103 | #endif // MSTR_MEM_REALLOC_FUNCTION_NOT_AVAL 104 | 105 | #define mstr_heap_free(m) \ 106 | do { \ 107 | _MSTR_MEM_FREE_FUNCTION(m); \ 108 | (m) = NULL; \ 109 | } while (0) 110 | #else 111 | #define mstr_heap_init(mem, leng) \ 112 | do { \ 113 | mstr_heap_init_sym((iptr_t)(mem), (usize_t)(leng)); \ 114 | } while (0) 115 | 116 | #define mstr_heap_alloc(s) (mstr_heap_allocate_sym((s), 4)) 117 | 118 | #define mstr_heap_realloc(p, new_s, old_s) \ 119 | mstr_heap_re_allocate_sym((p), (new_s), (old_s)) 120 | 121 | #define mstr_heap_free(m) \ 122 | do { \ 123 | mstr_heap_free_sym(m); \ 124 | (m) = NULL; \ 125 | } while (0) 126 | 127 | #endif // _MSTR_USE_MALLOC 128 | 129 | #endif // _INCLUDE_MU_HEAP_H_ 130 | -------------------------------------------------------------------------------- /.github/workflows/make-examples.yml: -------------------------------------------------------------------------------- 1 | name: "make examples" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "src/**.c" 8 | - "inc/**.h" 9 | - "inc/**.hpp" 10 | - "examples/**.h" 11 | - "examples/**.c" 12 | - "examples/**.cpp" 13 | - "Makefile" 14 | - ".github/workflows/make-examples.yml" 15 | pull_request: 16 | branches: [master] 17 | paths: 18 | - "src/**.c" 19 | - "inc/**.h" 20 | - "inc/**.hpp" 21 | - "examples/**.h" 22 | - "examples/**.c" 23 | - "examples/**.cpp" 24 | - "Makefile" 25 | - ".github/workflows/make-examples.yml" 26 | workflow_dispatch: 27 | inputs: 28 | logLevel: 29 | description: "Log level" 30 | required: true 31 | default: "warning" 32 | 33 | jobs: 34 | build-examples-linux: 35 | strategy: 36 | fail-fast: true 37 | matrix: 38 | os: [ubuntu-latest] 39 | arch: [x64] 40 | runs-on: "${{ matrix.os }}" 41 | name: "Build examples: ${{ matrix.os }} (${{ matrix.arch }})" 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | with: 46 | submodules: true 47 | # 设置GCC版本 48 | - name: Set up GCC 49 | uses: egor-tensin/setup-gcc@v1 50 | with: 51 | version: latest 52 | platform: "${{ matrix.arch }}" 53 | # 构建 54 | - name: Make examples at ${{ matrix.os }} 55 | run: | 56 | make examples 57 | env: 58 | MTFMT_BUILD_C_DEFS: >- 59 | -D_MSTR_USE_STD_IO=1 60 | -D_MSTR_USE_MALLOC=1 61 | -D_MSTR_USE_HARDWARE_DIV=1 62 | MTFMT_BUILD_USE_LTO: "1" 63 | MTFMT_BUILD_TARGET_NAME: "mtfmt_${{ matrix.os }}_${{ matrix.arch }}" 64 | MTFMT_BUILD_EXEFILE_EXT: ".out" 65 | # 打包 66 | - name: Upload artifact 67 | if: github.ref == 'refs/heads/master' 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: ${{ matrix.os }}_${{ matrix.arch }}-artifact 71 | path: target/*.out 72 | 73 | build-examples-windows: 74 | strategy: 75 | fail-fast: true 76 | matrix: 77 | os: [windows-latest] 78 | arch: [x64] 79 | runs-on: "${{ matrix.os }}" 80 | name: "Build examples: ${{ matrix.os }} (${{ matrix.arch }})" 81 | steps: 82 | - name: Checkout 83 | uses: actions/checkout@v4 84 | with: 85 | submodules: true 86 | - name: Install Cygwin 87 | uses: egor-tensin/setup-cygwin@v4 88 | with: 89 | packages: make gcc-g++ 90 | - name: Make examples at ${{ matrix.os }} 91 | run: | 92 | make examples 93 | env: 94 | MTFMT_BUILD_C_DEFS: >- 95 | -D_MSTR_USE_STD_IO=1 96 | -D_MSTR_USE_MALLOC=1 97 | -D_MSTR_USE_HARDWARE_DIV=1 98 | MTFMT_BUILD_USE_LTO: "1" 99 | MTFMT_BUILD_TARGET_NAME: "mtfmt_${{ matrix.os }}_${{ matrix.arch }}" 100 | MTFMT_BUILD_EXEFILE_EXT: ".exe" 101 | # 因为cygwin要1个dll...所以把它copy过去一起放到存档里 102 | - name: Copy cygwin dlls 103 | run: | 104 | copy C:\tools\cygwin\bin\cygwin1.dll target/ 105 | # 打包 106 | - name: Upload artifact 107 | if: github.ref == 'refs/heads/master' 108 | uses: actions/upload-artifact@v4 109 | with: 110 | name: ${{ matrix.os }}_${{ matrix.arch }}-artifact 111 | path: | 112 | target/*.exe 113 | target/*.dll 114 | -------------------------------------------------------------------------------- /inc/mm_fmt.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_fmt.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 字符串格式化 6 | * @version 1.0 7 | * @date 2023-03-21 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_FMT_H_) 13 | #define _INCLUDE_MM_FMT_H_ 14 | #include "mm_cfg.h" 15 | #include "mm_parser.h" 16 | #include "mm_result.h" 17 | #include "mm_string.h" 18 | #include "mm_type.h" 19 | 20 | /** 21 | * @brief 格式化参数 22 | * 23 | */ 24 | typedef struct tagMStrFmtFormatArgument 25 | { 26 | iptr_t value; 27 | MStrFmtArgType type; 28 | } MStrFmtFormatArgument; 29 | 30 | /** 31 | * @brief 格式化参数的context 32 | * 33 | */ 34 | typedef struct tagMStrFmtArgsContext 35 | { 36 | va_list* p_ap; 37 | usize_t max_place; 38 | MStrFmtFormatArgument cache[MFMT_PLACE_MAX_NUM]; 39 | } MStrFmtArgsContext; 40 | 41 | /** 42 | * @brief 转换整数时采用的进制 43 | * 44 | */ 45 | typedef enum tagMStrFmtIntIndex 46 | { 47 | //! 转换为二进制字符串 48 | MStrFmtIntIndex_Bin, 49 | 50 | //! 转换为八进制字符串 51 | MStrFmtIntIndex_Oct, 52 | 53 | //! 转换为十进制字符串 54 | MStrFmtIntIndex_Dec, 55 | 56 | //! 转换为小写的十六进制值字符串 57 | MStrFmtIntIndex_Hex, 58 | 59 | //! 转换为大写的十六进制值字符串 60 | MStrFmtIntIndex_Hex_UpperCase, 61 | 62 | //! 转换为小写的十六进制值字符串, 带0x前缀 63 | MStrFmtIntIndex_Hex_WithPrefix, 64 | 65 | //! 转换为大写的十六进制值字符串, 带0X前缀 66 | MStrFmtIntIndex_Hex_UpperCase_WithPrefix, 67 | } MStrFmtIntIndex; 68 | 69 | /** 70 | * @brief 格式化字符串 71 | * 72 | * @param[in] fmt: 格式化串 73 | * @param[out] res_str: 格式化结果输出 74 | * @param[in] fmt_place: 预期fmt中使用的参数数目. 75 | * 最大不超过16(MFMT_PLACE_MAX_NUM) 76 | * 77 | * @return minfmt_result_t: 格式化结果 78 | */ 79 | MSTR_EXPORT_API(mstr_result_t) 80 | mstr_format(MString* res_str, const char* fmt, usize_t fmt_place, ...); 81 | 82 | /** 83 | * @brief 格式化字符串 84 | * 85 | * @param[in] fmt: 格式化串 86 | * @param[out] res_str: 格式化结果输出 87 | * @param[in] fmt_place: 预期fmt中使用的参数数目. 88 | * 最大不超过16(MFMT_PLACE_MAX_NUM) 89 | * @param[in] ap_ptr: &ap 90 | * 91 | * @return minfmt_result_t: 格式化结果 92 | */ 93 | MSTR_EXPORT_API(mstr_result_t) 94 | mstr_vformat( 95 | const char* fmt, 96 | MString* res_str, 97 | usize_t fmt_place, 98 | va_list* ap_ptr 99 | ); 100 | 101 | /** 102 | * @brief 按照上下文进行格式化 103 | * 104 | * @param[out] res_str: 格式化结果 105 | * @param[in] fmt: 格式化串 106 | * @param[in] ctx: 格式化context 107 | * 108 | */ 109 | MSTR_EXPORT_API(mstr_result_t) 110 | mstr_context_format( 111 | MString* res_str, const char* fmt, MStrFmtArgsContext* ctx 112 | ); 113 | 114 | /** 115 | * @brief 将有符号整数转换为字符串 116 | * 117 | * @param[out] res_str: 转换结果 118 | * @param[in] value: 需要转换的值 119 | * @param[in] index: Index 120 | * @param[in] sign: 符号的显示方式 121 | * 122 | */ 123 | MSTR_EXPORT_API(mstr_result_t) 124 | mstr_fmt_itoa( 125 | MString* str, 126 | int32_t value, 127 | MStrFmtIntIndex index, 128 | MStrFmtSignDisplay sign 129 | ); 130 | 131 | /** 132 | * @brief 将无符号整数转换为字符串 133 | * 134 | * @param[out] res_str: 转换结果 135 | * @param[in] value: 需要转换的值 136 | * @param[in] index: Index 137 | * 138 | */ 139 | MSTR_EXPORT_API(mstr_result_t) 140 | mstr_fmt_utoa(MString* res_str, uint32_t value, MStrFmtIntIndex index); 141 | 142 | /** 143 | * @brief 将有符号量化值转换为字符串 144 | * 145 | * @param[out] res_str: 转换结果 146 | * @param[in] value: 需要转换的值 147 | * @param[in] quat: 量化精度 148 | * @param[in] sign: 符号的显示方式 149 | * 150 | */ 151 | MSTR_EXPORT_API(mstr_result_t) 152 | mstr_fmt_iqtoa( 153 | MString* res_str, 154 | int32_t value, 155 | uint32_t quat, 156 | MStrFmtSignDisplay sign 157 | ); 158 | 159 | /** 160 | * @brief 将无符号量化值转换为字符串 161 | * 162 | * @param[out] res_str: 转换结果 163 | * @param[in] value: 需要转换的值 164 | * @param[in] quat: 量化精度 165 | * 166 | */ 167 | MSTR_EXPORT_API(mstr_result_t) 168 | mstr_fmt_uqtoa(MString* res_str, uint32_t value, uint32_t quat); 169 | 170 | /** 171 | * @brief 将日期时间值转换为字符串 172 | * 173 | * 174 | * @param[out] res_str: 转换结果 175 | * @param[in] tm: 日期时间值 176 | * @param[in] spec: 格式化信息 177 | */ 178 | MSTR_EXPORT_API(mstr_result_t) 179 | mstr_fmt_ttoa( 180 | MString* res_str, 181 | const MStrTime* tm, 182 | const MStrFmtChronoFormatSpec* spec 183 | ); 184 | #endif // !_INCLUDE_MM_FMT_H_ 185 | -------------------------------------------------------------------------------- /inc/mm_io.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_io.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief IO 6 | * @version 1.0 7 | * @date 2023-06-07 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_IO_H_) 13 | #define _INCLUDE_MM_IO_H_ 1 14 | #include "mm_cfg.h" 15 | #include "mm_result.h" 16 | #include "mm_type.h" 17 | 18 | /** 19 | * @brief 写入数据 20 | * 21 | */ 22 | typedef mstr_result_t (*MStrIOWrite)( 23 | void* ctx, const byte_t* data, usize_t len 24 | ); 25 | 26 | /** 27 | * @brief IO回调的接口 28 | * 29 | */ 30 | typedef struct tagMStrIOCallback 31 | { 32 | /** 33 | * @brief 上下文 34 | * 35 | */ 36 | void* capture; 37 | 38 | /** 39 | * @brief 写入数据callback 40 | * 41 | */ 42 | MStrIOWrite io_write; 43 | } MStrIOCallback; 44 | 45 | /** 46 | * @brief 初始化IO的结构 47 | * 48 | * @param[in] context: 上下文 49 | * @param[inout] obj: IO结构对象 50 | * @param[in] cb_write: 写入数据callback, 不可留NULL 51 | * 52 | */ 53 | MSTR_EXPORT_API(mstr_result_t) 54 | mstr_io_init(void* context, MStrIOCallback* obj, MStrIOWrite cb_write); 55 | 56 | /** 57 | * @brief 格式化字符串到指定io 58 | * 59 | * @param[inout] io: IO 60 | * @param[in] fmt: 格式化串 61 | * @param[in] fmt_place: 预期fmt中使用的参数数目. 62 | * 最大不超过16(MFMT_PLACE_MAX_NUM) 63 | * 64 | * @return minfmt_result_t: 格式化结果 65 | */ 66 | MSTR_EXPORT_API(mstr_result_t) 67 | mstr_ioformat( 68 | MStrIOCallback* io, const char* fmt, usize_t fmt_place, ... 69 | ); 70 | 71 | /** 72 | * @brief 格式化字符串到指定io 73 | * 74 | * @param[inout] io: IO 75 | * @param[in] fmt: 格式化串 76 | * @param[in] fmt_place: 预期fmt中使用的参数数目. 77 | * 最大不超过16(MFMT_PLACE_MAX_NUM) 78 | * @param[in] ap_ptr: &ap 79 | * 80 | * @return minfmt_result_t: 格式化结果 81 | */ 82 | MSTR_EXPORT_API(mstr_result_t) 83 | mstr_iovformat( 84 | MStrIOCallback* io, 85 | const char* fmt, 86 | usize_t fmt_place, 87 | va_list* ap_ptr 88 | ); 89 | 90 | /** 91 | * @brief 取得stdout的内部handler 92 | * 93 | */ 94 | MSTR_EXPORT_API(MStrIOCallback*) mstr_get_stdout(void); 95 | 96 | #define MSTR_COUNT_VA_ARGS_HELPER( \ 97 | _0, \ 98 | _1, \ 99 | _2, \ 100 | _3, \ 101 | _4, \ 102 | _5, \ 103 | _6, \ 104 | _7, \ 105 | _8, \ 106 | _9, \ 107 | _10, \ 108 | _11, \ 109 | _12, \ 110 | _13, \ 111 | _14, \ 112 | _15, \ 113 | _16, \ 114 | N, \ 115 | ... \ 116 | ) \ 117 | N 118 | 119 | /** 120 | * @brief 取得可变参数宏的参数个数 121 | * 122 | */ 123 | #define MSTR_COUNT_VA_ARGS(...) \ 124 | MSTR_COUNT_VA_ARGS_HELPER( \ 125 | 0, \ 126 | ##__VA_ARGS__, \ 127 | 16, \ 128 | 15, \ 129 | 14, \ 130 | 13, \ 131 | 12, \ 132 | 11, \ 133 | 10, \ 134 | 9, \ 135 | 8, \ 136 | 7, \ 137 | 6, \ 138 | 5, \ 139 | 4, \ 140 | 3, \ 141 | 2, \ 142 | 1, \ 143 | 0 \ 144 | ) 145 | 146 | #if _MSTR_USE_STD_IO 147 | /** 148 | * @brief 写到 stdout 149 | * 150 | * @attention 宏 _MSTR_USE_STD_IO 为1时有效, 该宏假定格式化参数为最大值, 151 | * 可能会为坏坏刻意修改格式化串从而造成二进制安全问题 152 | */ 153 | #define mstr_print(fmt, ...) \ 154 | (mstr_ioformat( \ 155 | mstr_get_stdout(), \ 156 | fmt, \ 157 | MSTR_COUNT_VA_ARGS(__VA_ARGS__), \ 158 | ##__VA_ARGS__ \ 159 | )) 160 | #else 161 | /** 162 | * @brief 写到 stdout (disable) 163 | * 164 | * @note 宏 _MSTR_USE_STD_IO 为1时启用 165 | * 166 | */ 167 | #define mstr_print(fmt, ...) ((void)(0)) 168 | #endif // _MSTR_USE_STD_IO 169 | #endif // _INCLUDE_MM_IO_H_ 170 | -------------------------------------------------------------------------------- /support/themes/doxygen/css/header.scss: -------------------------------------------------------------------------------- 1 | @import 'config'; 2 | 3 | // SPDX-License-Identifier: AGPL-3.0 4 | 5 | // 顶部 6 | #top { 7 | padding-top: 0.5em; 8 | padding-bottom: 0.5em; 9 | border: none; 10 | border-bottom: 1px solid $light-line-color; 11 | 12 | } 13 | 14 | // 导航栏 15 | #titlearea, 16 | .header { 17 | margin: 0 auto; 18 | width: $content-width; 19 | min-width: $content-min-width; 20 | max-width: $header-footer-max-width; 21 | } 22 | 23 | .navpath { 24 | // 项目与项目的间隙 25 | $item-sapce: 0.5em; 26 | 27 | ul { 28 | padding: 0; 29 | margin: 0 auto; 30 | padding-top: 0.5em; 31 | padding-bottom: 0.5em; 32 | width: $content-width; 33 | min-width: $content-min-width; 34 | max-width: $header-footer-max-width; 35 | } 36 | 37 | li { 38 | font-weight: bold; 39 | font-size: 105%; 40 | margin-right: $item-sapce; 41 | display: inline-block; 42 | list-style-type: none; 43 | 44 | a { 45 | display: block; 46 | color: $hyper-link-color; 47 | text-decoration: none; 48 | outline: none; 49 | 50 | &:hover { 51 | text-decoration: underline; 52 | } 53 | 54 | &:visited { 55 | color: mix($hyper-link-color, black, 90%); 56 | } 57 | 58 | &::after { 59 | content: "->"; 60 | margin-left: $item-sapce; 61 | color: mix($hyper-link-color, black, 50%); 62 | } 63 | } 64 | 65 | // 消除最后一个元素的"箭头" 66 | &:last-child::after { 67 | content: ""; 68 | } 69 | } 70 | } 71 | 72 | // header 73 | #titlearea { 74 | padding-top: 1em; 75 | display: flex; 76 | flex-direction: row; 77 | justify-content: flex-start; 78 | align-items: baseline; 79 | 80 | #projectlogo { 81 | max-width: 4.5em; 82 | max-height: 4.5em; 83 | 84 | img { 85 | border: none; 86 | } 87 | } 88 | 89 | #project_descript { 90 | #projectname { 91 | font-size: 2em; 92 | font-weight: bold; 93 | } 94 | 95 | #projectbrief { 96 | color: #575757; 97 | font-size: 0.9em; 98 | margin-top: 1em; 99 | } 100 | 101 | #projectnumber { 102 | font-size: 0.5em; 103 | } 104 | } 105 | } 106 | 107 | // header title 108 | .title { 109 | font-size: 150%; 110 | font-weight: bold; 111 | margin-top: 0.75em; 112 | margin-bottom: 0.75em; 113 | color: $section-text-color; 114 | } 115 | 116 | // 导航栏 117 | #main-nav { 118 | margin: 0 auto; 119 | padding-top: 0.5em; 120 | padding-bottom: 0.5em; 121 | width: $content-width; 122 | min-width: $content-min-width; 123 | max-width: $header-footer-max-width; 124 | 125 | #main-menu { 126 | padding: 0; 127 | 128 | li { 129 | display: inline-block; 130 | margin-left: 1em; 131 | margin-right: 1em; 132 | 133 | &:first-child { 134 | margin-left: 0; 135 | } 136 | 137 | // 菜单项 138 | a { 139 | color: $hyper-link-color; 140 | text-decoration: none; 141 | outline: none; 142 | 143 | &:hover { 144 | text-decoration: underline; 145 | } 146 | } 147 | } 148 | 149 | // 搜索框 150 | #searchBoxPos2 { 151 | float: initial; 152 | display: inline-flex; 153 | } 154 | } 155 | } 156 | 157 | .sm ul { 158 | // 菜单项 159 | display: none; 160 | } 161 | 162 | div.sm.sm-dox { 163 | // "Toggle"的checkbox 164 | margin: 0 auto; 165 | width: $content-width; 166 | min-width: $content-min-width; 167 | max-width: $header-footer-max-width; 168 | text-align: right; 169 | 170 | input[type="checkbox"] { 171 | width: 14px; 172 | height: 14px; 173 | outline: none; 174 | position: relative; 175 | border: 1px solid $checkbox-border-color; 176 | border-radius: 2px; 177 | 178 | &:checked { 179 | border: 1px solid #1e6bb8; 180 | } 181 | 182 | &::after { 183 | inset: 0; 184 | pointer-events: none; 185 | background: $checkbox-color; 186 | mask-size: cover; 187 | } 188 | 189 | &:checked::after { 190 | content: ""; 191 | position: absolute; 192 | mask-image: $checkbox-img; 193 | } 194 | } 195 | } 196 | 197 | .sm-dox ul { 198 | // 子菜单项 199 | position: absolute; 200 | padding: 1em; 201 | border-radius: 2px; 202 | border: 1px solid $split-line-color; 203 | background-color: $bg-color; 204 | } -------------------------------------------------------------------------------- /tests/test_main.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_main.h 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 所有可用的测试 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MAIN_H_) 13 | #define _INCLUDE_MAIN_H_ 1 14 | 15 | #if __cplusplus 16 | extern "C" 17 | { 18 | #endif 19 | void allocate_then_free(void); 20 | 21 | void monadic_result_object_basic(void); 22 | void monadic_result_copy_non_trivial_type(void); 23 | void monadic_result_copy_assign_non_trivial_type(void); 24 | 25 | void monadic_result_conjugate(void); 26 | void monadic_result_flatten(void); 27 | void monadic_result_map(void); 28 | void monadic_result_map_err(void); 29 | void monadic_result_and_then(void); 30 | void monadic_result_or_else(void); 31 | void monadic_result_or_value(void); 32 | void monadic_result_or_exception(void); 33 | 34 | void string_copy_create(void); 35 | void string_move_create(void); 36 | void string_length(void); 37 | void string_char_at(void); 38 | void string_insert(void); 39 | void string_remove(void); 40 | 41 | void string_equal(void); 42 | void string_not_equal(void); 43 | void string_end_with(void); 44 | void string_start_with(void); 45 | 46 | void string_concat_object(void); 47 | void string_concat_c_str(void); 48 | void string_concat_c_slice(void); 49 | 50 | void string_append(void); 51 | void string_repeat_append(void); 52 | 53 | void string_trans_clear(void); 54 | void string_trans_reverse(void); 55 | 56 | void string_index(void); 57 | void string_const_iterator(void); 58 | void string_reverse_const_iterator(void); 59 | 60 | void string_find(void); 61 | void string_contain(void); 62 | void string_find_large(void); 63 | void string_find_or_error(void); 64 | 65 | void string_retain_all(void); 66 | void string_retain_endwith(void); 67 | void string_retain_startwith(void); 68 | 69 | void itoa_int_index(void); 70 | void itoa_int_type(void); 71 | void itoa_int_sign(void); 72 | void itoa_int_from(void); 73 | void itoa_uint_index(void); 74 | void itoa_uint_type(void); 75 | void itoa_uint_from(void); 76 | 77 | void qtoa_signed(void); 78 | void qtoa_unsigned(void); 79 | void qtoa_signed_from(void); 80 | void qtoa_unsigned_from(void); 81 | 82 | void ftoa_basic(void); 83 | 84 | void parser_err_invaild_begin(void); 85 | void parser_err_invaild_end(void); 86 | void parser_err_invaild_fmt_spec(void); 87 | 88 | void fmt_integer_i8(void); 89 | void fmt_integer_i16(void); 90 | void fmt_integer_i32(void); 91 | void fmt_integer_u8(void); 92 | void fmt_integer_u16(void); 93 | void fmt_integer_u32(void); 94 | 95 | void fmt_integer_array_i8(void); 96 | void fmt_integer_array_i16(void); 97 | void fmt_integer_array_i32(void); 98 | void fmt_integer_array_u8(void); 99 | void fmt_integer_array_u16(void); 100 | void fmt_integer_array_u32(void); 101 | 102 | void fmt_align_left(void); 103 | void fmt_align_left_fill(void); 104 | void fmt_align_left_long(void); 105 | void fmt_align_right(void); 106 | void fmt_align_right_fill(void); 107 | void fmt_align_right_long(void); 108 | void fmt_align_middle(void); 109 | void fmt_align_middle_fill(void); 110 | void fmt_align_middle_long(void); 111 | 112 | void fmt_seq_arg_id(void); 113 | void fmt_seq_arg_id_err(void); 114 | 115 | void fmt_quat_value_sign(void); 116 | void fmt_quat_value_dualprec(void); 117 | void fmt_quat_value_singprec(void); 118 | 119 | void fmt_behav_signed_bin(void); 120 | void fmt_behav_signed_oct(void); 121 | void fmt_behav_signed_dec(void); 122 | void fmt_behav_signed_hex(void); 123 | 124 | void fmt_sign_add(void); 125 | void fmt_sign_sub(void); 126 | void fmt_sign_space(void); 127 | 128 | void fmt_sty_bin(void); 129 | void fmt_sty_oct(void); 130 | void fmt_sty_dec(void); 131 | void fmt_sty_hex(void); 132 | void fmt_sty_hex_prefix(void); 133 | 134 | void fmt_array(void); 135 | void fmt_array_element_style(void); 136 | void fmt_array_userdefind_split(void); 137 | 138 | void fmt_chrono_default_f(void); 139 | void fmt_chrono_default_g(void); 140 | void fmt_chrono_userdef_year(void); 141 | void fmt_chrono_userdef_month(void); 142 | void fmt_chrono_userdef_day(void); 143 | void fmt_chrono_userdef_hour(void); 144 | void fmt_chrono_userdef_minute(void); 145 | void fmt_chrono_userdef_second(void); 146 | void fmt_chrono_userdef_subsecond(void); 147 | void fmt_chrono_userdef_week(void); 148 | 149 | void fmt_escape_bracket(void); 150 | 151 | void sync_io_write(void); 152 | 153 | void cpp_wrap_fmt(void); 154 | void cpp_wrap_fmt_parser(void); 155 | #if __cplusplus 156 | } 157 | #endif 158 | #endif // _INCLUDE_MAIN_H_ 159 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(mtfmt) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 11) 7 | 8 | # debug版的后缀 9 | set(CMAKE_DEBUG_POSTFIX "_d") 10 | 11 | # 构建输出 12 | set(BUILD_OUTPUT_DIR ${CMAKE_BINARY_DIR}) 13 | 14 | # 选项 15 | option(MTFMT_RT_USE_DIV "Use hardware divide instruction" OFF) 16 | option(MTFMT_RT_USE_MALLOC "Use malloc instead build-in heap manager" OFF) 17 | option(MTFMT_RT_USE_STDOUT "Enable stdio support in the library" OFF) 18 | option(MTFMT_RT_USE_ASSERT "Enable assert" OFF) 19 | option(MTFMT_RT_USE_UTF8 "Enable UTF-8 string support" ON) 20 | option(MTFMT_RT_USE_FP32 "Enable 32bits floating number formatting support" OFF) 21 | option(MTFMT_RT_USE_FP64 "Enable 64bits floating number formatting support" OFF) 22 | option(MTFMT_BUILD_SHARED "Build dymatic library" OFF) 23 | 24 | # 库文件输出目录 25 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${BUILD_OUTPUT_DIR}/target/lib) 26 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${BUILD_OUTPUT_DIR}/target/lib) 27 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${BUILD_OUTPUT_DIR}/target/bin) 28 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${BUILD_OUTPUT_DIR}/target/bin) 29 | 30 | # 源文件 31 | aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}/src" LIB_SRCS) 32 | 33 | # 资源文件 34 | if(MSVC AND WIN32) 35 | set(LIB_WIN_RC "${CMAKE_CURRENT_SOURCE_DIR}/src/win_resource.rc") 36 | else() 37 | set(LIB_WIN_RC "") 38 | endif() 39 | 40 | # 目标名 41 | set(TARGET_NAME mtfmt) 42 | 43 | # 构建目标 44 | if(MTFMT_BUILD_SHARED) 45 | add_library(${TARGET_NAME} SHARED ${LIB_SRCS} ${LIB_WIN_RC}) 46 | else() 47 | add_library(${TARGET_NAME} STATIC ${LIB_SRCS}) 48 | endif() 49 | 50 | # including path 51 | target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc") 52 | 53 | # debug? 54 | if(NOT MSVC) 55 | if(NOT CMAKE_BUILD_TYPE) 56 | message("NO build type was set, the default value is release") 57 | set(CMAKE_BUILD_TYPE Release) 58 | endif() 59 | else() 60 | # 默认release ... 61 | set(CMAKE_BUILD_TYPE Release) 62 | endif() 63 | 64 | # 宏开关 65 | # default: auto, following the compiler pre-defined macros 66 | if(MTFMT_RT_USE_DIV) 67 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_HARDWARE_DIV) 68 | endif() 69 | 70 | # default: 0 71 | if(MTFMT_RT_USE_MALLOC) 72 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_MALLOC) 73 | endif() 74 | 75 | # default: 0 76 | if(MTFMT_RT_USE_STDOUT) 77 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_STD_IO) 78 | endif() 79 | 80 | # default: 0 81 | if(MTFMT_RT_USE_ASSERT) 82 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_RUNTIME_ASSERT) 83 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_RUNTIME_CTRLFLOW_MARKER) 84 | endif() 85 | 86 | # default: 1 87 | if(MTFMT_RT_USE_UTF8) 88 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_UTF_8=1) 89 | else() 90 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_UTF_8=0) 91 | endif() 92 | 93 | # default: 0 94 | if(MTFMT_RT_USE_FP32) 95 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_FP_FLOAT32) 96 | endif() 97 | 98 | # default: auto, following the compiler pre-defined macros 99 | if(MTFMT_RT_USE_FP64) 100 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_USE_FP_FLOAT64) 101 | endif() 102 | 103 | if(MSVC) 104 | # 大多数东东没有second api 105 | # 因此mtfmt开发过程也没有用它们, disable掉 106 | target_compile_definitions(${TARGET_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) 107 | endif() 108 | 109 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 110 | target_compile_definitions(${TARGET_NAME} PRIVATE _DEBUG) 111 | endif() 112 | 113 | # dll export 114 | if(MTFMT_BUILD_SHARED) 115 | target_compile_definitions(${TARGET_NAME} PRIVATE _MSTR_BUILD_DLL) 116 | endif() 117 | 118 | install(TARGETS mtfmt 119 | EXPORT mtfmt-targets 120 | RUNTIME DESTINATION mtfmt/bin # dll 121 | LIBRARY DESTINATION mtfmt/lib # 动态库安装路径 122 | ARCHIVE DESTINATION mtfmt/lib/static # 静态库安装路径 123 | ) 124 | 125 | # 安装头文件 126 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/inc/ DESTINATION mtfmt/include) 127 | 128 | install(EXPORT mtfmt-targets 129 | FILE mtfmt-config.cmake 130 | NAMESPACE mtfmt:: 131 | DESTINATION mtfmt/lib/cmake/mtfmt) 132 | 133 | # 测试 134 | # enable_testing() 135 | 136 | # file(GLOB_RECURSE TEST_SOURCES tests/*.c thirds/unity/src/*.c) 137 | 138 | # foreach(FILE_PATH ${TEST_SOURCES}) 139 | # get_filename_component(FILE_NAME ${FILE_PATH} NAME_WE) 140 | # set(TEST_TARGET_NAME ${FILE_NAME}_test) 141 | # add_executable(${TEST_TARGET_NAME} ${FILE_PATH}) 142 | # target_include_directories(${TEST_TARGET_NAME} PRIVATE inc thirds/unity/src) 143 | # target_link_libraries(${TEST_TARGET_NAME} PRIVATE ${LIB_TARGET_NAME}) 144 | # if (MTFMT_BUILD_USE_LTO) 145 | # set_target_properties(${TEST_TARGET_NAME} PROPERTIES LINK_FLAGS -flto) 146 | # endif() 147 | # add_test(NAME ${FILE_NAME} COMMAND ${TEST_TARGET_NAME}) 148 | # endforeach() 149 | 150 | # 自定义目标 alltests 151 | # add_custom_target(alltests COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) 152 | -------------------------------------------------------------------------------- /tests/test_string_basic.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_string_basic.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 基础的字符串功能测试(长度等等) 6 | * @version 1.0 7 | * @date 2023-06-21 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.hpp" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | 18 | template 19 | constexpr mtfmt::unicode_t unicode_char(const char (&u8char)[N]) 20 | { 21 | return mtfmt::string::unicode_char(u8char); 22 | } 23 | 24 | extern "C" void string_copy_create(void) 25 | { 26 | } 27 | 28 | extern "C" void string_move_create(void) 29 | { 30 | } 31 | 32 | extern "C" void string_length(void) 33 | { 34 | // ASCII 35 | mtfmt::string str1 = u8"ASCII"; 36 | ASSERT_EQUAL_VALUE(str1.length(), 5); 37 | ASSERT_EQUAL_VALUE(str1.byte_count(), 5); 38 | #if _MSTR_USE_UTF_8 39 | // UTF-8 40 | mtfmt::string str2 = u8"😀😀😀"; 41 | ASSERT_EQUAL_VALUE(str2.length(), 3); 42 | ASSERT_EQUAL_VALUE(str2.byte_count(), 12); 43 | #endif // _MSTR_USE_UTF_8 44 | } 45 | 46 | extern "C" void string_char_at(void) 47 | { 48 | // @mstr_char_at 49 | mtfmt::string str_ascii = u8"MtF"; 50 | ASSERT_EQUAL_VALUE(str_ascii[0], 'M'); 51 | ASSERT_EQUAL_VALUE(str_ascii[1], 't'); 52 | ASSERT_EQUAL_VALUE(str_ascii[2], 'F'); 53 | // unicode 54 | #if _MSTR_USE_UTF_8 55 | mtfmt::string str_unicode = u8"S😀s"; 56 | ASSERT_EQUAL_VALUE(str_unicode[0], 'S'); 57 | ASSERT_EQUAL_VALUE(str_unicode[1], unicode_char(u8"😀")); 58 | ASSERT_EQUAL_VALUE(str_unicode[2], 's'); 59 | #endif // _MSTR_USE_UTF_8 60 | } 61 | 62 | extern "C" void string_insert(void) 63 | { 64 | // @mstr_insert 65 | // ascii 66 | mtfmt::string str_ascii = u8"abc"; 67 | // insert中间位置 68 | mtfmt::string str_ascii_test_1 = str_ascii; 69 | str_ascii_test_1.insert(1, unicode_char(u8"$")); 70 | ASSERT_EQUAL_VALUE(str_ascii_test_1, u8"a$bc"); 71 | // insert起始位置 72 | mtfmt::string str_ascii_test_2 = str_ascii; 73 | str_ascii_test_2.insert(0, unicode_char(u8"$")); 74 | ASSERT_EQUAL_VALUE(str_ascii_test_2, u8"$abc"); 75 | // insert末尾位置 76 | mtfmt::string str_ascii_test_3 = str_ascii; 77 | str_ascii_test_3.insert(2, unicode_char(u8"$")); 78 | ASSERT_EQUAL_VALUE(str_ascii_test_3, u8"ab$c"); 79 | // insert结束位置 80 | mtfmt::string str_ascii_test_4 = str_ascii; 81 | str_ascii_test_4.insert(3, unicode_char(u8"$")); 82 | ASSERT_EQUAL_VALUE(str_ascii_test_4, u8"abc$"); 83 | // unicode 84 | #if _MSTR_USE_UTF_8 85 | mtfmt::string str_unicode = u8"A😀C"; 86 | // insert中间位置 87 | mtfmt::string str_uni_test_1 = str_unicode; 88 | str_uni_test_1.insert(1, unicode_char(u8"😊")); 89 | ASSERT_EQUAL_VALUE(str_uni_test_1, u8"A😊😀C"); 90 | // insert起始位置 91 | mtfmt::string str_uni_test_2 = str_unicode; 92 | str_uni_test_2.insert(0, unicode_char(u8"😊")); 93 | ASSERT_EQUAL_VALUE(str_uni_test_2, u8"😊A😀C"); 94 | // insert末尾位置 95 | mtfmt::string str_uni_test_3 = str_unicode; 96 | str_uni_test_3.insert(2, unicode_char(u8"😊")); 97 | ASSERT_EQUAL_VALUE(str_uni_test_3, u8"A😀😊C"); 98 | // insert结束位置 99 | mtfmt::string str_uni_test_4 = str_unicode; 100 | str_uni_test_4.insert(3, unicode_char(u8"😊")); 101 | ASSERT_EQUAL_VALUE(str_uni_test_4, u8"A😀C😊"); 102 | // insert触发堆分配 103 | mtfmt::string str_uni_large = u8"😀🍥🌈"; 104 | str_uni_large.insert(1, unicode_char(u8"😊")); 105 | ASSERT_EQUAL_VALUE(str_uni_large, u8"😀😊🍥🌈"); 106 | str_uni_large.insert(2, unicode_char(u8"😙")); 107 | ASSERT_EQUAL_VALUE(str_uni_large, u8"😀😊😙🍥🌈"); 108 | #endif // _MSTR_USE_UTF_8 109 | } 110 | 111 | extern "C" void string_remove(void) 112 | { 113 | mtfmt::string str_ascii = u8"ABC"; 114 | // remove中间位置 115 | mtfmt::string str_ascii_test_1 = str_ascii; 116 | mtfmt::unicode_t ascii_ch1 = str_ascii_test_1.remove(1).or_value(0); 117 | ASSERT_EQUAL_VALUE(ascii_ch1, unicode_char(u8"B")); 118 | ASSERT_EQUAL_VALUE(str_ascii_test_1, u8"AC"); 119 | // remove起始位置 120 | mtfmt::string str_ascii_test_2 = str_ascii; 121 | mtfmt::unicode_t ascii_ch2 = str_ascii_test_2.remove(0).or_value(0); 122 | ASSERT_EQUAL_VALUE(ascii_ch2, unicode_char(u8"A")); 123 | ASSERT_EQUAL_VALUE(str_ascii_test_2, u8"BC"); 124 | // remove结束位置 125 | mtfmt::string str_ascii_test_3 = str_ascii; 126 | mtfmt::unicode_t ascii_ch3 = str_ascii_test_3.remove(2).or_value(0); 127 | ASSERT_EQUAL_VALUE(ascii_ch3, unicode_char(u8"C")); 128 | ASSERT_EQUAL_VALUE(str_ascii_test_3, u8"AB"); 129 | // unicode 130 | #if _MSTR_USE_UTF_8 131 | mtfmt::string str_unicode = u8"A😀C"; 132 | // remove中间位置 133 | mtfmt::string str_unicode_test_1 = str_unicode; 134 | mtfmt::unicode_t unicode_ch1 = 135 | str_unicode_test_1.remove(1).or_value(0); 136 | ASSERT_EQUAL_VALUE(unicode_ch1, unicode_char(u8"😀")); 137 | ASSERT_EQUAL_VALUE(str_unicode_test_1, u8"AC"); 138 | // remove起始位置 139 | mtfmt::string str_unicode_test_2 = str_unicode; 140 | mtfmt::unicode_t unicode_ch2 = 141 | str_unicode_test_2.remove(0).or_value(0); 142 | ASSERT_EQUAL_VALUE(unicode_ch2, unicode_char(u8"A")); 143 | ASSERT_EQUAL_VALUE(str_unicode_test_2, u8"😀C"); 144 | // remove结束位置 145 | mtfmt::string str_unicode_test_3 = str_unicode; 146 | mtfmt::unicode_t unicode_ch3 = 147 | str_unicode_test_3.remove(2).or_value(0); 148 | ASSERT_EQUAL_VALUE(unicode_ch3, unicode_char(u8"C")); 149 | ASSERT_EQUAL_VALUE(str_unicode_test_3, u8"A😀"); 150 | #endif // _MSTR_USE_UTF_8 151 | } 152 | -------------------------------------------------------------------------------- /tests/test_fmt_chrono.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_chrono.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 日期时间测试 6 | * @version 1.0 7 | * @date 2023-06-03 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "mtfmt.h" 13 | #include "test_helper.h" 14 | #include "test_main.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | // 默认的日期时间 20 | static const MStrTime time_value = { 21 | .year = 0x2023, 22 | .month = 0x05, 23 | .day = 0x06, 24 | .hour = 0x16, 25 | .minute = 0x55, 26 | .second = 0x19, 27 | .week = 0x6, 28 | .sub_second = 0x1234, 29 | }; 30 | 31 | void fmt_chrono_default_f(void) 32 | { 33 | MString s; 34 | EVAL(mstr_create_empty(&s)); 35 | EVAL(mstr_format(&s, "@{0:t:%f}@", 1, &time_value)); 36 | ASSERT_EQUAL_STRING(&s, "@2023-05-06 16:55:19.1234 6@"); 37 | mstr_free(&s); 38 | } 39 | 40 | void fmt_chrono_default_g(void) 41 | { 42 | MString s; 43 | EVAL(mstr_create_empty(&s)); 44 | EVAL(mstr_format(&s, "@{0:t:%g}@", 1, &time_value)); 45 | ASSERT_EQUAL_STRING(&s, "@2023-05-06 16:55:19.1234@"); 46 | mstr_free(&s); 47 | } 48 | 49 | void fmt_chrono_userdef_year(void) 50 | { 51 | // TODO 52 | } 53 | 54 | void fmt_chrono_userdef_month(void) 55 | { 56 | const MStrTime time_value_sing = {.month = 0x01}; 57 | const MStrTime time_value_doub = {.month = 0x10}; 58 | MString s; 59 | EVAL(mstr_create_empty(&s)); 60 | // 1位~2位 61 | EVAL(mstr_format( 62 | &s, "@{0:t:%M}@{1:t:%M}@", 2, &time_value_sing, &time_value_doub 63 | )); 64 | ASSERT_EQUAL_STRING(&s, "@1@10@"); 65 | // 固定2位 66 | mstr_clear(&s); 67 | EVAL(mstr_format( 68 | &s, 69 | "@{0:t:%MM}@{1:t:%MM}@", 70 | 2, 71 | &time_value_sing, 72 | &time_value_doub 73 | )); 74 | ASSERT_EQUAL_STRING(&s, "@01@10@"); 75 | mstr_free(&s); 76 | } 77 | 78 | void fmt_chrono_userdef_day(void) 79 | { 80 | const MStrTime time_value_sing = {.day = 0x01}; 81 | const MStrTime time_value_doub = {.day = 0x10}; 82 | MString s; 83 | EVAL(mstr_create_empty(&s)); 84 | // 1位~2位 85 | EVAL(mstr_format( 86 | &s, "@{0:t:%d}@{1:t:%d}@", 2, &time_value_sing, &time_value_doub 87 | )); 88 | ASSERT_EQUAL_STRING(&s, "@1@10@"); 89 | // 固定2位 90 | mstr_clear(&s); 91 | EVAL(mstr_format( 92 | &s, 93 | "@{0:t:%dd}@{1:t:%dd}@", 94 | 2, 95 | &time_value_sing, 96 | &time_value_doub 97 | )); 98 | ASSERT_EQUAL_STRING(&s, "@01@10@"); 99 | mstr_free(&s); 100 | } 101 | 102 | void fmt_chrono_userdef_hour(void) 103 | { 104 | const MStrTime time_value_sing = {.hour = 0x01}; 105 | const MStrTime time_value_doub = {.hour = 0x10}; 106 | MString s; 107 | EVAL(mstr_create_empty(&s)); 108 | // 24h: 1位~2位 109 | EVAL(mstr_format( 110 | &s, "@{0:t:%H}@{1:t:%H}@", 2, &time_value_sing, &time_value_doub 111 | )); 112 | ASSERT_EQUAL_STRING(&s, "@1@10@"); 113 | // 24h: 固定2位 114 | mstr_clear(&s); 115 | EVAL(mstr_format( 116 | &s, 117 | "@{0:t:%HH}@{1:t:%HH}@", 118 | 2, 119 | &time_value_sing, 120 | &time_value_doub 121 | )); 122 | ASSERT_EQUAL_STRING(&s, "@01@10@"); 123 | mstr_free(&s); 124 | } 125 | 126 | void fmt_chrono_userdef_minute(void) 127 | { 128 | const MStrTime time_value_sing = {.minute = 0x01}; 129 | const MStrTime time_value_doub = {.minute = 0x10}; 130 | MString s; 131 | EVAL(mstr_create_empty(&s)); 132 | // 1位~2位 133 | EVAL(mstr_format( 134 | &s, "@{0:t:%m}@{1:t:%m}@", 2, &time_value_sing, &time_value_doub 135 | )); 136 | ASSERT_EQUAL_STRING(&s, "@1@10@"); 137 | // 固定2位 138 | mstr_clear(&s); 139 | EVAL(mstr_format( 140 | &s, 141 | "@{0:t:%mm}@{1:t:%mm}@", 142 | 2, 143 | &time_value_sing, 144 | &time_value_doub 145 | )); 146 | ASSERT_EQUAL_STRING(&s, "@01@10@"); 147 | mstr_free(&s); 148 | } 149 | 150 | void fmt_chrono_userdef_second(void) 151 | { 152 | const MStrTime time_value_sing = {.second = 0x01}; 153 | const MStrTime time_value_doub = {.second = 0x10}; 154 | MString s; 155 | EVAL(mstr_create_empty(&s)); 156 | // 1位~2位 157 | EVAL(mstr_format( 158 | &s, "@{0:t:%s}@{1:t:%s}@", 2, &time_value_sing, &time_value_doub 159 | )); 160 | ASSERT_EQUAL_STRING(&s, "@1@10@"); 161 | // 固定2位 162 | mstr_clear(&s); 163 | EVAL(mstr_format( 164 | &s, 165 | "@{0:t:%ss}@{1:t:%ss}@", 166 | 2, 167 | &time_value_sing, 168 | &time_value_doub 169 | )); 170 | ASSERT_EQUAL_STRING(&s, "@01@10@"); 171 | mstr_free(&s); 172 | } 173 | 174 | void fmt_chrono_userdef_subsecond(void) 175 | { 176 | MString s; 177 | EVAL(mstr_create_empty(&s)); 178 | // 1位 179 | EVAL(mstr_format(&s, "@{0:t:%x}@", 1, &time_value)); 180 | ASSERT_EQUAL_STRING(&s, "@1@"); 181 | // 前2位 182 | mstr_clear(&s); 183 | EVAL(mstr_format(&s, "@{0:t:%xx}@", 1, &time_value)); 184 | ASSERT_EQUAL_STRING(&s, "@12@"); 185 | // 前3位 186 | mstr_clear(&s); 187 | EVAL(mstr_format(&s, "@{0:t:%xxx}@", 1, &time_value)); 188 | ASSERT_EQUAL_STRING(&s, "@123@"); 189 | // 前4位 190 | mstr_clear(&s); 191 | EVAL(mstr_format(&s, "@{0:t:%xxxx}@", 1, &time_value)); 192 | ASSERT_EQUAL_STRING(&s, "@1234@"); 193 | mstr_free(&s); 194 | } 195 | 196 | void fmt_chrono_userdef_week(void) 197 | { 198 | MString s; 199 | EVAL(mstr_create_empty(&s)); 200 | EVAL(mstr_format(&s, "@{0:t:%w}@", 1, &time_value)); 201 | ASSERT_EQUAL_STRING(&s, "@6@"); 202 | mstr_free(&s); 203 | } 204 | -------------------------------------------------------------------------------- /tests/test_main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file test_fmt_align.c 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief 不同格式化对齐方式下的测试 6 | * @version 1.0 7 | * @date 2023-05-28 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #include "test_main.h" 13 | #include "mtfmt.h" 14 | #include "test_helper.h" 15 | #include "unity.h" 16 | #include 17 | #include 18 | 19 | #define RUNTIME_HEAP_SIZE 2048 20 | 21 | /** 22 | * @brief 堆 23 | * 24 | */ 25 | static byte_t heap[RUNTIME_HEAP_SIZE]; 26 | 27 | void setUp(void) 28 | { 29 | } 30 | 31 | void tearDown(void) 32 | { 33 | } 34 | 35 | int main() 36 | { 37 | UNITY_BEGIN(); 38 | 39 | uint32_t cfg = mstr_configure(); 40 | printf( 41 | "Build configure - version: 0x%x\n", MSTR_CONFIGURE_VER_VAL(cfg) 42 | ); 43 | printf( 44 | "Build configure - compile: %d\n", MSTR_CONFIGURE_CC_VAL(cfg) 45 | ); 46 | printf( 47 | "Build configure - configure: 0x%x\n", 48 | MSTR_CONFIGURE_CFG_VAL(cfg) 49 | ); 50 | 51 | // 初始化堆 52 | mstr_heap_init(heap, RUNTIME_HEAP_SIZE); 53 | 54 | RUN_TEST(allocate_then_free); 55 | 56 | RUN_TEST(monadic_result_object_basic); 57 | RUN_TEST(monadic_result_copy_non_trivial_type); 58 | RUN_TEST(monadic_result_copy_assign_non_trivial_type); 59 | 60 | RUN_TEST(monadic_result_conjugate); 61 | RUN_TEST(monadic_result_flatten); 62 | RUN_TEST(monadic_result_map); 63 | RUN_TEST(monadic_result_map_err); 64 | RUN_TEST(monadic_result_and_then); 65 | RUN_TEST(monadic_result_or_else); 66 | RUN_TEST(monadic_result_or_value); 67 | RUN_TEST(monadic_result_or_exception); 68 | 69 | RUN_TEST(string_copy_create); 70 | RUN_TEST(string_move_create); 71 | RUN_TEST(string_length); 72 | RUN_TEST(string_char_at); 73 | RUN_TEST(string_insert); 74 | RUN_TEST(string_remove); 75 | 76 | RUN_TEST(string_equal); 77 | RUN_TEST(string_not_equal); 78 | RUN_TEST(string_end_with); 79 | RUN_TEST(string_start_with); 80 | 81 | RUN_TEST(string_concat_object); 82 | RUN_TEST(string_concat_c_str); 83 | RUN_TEST(string_concat_c_slice); 84 | 85 | RUN_TEST(string_append); 86 | RUN_TEST(string_repeat_append); 87 | 88 | RUN_TEST(string_trans_clear); 89 | RUN_TEST(string_trans_reverse); 90 | 91 | RUN_TEST(string_index); 92 | RUN_TEST(string_const_iterator); 93 | RUN_TEST(string_reverse_const_iterator); 94 | 95 | RUN_TEST(string_find); 96 | RUN_TEST(string_find_large); 97 | RUN_TEST(string_find_or_error); 98 | RUN_TEST(string_contain); 99 | 100 | RUN_TEST(string_retain_all); 101 | RUN_TEST(string_retain_endwith); 102 | RUN_TEST(string_retain_startwith); 103 | 104 | RUN_TEST(itoa_int_index); 105 | RUN_TEST(itoa_int_type); 106 | RUN_TEST(itoa_int_sign); 107 | RUN_TEST(itoa_int_from); 108 | RUN_TEST(itoa_uint_index); 109 | RUN_TEST(itoa_uint_type); 110 | RUN_TEST(itoa_uint_from); 111 | 112 | RUN_TEST(qtoa_signed); 113 | RUN_TEST(qtoa_signed_from); 114 | RUN_TEST(qtoa_unsigned); 115 | RUN_TEST(qtoa_unsigned_from); 116 | 117 | RUN_TEST(ftoa_basic); 118 | 119 | RUN_TEST(parser_err_invaild_begin); 120 | RUN_TEST(parser_err_invaild_end); 121 | RUN_TEST(parser_err_invaild_fmt_spec); 122 | 123 | RUN_TEST(fmt_integer_i8); 124 | RUN_TEST(fmt_integer_i16); 125 | RUN_TEST(fmt_integer_i32); 126 | RUN_TEST(fmt_integer_u8); 127 | RUN_TEST(fmt_integer_u16); 128 | RUN_TEST(fmt_integer_u32); 129 | 130 | RUN_TEST(fmt_integer_array_i8); 131 | RUN_TEST(fmt_integer_array_i16); 132 | RUN_TEST(fmt_integer_array_i32); 133 | RUN_TEST(fmt_integer_array_u8); 134 | RUN_TEST(fmt_integer_array_u16); 135 | RUN_TEST(fmt_integer_array_u32); 136 | 137 | RUN_TEST(fmt_align_left); 138 | RUN_TEST(fmt_align_left_fill); 139 | RUN_TEST(fmt_align_left_long); 140 | RUN_TEST(fmt_align_right); 141 | RUN_TEST(fmt_align_right_fill); 142 | RUN_TEST(fmt_align_right_long); 143 | RUN_TEST(fmt_align_middle); 144 | RUN_TEST(fmt_align_middle_fill); 145 | RUN_TEST(fmt_align_middle_long); 146 | RUN_TEST(fmt_seq_arg_id); 147 | RUN_TEST(fmt_seq_arg_id_err); 148 | 149 | RUN_TEST(fmt_quat_value_sign); 150 | RUN_TEST(fmt_quat_value_dualprec); 151 | RUN_TEST(fmt_quat_value_singprec); 152 | 153 | RUN_TEST(fmt_behav_signed_bin); 154 | RUN_TEST(fmt_behav_signed_oct); 155 | RUN_TEST(fmt_behav_signed_dec); 156 | RUN_TEST(fmt_behav_signed_hex); 157 | 158 | RUN_TEST(fmt_sign_add); 159 | RUN_TEST(fmt_sign_sub); 160 | RUN_TEST(fmt_sign_space); 161 | 162 | RUN_TEST(fmt_sty_bin); 163 | RUN_TEST(fmt_sty_oct); 164 | RUN_TEST(fmt_sty_dec); 165 | RUN_TEST(fmt_sty_hex); 166 | RUN_TEST(fmt_sty_hex_prefix); 167 | 168 | RUN_TEST(fmt_array); 169 | RUN_TEST(fmt_array_element_style); 170 | RUN_TEST(fmt_array_userdefind_split); 171 | 172 | RUN_TEST(fmt_chrono_default_f); 173 | RUN_TEST(fmt_chrono_default_g); 174 | RUN_TEST(fmt_chrono_userdef_year); 175 | RUN_TEST(fmt_chrono_userdef_month); 176 | RUN_TEST(fmt_chrono_userdef_day); 177 | RUN_TEST(fmt_chrono_userdef_hour); 178 | RUN_TEST(fmt_chrono_userdef_minute); 179 | RUN_TEST(fmt_chrono_userdef_second); 180 | RUN_TEST(fmt_chrono_userdef_subsecond); 181 | RUN_TEST(fmt_chrono_userdef_week); 182 | 183 | RUN_TEST(fmt_escape_bracket); 184 | 185 | RUN_TEST(sync_io_write); 186 | 187 | RUN_TEST(cpp_wrap_fmt); 188 | RUN_TEST(cpp_wrap_fmt_parser); 189 | 190 | usize_t alloc_cnt, free_cnt, usage_mark; 191 | mstr_heap_get_allocate_count(&alloc_cnt, &free_cnt); 192 | usage_mark = RUNTIME_HEAP_SIZE - mstr_heap_get_high_water_mark(); 193 | printf("\nHeap usage: %u bytes(max)\n", (uint32_t)usage_mark); 194 | printf(" %u times allocating\n", (uint32_t)alloc_cnt); 195 | printf(" %u times free\n", (uint32_t)free_cnt); 196 | 197 | TEST_ASSERT_TRUE_MESSAGE( 198 | alloc_cnt == free_cnt, "alloc_cnt != free_cnt" 199 | ); 200 | 201 | return UNITY_END(); 202 | } 203 | -------------------------------------------------------------------------------- /inc/mm_type.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0 2 | /** 3 | * @file mm_type.hpp 4 | * @author 向阳 (hinata.hoshino@foxmail.com) 5 | * @brief type trait, type alias和type def 6 | * @version 1.0 7 | * @date 2023-06-15 8 | * 9 | * @copyright Copyright (c) 向阳, all rights reserved. 10 | * 11 | */ 12 | #if !defined(_INCLUDE_MM_TYPE_HPP_) 13 | #define _INCLUDE_MM_TYPE_HPP_ 1 14 | #include "mm_cfg.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | namespace mtfmt 21 | { 22 | namespace details 23 | { 24 | /** 25 | * @brief 单位类型 26 | * 27 | */ 28 | struct unit_t 29 | { 30 | constexpr bool operator==(unit_t) noexcept 31 | { 32 | return true; 33 | } 34 | 35 | constexpr bool operator!=(unit_t) noexcept 36 | { 37 | return false; 38 | } 39 | 40 | constexpr bool operator<(unit_t) noexcept 41 | { 42 | return false; 43 | } 44 | 45 | constexpr bool operator>(unit_t) noexcept 46 | { 47 | return false; 48 | } 49 | 50 | constexpr bool operator<=(unit_t) noexcept 51 | { 52 | return true; 53 | } 54 | 55 | constexpr bool operator>=(unit_t) noexcept 56 | { 57 | return true; 58 | } 59 | }; 60 | 61 | /** 62 | * @brief 用于存放函数类型信息的东东 63 | */ 64 | template struct function_traits_base 65 | { 66 | //! 返回值类型 67 | using return_type_t = Ret; 68 | //! 参数数目 69 | static constexpr size_t NArgs = sizeof...(Args); 70 | //! 参数(元组) 71 | using arg_tuple_t = std::tuple; 72 | }; 73 | 74 | template struct function_trait_impl; 75 | 76 | template 77 | struct function_trait_impl> 78 | : public function_trait_impl 79 | { 80 | }; 81 | 82 | // for: function pointer 83 | template 84 | struct function_trait_impl 85 | : public function_traits_base 86 | { 87 | }; 88 | 89 | // for: member function pointer 90 | template 91 | struct function_trait_impl 92 | : public function_traits_base 93 | { 94 | }; 95 | 96 | // for: const member function pointer 97 | template 98 | struct function_trait_impl 99 | : public function_traits_base 100 | { 101 | }; 102 | 103 | // 除去如上三种情况的case 104 | template 105 | struct function_trait_impl 106 | : public function_trait_impl 107 | { 108 | }; 109 | 110 | /** 111 | * @brief 取得函数的各种细节 112 | * 113 | */ 114 | template 115 | struct function_trait 116 | : public function_trait_impl::type> 117 | { 118 | }; 119 | 120 | /** 121 | * @brief 取得函数的返回值类型 122 | * 123 | */ 124 | template 125 | using function_return_type_t = 126 | typename function_trait::return_type_t; 127 | 128 | /** 129 | * @brief 取得函数的参数类型 130 | * 131 | */ 132 | template 133 | using function_arg_tuple_t = typename function_trait::arg_tuple_t; 134 | 135 | /** 136 | * @brief 取得函数的参数的个数 137 | * 138 | */ 139 | template struct function_arg_count 140 | { 141 | static constexpr std::size_t N = function_trait::NArgs; 142 | }; 143 | 144 | /** 145 | * @brief 判断Ti是否为模板Tt的实例化 146 | * 147 | */ 148 | template