@@ -107,6 +107,7 @@ int fl_utf8len(char c)A similar change can be made to fl_utf8len1() for symmetry.
if (c & 0x10) {
if (c & 0x08) {
if (c & 0x04) {
+ if (c & 0x02) return -1;
return 6;
}
return 5;
I carefully made a test app comparing old
code to the new for all 256 possible input values to the function, and the only
difference the change makes is it now returns -1 for the 0xFE
and 0xFF cases.
Since this isn't my code, I thought it best to run it up the flagpole to
see if there's any negative
effects you all who know utf8 might foresee.
I'm working on Fl_Terminal to make sure it properly detects invalid UTF-8 strings, and since the single characters 0xFF and 0xFE are invalid anywhere in UTF-8 strings, they make a convenient test case. However, I noticed our fl_utf8len() function wasn't catching them.
Suggesting this one line change to fl_utf8len() to detect the two invalid
single character UTF-8 chars 0xFE and 0xFF:@@ -107,6 +107,7 @@ int fl_utf8len(char c)
if (c & 0x10) {
if (c & 0x08) {
if (c & 0x04) {
+ if (c & 0x02) return -1;
return 6;
}
return 5;
Suggesting this one line change to fl_utf8len() to detect the two invalid
single character UTF-8 chars 0xFE and 0xFF:@@ -107,6 +107,7 @@ int fl_utf8len(char c)A similar change can be made to fl_utf8len1() for symmetry.
if (c & 0x10) {
if (c & 0x08) {
if (c & 0x04) {
+ if (c & 0x02) return -1;
return 6;
}
return 5;
The change seems a good way to implement, as it /only/ does the extra test if the utf-8 string is the rare length of 6, so it won't even affect the speed of shorter lengths.
UTF-8 encodes code points in one to four bytes, depending on the value of the code point. In the following table, the letters u through z represent the hexadecimal digits of a character’s Unicode number (U+uvwxyz, or U+wxyz for a four-digit number). In the binary column, each hex digit is expanded into its 4-bit binary equivalent (uuuu to zzzz) to show how its bits are distributed across the UTF-8 bytes.
| First code point | Last code point | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
|---|---|---|---|---|---|
| U+0000 | U+007F | 0yyyzzzz | |||
| U+0080 | U+07FF | 110xxxyy | 10yyzzzz | ||
| U+0800 | U+FFFF | 1110wwww | 10xxxxyy | 10yyzzzz | |
| U+010000 | U+10FFFF | 11110uvv | 10vvwwww | 10xxxxyy | 10yyzzzz |
I carefully made a test app comparing old code to the new for all 256 possible input values to the function, and the only difference the change makes is it now returns -1 for the 0xFE and 0xFF cases.
Why this came up:
Since this isn't my code, I thought it best to run it up the flagpole to see if there's any negative effects you all who know utf8 might foresee.
I'm working on Fl_Terminal to make sure it properly detects invalid UTF-8 strings, and since the single characters 0xFF and 0xFE are invalid anywhere in UTF-8 strings, they make a convenient test case. However, I noticed our fl_utf8len() function wasn't catching them.
The problem with the current implementation is (and has "always" been) that proper UTF-8 encoding covers only 4 bytes (21 bits of Unicode code points, which is, AFAICT, the limit of current and future Unicode), and therefore returning 5 or 6 is faulty anyway.
Instead of adding this line we should return -1 instead of 5 or 6 anyway, to be Unicode compliant.
I'm not sure if it is now a good time (before the release of 1.5) to change such a function in the way I would suggest for correctness (handling length 5 and 6 as error and returning -1 instead), and I didn't check FLTK's code for any side effects, but I think it would be OK. It would definitely not violate the documentation because the documentation doesn't specify what valid and invalid encodings are.
Regarding the tests in Fl_Terminal: you could test for `x < 0 || x > 4` (or equivalent) to catch all really invalid UTF-8 encodings - which would catch the mentioned byte values but doesn't mean that this would catch all invalid Unicode code points, BTW.
I'm not sure if it is now a good time (before the release of 1.5) to change such a function in the way I would suggest for correctness (handling length 5 and 6 as error and returning -1 instead), and I didn't check FLTK's code for any side effects, but I think it would be OK.
int fl_utf8len(char c) {..but I didn't want to make such a large change, and asked for a simpler modification retaining the spirit and formatting of the old code, so it suggested that one line mod for the return 6 case instead.
const unsigned char u = (unsigned char)c;
if (u <= 0x7f) return 1;
if (u >= 0xc2 && u <= 0xdf) return 2;
if (u >= 0xe0 && u <= 0xef) return 3;
if (u >= 0xf0 && u <= 0xf4) return 4;
return -1;
}
int fl_utf8len1(char c) {
const int len = fl_utf8len(c);
return len < 0 ? 1 : len;
}
..that last bit being relevant, and seems to be exactly what the new code (above) does.
On 8/12/26 09:30, Greg Ercolano wrote:
I should mention though; FWIW, codex initially suggested rewriting the code to be as follows, as per RFC 3629:int fl_utf8len(char c) {
const unsigned char u = (unsigned char)c;
if (u <= 0x7f) return 1;
if (u >= 0xc2 && u <= 0xdf) return 2;
if (u >= 0xe0 && u <= 0xef) return 3;
if (u >= 0xf0 && u <= 0xf4) return 4;
return -1;
}
int fl_utf8len1(char c) {
const int len = fl_utf8len(c);
return len < 0 ? 1 : len;
}
[..]
On 8/12/26 09:30, Greg Ercolano wrote:
I should mention though; FWIW, codex initially suggested rewriting the code to be as follows, as per RFC 3629:int fl_utf8len(char c) {[..]
const unsigned char u = (unsigned char)c;
if (u <= 0x7f) return 1;
if (u >= 0xc2 && u <= 0xdf) return 2;
if (u >= 0xe0 && u <= 0xef) return 3;
if (u >= 0xf0 && u <= 0xf4) return 4;
return -1;
}
int fl_utf8len1(char c) {
const int len = fl_utf8len(c);
return len < 0 ? 1 : len;
}
On 8/12/26 09:30, Greg Ercolano wrote:+1 on this version of the code, for these reasons:I should mention though; FWIW, codex initially suggested rewriting the code to be as follows, as per RFC 3629:int fl_utf8len(char c) {
const unsigned char u = (unsigned char)c;
if (u <= 0x7f) return 1;
if (u >= 0xc2 && u <= 0xdf) return 2;
if (u >= 0xe0 && u <= 0xef) return 3;
if (u >= 0xf0 && u <= 0xf4) return 4;
return -1;
}
int fl_utf8len1(char c) {
const int len = fl_utf8len(c);
return len < 0 ? 1 : len;
}
[..]
- clarity and maintainability (fl_utf8len)
- correctness for UTF-8, according to RFC 3629 (fl_utf8len)
- no duplication of code in fl_utf8len1
[..]
Note: meanwhile I'm convinced that we should make this kind of changes NOW, i.e. before we release FLTK 1.5.0 !
I should mention though; FWIW, codex initially suggested rewriting the code to be as follows, as per RFC 3629:int fl_utf8len(char c) {
const unsigned char u = (unsigned char)c;
if (u <= 0x7f) return 1;
if (u >= 0xc2 && u <= 0xdf) return 2;
if (u >= 0xe0 && u <= 0xef) return 3;
if (u >= 0xf0 && u <= 0xf4) return 4;
return -1;
}
int fl_utf8len1(char c) {
const int len = fl_utf8len(c);
return len < 0 ? 1 : len;
}
[..]
[..]
OK, great, I'll commit it today, as it would seriously help the terminal tests.
We can always revert if other devs makes a compelling argument to the contrary.
Albrecht-S schrieb am Mittwoch, 12. August 2026 um 23:14:21 UTC+2:
+1 on this version of the code [..]
+1 from me FWIW