From 2a67f9cb2f6f4545213c8290be380126746b7af4 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 24 Nov 2025 16:06:17 +0900 Subject: [PATCH 1/8] Optimize dispstring parsing --- sfparse.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/sfparse.c b/sfparse.c index 77ad937..cfd5870 100644 --- a/sfparse.c +++ b/sfparse.c @@ -856,27 +856,30 @@ static int parser_dispstring(sfparse_parser *sfp, sfparse_value *dest) { case 0: return SFPARSE_ERR_PARSE; case 1: - if (utf8state != UTF8_ACCEPT) { - return SFPARSE_ERR_PARSE; - } - ++sfp->pos; break; case 2: - ++sfp->pos; + for (;;) { + ++sfp->pos; - if (sfp->pos + 2 > sfp->end) { - return SFPARSE_ERR_PARSE; - } + if (sfp->pos + 2 > sfp->end || pctdecode(&c, &sfp->pos) != 0) { + return SFPARSE_ERR_PARSE; + } - if (pctdecode(&c, &sfp->pos) != 0) { - return SFPARSE_ERR_PARSE; - } + utf8_decode(&utf8state, c); + if (utf8state == UTF8_ACCEPT) { + if (sfp->pos != sfp->end && *sfp->pos == '%') { + continue; + } - utf8_decode(&utf8state, c); - if (utf8state == UTF8_REJECT) { - return SFPARSE_ERR_PARSE; + break; + } + + if (utf8state == UTF8_REJECT || sfp->pos + 1 > sfp->end || + *sfp->pos != '%') { + return SFPARSE_ERR_PARSE; + } } break; From 87e6456eeed393d8b83c930073e0aa77d0839810 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:52:26 +0000 Subject: [PATCH 2/8] Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1542d5a..c9d8f70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Linux setup @@ -93,7 +93,7 @@ jobs: HOST: ${{ matrix.host }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Prepare for i386 From 3817187c74b61137875837083fdf98385ceb16fc Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 23 Dec 2025 17:54:17 +0900 Subject: [PATCH 3/8] Remove redundant check --- sfparse.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sfparse.c b/sfparse.c index cfd5870..cbcf813 100644 --- a/sfparse.c +++ b/sfparse.c @@ -884,9 +884,7 @@ static int parser_dispstring(sfparse_parser *sfp, sfparse_value *dest) { break; case 3: - if (utf8state != UTF8_ACCEPT) { - return SFPARSE_ERR_PARSE; - } + assert(utf8state == UTF8_ACCEPT); if (dest) { dest->type = SFPARSE_TYPE_DISPSTRING; From 2982d3cb7a85815460ffa7015306d3f9accc36b6 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 4 Mar 2026 19:41:06 +0900 Subject: [PATCH 4/8] Build with macos-26 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9d8f70..227073f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: build: strategy: matrix: - os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, macos-15] + os: [ubuntu-24.04, ubuntu-24.04-arm, macos-26, macos-15] compiler: [gcc, clang] runs-on: ${{ matrix.os }} From aa3c99a4652c47e9c2a1cebbafee7a3ca1227795 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 4 Jun 2026 20:59:31 +0900 Subject: [PATCH 5/8] Upper case hex literals --- sfparse.c | 38 +++++++++++++++++++------------------- sfparse.h | 4 ++-- sfparse_test.c | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/sfparse.c b/sfparse.c index cbcf813..c312c04 100644 --- a/sfparse.c +++ b/sfparse.c @@ -34,18 +34,18 @@ # include #endif /* __AVX2__ */ -#define SFPARSE_STATE_DICT 0x08u -#define SFPARSE_STATE_LIST 0x10u -#define SFPARSE_STATE_ITEM 0x18u +#define SFPARSE_STATE_DICT 0x08U +#define SFPARSE_STATE_LIST 0x10U +#define SFPARSE_STATE_ITEM 0x18U -#define SFPARSE_STATE_INNER_LIST 0x04u +#define SFPARSE_STATE_INNER_LIST 0x04U -#define SFPARSE_STATE_BEFORE 0x00u -#define SFPARSE_STATE_BEFORE_PARAMS 0x01u -#define SFPARSE_STATE_PARAMS 0x02u -#define SFPARSE_STATE_AFTER 0x03u +#define SFPARSE_STATE_BEFORE 0x00U +#define SFPARSE_STATE_BEFORE_PARAMS 0x01U +#define SFPARSE_STATE_PARAMS 0x02U +#define SFPARSE_STATE_AFTER 0x03U -#define SFPARSE_STATE_OP_MASK 0x03u +#define SFPARSE_STATE_OP_MASK 0x03U #define SFPARSE_SET_STATE_AFTER(NAME) \ (SFPARSE_STATE_##NAME | SFPARSE_STATE_AFTER) @@ -69,7 +69,7 @@ #define SFPARSE_STATE_ITEM_INNER_LIST_BEFORE \ SFPARSE_SET_STATE_INNER_LIST_BEFORE(ITEM) -#define SFPARSE_STATE_INITIAL 0x00u +#define SFPARSE_STATE_INITIAL 0x00U #define LCALPHAS \ ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1, \ @@ -200,7 +200,7 @@ static int parser_key(sfparse_parser *sfp, sfparse_vec *dest) { #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { - last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); + last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1FU); sfp->pos = find_char_key(sfp->pos, last); if (sfp->pos != last) { @@ -349,7 +349,7 @@ static const uint8_t *find_char_string(const uint8_t *first, const uint8_t *last) { const __m256i bs = _mm256_set1_epi8('\\'); const __m256i dq = _mm256_set1_epi8('"'); - const __m256i del = _mm256_set1_epi8(0x7f); + const __m256i del = _mm256_set1_epi8(0x7F); const __m256i sp = _mm256_set1_epi8(' '); __m256i s, x; uint32_t m; @@ -394,7 +394,7 @@ static int parser_string(sfparse_parser *sfp, sfparse_value *dest) { #ifdef __AVX2__ for (; sfp->end - sfp->pos >= 32; ++sfp->pos) { - last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); + last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1FU); sfp->pos = find_char_string(sfp->pos, last); if (sfp->pos == last) { @@ -541,7 +541,7 @@ static int parser_token(sfparse_parser *sfp, sfparse_value *dest) { #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { - last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); + last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1FU); sfp->pos = find_char_token(sfp->pos, last); if (sfp->pos != last) { @@ -622,7 +622,7 @@ static int parser_byteseq(sfparse_parser *sfp, sfparse_value *dest) { #ifdef __AVX2__ if (sfp->end - sfp->pos >= 32) { - last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1fu); + last = sfp->pos + ((sfp->end - sfp->pos) & ~0x1FU); sfp->pos = find_char_byteseq(sfp->pos, last); } #endif /* __AVX2__ */ @@ -1429,8 +1429,8 @@ void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src) { } *o++ = (uint8_t)(n >> 16); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; + *o++ = (n >> 8) & 0xFFU; + *o++ = n & 0xFFU; } switch (left) { @@ -1467,8 +1467,8 @@ void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src) { n = (uint32_t)(index_tbl[*p++] << 10); n += (uint32_t)(index_tbl[*p++] << 4); n += (uint32_t)(index_tbl[*p++] >> 2); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; + *o++ = (n >> 8) & 0xFFU; + *o++ = n & 0xFFU; break; } diff --git a/sfparse.h b/sfparse.h index 9341221..7f36ce8 100644 --- a/sfparse.h +++ b/sfparse.h @@ -130,7 +130,7 @@ typedef struct sfparse_vec { * * :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set. */ -#define SFPARSE_VALUE_FLAG_NONE 0x0u +#define SFPARSE_VALUE_FLAG_NONE 0x0U /** * @macro @@ -138,7 +138,7 @@ typedef struct sfparse_vec { * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string * contains escaped character(s). */ -#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u +#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1U /** * @struct diff --git a/sfparse_test.c b/sfparse_test.c index 5c1952a..49780ab 100644 --- a/sfparse_test.c +++ b/sfparse_test.c @@ -3848,8 +3848,8 @@ void test_sfparse_parser_string_generated(void) { rv = sfparse_parser_item(&sfp, &val); - if (i == 0x20 || i == 0x21 || (0x23 <= i && i <= 0x5b) || - (0x5d <= i && i <= 0x7e) || i == 0x22) { + if (i == 0x20 || i == 0x21 || (0x23 <= i && i <= 0x5B) || + (0x5D <= i && i <= 0x7E) || i == 0x22) { assert_int(0, ==, rv); rv = sfparse_parser_item(&sfp, NULL); From a25477627b6f38be50fb8b8a1c6d0c0ff9df08fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:22:36 +0000 Subject: [PATCH 6/8] Bump actions/checkout from 6 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 227073f..422e500 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive - name: Linux setup @@ -93,7 +93,7 @@ jobs: HOST: ${{ matrix.host }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive - name: Prepare for i386 From c2151bb9cebac3494c1dbf15503e2e549de31b31 Mon Sep 17 00:00:00 2001 From: xiaozhuai <798047000@qq.com> Date: Tue, 28 Jul 2026 15:23:33 +0800 Subject: [PATCH 7/8] Add SFPARSE_API for shared library symbol visibility --- sfparse.h | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/sfparse.h b/sfparse.h index 7f36ce8..31a02c0 100644 --- a/sfparse.h +++ b/sfparse.h @@ -33,6 +33,26 @@ # define WIN32 #endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ +#if defined(_WIN32) +# if defined(SFPARSE_STATIC) +# define SFPARSE_API +# elif defined(SFPARSE_BUILDING) +# define SFPARSE_API __declspec(dllexport) +# else +# define SFPARSE_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) || defined(__clang__) +# if defined(SFPARSE_STATIC) +# define SFPARSE_API +# elif defined(SFPARSE_BUILDING) +# define SFPARSE_API __attribute__((visibility("default"))) +# else +# define SFPARSE_API +# endif +#else +# define SFPARSE_API +#endif + #ifdef __cplusplus extern "C" { #endif /* defined(__cplusplus) */ @@ -247,8 +267,8 @@ typedef struct sfparse_parser { * `sfparse_parser_init` initializes |sfp| with the given data encoded * in Structured Field Values pointed by |data| of length |datalen|. */ -void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, - size_t datalen); +SFPARSE_API void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, + size_t datalen); /** * @function @@ -266,8 +286,8 @@ void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, * it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error * while parsing field value. */ -int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, - sfparse_value *dest_value); +SFPARSE_API int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function @@ -294,8 +314,8 @@ int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, - sfparse_value *dest_value); +SFPARSE_API int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function @@ -318,7 +338,7 @@ int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); +SFPARSE_API int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function @@ -345,7 +365,7 @@ int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); +SFPARSE_API int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); /** * @function @@ -372,7 +392,8 @@ int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); +SFPARSE_API int sfparse_parser_inner_list(sfparse_parser *sfp, + sfparse_value *dest); /** * @function @@ -393,7 +414,7 @@ int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); * This function sets the length of unescaped string to * :member:`dest->len `. */ -void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); +SFPARSE_API void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); /** * @function @@ -412,7 +433,8 @@ void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); * This function sets the length of decoded byte string to * :member:`dest->len `. */ -void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); +SFPARSE_API void sfparse_base64decode(sfparse_vec *dest, + const sfparse_vec *src); /** * @function @@ -433,7 +455,7 @@ void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); * This function sets the length of decoded byte string to * :member:`dest->len `. */ -void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); +SFPARSE_API void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); #ifdef __cplusplus } From 64768215ac73ff4e7f25d6314550dcc4d166ad29 Mon Sep 17 00:00:00 2001 From: xiaozhuai <798047000@qq.com> Date: Tue, 28 Jul 2026 15:24:26 +0800 Subject: [PATCH 8/8] Add cmake build system support --- CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ce3f7d9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.15) +project(sfparse C) + +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(SFPARSE_ENABLE_INSTALL "Enable install" OFF) + +include(GNUInstallDirs) + +add_library(sfparse sfparse.c sfparse.h) +add_library(sfparse::sfparse ALIAS sfparse) + +set(DEFINITIONS) +list(APPEND DEFINITIONS SFPARSE_BUILDING) +if(NOT BUILD_SHARED_LIBS) + list(APPEND DEFINITIONS SFPARSE_STATIC) +endif() +target_compile_definitions(sfparse PRIVATE ${DEFINITIONS}) +target_include_directories(sfparse PUBLIC + "$" + "$" +) + +if(SFPARSE_ENABLE_INSTALL) + install( + FILES "${CMAKE_CURRENT_LIST_DIR}/sfparse.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/sfparse" + ) + install(TARGETS sfparse EXPORT sfparse) + install( + EXPORT sfparse + NAMESPACE sfparse:: + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/sfparse" + ) +endif()