From 22f25b53fc030e73c3525fa09ef8144e84fd252e Mon Sep 17 00:00:00 2001 From: Sirac Ozmen Date: Thu, 5 Nov 2026 08:13:43 +0300 Subject: [PATCH 1/3] build(deps): move Maul UI to 1.3.3 and Maul Unicode to 1.1.1 Maul UI 1.3.3 and Maul Unicode 1.1.1 mark every status they return as must-use under GCC 14 in C23 too. Unicode 1.1.0 also refuses an iterator no init call set up. UI 1.3.3 still names Unicode 1.0.0, which 1.1.1 serves within 1.x. --- docs/operations/dependencies.md | 4 +- third_party/maul-ui/include/maul-ui/base.h | 13 +- third_party/maul-unicode/CMakeLists.txt | 5 +- .../maul-unicode/include/maul-unicode/base.h | 15 +- .../include/maul-unicode/script.h | 13 +- .../include/maul-unicode/segment.h | 35 ++-- third_party/maul-unicode/src/cursor.h | 27 +++ third_party/maul-unicode/src/grapheme.c | 2 + third_party/maul-unicode/src/invariant.h | 6 + third_party/maul-unicode/src/line_rules.h | 3 + third_party/maul-unicode/src/script_runs.c | 100 +++++++--- third_party/maul-unicode/src/script_state.h | 35 ++++ third_party/maul-unicode/src/segment.c | 187 ++++++++++++++++-- third_party/maul-unicode/src/segmenter.h | 18 ++ third_party/maul-unicode/src/sentence.c | 2 + 15 files changed, 393 insertions(+), 72 deletions(-) create mode 100644 third_party/maul-unicode/src/script_state.h diff --git a/docs/operations/dependencies.md b/docs/operations/dependencies.md index 0a0f23623..3861aa7cf 100644 --- a/docs/operations/dependencies.md +++ b/docs/operations/dependencies.md @@ -15,10 +15,10 @@ Every third-party library is vendored under `third_party/` at an exact revision | Kest | `Rawframe-Project/kest` | `fa092f540ffe2cb94f75199842792fb61e68451f` | MIT | `include/`, `src/` except `main.c`, `lib/`, `LICENSE` | | Maul2D 0.6.0 | `Rawframe-Project/maul2d` release archive `maul2d-0.6.0.tar.gz`, SHA-256 `f97262cd98a47e2f52ed7a811ce144bff1c3f51931323457d1313484c9a845ae` | `501ac5c191658435cbdd4f70a0abd6cdd9e37f03` (`v0.6.0`) | MIT | `include/`, `src/`, `LICENSE`; the build file is the engine's | | Maul3D 0.7.0 | `Rawframe-Project/maul3d` release archive `maul3d-0.7.0.tar.gz`, SHA-256 `b4e42736c2c8413f8218846b9ce777bab79506d75c620707d571f24cb0632d4f` | `bf11f1fc3976ffb8b2ab4e4604252e760aea221c` (`v0.7.0`) | MIT | `include/`, `src/`, `LICENSE`; the build file is the engine's | -| Maul Unicode 1.0.0 | `Rawframe-Project/maul-unicode` release archive `maul-unicode-1.0.0.tar.gz`, SHA-256 `3e1eea1ae0f7f508771d272baa53684872ea508a306110d1f6b42afb7f3292b6` | `cc997f185471d1c28eb90d42cc8ddd095674e158` (`v1.0.0`) | MIT; its Unicode tables Unicode-3.0 | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | +| Maul Unicode 1.1.1 | `Rawframe-Project/maul-unicode` release archive `maul-unicode-1.1.1.tar.gz`, SHA-256 `89f231e4d331bed957210b733dd07f8020ae8e0476718caf3d906e1544806ef7` | `39c4a2c43012b1cb4aadfa738c7691e17c104d78` (`v1.1.1`) | MIT; its Unicode tables Unicode-3.0 | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | | Maul Window 0.12.0 | `Rawframe-Project/maul-window` release archive `maul-window-0.12.0.tar.gz`, SHA-256 `389b522f338f9584c2d96e682ef60a0c0dd31a5939ef2770b52d7df2247e1764` | `f904519e6c8f86071862080bdbca6f14065f7ea1` (`v0.12.0`) | MIT | `include/`, `src/`, `cmake/`, `protocols/`, `java/`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE` | | Maul RHI 0.15.0 | `Rawframe-Project/maul-rhi` release archive `maul-rhi-0.15.0.tar.gz`, SHA-256 `ef0e1ec5214cdfbdaa2bc3b31c1f5558cbfbfef07889192eda79f456bbb729fa` | `57f11cf50da9380dfa6af75eb49ff6612e7d3387` (`v0.15.0`) | MIT; Khronos headers Apache-2.0 or MIT; DirectX headers MIT | `include/`, `src/`, `khronos/`, `directx/`, `cmake/`, the container, MSL and DXIL tools, `contract/mrhi.json`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE` | -| Maul UI 1.3.2 | `Rawframe-Project/maul-ui` release archive `maul-ui-1.3.2.tar.gz`, SHA-256 `64fb2c13279c087b1352fa4cb4caf86cb1951e4aca0af47976d970659cd3b806` | `ea8e2c6fe328eb3c94071e5bbe3dde08c95374a3` (`v1.3.2`) | MIT | `include/`, `src/`, `cmake/`, `java/`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE`, `docs/user/licenses.md` | +| Maul UI 1.3.3 | `Rawframe-Project/maul-ui` release archive `maul-ui-1.3.3.tar.gz`, SHA-256 `ba431b0f5b48dac01db208580a832ea44cb0792b4a9641186805630c2033d62f` | `713c044bf1a45fe0f537ae8ef0adba71abcff17c` (`v1.3.3`) | MIT | `include/`, `src/`, `cmake/`, `java/`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE`, `docs/user/licenses.md` | | Maul Tween 1.0.0 | `Rawframe-Project/maul-tween` release archive `maul-tween-1.0.0.tar.gz`, SHA-256 `8c3776f79cf482adc98a9b220e5405d6c5dde0e48342c9ab06d89f74d72e10f4` | `990fc0ef60ffb672b968043125f7fcb23a8388fa` (`v1.0.0`) | MIT | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | | Maul Nav 0.2.0 | `Rawframe-Project/maul-nav` release archive `maul-nav-0.2.0.tar.gz`, SHA-256 `e5867312f2c981a198f774cead5692e4fce15f412af2af9e7330957d663dc8fd` | `db1e65197b809af34689aeca4e6a6da37f95f08f` (`v0.2.0`) | MIT | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | | Maul Audio 0.4.0 | `Rawframe-Project/maul-audio` release archive `maul-audio-0.4.0.tar.gz`, SHA-256 `572bf30ba89f56b6a32452585bf46265ece3a1ec285b5233c04cff110ff4926d` | `fbad1b28250657a2b5704830454c8bc097dbb590` (`v0.4.0`) | MIT | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | diff --git a/third_party/maul-ui/include/maul-ui/base.h b/third_party/maul-ui/include/maul-ui/base.h index b0996dc55..8f920b3dd 100644 --- a/third_party/maul-ui/include/maul-ui/base.h +++ b/third_party/maul-ui/include/maul-ui/base.h @@ -25,7 +25,7 @@ extern "C" #define MUI_VERSION_MINOR 3 // x-release-please-end // x-release-please-start-patch -#define MUI_VERSION_PATCH 2 +#define MUI_VERSION_PATCH 3 // x-release-please-end // The binary interface a program is built for: the major and minor @@ -67,14 +67,21 @@ extern "C" // MUI_NODISCARD marks a function whose result must be read: every // function that returns a status. The attribute is standard in C23 and // C++17 and left out for older dialects. MSVC keeps __cplusplus at -// 199711L unless /Zc:__cplusplus is given, so _MSVC_LANG is read too. +// 199711L unless /Zc:__cplusplus is given, so _MSVC_LANG is read too. In +// C, any dialect after C17 whose compiler has the attribute takes it: +// GCC 14 reports 202000L in its C23 mode. #if (defined(__cplusplus) && __cplusplus >= 201703L) || \ (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #define MUI_NODISCARD [[nodiscard]] -#elif !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#elif !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L && \ + defined(__has_c_attribute) +#if __has_c_attribute(nodiscard) #define MUI_NODISCARD [[nodiscard]] #else #define MUI_NODISCARD +#endif +#else +#define MUI_NODISCARD #endif // The status a fallible function returns. Zero is success, positive diff --git a/third_party/maul-unicode/CMakeLists.txt b/third_party/maul-unicode/CMakeLists.txt index 181ba1715..83ae4bf27 100644 --- a/third_party/maul-unicode/CMakeLists.txt +++ b/third_party/maul-unicode/CMakeLists.txt @@ -224,7 +224,7 @@ if(MAUL_UNICODE_FUZZ) target_compile_options(maul-unicode PRIVATE -fsanitize=fuzzer-no-link,address,undefined -fno-sanitize-recover=all) target_link_options(maul-unicode PUBLIC -fsanitize=address,undefined -fno-sanitize-recover=all) - set(MUNI_FUZZ_AREAS encoding segment bidi bidi_long script) + set(MUNI_FUZZ_AREAS encoding segment bidi bidi_long script iterators) if(MAUL_UNICODE_NORMALIZATION) list(APPEND MUNI_FUZZ_AREAS normalize) endif() @@ -249,6 +249,9 @@ if(MAUL_UNICODE_FUZZ) target_link_options(fuzz_${area} PRIVATE -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=all) endforeach() + # fuzz_iterators sets the fields of the iterators' state, which only + # src/ declares. + target_include_directories(fuzz_iterators PRIVATE src) # ICU is an oracle for the tests alone, never linked into the library. if(TARGET fuzz_icu) find_package(PkgConfig REQUIRED) diff --git a/third_party/maul-unicode/include/maul-unicode/base.h b/third_party/maul-unicode/include/maul-unicode/base.h index 437dc7e13..a69783f8b 100644 --- a/third_party/maul-unicode/include/maul-unicode/base.h +++ b/third_party/maul-unicode/include/maul-unicode/base.h @@ -23,10 +23,10 @@ extern "C" #define MUNI_VERSION_MAJOR 1 // x-release-please-end // x-release-please-start-minor -#define MUNI_VERSION_MINOR 0 +#define MUNI_VERSION_MINOR 1 // x-release-please-end // x-release-please-start-patch -#define MUNI_VERSION_PATCH 0 +#define MUNI_VERSION_PATCH 1 // x-release-please-end // The one Unicode version every table in the library comes from. @@ -64,14 +64,21 @@ extern "C" // MUNI_NODISCARD marks a function whose result must be read: every // function that returns a status. The attribute is standard in C23 and // C++17 and left out for older dialects. MSVC keeps __cplusplus at -// 199711L unless /Zc:__cplusplus is given, so _MSVC_LANG is read too. +// 199711L unless /Zc:__cplusplus is given, so _MSVC_LANG is read too. In +// C, any dialect after C17 whose compiler has the attribute takes it: +// GCC 14 reports 202000L in its C23 mode. #if (defined(__cplusplus) && __cplusplus >= 201703L) || \ (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #define MUNI_NODISCARD [[nodiscard]] -#elif !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#elif !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L && \ + defined(__has_c_attribute) +#if __has_c_attribute(nodiscard) #define MUNI_NODISCARD [[nodiscard]] #else #define MUNI_NODISCARD +#endif +#else +#define MUNI_NODISCARD #endif // The status a fallible function returns. Zero is success, positive diff --git a/third_party/maul-unicode/include/maul-unicode/script.h b/third_party/maul-unicode/include/maul-unicode/script.h index 531fbe4b6..486e550fb 100644 --- a/third_party/maul-unicode/include/maul-unicode/script.h +++ b/third_party/maul-unicode/include/maul-unicode/script.h @@ -28,7 +28,10 @@ extern "C" #endif // An iterator over script runs. Its contents are private; it is plain - // data the caller keeps anywhere and needs no cleanup. + // data the caller keeps anywhere and needs no cleanup. A copy of an + // iterator an init call set up is one too; every call refuses one no + // init call set up, such as a zeroed one or one copied from other + // bytes, as an invalid argument. typedef struct muniScriptIterator { uint64_t opaque[26]; @@ -65,8 +68,9 @@ extern "C" /// @param moreFollows true when still more pieces will be fed. /// @return `muni_success`; `muni_errorState` for an iterator that did /// not ask for more text, such as one initialized without more - /// text to follow; `muni_errorInvalid` for a NULL argument. - /// `muni_errorInvalid` is reported before `muni_errorState`. + /// text to follow; `muni_errorInvalid` for a NULL argument or + /// an iterator no init call set up. `muni_errorInvalid` is + /// reported before `muni_errorState`. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_NODISCARD MUNI_API muniResult muniScriptIterator_Feed(muniScriptIterator* iterator, @@ -83,7 +87,8 @@ extern "C" /// returns `muni_success`. /// @return `muni_success` with a run; `muni_done` after the last one; /// `muni_needMoreText` when the run's end or script depends on - /// text not yet fed; `muni_errorInvalid` for a NULL argument. + /// text not yet fed; `muni_errorInvalid` for a NULL argument or + /// an iterator no init call set up. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_NODISCARD MUNI_API muniResult muniScriptIterator_NextRun(muniScriptIterator* iterator, diff --git a/third_party/maul-unicode/include/maul-unicode/segment.h b/third_party/maul-unicode/include/maul-unicode/segment.h index 8192b7a92..3a27577ca 100644 --- a/third_party/maul-unicode/include/maul-unicode/segment.h +++ b/third_party/maul-unicode/include/maul-unicode/segment.h @@ -36,7 +36,9 @@ extern "C" // An iterator over grapheme cluster, word or sentence boundaries or // line break opportunities. Its contents are private; it is plain data // the caller keeps anywhere, usually on the stack, and needs no - // cleanup. + // cleanup. A copy of an iterator an init call set up is one too; every + // call refuses one no init call set up, such as a zeroed one or one + // copied from other bytes, as an invalid argument. typedef struct muniSegmentIterator { uint64_t opaque[16]; @@ -192,7 +194,8 @@ extern "C" /// @return `muni_success`; `muni_errorState` for an iterator already /// asked for a break; `muni_errorBusy` when called from inside /// the iterator's word segmenter; `muni_errorInvalid` for a NULL - /// iterator or another kind of iterator. `muni_errorInvalid` is + /// iterator, one no init call set up or another kind of + /// iterator. `muni_errorInvalid` is /// reported before `muni_errorBusy`, and that before /// `muni_errorState`. /// @par Thread safety @@ -236,7 +239,8 @@ extern "C" /// assigns. /// @return `muni_success`; `muni_errorState` for an iterator already /// asked for a boundary; `muni_errorInvalid` for a NULL - /// iterator, NULL overrides with a nonzero count, another kind + /// iterator or one no init call set up, NULL overrides with a + /// nonzero count, another kind /// of iterator, a count over 0x10F800, a code point past /// U+10FFFF or a surrogate, code points out of order or /// repeated, or a value that is not a class of the iterator's @@ -257,7 +261,8 @@ extern "C" /// @return `muni_success`; `muni_errorState` for an iterator already /// asked for a break; `muni_errorBusy` when called from inside /// the iterator's word segmenter; `muni_errorInvalid` for a NULL - /// iterator, another kind of iterator or an unknown strictness. + /// iterator, one no init call set up, another kind of iterator + /// or an unknown strictness. /// `muni_errorInvalid` is reported before `muni_errorBusy`, and /// that before `muni_errorState`. /// @par Thread safety @@ -270,7 +275,8 @@ extern "C" /// @param iterator The iterator. /// @return The strictness muniSegmentIterator_SetLineBreakStrictness /// set, muni_lineBreakNormal when none was set, and for a NULL - /// iterator or another kind of iterator. + /// iterator, one no init call set up or another kind of + /// iterator. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_API muniLineBreakStrictness @@ -284,7 +290,8 @@ extern "C" /// @return `muni_success`; `muni_errorState` for an iterator already /// asked for a break; `muni_errorBusy` when called from inside /// the iterator's word segmenter; `muni_errorInvalid` for a NULL - /// iterator, another kind of iterator or an unknown writing + /// iterator, one no init call set up, another kind of iterator + /// or an unknown writing /// system. `muni_errorInvalid` is reported before /// `muni_errorBusy`, and that before `muni_errorState`. /// @par Thread safety @@ -296,8 +303,8 @@ extern "C" /// /// @param iterator The iterator. /// @return The writing system muniSegmentIterator_SetWritingSystem set, - /// muni_writingOther when none was set, and for a NULL iterator - /// or another kind of iterator. + /// muni_writingOther when none was set, and for a NULL iterator, + /// one no init call set up or another kind of iterator. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_API muniWritingSystem @@ -314,9 +321,9 @@ extern "C" /// not ask for more text, such as one initialized without more /// text to follow or one fed before it asked; `muni_errorBusy` /// when called from inside the iterator's word segmenter; - /// `muni_errorInvalid` for a NULL argument. `muni_errorInvalid` - /// is reported before `muni_errorBusy`, and that before - /// `muni_errorState`. + /// `muni_errorInvalid` for a NULL argument or an iterator no + /// init call set up. `muni_errorInvalid` is reported before + /// `muni_errorBusy`, and that before `muni_errorState`. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_NODISCARD MUNI_API muniResult muniSegmentIterator_Feed(muniSegmentIterator* iterator, @@ -333,7 +340,8 @@ extern "C" /// one; `muni_needMoreText` when the next boundary depends on /// text not yet fed; `muni_errorBusy` when called from inside /// the iterator's word segmenter, and then the iterator is left - /// as it was; `muni_errorInvalid` for a NULL argument. + /// as it was; `muni_errorInvalid` for a NULL argument or an + /// iterator no init call set up. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_NODISCARD MUNI_API muniResult muniSegmentIterator_NextBreak(muniSegmentIterator* iterator, @@ -390,7 +398,8 @@ extern "C" /// `muni_success`. /// @return `muni_success`; `muni_errorBusy` when called from inside the /// iterator's word segmenter; `muni_errorInvalid` for a NULL - /// argument or an iterator of another kind. + /// argument, an iterator no init call set up or one of another + /// kind. /// @par Thread safety /// Safe from any thread; an iterator is used by one thread at a time. MUNI_NODISCARD MUNI_API muniResult diff --git a/third_party/maul-unicode/src/cursor.h b/third_party/maul-unicode/src/cursor.h index 34a9d5f4c..98d9e0ad8 100644 --- a/third_party/maul-unicode/src/cursor.h +++ b/third_party/maul-unicode/src/cursor.h @@ -23,10 +23,37 @@ typedef struct muniCursor uint8_t pending[4]; // the start of a sequence the previous piece cut off uint8_t pendingCount; // how many bytes of it there are bool moreFollows; // whether more pieces will come + uint16_t cookie; // the iterator's, written by its init call and checked by every call } muniCursor; void muniCursorInit(muniCursor* cursor, const char* text, size_t length, bool moreFollows); +// Whether the byte at offset in raw, state the caller holds, is a flag's +// 0 or 1. The iterators check every flag so before the copy of the state +// that holds it is read: any other byte would be no bool at all. +static inline bool muniIsFlagByte(const void* raw, size_t offset) +{ + return ((const unsigned char*)raw)[offset] <= 1; +} + +// Whether a cursor copied out of raw, from offset at, keeps its counts +// within its piece and its pending bytes, and its flag a 0 or 1; when it +// is waiting for a piece, whether more were announced and less than a +// code point is unread, as muniCursorFeed relies on. +static inline bool muniCursorValid(const muniCursor* cursor, const void* raw, size_t at, + bool waiting) +{ + if (!muniIsFlagByte(raw, at + offsetof(muniCursor, moreFollows)) || + cursor->pendingCount > sizeof(cursor->pending) || cursor->position > cursor->length || + (cursor->text == nullptr && cursor->length != 0)) + { + return false; + } + return !waiting || + (cursor->moreFollows && + cursor->pendingCount + (cursor->length - cursor->position) < sizeof(cursor->pending)); +} + // Hands the cursor its next piece. Bytes still unread in the current // piece are kept as pending. Only a cursor that answered // muni_needMoreText may be fed: more pieces were announced, and at most diff --git a/third_party/maul-unicode/src/grapheme.c b/third_party/maul-unicode/src/grapheme.c index 5f4dd9a2d..c272e9d7e 100644 --- a/third_party/maul-unicode/src/grapheme.c +++ b/third_party/maul-unicode/src/grapheme.c @@ -24,6 +24,8 @@ enum EmojiPictographic = 1, // Extended_Pictographic Extend* EmojiJoined = 2, // Extended_Pictographic Extend* ZWJ }; +static_assert((int)EmojiJoined == (int)muni_graphemeEmojiLast, + "segment.c checks the emoji state up to it"); static bool IsControl(uint8_t value) { diff --git a/third_party/maul-unicode/src/invariant.h b/third_party/maul-unicode/src/invariant.h index b94d3aed2..b049630fb 100644 --- a/third_party/maul-unicode/src/invariant.h +++ b/third_party/maul-unicode/src/invariant.h @@ -18,4 +18,10 @@ #define MUNI_ASSERT(condition) ((condition) ? (void)0 : __builtin_trap()) #endif +// MUNI_ALWAYS_INLINE asks that a small function be inlined into every +// caller, for a check on a call's hot path that the compiler would +// otherwise leave as a call of its own. Every compiler the family builds +// with, clang-cl included, takes the attribute. +#define MUNI_ALWAYS_INLINE inline __attribute__((always_inline)) + #endif // MAUL_UNICODE_SRC_INVARIANT_H diff --git a/third_party/maul-unicode/src/line_rules.h b/third_party/maul-unicode/src/line_rules.h index 55a149936..d591e5d32 100644 --- a/third_party/maul-unicode/src/line_rules.h +++ b/third_party/maul-unicode/src/line_rules.h @@ -56,6 +56,7 @@ enum HoldPrefix = 3, // LB25: (PR | PO) × OP, if (IS)? NU follows HoldAksara = 4, // LB28a: aksara × aksara, if VF follows }; +static_assert((int)HoldAksara == (int)muni_lineHoldLast, "segment.c checks the hold kind up to it"); // Where an LB25 number stands. enum @@ -64,6 +65,8 @@ enum NumberDigits = 1, // NU (SY | IS)* NumberClosed = 2, // NU (SY | IS)* (CL | CP) }; +static_assert((int)NumberClosed == (int)muni_lineNumberLast, + "segment.c checks the number state up to it"); enum : uint64_t { diff --git a/third_party/maul-unicode/src/script_runs.c b/third_party/maul-unicode/src/script_runs.c index 496aa16c6..1ed1fcc32 100644 --- a/third_party/maul-unicode/src/script_runs.c +++ b/third_party/maul-unicode/src/script_runs.c @@ -10,35 +10,14 @@ // a closing bracket that matches one counts as a code point of that // script, so "(" and ")" around foreign text land in the same script. -#include "cursor.h" #include "invariant.h" +#include "script_state.h" #include "tables.h" #include "maul-unicode/script.h" #include -#define BRACKET_DEPTH 32 -#define NO_SCRIPT 0xFFu - -typedef struct Scripts -{ - uint64_t bits[4]; -} Scripts; - -typedef struct ScriptState -{ - muniCursor cursor; - Scripts candidates; // the scripts the run can still have - uint32_t brackets[BRACKET_DEPTH]; // closing bracket << 8 | Script index or NO_SCRIPT - uint8_t bracketCount; - uint8_t firstScript; // the Script of the run's first code point that has one - bool determined; // a code point with a real script is in the run - bool started; // a code point has been read - bool waiting; // the last call returned muni_needMoreText - bool finished; // the last run has been reported -} ScriptState; - // The public iterator's size follows the state: it holds the state on // every target, and on 64-bit targets, where the state is largest, it // has no whole uint64_t to spare. A change to the state that breaks @@ -265,11 +244,67 @@ static muniResult Next(ScriptState* state, muniScriptRun* runOut) } } -static ScriptState Load(const muniScriptIterator* iterator) +// What an init call writes in the cursor's cookie, in bytes that were its +// padding, so that an iterator no init call set up is refused instead of +// read. Its top bit is set, so a small count or an ASCII byte pattern is +// no cookie; the checks after it keep any other bit pattern safe too. +#define COOKIE 0xB3D0u + +// Whether a script index names one of muniScriptTags, or is NO_SCRIPT +// where that is allowed. +static bool IsScript(uint8_t script, bool none) { - ScriptState state; - memcpy(&state, iterator, sizeof(state)); - return state; + return script < muniScriptTagCount || (none && script == NO_SCRIPT); +} + +// Copies the iterator's state out of caller memory when it is one an +// init call set up and every flag, count and script in it is in range, +// so that no bit pattern makes a call read outside the state, the text +// it names or the script tables; false otherwise. An iterator with the +// cookie whose fields were changed by hand is outside the contract, and +// how its fields agree with each other is not checked again. +static bool Load(const muniScriptIterator* iterator, ScriptState* stateOut) +{ + memcpy(stateOut, iterator, sizeof(*stateOut)); + const ScriptState* state = stateOut; + if (state->cursor.cookie != COOKIE || + !muniIsFlagByte(iterator, offsetof(ScriptState, determined)) || + !muniIsFlagByte(iterator, offsetof(ScriptState, started)) || + !muniIsFlagByte(iterator, offsetof(ScriptState, waiting)) || + !muniIsFlagByte(iterator, offsetof(ScriptState, finished)) || + !muniCursorValid(&state->cursor, iterator, offsetof(ScriptState, cursor), state->waiting) || + state->bracketCount > BRACKET_DEPTH || !IsScript(state->firstScript, true)) + { + return false; + } + for (uint8_t k = 0; k < state->bracketCount; k++) + { + if (!IsScript((uint8_t)(state->brackets[k] & 0xFF), true)) + { + return false; + } + } + // No candidate past the last script, and at least one in a determined + // run, which Best relies on. + bool any = false; + for (unsigned word = 0; word < 4; word++) + { + uint64_t bits = state->candidates.bits[word]; + unsigned first = word * 64; + if (first >= muniScriptTagCount) + { + if (bits != 0) + { + return false; + } + } + else if (muniScriptTagCount - first < 64 && bits >> (muniScriptTagCount - first) != 0) + { + return false; + } + any = any || bits != 0; + } + return any || !state->determined; } static void Store(muniScriptIterator* iterator, const ScriptState* state) @@ -282,6 +317,7 @@ static void Start(ScriptState* state, const char* text, size_t length, bool more memset(state, 0, sizeof(*state)); state->firstScript = NO_SCRIPT; muniCursorInit(&state->cursor, text, length, moreFollows); + state->cursor.cookie = COOKIE; } muniResult muniInitScriptIterator(muniScriptIterator* iterator, const char* text, size_t length, @@ -305,7 +341,11 @@ muniResult muniScriptIterator_Feed(muniScriptIterator* iterator, const char* tex { return muni_errorInvalid; } - ScriptState state = Load(iterator); + ScriptState state; + if (!Load(iterator, &state)) + { + return muni_errorInvalid; + } if (!state.waiting) { return muni_errorState; @@ -322,7 +362,11 @@ muniResult muniScriptIterator_NextRun(muniScriptIterator* iterator, muniScriptRu { return muni_errorInvalid; } - ScriptState state = Load(iterator); + ScriptState state; + if (!Load(iterator, &state)) + { + return muni_errorInvalid; + } muniResult status = Next(&state, runOut); state.waiting = status == muni_needMoreText; Store(iterator, &state); diff --git a/third_party/maul-unicode/src/script_state.h b/third_party/maul-unicode/src/script_state.h new file mode 100644 index 000000000..b01160fcd --- /dev/null +++ b/third_party/maul-unicode/src/script_state.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 Sirac Ozmen +// +// The state of a script iterator, which script_runs.c keeps in the public +// iterator's opaque storage. Here so that a fuzz target can change the +// fields that hold no pointer and see every call refuse what is out of +// range. + +#ifndef MAUL_UNICODE_SRC_SCRIPT_STATE_H +#define MAUL_UNICODE_SRC_SCRIPT_STATE_H + +#include "cursor.h" + +#define BRACKET_DEPTH 32 +#define NO_SCRIPT 0xFFu + +typedef struct Scripts +{ + uint64_t bits[4]; +} Scripts; + +typedef struct ScriptState +{ + muniCursor cursor; + Scripts candidates; // the scripts the run can still have + uint32_t brackets[BRACKET_DEPTH]; // closing bracket << 8 | Script index or NO_SCRIPT + uint8_t bracketCount; + uint8_t firstScript; // the Script of the run's first code point that has one + bool determined; // a code point with a real script is in the run + bool started; // a code point has been read + bool waiting; // the last call returned muni_needMoreText + bool finished; // the last run has been reported +} ScriptState; + +#endif // MAUL_UNICODE_SRC_SCRIPT_STATE_H diff --git a/third_party/maul-unicode/src/segment.c b/third_party/maul-unicode/src/segment.c index e51d24580..8f55704f0 100644 --- a/third_party/maul-unicode/src/segment.c +++ b/third_party/maul-unicode/src/segment.c @@ -13,11 +13,131 @@ #include #include -static muniSegmenter Load(const muniSegmentIterator* iterator) +// What an init call writes in the cursor's cookie, in bytes that were its +// padding, so that an iterator no init call set up is refused instead of +// read. Its top bit is set, so a small count or an ASCII byte pattern is +// no cookie; the checks after it keep any other bit pattern safe too. +#define COOKIE 0xC5E6u + +// The most overrides a table can hold: one per scalar value, since its +// code points strictly increase. +#define MAX_OVERRIDES 0x10F800u + +// The byte at offset in the iterator in caller memory. The flags are +// checked as these bytes, before the copy is read as bools. Reading them +// from the iterator rather than the copy just made of it keeps each load +// from waiting on the copy's stores. +static inline unsigned Byte(const muniSegmentIterator* iterator, size_t offset) { - muniSegmenter segmenter; - memcpy(&segmenter, iterator, sizeof(segmenter)); - return segmenter; + return ((const unsigned char*)iterator)[offset]; +} + +// Whether the rules of a segmenter copied out of iterator hold what their +// rule set writes, each flag's byte a 0 or 1 included. +static MUNI_ALWAYS_INLINE bool RulesValid(const muniSegmenter* segmenter, + const muniSegmentIterator* iterator) +{ + switch (segmenter->kind) + { + case muni_segmentGrapheme: + { + const muniGraphemeRules* rules = &segmenter->rules.grapheme; + return segmenter->lineSettings == 0 && segmenter->holdKind == 0 && + rules->previous <= muni_gcbLvt && rules->emoji <= muni_graphemeEmojiLast && + (Byte(iterator, offsetof(muniSegmenter, rules.grapheme.linker)) | + Byte(iterator, offsetof(muniSegmenter, rules.grapheme.oddRegional))) <= 1; + } + case muni_segmentWord: + { + const muniWordRules* rules = &segmenter->rules.word; + uint32_t count = rules->overrideCount; + return segmenter->lineSettings == 0 && segmenter->holdKind == 0 && + rules->actual <= muni_wbWSegSpace && rules->previous <= muni_wbWSegSpace && + rules->before <= muni_wbWSegSpace && rules->kind <= muni_wordIdeograph && + rules->reportedKind <= muni_wordIdeograph && count <= MAX_OVERRIDES && + (count == 0 || segmenter->overrides != nullptr) && + Byte(iterator, offsetof(muniSegmenter, rules.word.oddRegional)) <= 1; + } + case muni_segmentSentence: + { + const muniSentenceRules* rules = &segmenter->rules.sentence; + uint32_t count = rules->overrideCount; + return segmenter->lineSettings == 0 && segmenter->holdKind == 0 && + rules->actual <= muni_sbClose && rules->previous <= muni_sbClose && + rules->before <= muni_sbClose && rules->ending <= muni_sentenceEndingLast && + count <= MAX_OVERRIDES && (count == 0 || segmenter->overrides != nullptr) && + Byte(iterator, offsetof(muniSegmenter, rules.sentence.aterm)) <= 1; + } + case muni_segmentLine: + { + // Breaking anywhere keeps the grapheme rules where the other + // settings keep the classes before the position. + const muniLineRules* rules = &segmenter->rules.line; + if (segmenter->lineSettings >> 3 != 0 || rules->actual > muni_lbHh) + { + return false; + } + if (muniLineStrictness(segmenter) == muni_lineBreakAnywhere) + { + return segmenter->holdKind == 0 && rules->grapheme.previous <= muni_gcbLvt && + rules->grapheme.emoji <= muni_graphemeEmojiLast && + (Byte(iterator, offsetof(muniSegmenter, rules.line.grapheme.linker)) | + Byte(iterator, offsetof(muniSegmenter, rules.line.grapheme.oddRegional))) <= 1; + } + return segmenter->holdKind <= muni_lineHoldLast && rules->previous <= muni_lbHh && + rules->before <= muni_lbHh && rules->lead <= muni_lbHh && + rules->number <= muni_lineNumberLast && + (Byte(iterator, offsetof(muniSegmenter, rules.line.leadInitialQuote)) | + Byte(iterator, offsetof(muniSegmenter, rules.line.sawInfix)) | + Byte(iterator, offsetof(muniSegmenter, rules.line.oddRegional))) <= 1; + } + default: + return false; + } +} + +// Copies the iterator's state out of caller memory when it is one an +// init call set up and every flag, count and value in it is in range, so +// that no bit pattern makes a call read outside the state or the text +// and overrides it names; false otherwise. An iterator with the cookie +// whose fields were changed by hand is outside the contract, and how its +// fields agree with each other is not checked again. Every call reads +// most of the state, so all of it is checked, inline in each call so that +// the copy stays inline too. +static MUNI_ALWAYS_INLINE bool Load(const muniSegmentIterator* iterator, + muniSegmenter* segmenterOut) +{ + memcpy(segmenterOut, iterator, sizeof(*segmenterOut)); + const muniSegmenter* segmenter = segmenterOut; + const muniCursor* cursor = &segmenter->cursor; + size_t at = offsetof(muniSegmenter, cursor); + unsigned more = Byte(iterator, at + offsetof(muniCursor, moreFollows)); + unsigned started = Byte(iterator, offsetof(muniSegmenter, started)); + unsigned holding = Byte(iterator, offsetof(muniSegmenter, holding)); + if (cursor->cookie != COOKIE || segmenter->phase > muni_phaseBusy || + (more | started | holding | Byte(iterator, offsetof(muniSegmenter, endReported)) | + Byte(iterator, offsetof(muniSegmenter, mandatory))) > 1 || + cursor->pendingCount > sizeof(cursor->pending) || cursor->position > cursor->length || + (cursor->text == nullptr && cursor->length != 0)) + { + return false; + } + // A waiting cursor holds less than a code point, as muniCursorFeed + // relies on; a boundary lies after a code point and a hold before the + // cursor, as the engine's assertions do; an SA run starts at or before + // the cursor, so the offset its breaker is asked about lies in the run. + size_t offset = cursor->offset; + const muniComplexRun* run = &segmenter->complex; + if ((segmenter->phase == muni_phaseWaiting && + (more == 0 || + cursor->pendingCount + (cursor->length - cursor->position) >= sizeof(cursor->pending))) || + (started != 0 && offset == 0) || + (holding != 0 && (segmenter->heldOffset == 0 || segmenter->heldOffset >= offset)) || + (run->text != nullptr && (run->next > run->length || run->start > offset))) + { + return false; + } + return RulesValid(segmenter, iterator); } static void Store(muniSegmentIterator* iterator, const muniSegmenter* segmenter) @@ -49,6 +169,7 @@ static muniResult Init(muniSegmentIterator* iterator, uint8_t kind, const char* memset(&segmenter, 0, sizeof(segmenter)); segmenter.kind = kind; muniCursorInit(&segmenter.cursor, text, length, moreFollows); + segmenter.cursor.cookie = COOKIE; memset(iterator, 0, sizeof(*iterator)); Store(iterator, &segmenter); return muni_success; @@ -85,7 +206,11 @@ muniResult muniSegmentIterator_SetComplexBreaker(muniSegmentIterator* iterator, { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } bool supported = segmenter.kind == muni_segmentLine || segmenter.kind == muni_segmentWord; if (!supported) { @@ -105,10 +230,6 @@ muniResult muniSegmentIterator_SetComplexBreaker(muniSegmentIterator* iterator, return muni_success; } -// The most overrides a table can hold: one per scalar value, since its -// code points strictly increase. -#define MAX_OVERRIDES 0x10F800u - // Whether a table of overrides is in strictly increasing order of code // point and names only code points and known classes: up to classCount. static bool ValidOverrides(const muniBreakClassOverride* overrides, size_t count, @@ -135,7 +256,11 @@ muniResult muniSegmentIterator_SetBreakClassOverrides(muniSegmentIterator* itera { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } uint32_t* countField = nullptr; uint8_t classCount = 0; if (segmenter.kind == muni_segmentWord) @@ -176,7 +301,11 @@ static muniResult SetLineSettings(muniSegmentIterator* iterator, int32_t value, { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } if (segmenter.kind != muni_segmentLine) { return muni_errorInvalid; @@ -208,7 +337,11 @@ muniSegmentIterator_GetLineBreakStrictness(const muniSegmentIterator* iterator) { return muni_lineBreakNormal; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_lineBreakNormal; + } return segmenter.kind == muni_segmentLine ? muniLineStrictness(&segmenter) : muni_lineBreakNormal; } @@ -226,7 +359,11 @@ muniWritingSystem muniSegmentIterator_GetWritingSystem(const muniSegmentIterator { return muni_writingOther; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_writingOther; + } return segmenter.kind == muni_segmentLine ? muniLineWritingSystem(&segmenter) : muni_writingOther; } @@ -238,7 +375,11 @@ muniResult muniSegmentIterator_Feed(muniSegmentIterator* iterator, const char* t { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } if (segmenter.phase == muni_phaseBusy) { return muni_errorBusy; @@ -283,7 +424,11 @@ muniResult muniSegmentIterator_NextBreak(muniSegmentIterator* iterator, size_t* { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } if (segmenter.phase == muni_phaseBusy) { return muni_errorBusy; @@ -302,7 +447,11 @@ muniResult muniSegmentIterator_NextLineBreak(muniSegmentIterator* iterator, size { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } if (segmenter.kind != muni_segmentLine) { return muni_errorInvalid; @@ -380,7 +529,11 @@ muniResult muniSegmentIterator_GetWordKind(const muniSegmentIterator* iterator, { return muni_errorInvalid; } - muniSegmenter segmenter = Load(iterator); + muniSegmenter segmenter; + if (!Load(iterator, &segmenter)) + { + return muni_errorInvalid; + } if (segmenter.kind != muni_segmentWord) { return muni_errorInvalid; diff --git a/third_party/maul-unicode/src/segmenter.h b/third_party/maul-unicode/src/segmenter.h index 75671faef..0ad34b32d 100644 --- a/third_party/maul-unicode/src/segmenter.h +++ b/third_party/maul-unicode/src/segmenter.h @@ -110,6 +110,17 @@ typedef struct muniLineRules bool oddRegional; // an odd number of regional indicators precedes } muniLineRules; +// The last value each rule set writes in its states, for the check of an +// iterator the caller holds; each rule set ties its own enumeration to +// these with a static_assert. +enum +{ + muni_graphemeEmojiLast = 2, // grapheme.c's EmojiJoined + muni_sentenceEndingLast = 2, // sentence.c's EndingSpace + muni_lineNumberLast = 2, // line_rules.h's NumberClosed + muni_lineHoldLast = 4, // line_rules.h's HoldAksara +}; + // A run of Thai, Lao, Khmer or Burmese (Line_Break=SA) that a caller's // breaker segments. The run lies in the current piece. typedef struct muniComplexRun @@ -149,8 +160,15 @@ typedef struct muniSegmenter const muniBreakClassOverride* overrides; } muniSegmenter; +// The public iterator's size follows the segmenter: it holds it on every +// target, and on 64-bit targets, where the segmenter is largest, it has +// no whole uint64_t to spare. A change that breaks either resizes the +// public iterator, which a minor release may do. static_assert(sizeof(muniSegmenter) <= sizeof(muniSegmentIterator), "the public iterator must hold the segmenter"); +static_assert(sizeof(void*) < 8 || + sizeof(muniSegmentIterator) - sizeof(muniSegmenter) < sizeof(uint64_t), + "the public iterator must be no larger than the segmenter needs"); // The parts of lineSettings: a muni_lineBreak value in the low two bits, // a muni_writing value in the third. diff --git a/third_party/maul-unicode/src/sentence.c b/third_party/maul-unicode/src/sentence.c index f204f75a5..bf3ab1a91 100644 --- a/third_party/maul-unicode/src/sentence.c +++ b/third_party/maul-unicode/src/sentence.c @@ -22,6 +22,8 @@ enum EndingTerm = 1, // SATerm Close* EndingSpace = 2, // SATerm Close* Sp+ }; +static_assert((int)EndingSpace == (int)muni_sentenceEndingLast, + "segment.c checks the ending up to it"); enum { From 59e6f859e6cd159dd250e8556b1b2b2ec5721b52 Mon Sep 17 00:00:00 2001 From: Sirac Ozmen Date: Thu, 5 Nov 2026 10:45:27 +0300 Subject: [PATCH 2/3] build(deps): move Maul Unicode to 1.1.2 --- docs/operations/dependencies.md | 2 +- third_party/maul-unicode/cmake/Testing.cmake | 17 +++++++++++++---- .../maul-unicode/include/maul-unicode/base.h | 2 +- third_party/maul-unicode/src/bidi_core.h | 2 +- third_party/maul-unicode/src/bidi_explicit.c | 2 +- third_party/maul-unicode/src/bidi_explicit.h | 2 +- third_party/maul-unicode/src/bidi_implicit.c | 4 ++-- third_party/maul-unicode/src/bidi_line.c | 4 ++-- third_party/maul-unicode/src/case.c | 10 +++++----- third_party/maul-unicode/src/cursor.h | 6 +++--- third_party/maul-unicode/src/decompose.c | 4 ++-- third_party/maul-unicode/src/decompose.h | 9 +++++---- third_party/maul-unicode/src/encoding.c | 9 +++++---- third_party/maul-unicode/src/grapheme.c | 4 ++-- third_party/maul-unicode/src/grapheme.h | 3 ++- third_party/maul-unicode/src/line_anywhere.c | 2 +- third_party/maul-unicode/src/line_rules.h | 17 +++++++++-------- third_party/maul-unicode/src/nfkc_casefold.c | 2 +- third_party/maul-unicode/src/normalize.c | 2 +- third_party/maul-unicode/src/normalizer.h | 2 +- third_party/maul-unicode/src/overlap.h | 6 ++++-- third_party/maul-unicode/src/script_runs.c | 15 ++++++++------- third_party/maul-unicode/src/security.c | 9 +++++---- third_party/maul-unicode/src/segment.c | 12 ++++++------ third_party/maul-unicode/src/sentence.c | 2 +- third_party/maul-unicode/src/skeleton.c | 5 +++-- third_party/maul-unicode/src/word.c | 2 +- third_party/maul-unicode/src/writer.h | 4 +++- 28 files changed, 90 insertions(+), 70 deletions(-) diff --git a/docs/operations/dependencies.md b/docs/operations/dependencies.md index 3861aa7cf..d94c7e40c 100644 --- a/docs/operations/dependencies.md +++ b/docs/operations/dependencies.md @@ -15,7 +15,7 @@ Every third-party library is vendored under `third_party/` at an exact revision | Kest | `Rawframe-Project/kest` | `fa092f540ffe2cb94f75199842792fb61e68451f` | MIT | `include/`, `src/` except `main.c`, `lib/`, `LICENSE` | | Maul2D 0.6.0 | `Rawframe-Project/maul2d` release archive `maul2d-0.6.0.tar.gz`, SHA-256 `f97262cd98a47e2f52ed7a811ce144bff1c3f51931323457d1313484c9a845ae` | `501ac5c191658435cbdd4f70a0abd6cdd9e37f03` (`v0.6.0`) | MIT | `include/`, `src/`, `LICENSE`; the build file is the engine's | | Maul3D 0.7.0 | `Rawframe-Project/maul3d` release archive `maul3d-0.7.0.tar.gz`, SHA-256 `b4e42736c2c8413f8218846b9ce777bab79506d75c620707d571f24cb0632d4f` | `bf11f1fc3976ffb8b2ab4e4604252e760aea221c` (`v0.7.0`) | MIT | `include/`, `src/`, `LICENSE`; the build file is the engine's | -| Maul Unicode 1.1.1 | `Rawframe-Project/maul-unicode` release archive `maul-unicode-1.1.1.tar.gz`, SHA-256 `89f231e4d331bed957210b733dd07f8020ae8e0476718caf3d906e1544806ef7` | `39c4a2c43012b1cb4aadfa738c7691e17c104d78` (`v1.1.1`) | MIT; its Unicode tables Unicode-3.0 | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | +| Maul Unicode 1.1.2 | `Rawframe-Project/maul-unicode` release archive `maul-unicode-1.1.2.tar.gz`, SHA-256 `a0e8ed3f04d7316763c7f4658a466eaf7438bd6c9bc26a68bc7e90d7a706991e` | `b026e91fa336eddce422ea6181857417f4e1ec04` (`v1.1.2`) | MIT; its Unicode tables Unicode-3.0 | `include/`, `src/`, `cmake/`, `CMakeLists.txt`, `LICENSE` | | Maul Window 0.12.0 | `Rawframe-Project/maul-window` release archive `maul-window-0.12.0.tar.gz`, SHA-256 `389b522f338f9584c2d96e682ef60a0c0dd31a5939ef2770b52d7df2247e1764` | `f904519e6c8f86071862080bdbca6f14065f7ea1` (`v0.12.0`) | MIT | `include/`, `src/`, `cmake/`, `protocols/`, `java/`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE` | | Maul RHI 0.15.0 | `Rawframe-Project/maul-rhi` release archive `maul-rhi-0.15.0.tar.gz`, SHA-256 `ef0e1ec5214cdfbdaa2bc3b31c1f5558cbfbfef07889192eda79f456bbb729fa` | `57f11cf50da9380dfa6af75eb49ff6612e7d3387` (`v0.15.0`) | MIT; Khronos headers Apache-2.0 or MIT; DirectX headers MIT | `include/`, `src/`, `khronos/`, `directx/`, `cmake/`, the container, MSL and DXIL tools, `contract/mrhi.json`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE` | | Maul UI 1.3.3 | `Rawframe-Project/maul-ui` release archive `maul-ui-1.3.3.tar.gz`, SHA-256 `ba431b0f5b48dac01db208580a832ea44cb0792b4a9641186805630c2033d62f` | `713c044bf1a45fe0f537ae8ef0adba71abcff17c` (`v1.3.3`) | MIT | `include/`, `src/`, `cmake/`, `java/`, `tools/gen_web_glue.py`, `CMakeLists.txt`, `LICENSE`, `docs/user/licenses.md` | diff --git a/third_party/maul-unicode/cmake/Testing.cmake b/third_party/maul-unicode/cmake/Testing.cmake index 144bbe4e4..68163e3b1 100644 --- a/third_party/maul-unicode/cmake/Testing.cmake +++ b/third_party/maul-unicode/cmake/Testing.cmake @@ -86,10 +86,12 @@ endfunction() # the family requires. ROOT and NAME default to the project's # source directory and name; cmake/HeaderCheck passes them to check a # library's headers alone. MSVC's cl has no C23 mode (its /std:clatest is -# a draft mode), so the C23 check is skipped there. In C++17 the -# library's _NODISCARD, found in base.h, must not be empty. A macro, -# because enable_language must run at directory scope. Without a C++ -# compiler the C++ checks are skipped. +# a draft mode), so the C23 check is skipped there. In C23 and C++17 the +# library's _NODISCARD, found in base.h, must not be empty: GCC 14 +# reports its C23 as 202000L, so a macro that waits for 202311L would +# leave a dropped status unwarned there. A macro, because enable_language +# must run at directory scope. Without a C++ compiler the C++ checks are +# skipped. macro(maul_add_header_tests) cmake_parse_arguments(MAUL_HT "" "ROOT;NAME" "" ${ARGN}) if(NOT MAUL_HT_ROOT) @@ -131,6 +133,13 @@ macro(maul_add_header_tests) "static_assert(sizeof(MAUL_TEXT(${maul_nodiscard})) > 1,\n" " \"${maul_nodiscard} is empty in C++17\");\n") list(APPEND maul_cxx_sources ${maul_header_dir}/cxx_nodiscard.cpp) + file(WRITE ${maul_header_dir}/c23_nodiscard.c + "#include \"${MAUL_HT_NAME}/base.h\"\n" + "#define MAUL_TEXT(x) MAUL_TEXT_OF(x)\n" + "#define MAUL_TEXT_OF(x) #x\n" + "static_assert(sizeof(MAUL_TEXT(${maul_nodiscard})) > 1,\n" + " \"${maul_nodiscard} is empty in C23\");\n") + list(APPEND maul_c23_sources ${maul_header_dir}/c23_nodiscard.c) endif() if(MSVC) set(maul_header_flags /W4 /WX) diff --git a/third_party/maul-unicode/include/maul-unicode/base.h b/third_party/maul-unicode/include/maul-unicode/base.h index a69783f8b..a09fdd3a5 100644 --- a/third_party/maul-unicode/include/maul-unicode/base.h +++ b/third_party/maul-unicode/include/maul-unicode/base.h @@ -26,7 +26,7 @@ extern "C" #define MUNI_VERSION_MINOR 1 // x-release-please-end // x-release-please-start-patch -#define MUNI_VERSION_PATCH 1 +#define MUNI_VERSION_PATCH 2 // x-release-please-end // The one Unicode version every table in the library comes from. diff --git a/third_party/maul-unicode/src/bidi_core.h b/third_party/maul-unicode/src/bidi_core.h index e5b26f58a..7732404f8 100644 --- a/third_party/maul-unicode/src/bidi_core.h +++ b/third_party/maul-unicode/src/bidi_core.h @@ -71,7 +71,7 @@ static inline uint8_t muniBidiOriginalType(const muniBidiParagraph* paragraph, s return muniLookupBidiClass(muniBidiCodePoint(paragraph->text, paragraph->length, index, &size)); } -static inline bool muniBidiIsIsolateInitiator(uint8_t type) +MUNI_NODISCARD static inline bool muniBidiIsIsolateInitiator(uint8_t type) { return type == muni_bcLri || type == muni_bcRli || type == muni_bcFsi; } diff --git a/third_party/maul-unicode/src/bidi_explicit.c b/third_party/maul-unicode/src/bidi_explicit.c index 37c4c43ec..047be80a9 100644 --- a/third_party/maul-unicode/src/bidi_explicit.c +++ b/third_party/maul-unicode/src/bidi_explicit.c @@ -241,7 +241,7 @@ static void Apply(Explicit* state, const muniBidiParagraph* paragraph, size_t in // The classes after which a left-to-right paragraph can hold other levels // than 0. -static bool NeedsImplicit(uint8_t type) +MUNI_NODISCARD static bool NeedsImplicit(uint8_t type) { return type == muni_bcR || type == muni_bcAl || type == muni_bcAn || (type >= muni_bcLre && type <= muni_bcPdi); diff --git a/third_party/maul-unicode/src/bidi_explicit.h b/third_party/maul-unicode/src/bidi_explicit.h index a9654ff8d..a2abff5c4 100644 --- a/third_party/maul-unicode/src/bidi_explicit.h +++ b/third_party/maul-unicode/src/bidi_explicit.h @@ -25,6 +25,6 @@ uint8_t muniBidiFirstStrongLevel(const muniBidiParagraph* paragraph, size_t inde // Returns false when the implicit phase can only give level 0 everywhere: // the paragraph level is 0 and no code point is R, AL, AN or an explicit // formatting character, so every class resolves to L. -bool muniBidiResolveExplicit(const muniBidiParagraph* paragraph); +MUNI_NODISCARD bool muniBidiResolveExplicit(const muniBidiParagraph* paragraph); #endif // MAUL_UNICODE_SRC_BIDI_EXPLICIT_H diff --git a/third_party/maul-unicode/src/bidi_implicit.c b/third_party/maul-unicode/src/bidi_implicit.c index 4a4314821..632adc0e4 100644 --- a/third_party/maul-unicode/src/bidi_implicit.c +++ b/third_party/maul-unicode/src/bidi_implicit.c @@ -11,7 +11,7 @@ #include "bidi_brackets.h" -static bool IsNeutral(uint8_t type) +MUNI_NODISCARD static bool IsNeutral(uint8_t type) { return type == muni_bcB || type == muni_bcS || type == muni_bcWs || type == muni_bcOn || muniBidiIsIsolateInitiator(type) || type == muni_bcPdi; @@ -182,7 +182,7 @@ static void ResolveNeutrals(const muniBidiSequence* sequence) // Whether the level run starting at index starts an isolating run // sequence: it does unless it starts with a PDI that matches an isolate // initiator, found as the previous code point at or below its level. -static bool StartsSequence(const muniBidiParagraph* paragraph, size_t index) +MUNI_NODISCARD static bool StartsSequence(const muniBidiParagraph* paragraph, size_t index) { if (muniBidiOriginalType(paragraph, index) != muni_bcPdi) { diff --git a/third_party/maul-unicode/src/bidi_line.c b/third_party/maul-unicode/src/bidi_line.c index dc58666a5..fa1e711c8 100644 --- a/third_party/maul-unicode/src/bidi_line.c +++ b/third_party/maul-unicode/src/bidi_line.c @@ -20,7 +20,7 @@ // limit 125 raised by one, or the even 124 raised by two. #define MAX_LINE_LEVEL (MUNI_BIDI_MAX_DEPTH + 1) -static bool LevelsInRange(const uint8_t* levels, size_t count) +MUNI_NODISCARD static bool LevelsInRange(const uint8_t* levels, size_t count) { for (size_t k = 0; k < count; k++) { @@ -76,7 +76,7 @@ static void AddOwn(Runs* runs, const uint8_t* levels, size_t start, size_t end) // What rule L1 resets before a separator or at the end of the line: // whitespace and isolate formatting characters, and the characters rule // X9 removed, which have no level of their own. -static bool IsTrailing(uint8_t type) +MUNI_NODISCARD static bool IsTrailing(uint8_t type) { switch (type) { diff --git a/third_party/maul-unicode/src/case.c b/third_party/maul-unicode/src/case.c index 2544dbaa4..de9ab3085 100644 --- a/third_party/maul-unicode/src/case.c +++ b/third_party/maul-unicode/src/case.c @@ -114,7 +114,7 @@ static uint32_t Decode(const Converter* converter, size_t offset, size_t* sizeOu } // Final_Sigma after the sigma: no cased letter follows past case-ignorables. -static bool EndsWord(const Converter* converter, size_t offset) +MUNI_NODISCARD static bool EndsWord(const Converter* converter, size_t offset) { while (offset < converter->length) { @@ -134,7 +134,7 @@ static bool EndsWord(const Converter* converter, size_t offset) } // Before_Dot: a U+0307 follows, past marks of classes other than 0 and 230. -static bool BeforeDot(const Converter* converter, size_t offset) +MUNI_NODISCARD static bool BeforeDot(const Converter* converter, size_t offset) { while (offset < converter->length) { @@ -156,7 +156,7 @@ static bool BeforeDot(const Converter* converter, size_t offset) // More_Above: a mark of class 230 follows, past marks of other classes // but 0. -static bool MoreAbove(const Converter* converter, size_t offset) +MUNI_NODISCARD static bool MoreAbove(const Converter* converter, size_t offset) { while (offset < converter->length) { @@ -171,7 +171,7 @@ static bool MoreAbove(const Converter* converter, size_t offset) return false; } -static bool IsSoftDotted(uint32_t codePoint) +MUNI_NODISCARD static bool IsSoftDotted(uint32_t codePoint) { uint32_t low = 0; uint32_t high = muniSoftDottedCount; @@ -197,7 +197,7 @@ static bool IsSoftDotted(uint32_t codePoint) // Lithuanian lowercasing keeps the dot of an i or a j that other accents // above follow, writing it out; true when it applied. -static bool PutLithuanianLower(Converter* converter, uint32_t codePoint, size_t next) +MUNI_NODISCARD static bool PutLithuanianLower(Converter* converter, uint32_t codePoint, size_t next) { uint32_t accent = codePoint == CAPITAL_I_GRAVE ? COMBINING_GRAVE : codePoint == CAPITAL_I_ACUTE ? COMBINING_ACUTE diff --git a/third_party/maul-unicode/src/cursor.h b/third_party/maul-unicode/src/cursor.h index 98d9e0ad8..b6375401b 100644 --- a/third_party/maul-unicode/src/cursor.h +++ b/third_party/maul-unicode/src/cursor.h @@ -31,7 +31,7 @@ void muniCursorInit(muniCursor* cursor, const char* text, size_t length, bool mo // Whether the byte at offset in raw, state the caller holds, is a flag's // 0 or 1. The iterators check every flag so before the copy of the state // that holds it is read: any other byte would be no bool at all. -static inline bool muniIsFlagByte(const void* raw, size_t offset) +MUNI_NODISCARD static inline bool muniIsFlagByte(const void* raw, size_t offset) { return ((const unsigned char*)raw)[offset] <= 1; } @@ -40,8 +40,8 @@ static inline bool muniIsFlagByte(const void* raw, size_t offset) // within its piece and its pending bytes, and its flag a 0 or 1; when it // is waiting for a piece, whether more were announced and less than a // code point is unread, as muniCursorFeed relies on. -static inline bool muniCursorValid(const muniCursor* cursor, const void* raw, size_t at, - bool waiting) +MUNI_NODISCARD static inline bool muniCursorValid(const muniCursor* cursor, const void* raw, + size_t at, bool waiting) { if (!muniIsFlagByte(raw, at + offsetof(muniCursor, moreFollows)) || cursor->pendingCount > sizeof(cursor->pending) || cursor->position > cursor->length || diff --git a/third_party/maul-unicode/src/decompose.c b/third_party/maul-unicode/src/decompose.c index ba17405dc..383740502 100644 --- a/third_party/maul-unicode/src/decompose.c +++ b/third_party/maul-unicode/src/decompose.c @@ -56,7 +56,7 @@ static uint32_t PairSource(uint32_t position) return (uint32_t)muniDecompositionStarts[low] << 6 | muniLowestBit(map); } -static bool IsHangulSyllable(uint32_t codePoint) +MUNI_NODISCARD static bool IsHangulSyllable(uint32_t codePoint) { return codePoint - HangulSBase < HangulSCount; } @@ -191,7 +191,7 @@ bool muniIsCompositionExcluded(uint32_t codePoint) // Whether codePoint is a second code point that composes: a mark of a // composing pair, or a Hangul vowel or trailing consonant. -static bool Combines(uint32_t codePoint) +MUNI_NODISCARD static bool Combines(uint32_t codePoint) { if (codePoint - HangulVBase < HangulVCount || codePoint - HangulTBase - 1 < HangulTCount - 1) { diff --git a/third_party/maul-unicode/src/decompose.h b/third_party/maul-unicode/src/decompose.h index 2b33f6863..60f73afea 100644 --- a/third_party/maul-unicode/src/decompose.h +++ b/third_party/maul-unicode/src/decompose.h @@ -16,21 +16,22 @@ // The canonical mapping of codePoint, one level deep: two code points, // or one with secondOut 0; false when it has none. Hangul syllables map // to LV + T or L + V, as HarfBuzz expects. -bool muniCanonicalMapping(uint32_t codePoint, uint32_t* firstOut, uint32_t* secondOut); +MUNI_NODISCARD bool muniCanonicalMapping(uint32_t codePoint, uint32_t* firstOut, + uint32_t* secondOut); // The primary composite of first and second, or 0 when there is none. uint32_t muniComposeCanonical(uint32_t first, uint32_t second); // Whether codePoint may combine with what precedes it (NFC_QC=Maybe). -bool muniCombinesBackward(uint32_t codePoint); +MUNI_NODISCARD bool muniCombinesBackward(uint32_t codePoint); // Whether the full decomposition of codePoint uses a compatibility // mapping somewhere, so NFKD differs from NFD for it. -bool muniUsesCompatibility(uint32_t codePoint); +MUNI_NODISCARD bool muniUsesCompatibility(uint32_t codePoint); // Whether codePoint has a canonical decomposition that NFC does not // recompose: a singleton or a pair excluded from composition. -bool muniIsCompositionExcluded(uint32_t codePoint); +MUNI_NODISCARD bool muniIsCompositionExcluded(uint32_t codePoint); // Writes the full decomposition of codePoint (NFD, or NFKD with // compatibility) into out, which holds MUNI_MAX_DECOMPOSITION code points, diff --git a/third_party/maul-unicode/src/encoding.c b/third_party/maul-unicode/src/encoding.c index 57b850262..c998aab8d 100644 --- a/third_party/maul-unicode/src/encoding.c +++ b/third_party/maul-unicode/src/encoding.c @@ -13,12 +13,12 @@ #include -static bool IsHighSurrogate(uint32_t unit) +MUNI_NODISCARD static bool IsHighSurrogate(uint32_t unit) { return unit >= 0xD800 && unit <= 0xDBFF; } -static bool IsLowSurrogate(uint32_t unit) +MUNI_NODISCARD static bool IsLowSurrogate(uint32_t unit) { return unit >= 0xDC00 && unit <= 0xDFFF; } @@ -294,8 +294,9 @@ static muniTextResult Finish(size_t needed, size_t capacity, size_t stopped, siz return (muniTextResult){fits ? muni_success : muni_errorCapacity, stopped}; } -static bool BadArguments(const void* input, size_t length, const void* output, size_t capacity, - muniConvertMode mode, const size_t* neededOut) +MUNI_NODISCARD static bool BadArguments(const void* input, size_t length, const void* output, + size_t capacity, muniConvertMode mode, + const size_t* neededOut) { return (input == nullptr && length != 0) || (output == nullptr && capacity != 0) || neededOut == nullptr || (uint32_t)mode > muni_convertReplace; diff --git a/third_party/maul-unicode/src/grapheme.c b/third_party/maul-unicode/src/grapheme.c index c272e9d7e..b9704e3ff 100644 --- a/third_party/maul-unicode/src/grapheme.c +++ b/third_party/maul-unicode/src/grapheme.c @@ -27,13 +27,13 @@ enum static_assert((int)EmojiJoined == (int)muni_graphemeEmojiLast, "segment.c checks the emoji state up to it"); -static bool IsControl(uint8_t value) +MUNI_NODISCARD static bool IsControl(uint8_t value) { return value == muni_gcbControl || value == muni_gcbCr || value == muni_gcbLf; } // GB6 to GB8: Hangul syllable sequences stay together. -static bool JoinsHangul(uint8_t before, uint8_t after) +MUNI_NODISCARD static bool JoinsHangul(uint8_t before, uint8_t after) { if (before == muni_gcbL) { diff --git a/third_party/maul-unicode/src/grapheme.h b/third_party/maul-unicode/src/grapheme.h index 226fa9c56..5665c0e7f 100644 --- a/third_party/maul-unicode/src/grapheme.h +++ b/third_party/maul-unicode/src/grapheme.h @@ -11,7 +11,8 @@ // Whether there is a boundary between the text so far and a code point // whose Grapheme_Cluster_Break is after. No rule looks ahead. -bool muniGraphemeIsBoundary(const muniGraphemeRules* state, uint8_t after, uint32_t codePoint); +MUNI_NODISCARD bool muniGraphemeIsBoundary(const muniGraphemeRules* state, uint8_t after, + uint32_t codePoint); // Moves the state past a code point whose Grapheme_Cluster_Break is value. void muniGraphemeAdvance(muniGraphemeRules* state, uint8_t value, uint32_t codePoint); diff --git a/third_party/maul-unicode/src/line_anywhere.c b/third_party/maul-unicode/src/line_anywhere.c index adc404e43..4a1c80045 100644 --- a/third_party/maul-unicode/src/line_anywhere.c +++ b/third_party/maul-unicode/src/line_anywhere.c @@ -20,7 +20,7 @@ enum : uint32_t Hard = BIT(muni_lbBk) | BIT(muni_lbCr) | BIT(muni_lbLf) | BIT(muni_lbNl), }; -static bool In(uint8_t value, uint32_t set) +MUNI_NODISCARD static bool In(uint8_t value, uint32_t set) { return value < 32 && ((set >> value) & 1u) != 0; } diff --git a/third_party/maul-unicode/src/line_rules.h b/third_party/maul-unicode/src/line_rules.h index d591e5d32..262d5a95a 100644 --- a/third_party/maul-unicode/src/line_rules.h +++ b/third_party/maul-unicode/src/line_rules.h @@ -91,12 +91,12 @@ enum : uint64_t BIT(Sot) | Hard | BIT(muni_lbSp) | BIT(muni_lbZw) | BIT(muni_lbCb) | BIT(muni_lbGl), }; -static inline bool In(uint8_t value, uint64_t set) +MUNI_NODISCARD static inline bool In(uint8_t value, uint64_t set) { return ((set >> (value & 63u)) & 1u) != 0; } -static inline bool IsEastAsian(uint32_t codePoint) +MUNI_NODISCARD static inline bool IsEastAsian(uint32_t codePoint) { uint8_t width = muniLookupEastAsianWidth(codePoint); return width == muni_eawFullwidth || width == muni_eawWide || width == muni_eawHalfwidth; @@ -104,19 +104,19 @@ static inline bool IsEastAsian(uint32_t codePoint) // East_Asian_Width Ambiguous, Fullwidth or Wide, the affixes CSS's loose // level lets a line break around in Chinese and Japanese. -static inline bool IsWide(uint32_t codePoint) +MUNI_NODISCARD static inline bool IsWide(uint32_t codePoint) { uint8_t width = muniLookupEastAsianWidth(codePoint); return width == muni_eawAmbiguous || width == muni_eawFullwidth || width == muni_eawWide; } -static inline bool IsIterationMark(uint32_t codePoint) +MUNI_NODISCARD static inline bool IsIterationMark(uint32_t codePoint) { return codePoint == 0x3005 || codePoint == 0x303B || codePoint == 0x309D || codePoint == 0x309E || codePoint == 0x30FD || codePoint == 0x30FE; } -static inline bool IsCenteredPunctuation(uint32_t codePoint) +MUNI_NODISCARD static inline bool IsCenteredPunctuation(uint32_t codePoint) { switch (codePoint) { @@ -142,7 +142,7 @@ static inline uint8_t Category(uint32_t codePoint) } // AK, AS or U+25CC DOTTED CIRCLE, the bases of LB28a. -static inline bool IsAksara(uint8_t value, uint32_t codePoint) +MUNI_NODISCARD static inline bool IsAksara(uint8_t value, uint32_t codePoint) { return value == muni_lbAk || value == muni_lbAs || codePoint == DOTTED_CIRCLE; } @@ -251,8 +251,9 @@ static inline muniDecision DecideGlue(const muniLineRules* state, uint8_t value) // the rules from LB13 on would forbid, but not after an opening // punctuation or quotation mark (LB14, LB15a). Loose resolves CJ as ID in // the lookup instead. -static inline bool AllowedByLevel(const muniSegmenter* segmenter, const muniLineRules* state, - uint8_t value, uint32_t codePoint) +MUNI_NODISCARD static inline bool AllowedByLevel(const muniSegmenter* segmenter, + const muniLineRules* state, uint8_t value, + uint32_t codePoint) { uint8_t level = muniLineStrictness(segmenter); bool chineseJapanese = muniLineWritingSystem(segmenter) == muni_writingChineseJapanese; diff --git a/third_party/maul-unicode/src/nfkc_casefold.c b/third_party/maul-unicode/src/nfkc_casefold.c index 894c8f767..12db9f557 100644 --- a/third_party/maul-unicode/src/nfkc_casefold.c +++ b/third_party/maul-unicode/src/nfkc_casefold.c @@ -93,7 +93,7 @@ static size_t Map(uint32_t codePoint, uint32_t* out) // Feeds the mapping of one code point to the normalizer; false when it // outgrows a limit. -static bool Feed(muniNormalizer* normalizer, uint32_t codePoint) +MUNI_NODISCARD static bool Feed(muniNormalizer* normalizer, uint32_t codePoint) { if (codePoint < 0xA0) { diff --git a/third_party/maul-unicode/src/normalize.c b/third_party/maul-unicode/src/normalize.c index c2c416b6e..b7822015b 100644 --- a/third_party/maul-unicode/src/normalize.c +++ b/third_party/maul-unicode/src/normalize.c @@ -13,7 +13,7 @@ #include "overlap.h" #include "tables.h" -static bool IsForm(muniNormalForm form) +MUNI_NODISCARD static bool IsForm(muniNormalForm form) { return form == muni_nfc || form == muni_nfd || form == muni_nfkc || form == muni_nfkd; } diff --git a/third_party/maul-unicode/src/normalizer.h b/third_party/maul-unicode/src/normalizer.h index eae3669c1..9b1cd79cd 100644 --- a/third_party/maul-unicode/src/normalizer.h +++ b/third_party/maul-unicode/src/normalizer.h @@ -88,7 +88,7 @@ static inline void muniNormalizerFlush(muniNormalizer* normalizer) // Adds one code point of a full decomposition; false when the segment // would outgrow MUNI_MAX_NORMALIZATION_SEGMENT. -static inline bool muniNormalizerAdd(muniNormalizer* normalizer, uint32_t codePoint) +MUNI_NODISCARD static inline bool muniNormalizerAdd(muniNormalizer* normalizer, uint32_t codePoint) { uint8_t markClass = codePoint < 0x300 ? 0 : muniLookupCombiningClass(codePoint); if (markClass == 0) diff --git a/third_party/maul-unicode/src/overlap.h b/third_party/maul-unicode/src/overlap.h index cdf1c491a..d6fc54968 100644 --- a/third_party/maul-unicode/src/overlap.h +++ b/third_party/maul-unicode/src/overlap.h @@ -9,14 +9,16 @@ #ifndef MAUL_UNICODE_SRC_OVERLAP_H #define MAUL_UNICODE_SRC_OVERLAP_H +#include "maul-unicode/base.h" + #include #include #include // Whether count1 elements at one and count2 at two, each size bytes, // share a byte. An empty array overlaps nothing. -static inline bool muniOverlaps(const void* one, size_t count1, const void* two, size_t count2, - size_t size) +MUNI_NODISCARD static inline bool muniOverlaps(const void* one, size_t count1, const void* two, + size_t count2, size_t size) { uintptr_t a = (uintptr_t)one; uintptr_t b = (uintptr_t)two; diff --git a/third_party/maul-unicode/src/script_runs.c b/third_party/maul-unicode/src/script_runs.c index 1ed1fcc32..ddea8ec71 100644 --- a/third_party/maul-unicode/src/script_runs.c +++ b/third_party/maul-unicode/src/script_runs.c @@ -30,7 +30,7 @@ static_assert(sizeof(void*) < 8 || sizeof(muniScriptIterator) - sizeof(ScriptState) < sizeof(uint64_t), "the public iterator must be no larger than the state needs"); -static bool IsNeutral(uint8_t script) +MUNI_NODISCARD static bool IsNeutral(uint8_t script) { uint32_t tag = muniScriptTags[script]; return tag == MUNI_SCRIPT_COMMON || tag == MUNI_SCRIPT_INHERITED; @@ -41,14 +41,14 @@ static void Add(Scripts* scripts, uint8_t script) scripts->bits[script >> 6] |= (uint64_t)1 << (script & 63); } -static bool Has(const Scripts* scripts, uint8_t script) +MUNI_NODISCARD static bool Has(const Scripts* scripts, uint8_t script) { return (scripts->bits[script >> 6] >> (script & 63) & 1) != 0; } // The scripts a code point belongs to; false for Common and Inherited // code points without extensions, which join any run. -static bool ScriptsOf(uint32_t codePoint, uint8_t* scriptOut, Scripts* scriptsOut) +MUNI_NODISCARD static bool ScriptsOf(uint32_t codePoint, uint8_t* scriptOut, Scripts* scriptsOut) { uint8_t script = muniLookupScript(codePoint); uint8_t set = muniLookupScriptExtensions(codePoint); @@ -137,7 +137,8 @@ static void SettlePending(ScriptState* state) // Brackets among neutral code points: an opening one is pushed, and a // closing one that matches takes its script. Returns whether the code // point now has a script. -static bool Bracket(ScriptState* state, uint32_t codePoint, uint8_t* scriptOut, Scripts* scriptsOut) +MUNI_NODISCARD static bool Bracket(ScriptState* state, uint32_t codePoint, uint8_t* scriptOut, + Scripts* scriptsOut) { uint8_t type = muniLookupBidiBracket(codePoint); if (type == muni_bracketOpen) @@ -172,7 +173,7 @@ static void StartRun(ScriptState* state, uint8_t script, const Scripts* scripts) } // Reads code points until a run ends; true with the run when one does. -static bool Step(ScriptState* state, uint32_t codePoint, muniScriptRun* runOut) +MUNI_NODISCARD static bool Step(ScriptState* state, uint32_t codePoint, muniScriptRun* runOut) { uint8_t script; Scripts scripts; @@ -252,7 +253,7 @@ static muniResult Next(ScriptState* state, muniScriptRun* runOut) // Whether a script index names one of muniScriptTags, or is NO_SCRIPT // where that is allowed. -static bool IsScript(uint8_t script, bool none) +MUNI_NODISCARD static bool IsScript(uint8_t script, bool none) { return script < muniScriptTagCount || (none && script == NO_SCRIPT); } @@ -263,7 +264,7 @@ static bool IsScript(uint8_t script, bool none) // it names or the script tables; false otherwise. An iterator with the // cookie whose fields were changed by hand is outside the contract, and // how its fields agree with each other is not checked again. -static bool Load(const muniScriptIterator* iterator, ScriptState* stateOut) +MUNI_NODISCARD static bool Load(const muniScriptIterator* iterator, ScriptState* stateOut) { memcpy(stateOut, iterator, sizeof(*stateOut)); const ScriptState* state = stateOut; diff --git a/third_party/maul-unicode/src/security.c b/third_party/maul-unicode/src/security.c index 6317ae633..73bf40f3f 100644 --- a/third_party/maul-unicode/src/security.c +++ b/third_party/maul-unicode/src/security.c @@ -73,7 +73,7 @@ static void Add(Scripts* scripts, unsigned index) scripts->bits[index >> 6] |= (uint64_t)1 << (index & 63); } -static bool Has(const Scripts* scripts, unsigned index) +MUNI_NODISCARD static bool Has(const Scripts* scripts, unsigned index) { return (scripts->bits[index >> 6] >> (index & 63) & 1) != 0; } @@ -86,12 +86,12 @@ static void Intersect(Scripts* scripts, const Scripts* other) } } -static bool IsEmpty(const Scripts* scripts) +MUNI_NODISCARD static bool IsEmpty(const Scripts* scripts) { return (scripts->bits[0] | scripts->bits[1] | scripts->bits[2] | scripts->bits[3]) == 0; } -static bool IsAll(const Scripts* scripts) +MUNI_NODISCARD static bool IsAll(const Scripts* scripts) { return (scripts->bits[0] & scripts->bits[1] & scripts->bits[2] & scripts->bits[3]) == UINT64_MAX; @@ -133,7 +133,8 @@ static Indexes FindIndexes(void) // The augmented script set of a code point (UTS #39 section 5.1); false // when it is every script. -static bool Augmented(uint32_t codePoint, const Indexes* indexes, Scripts* scriptsOut) +MUNI_NODISCARD static bool Augmented(uint32_t codePoint, const Indexes* indexes, + Scripts* scriptsOut) { uint8_t script = muniLookupScript(codePoint); uint8_t set = muniLookupScriptExtensions(codePoint); diff --git a/third_party/maul-unicode/src/segment.c b/third_party/maul-unicode/src/segment.c index 8f55704f0..cc357e659 100644 --- a/third_party/maul-unicode/src/segment.c +++ b/third_party/maul-unicode/src/segment.c @@ -34,8 +34,8 @@ static inline unsigned Byte(const muniSegmentIterator* iterator, size_t offset) // Whether the rules of a segmenter copied out of iterator hold what their // rule set writes, each flag's byte a 0 or 1 included. -static MUNI_ALWAYS_INLINE bool RulesValid(const muniSegmenter* segmenter, - const muniSegmentIterator* iterator) +MUNI_NODISCARD static MUNI_ALWAYS_INLINE bool RulesValid(const muniSegmenter* segmenter, + const muniSegmentIterator* iterator) { switch (segmenter->kind) { @@ -104,8 +104,8 @@ static MUNI_ALWAYS_INLINE bool RulesValid(const muniSegmenter* segmenter, // fields agree with each other is not checked again. Every call reads // most of the state, so all of it is checked, inline in each call so that // the copy stays inline too. -static MUNI_ALWAYS_INLINE bool Load(const muniSegmentIterator* iterator, - muniSegmenter* segmenterOut) +MUNI_NODISCARD static MUNI_ALWAYS_INLINE bool Load(const muniSegmentIterator* iterator, + muniSegmenter* segmenterOut) { memcpy(segmenterOut, iterator, sizeof(*segmenterOut)); const muniSegmenter* segmenter = segmenterOut; @@ -232,8 +232,8 @@ muniResult muniSegmentIterator_SetComplexBreaker(muniSegmentIterator* iterator, // Whether a table of overrides is in strictly increasing order of code // point and names only code points and known classes: up to classCount. -static bool ValidOverrides(const muniBreakClassOverride* overrides, size_t count, - uint8_t classCount) +MUNI_NODISCARD static bool ValidOverrides(const muniBreakClassOverride* overrides, size_t count, + uint8_t classCount) { for (size_t i = 0; i < count; ++i) { diff --git a/third_party/maul-unicode/src/sentence.c b/third_party/maul-unicode/src/sentence.c index bf3ab1a91..155c82c81 100644 --- a/third_party/maul-unicode/src/sentence.c +++ b/third_party/maul-unicode/src/sentence.c @@ -35,7 +35,7 @@ enum Stops = BIT(muni_sbOLetter) | BIT(muni_sbUpper) | ParaSep | SATerm, }; -static bool In(uint8_t value, uint32_t set) +MUNI_NODISCARD static bool In(uint8_t value, uint32_t set) { return ((set >> (value & 31u)) & 1u) != 0; } diff --git a/third_party/maul-unicode/src/skeleton.c b/third_party/maul-unicode/src/skeleton.c index 3cfd2264f..cbb3ae0a6 100644 --- a/third_party/maul-unicode/src/skeleton.c +++ b/third_party/maul-unicode/src/skeleton.c @@ -236,7 +236,7 @@ static void AddForward(Skeleton* skeleton, size_t start, size_t end) } } -static bool IsMark(uint32_t codePoint) +MUNI_NODISCARD static bool IsMark(uint32_t codePoint) { uint8_t category = muniLookupGeneralCategory(codePoint); return category == muni_gcMn || category == muni_gcMc || category == muni_gcMe; @@ -309,7 +309,8 @@ static void AddStretch(Skeleton* skeleton, size_t start, size_t end, uint8_t lev // Whether the text needs bidi: a right-to-left paragraph, or a code // point that can open an odd level. -static bool NeedsBidi(const uint8_t* text, size_t length, muniBidiDirection direction) +MUNI_NODISCARD static bool NeedsBidi(const uint8_t* text, size_t length, + muniBidiDirection direction) { if (direction == muni_bidiRightToLeft) { diff --git a/third_party/maul-unicode/src/word.c b/third_party/maul-unicode/src/word.c index d20c76870..6177e7600 100644 --- a/third_party/maul-unicode/src/word.c +++ b/third_party/maul-unicode/src/word.c @@ -34,7 +34,7 @@ enum Hebrew = BIT(muni_wbHebrewLetter), }; -static bool In(uint8_t value, uint32_t set) +MUNI_NODISCARD static bool In(uint8_t value, uint32_t set) { return ((set >> (value & 31u)) & 1u) != 0; } diff --git a/third_party/maul-unicode/src/writer.h b/third_party/maul-unicode/src/writer.h index 433e24558..7171cdb72 100644 --- a/third_party/maul-unicode/src/writer.h +++ b/third_party/maul-unicode/src/writer.h @@ -10,6 +10,8 @@ #ifndef MAUL_UNICODE_SRC_WRITER_H #define MAUL_UNICODE_SRC_WRITER_H +#include "maul-unicode/base.h" + #include #include @@ -67,7 +69,7 @@ static inline void muniWriterPut(muniWriter* writer, uint32_t codePoint) } // Whether the whole output fit in the buffer. -static inline bool muniWriterFits(const muniWriter* writer) +MUNI_NODISCARD static inline bool muniWriterFits(const muniWriter* writer) { return writer->needed <= writer->capacity && writer->needed != SIZE_MAX; } From a6dc372aca12cede62a6c5c3837c1dfcd7d787e2 Mon Sep 17 00:00:00 2001 From: Sirac Ozmen Date: Thu, 5 Nov 2026 12:08:59 +0300 Subject: [PATCH 3/3] docs(deps): say why Maul Unicode may run ahead of what Maul UI names --- docs/operations/dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operations/dependencies.md b/docs/operations/dependencies.md index d94c7e40c..987757752 100644 --- a/docs/operations/dependencies.md +++ b/docs/operations/dependencies.md @@ -67,7 +67,7 @@ Vendored code is upstream's, with these changes only. Each is a patch in `tools/ - **Maul RHI** cooks the engine's shader containers: a move runs `tools/gen_shaders.py` with the shader toolchain and commits the containers and their stamps, which the check refuses from another release ([development.md](development.md#generated-files)). - **Maul2D and Maul3D** keep the engine's own `CMakeLists.txt` beside their sources; bring its source list in line with upstream's when it changes. The other Maul libraries build with their own CMake, configured by `third_party/maul_*.cmake`. -- **Maul UI, Maul Window, Maul Unicode and Maul Tween** move together: Maul UI and Maul Window both build against the vendored Maul Unicode (`third_party/maul_ui.cmake`, included first, configures it with its HarfBuzz functions), and Maul UI builds the vendored Maul Tween into its core. Each names the one release of the others it was built against: Maul Window its Maul Unicode, Maul UI its Maul Window, Maul Unicode, Maul Tween and Maul RHI, and Maul UI's Window glue refuses to configure when its Maul Unicode and Maul Window's differ. So the four move to the releases Maul UI names, with Maul RHI at the one it names too. A move of any of them is built and checked with the others. Maul snapshots and journals refuse another build, so both sides of anything that exchanges them need the same pins. +- **Maul UI, Maul Window, Maul Unicode and Maul Tween** move together: Maul UI and Maul Window both build against the vendored Maul Unicode (`third_party/maul_ui.cmake`, included first, configures it with its HarfBuzz functions), and Maul UI builds the vendored Maul Tween into its core. Each names the release of the others it was built against: Maul Window its Maul Unicode, Maul UI its Maul Window, Maul Unicode, Maul Tween and Maul RHI. Maul Window, Maul Tween and Maul RHI move to the releases Maul UI names. Maul Unicode may be a later release of the same major version: from 1.0.0 it makes no breaking release within a major version, and its CMake package takes a request for any earlier release of it. No check in the engine's build stops that. `third_party/maul_ui.cmake` points FetchContent's `maul-unicode` at `third_party/maul-unicode`, which Maul UI's text component and Maul Window both take whatever tag they declare. The check that refuses a Maul Unicode differing from Maul Window's, in Maul UI's `cmake/Window.cmake`, belongs to Maul UI's Window glue, which only `MAUL_UI_WINDOW` builds, off by default and never turned on here, since the engine drives Maul Window itself. A later Maul Unicode is still built and checked with the others, which then run on it inside the engine. A move of any of them is built and checked with the others. Maul snapshots and journals refuse another build, so both sides of anything that exchanges them need the same pins. - **MsQuic and OpenSSL** move together: the OpenSSL revision is the one MsQuic pins as its submodule. Configure builds both once per machine with `tools/build_quic.sh` into `~/.cache/rawframe` (or `$RAWFRAME_DEPENDENCY_CACHE`), and again only when the revisions, the script or the compiler change. For the web build, OpenSSL's libcrypto alone is built for wasm32-wasi by `tools/build_openssl_wasm.sh`. - **Zstandard** is part of the packing format: a new revision may change compressed bytes, so moving it is a new packer generation, never a silent change. Runtimes link the decoder only. - **cgltf, miniaudio's decoders, stb_vorbis and the Opus encoder** are import tooling: they are linked into the cook, never into a runtime. The runtime builds only Maul Audio's device layer and the Opus decoder.