Avoid UB breaking the build with llvm22

https://github.com/fcitx/libime/pull/99

Index: src/libime/core/datrie.cpp
--- src/libime/core/datrie.cpp.orig
+++ src/libime/core/datrie.cpp
@@ -23,7 +23,9 @@
 #include <fstream>
 #include <ios>
 #include <istream>
+#include <iterator>
 #include <limits>
+#include <optional>
 #include <ostream>
 #include <stdexcept>
 #include <string>
@@ -96,7 +98,8 @@ class DATriePrivate { (public)
     static inline const int32_t CEDAR_NO_VALUE = NanValue<V>::NO_VALUE();
     static inline const int32_t CEDAR_NO_PATH = NanValue<V>::NO_PATH();
 
-    static constexpr int MAX_ALLOC_SIZE = 1 << 16; // must be divisible by 256
+    static constexpr size_t MAX_ALLOC_SIZE = 1
+                                             << 16; // must be divisible by 256
     using result_type = value_type;
     using uchar = uint8_t;
     static_assert(sizeof(value_type) <= sizeof(int32_t),
@@ -481,10 +484,7 @@ class DATriePrivate { (public)
         if (m_tail.capacity() < m_tail.size() + needed) {
             auto quota =
                 m_tail.capacity() +
-                ((needed > m_tail.size() || needed > MAX_ALLOC_SIZE)
-                     ? needed
-                     : (m_tail.size() >= MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE
-                                                        : m_tail.size()));
+                std::max(needed, std::min(MAX_ALLOC_SIZE, m_tail.size()));
             m_tail.reserve(quota);
         }
         m_array[from].base = -m_tail.size();
@@ -592,8 +592,11 @@ class DATriePrivate { (public)
             npos.offset ? -static_cast<int>(npos.offset) : m_array[from].base;
         if (base >= 0) { // on trie
             uchar c = m_ninfo[from].child;
-            if (!from && !(c = m_ninfo[base ^ c].sibling)) { // bug fix
-                return CEDAR_NO_PATH;                        // no entry
+            if (!from) {
+                c = m_ninfo[base ^ c].sibling;
+                if (!c) {                 // bug fix
+                    return CEDAR_NO_PATH; // no entry
+                }
             }
             for (; c && base >= 0; ++len) {
                 from = static_cast<size_t>(base) ^ c;
@@ -638,11 +641,13 @@ class DATriePrivate { (public)
     int _follow(uint32_t &from, const uchar label, const T &cf) {
         int to = 0;
         const int base = m_array[from].base;
-        if (base < 0 || m_array[to = base ^ label].check < 0) {
+        if (base < 0 || m_array[base ^ label].check < 0) {
             to = _pop_enode(base, label, static_cast<int>(from));
             _push_sibling(from, to ^ label, label, base >= 0);
-        } else if (m_array[to].check != static_cast<int>(from)) {
+        } else if (m_array[base ^ label].check != static_cast<int>(from)) {
             to = _resolve(from, base, label, cf);
+        } else {
+            to = base ^ label;
         }
         return to;
     }
@@ -707,7 +712,7 @@ class DATriePrivate { (public)
     int _find_place(const uchar *const first, const uchar *const last) {
         if (auto bi = m_bheadO) {
             const auto bz = m_block[m_bheadO].prev;
-            const auto nc = static_cast<short>(last - first + 1);
+            const auto nc = std::distance(first, last);
             while (true) { // set candidate block
                 block &b = m_block[bi];
                 if (b.num >= nc && nc < b.reject) { // explore configuration
@@ -715,11 +720,13 @@ class DATriePrivate { (public)
                         const int base = e ^ *first;
                         for (const uchar *p = first;
                              m_array[base ^ *++p].check < 0;) {
-                            if (p == last) {
-                                return b.ehead = e; // no conflict
+                            if (p + 1 == last) {
+                                b.ehead = e;
+                                return b.ehead; // no conflict
                             }
                         }
-                        if ((e = -m_array[e].check) == b.ehead) {
+                        e = -m_array[e].check;
+                        if (e == b.ehead) {
                             break;
                         }
                     }
@@ -892,23 +899,27 @@ class DATriePrivate { (public)
         return c_p;
     }
     // enumerate (equal to or more than one) child nodes
-    uchar *_set_child(uchar *p, const int base, uchar c, const int label = -1) {
-        --p;
+    uchar *_set_child(uchar *p, const int base, uchar c,
+                      const std::optional<uchar> label = std::nullopt) {
         if (!c) {
-            *++p = c;
+            *p = c;
+            p++;
             c = m_ninfo[base ^ c].sibling;
         } // 0: terminal
-        if (ORDERED) {
-            while (c && c < label) {
-                *++p = c;
+        if (ORDERED && label.has_value()) {
+            while (c && c < *label) {
+                *p = c;
+                p++;
                 c = m_ninfo[base ^ c].sibling;
             }
         }
-        if (label != -1) {
-            *++p = static_cast<uchar>(label);
+        if (label.has_value()) {
+            *p = *label;
+            p++;
         }
         while (c) {
-            *++p = c;
+            *p = c;
+            p++;
             c = m_ninfo[base ^ c].sibling;
         }
         return p;
@@ -925,30 +936,33 @@ class DATriePrivate { (public)
             = _consult(base_n, base_p, m_ninfo[from_n].child,
                        m_ninfo[from_p].child);
         uchar child[256];
-        uchar *const first = &child[0];
+        uchar *const first = child;
         uchar *const last =
             flag ? _set_child(first, base_n, m_ninfo[from_n].child, label_n)
                  : _set_child(first, base_p, m_ninfo[from_p].child);
+        assert(first < last);
         const int base =
-            (first == last ? _find_place() : _find_place(first, last)) ^ *first;
+            (first + 1 == last ? _find_place() : _find_place(first, last)) ^
+            *first;
         // replace & modify empty list
         const int from = flag ? static_cast<int>(from_n) : from_p;
         const int base_ = flag ? base_n : base_p;
         if (flag && *first == label_n) {
             m_ninfo[from].child = label_n; // new child
         }
-        m_array[from].base = base;                     // new base
-        for (const uchar *p = first; p <= last; ++p) { // to_ => to
+        m_array[from].base = base;                    // new base
+        for (const uchar *p = first; p < last; ++p) { // to_ => to
             const int to = _pop_enode(base, *p, from);
             const int to_ = base_ ^ *p;
-            m_ninfo[to].sibling = (p == last ? 0 : *(p + 1));
+            m_ninfo[to].sibling = (p + 1 == last) ? 0 : *(p + 1);
             if (flag && to_ == to_pn) {
                 continue; // skip newcomer (no child)
             }
             cf(to_, to);
             node &n = m_array[to];
             node &n_ = m_array[to_];
-            if ((n.base = n_.base) > 0 && *p) { // copy base; bug fix
+            n.base = n_.base; // copy base; bug fix
+            if (n.base > 0 && *p) {
                 uchar c = m_ninfo[to].child = m_ninfo[to_].child;
                 do {
                     m_array[n.base ^ c].check = to; // adjust grand son's check
