commit 7daf886
uint
·
2026-10-18 22:34:10 +0000 UTC
parent 7224752
UTF: Add uleutf_[casecmp, cat, chr, str]
2 files changed,
+139,
-5
+12,
-5
1@@ -20,7 +20,18 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
2
3 #include "uletypes.h"
4
5-/* Implemented */
6+/* Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
7+ src2 respectively, case insensitive */
8+int uleutf_casecmp(const char* src1, const char* src2);
9+
10+/* Append the utf8 string src onto the utf8 string dst */
11+char* uleutf_cat(char* dst, const char* src);
12+
13+/* Find the first match of the utf8 codepoint chr in the utf8 string src */
14+char* uleutf_chr(const char* src, ule_i32 chr);
15+
16+/* The position of the utf8 string needle in the utf8 string haystack */
17+char* uleutf_str(const char* haystack, const char* needle);
18
19 /* Sets out_codepoint to the current utf8 codepoint in str, and returns the
20 address of the next utf8 codepoint after the current one in str */
21@@ -39,9 +50,6 @@ size_t uleutf_codepointsize(ule_i32 chr);
22 int uleutf_codepointvisualwidth(ule_i32 chr);
23
24 /* TODO */
25-int uleutf_casecmp(const char* src1, const char* src2);
26-char* uleutf_cat(char* dst, const char* src);
27-char* uleutf_chr(const char* src, ule_i32 chr);
28 int uleutf_cmp(const char* src1, const char* src2);
29 char* uleutf_cpy(char* dst, const char* src);
30 size_t uleutf_cspn(const char* src, const char* reject);
31@@ -59,7 +67,6 @@ size_t uleutf_size(const char* str);
32 size_t uleutf_size_lazy(const char* str);
33 size_t uleutf_nsize_lazy(const char* str, size_t n);
34 size_t uleutf_spn(const char* src, const char* accept);
35-char* uleutf_str(const char* haystack, const char* needle);
36 char* uleutf_casestr(const char* haystack, const char* needle);
37 char* uleutf_valid(const char* str);
38 char* uleutf_nvalid(const char* str, size_t n);
+127,
-0
1@@ -2,6 +2,133 @@
2
3 #include "uleutf.h"
4
5+int uleutf_casecmp(const char* src1, const char* src2)
6+{
7+ ule_i32 src1_lwr_cp = 0;
8+ ule_i32 src2_lwr_cp = 0;
9+ ule_i32 src1_upr_cp = 0;
10+ ule_i32 src2_upr_cp = 0;
11+ ule_i32 src1_orig_cp = 0;
12+ ule_i32 src2_orig_cp = 0;
13+
14+ for (;;) {
15+ src1 = uleutf_codepoint(src1, &src1_orig_cp);
16+ src2 = uleutf_codepoint(src2, &src2_orig_cp);
17+
18+ /* lower the srcs if required */
19+ src1_lwr_cp = utf8lwrcodepoint(src1_orig_cp);
20+ src2_lwr_cp = utf8lwrcodepoint(src2_orig_cp);
21+
22+ /* lower the srcs if required */
23+ src1_upr_cp = utf8uprcodepoint(src1_orig_cp);
24+ src2_upr_cp = utf8uprcodepoint(src2_orig_cp);
25+
26+ /* check if the lowered codepoints match */
27+ if ((0 == src1_orig_cp) && (0 == src2_orig_cp))
28+ return 0;
29+ else if ((src1_lwr_cp == src2_lwr_cp) || (src1_upr_cp == src2_upr_cp))
30+ continue;
31+
32+ /* if they don't match, then we return the difference between the characters */
33+ return src1_lwr_cp - src2_lwr_cp;
34+ }
35+}
36+
37+char* uleutf_cat(char* dst, const char* src)
38+{
39+ char* d = dst;
40+
41+ /* find the null terminating byte in dst */
42+ while ('\0' != *d)
43+ d++;
44+
45+ /* overwriting the null terminating byte in dst, append src byte-by-byte */
46+ while ('\0' != *src)
47+ *d++ = *src++;
48+
49+ /* write out a new null terminating byte into dst */
50+ *d = '\0';
51+
52+ return dst;
53+}
54+
55+char* uleutf_chr(const char* src, ule_i32 chr)
56+{
57+ char c[5] = {'\0', '\0', '\0', '\0', '\0'};
58+
59+ if (0 == chr) {
60+ /* being asked to return position of null terminating byte, so
61+ just run s to the end, and return! */
62+ while ('\0' != *src)
63+ src++;
64+
65+ return (char*)src;
66+ }
67+ else if (0 == ((ule_i32)0xffffff80 & chr)) {
68+ /* 1-byte/7-bit ascii
69+ (0b0xxxxxxx) */
70+ c[0] = (ule_i8)chr;
71+ }
72+ else if (0 == ((ule_i32)0xfffff800 & chr)) {
73+ /* 2-byte/11-bit utf8 code point
74+ (0b110xxxxx 0b10xxxxxx) */
75+ c[0] = (ule_i8)(0xc0 | (ule_i8)(chr >> 6));
76+ c[1] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
77+ }
78+ else if (0 == ((ule_i32)0xffff0000 & chr)) {
79+ /* 3-byte/16-bit utf8 code point
80+ (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) */
81+ c[0] = (ule_i8)(0xe0 | (ule_i8)(chr >> 12));
82+ c[1] = (ule_i8)(0x80 | (ule_i8)((chr >> 6) & 0x3f));
83+ c[2] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
84+ }
85+ else { /* if (0 == ((int)0xffe00000 & chr)) { */
86+ /* 4-byte/21-bit utf8 code point
87+ (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) */
88+ c[0] = (ule_i8)(0xf0 | (ule_i8)(chr >> 18));
89+ c[1] = (ule_i8)(0x80 | (ule_i8)((chr >> 12) & 0x3f));
90+ c[2] = (ule_i8)(0x80 | (ule_i8)((chr >> 6) & 0x3f));
91+ c[3] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
92+ }
93+
94+ /* we've made c into a 2 utf8 codepoint string, one for the chr we are
95+ seeking, another for the null terminating byte. now use utf8str to search */
96+ return uleutf_str(src, c);
97+}
98+
99+char* uleutf_str(const char* haystack, const char* needle)
100+{
101+ ule_i32 throwaway_codepoint = 0;
102+
103+ /* if needle has no utf8 codepoints before the null terminating
104+ byte then return haystack */
105+ if ('\0' == *needle)
106+ return (char*)haystack;
107+
108+ while ('\0' != *haystack) {
109+ const char* maybe_match = haystack;
110+ const char* n = needle;
111+
112+ while (*haystack == *n && (*haystack != '\0' && *n != '\0')) {
113+ n++;
114+ haystack++;
115+ }
116+
117+ if ('\0' == *n)
118+ /* we found the whole utf8 string for needle in haystack at
119+ maybeMatch, so return it */
120+ return (char*)maybe_match;
121+ else
122+ /* h could be in the middle of an unmatching utf8 codepoint,
123+ so we need to march it on to the next character beginning
124+ starting from the current character */
125+ haystack = uleutf_codepoint(maybe_match, &throwaway_codepoint);
126+ }
127+
128+ /* no match */
129+ return NULL;
130+}
131+
132 char* uleutf_codepoint(const char* str, ule_i32* out_codepoint)
133 {
134 size_t n = 1;