This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository terminology.

View the commit online.

commit ebc6ab5cc037dee1261c325c1299f6bfa58e329b
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 3 13:25:45 2026 -0600

    utf8: share one UTF-8 decode loop with the test harness
    
    The pty read path decoded UTF-8 inline, and tytest_common.c carried a
    near-identical copy of the same loop, differing only in variable names and
    casts. Two copies of the same tricky logic means the tests can end up
    validating code that is not the code being shipped.
    
    Move it into utf8.c as utf8_to_codepoints(), reporting how many input bytes
    it used so the caller can retain a sequence the read boundary cut in half.
    No behaviour change.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 src/bin/termpty.c | 39 ++++++---------------------------------
 src/bin/utf8.c    | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/bin/utf8.h    |  8 ++++++++
 3 files changed, 66 insertions(+), 33 deletions(-)

diff --git a/src/bin/termpty.c b/src/bin/termpty.c
index d4ab1e38..93ad4e25 100644
--- a/src/bin/termpty.c
+++ b/src/bin/termpty.c
@@ -7,6 +7,7 @@
 #include "termptyesc.h"
 #include "termptyops.h"
 #include "backlog.h"
+#include "utf8.h"
 #include "keyin.h"
 #if !defined(BINARY_TYFUZZ) && !defined(BINARY_TYTEST)
 # include "win.h"
@@ -236,7 +237,7 @@ _handle_read(Termpty *ty, Eina_Bool false_on_empty)
         Eina_Unicode codepoint[4097];
         char buf[4097];
         char *rbuf = buf;
-        int i, j;
+        int i, j, consumed;
         len = sizeof(buf) - 1;
 
         for (i = 0; i < (int)sizeof(ty->oldbuf) && ty->oldbuf[i] & 0x80; i++)
@@ -283,38 +284,10 @@ _handle_read(Termpty *ty, Eina_Bool false_on_empty)
         */
         buf[len] = 0;
         // convert UTF8 to codepoint integers
-        j = 0;
-        for (i = 0; i < len;)
-          {
-             Eina_Unicode g = 0, prev_i = i;
-
-             if (buf[i])
-               {
-                  g = eina_unicode_utf8_next_get(buf, &i);
-                  if ((0xdc80 <= g) && (g <= 0xdcff) &&
-                      (len - (int)prev_i) <= (int)sizeof(ty->oldbuf))
-                    {
-                       unsigned int k;
-
-                       for (k = 0;
-                            (k < (unsigned int)sizeof(ty->oldbuf)) &&
-                            (k < (unsigned int)(len - prev_i));
-                            k++)
-                         {
-                            ty->oldbuf[k] = buf[prev_i+k];
-                         }
-                       DBG("failure at %d/%d/%d", (int)prev_i, (int)i, len);
-                       break;
-                    }
-               }
-             else
-               {
-                  g = 0;
-                  i++;
-               }
-             codepoint[j] = g;
-             j++;
-          }
+        j = utf8_to_codepoints(buf, len, codepoint, &consumed);
+        /* Retain a multibyte sequence cut in half by the read boundary. */
+        for (i = 0; (i < len - consumed) && (i < (int)sizeof(ty->oldbuf)); i++)
+          ty->oldbuf[i] = buf[consumed + i];
         codepoint[j] = 0;
 //        DBG("---------------- handle buf %i", j);
         termpty_handle_buf(ty, codepoint, j);
diff --git a/src/bin/utf8.c b/src/bin/utf8.c
index 9b7b34f6..4f674132 100644
--- a/src/bin/utf8.c
+++ b/src/bin/utf8.c
@@ -1,6 +1,58 @@
 #include "private.h"
 #include "utf8.h"
 
+/* Decode UTF-8 bytes into codepoints.
+ *
+ * 'buf' must hold 'len' bytes and be NUL-terminated at buf[len], as
+ * eina_unicode_utf8_next_get() works on NUL-terminated strings. 'codepoints'
+ * must have room for 'len' entries, which is always enough since multibyte
+ * sequences only ever shrink the count.
+ *
+ * Returns the number of codepoints written. '*consumed' gets the number of
+ * input bytes decoded; anything left over is a multibyte sequence truncated by
+ * the end of the buffer, which the caller carries over and re-submits in front
+ * of the next chunk.
+ */
+int
+utf8_to_codepoints(const char *buf, int len, Eina_Unicode *codepoints,
+                   int *consumed)
+{
+   int i = 0, j = 0;
+
+   while (i < len)
+     {
+        Eina_Unicode g;
+
+        if (buf[i])
+          {
+             int prev_i = i;
+
+             g = eina_unicode_utf8_next_get(buf, &i);
+             /* EFL maps invalid and truncated sequences alike into the
+              * surrogate-escape range; near the end of the buffer, assume
+              * truncation and hand the tail back to the caller. */
+             if ((0xdc80 <= g) && (g <= 0xdcff) &&
+                 ((len - prev_i) <= UTF8_CARRY_MAX))
+               {
+                  i = prev_i;
+                  break;
+               }
+          }
+        else
+          {
+             /* eina_unicode_utf8_next_get() stops at NUL, so an embedded one
+              * has to be stepped over by hand. */
+             g = 0;
+             i++;
+          }
+        codepoints[j] = g;
+        j++;
+     }
+
+   *consumed = i;
+   return j;
+}
+
 int
 codepoint_to_utf8(Eina_Unicode g, char *txt)
 {
diff --git a/src/bin/utf8.h b/src/bin/utf8.h
index 87b63b78..e08cbd0b 100644
--- a/src/bin/utf8.h
+++ b/src/bin/utf8.h
@@ -3,4 +3,12 @@
 #include <Eina.h>
 int codepoint_to_utf8(Eina_Unicode g, char *txt);
 
+/* Longest UTF-8 sequence this decoder will carry across a read() boundary.
+ * The leftover tail itself is always shorter than this, since a sequence is
+ * only retained while at least one of its bytes is still missing. */
+#define UTF8_CARRY_MAX 4
+
+int utf8_to_codepoints(const char *buf, int len, Eina_Unicode *codepoints,
+                       int *consumed);
+
 #endif

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to