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;