master uint/libule / source / uleutf.c
  1#include <stddef.h>
  2
  3#include "uleutf.h"
  4
  5int 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
 37char* 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
 55char* 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
 99int uleutf_cmp(const char* src1, const char* src2, size_t n)
100{
101	while (('\0' != *src1) || ('\0' != *src2)) {
102		if ((unsigned char)*src1 < (unsigned char)*src2)
103			return -1;
104		else if ((unsigned char)*src1 > (unsigned char)*src2)
105			return 1;
106
107		src1++;
108		src2++;
109	}
110
111	/* both utf8 strings matched */
112	return 0;
113}
114
115int uleutf_ncmp(const char* src1, const char* src2, size_t n)
116{
117	while ((0 != n--) && (('\0' != *src1) || ('\0' != *src2))) {
118		if ((unsigned char)*src1 < (unsigned char)*src2)
119			return -1;
120		else if ((unsigned char)*src1 > (unsigned char)*src2)
121			return 1;
122
123		src1++;
124		src2++;
125	}
126
127	/* both utf8 strings matched */
128	return 0;
129}
130
131char* uleutf_str(const char* haystack, const char* needle)
132{
133	ule_i32 throwaway_codepoint = 0;
134
135	/* if needle has no utf8 codepoints before the null terminating
136	   byte then return haystack */
137	if ('\0' == *needle)
138		return (char*)haystack;
139
140	while ('\0' != *haystack) {
141		const char* maybe_match = haystack;
142		const char* n = needle;
143
144		while (*haystack == *n && (*haystack != '\0' && *n != '\0')) {
145			n++;
146			haystack++;
147		}
148
149		if ('\0' == *n)
150			/* we found the whole utf8 string for needle in haystack at
151			   maybeMatch, so return it */
152			return (char*)maybe_match;
153		else
154			/* h could be in the middle of an unmatching utf8 codepoint,
155			   so we need to march it on to the next character beginning
156			   starting from the current character */
157			haystack = uleutf_codepoint(maybe_match, &throwaway_codepoint);
158	}
159
160	/* no match */
161	return NULL;
162}
163
164char* uleutf_codepoint(const char* str, ule_i32* out_codepoint)
165{
166	size_t n = 1;
167	size_t i = 1;
168	ule_i32 cp = (ule_i32)str[0];
169
170	if (0xf0 == (0xf8 & str[0])) {
171		/* 4 byte utf8 codepoint */
172		n = 4;
173		cp = 0x07 & str[0];
174	}
175	else if (0xe0 == (0xf0 & str[0])) {
176		/* 3 byte utf8 codepoint */
177		n = 3;
178		cp = 0x0f & str[0];
179	}
180	else if (0xc0 == (0xe0 & str[0])) {
181		/* 2 byte utf8 codepoint */
182		n = 2;
183		cp = 0x1f & str[0];
184	}
185
186	/* stop at the NUL if the sequence is truncated */
187	for (i = 1; i < n && '\0' != str[i]; i++)
188		cp = (cp << 6) | (0x3f & str[i]);
189
190	*out_codepoint = cp;
191	return (char*)(str + i);
192}
193
194size_t uleutf_codepointcalcsize(const char* str)
195{
196	if (0xf0 == (0xf8 & str[0]))
197		return 4; /* 4 byte utf8 codepoint */
198	else if (0xe0 == (0xf0 & str[0]))
199		return 3; /* 3 byte utf8 codepoint */
200	else if (0xc0 == (0xe0 & str[0]))
201		return 2; /* 2 byte utf8 codepoint */
202
203	return 1; /* 1 byte utf8 codepoint otherwise */
204}
205
206size_t uleutf_codepointsize(ule_i32 chr)
207{
208	if (0 == ((ule_i32)0xffffff80 & chr))
209		return 1;
210	else if (0 == ((ule_i32)0xfffff800 & chr))
211		return 2;
212	else if (0 == ((ule_i32)0xffff0000 & chr))
213		return 3;
214	else /* if (0 == ((int)0xffe00000 & chr)) */
215		return 4;
216}
217
218int uleutf_codepointvisualwidth(ule_i32 chr)
219{
220	/* the null codepoint takes up no visual space */
221	if (0 == chr)
222		return 0;
223
224	/* C0 and C1 control codepoints have no printable representation. */
225	if ((chr < 0x20) || ((0x7f <= chr) && (chr < 0xa0)))
226		return -1;
227
228	/* zero-width combining marks and format codepoints. the ranges follow the
229	   widely used wcwidth() tables and cover Unicode general categories Mn, Me
230	   and Cf that do not advance the cursor */
231	if (((0x0300 <= chr) && (0x036f >= chr)) ||
232	    ((0x0483 <= chr) && (0x0486 >= chr)) ||
233	    ((0x0488 <= chr) && (0x0489 >= chr)) ||
234	    ((0x0591 <= chr) && (0x05bd >= chr)) || (0x05bf == chr) ||
235	    ((0x05c1 <= chr) && (0x05c2 >= chr)) ||
236	    ((0x05c4 <= chr) && (0x05c5 >= chr)) || (0x05c7 == chr) ||
237	    ((0x0600 <= chr) && (0x0603 >= chr)) ||
238	    ((0x0610 <= chr) && (0x0615 >= chr)) ||
239	    ((0x064b <= chr) && (0x065e >= chr)) || (0x0670 == chr) ||
240	    ((0x06d6 <= chr) && (0x06e4 >= chr)) ||
241	    ((0x06e7 <= chr) && (0x06e8 >= chr)) ||
242	    ((0x06ea <= chr) && (0x06ed >= chr)) || (0x070f == chr) ||
243	    (0x0711 == chr) || ((0x0730 <= chr) && (0x074a >= chr)) ||
244	    ((0x07a6 <= chr) && (0x07b0 >= chr)) ||
245	    ((0x07eb <= chr) && (0x07f3 >= chr)) ||
246	    ((0x0901 <= chr) && (0x0902 >= chr)) || (0x093c == chr) ||
247	    ((0x0941 <= chr) && (0x0948 >= chr)) || (0x094d == chr) ||
248	    ((0x0951 <= chr) && (0x0954 >= chr)) ||
249	    ((0x0962 <= chr) && (0x0963 >= chr)) || (0x0981 == chr) ||
250	    (0x09bc == chr) || ((0x09c1 <= chr) && (0x09c4 >= chr)) ||
251	    (0x09cd == chr) || ((0x09e2 <= chr) && (0x09e3 >= chr)) ||
252	    ((0x0a01 <= chr) && (0x0a02 >= chr)) || (0x0a3c == chr) ||
253	    ((0x0a41 <= chr) && (0x0a42 >= chr)) ||
254	    ((0x0a47 <= chr) && (0x0a48 >= chr)) ||
255	    ((0x0a4b <= chr) && (0x0a4d >= chr)) ||
256	    ((0x0a70 <= chr) && (0x0a71 >= chr)) ||
257	    ((0x0a81 <= chr) && (0x0a82 >= chr)) || (0x0abc == chr) ||
258	    ((0x0ac1 <= chr) && (0x0ac5 >= chr)) ||
259	    ((0x0ac7 <= chr) && (0x0ac8 >= chr)) || (0x0acd == chr) ||
260	    ((0x0ae2 <= chr) && (0x0ae3 >= chr)) || (0x0b01 == chr) ||
261	    (0x0b3c == chr) || (0x0b3f == chr) ||
262	    ((0x0b41 <= chr) && (0x0b43 >= chr)) || (0x0b4d == chr) ||
263	    (0x0b56 == chr) || (0x0b82 == chr) || (0x0bc0 == chr) ||
264	    (0x0bcd == chr) || ((0x0c3e <= chr) && (0x0c40 >= chr)) ||
265	    ((0x0c46 <= chr) && (0x0c48 >= chr)) ||
266	    ((0x0c4a <= chr) && (0x0c4d >= chr)) ||
267	    ((0x0c55 <= chr) && (0x0c56 >= chr)) || (0x0cbc == chr) ||
268	    (0x0cbf == chr) || (0x0cc6 == chr) ||
269	    ((0x0ccc <= chr) && (0x0ccd >= chr)) ||
270	    ((0x0ce2 <= chr) && (0x0ce3 >= chr)) ||
271	    ((0x0d41 <= chr) && (0x0d43 >= chr)) || (0x0d4d == chr) ||
272	    (0x0dca == chr) || ((0x0dd2 <= chr) && (0x0dd4 >= chr)) ||
273	    (0x0dd6 == chr) || (0x0e31 == chr) ||
274	    ((0x0e34 <= chr) && (0x0e3a >= chr)) ||
275	    ((0x0e47 <= chr) && (0x0e4e >= chr)) || (0x0eb1 == chr) ||
276	    ((0x0eb4 <= chr) && (0x0eb9 >= chr)) ||
277	    ((0x0ebb <= chr) && (0x0ebc >= chr)) ||
278	    ((0x0ec8 <= chr) && (0x0ecd >= chr)) ||
279	    ((0x0f18 <= chr) && (0x0f19 >= chr)) || (0x0f35 == chr) ||
280	    (0x0f37 == chr) || (0x0f39 == chr) ||
281	    ((0x0f71 <= chr) && (0x0f7e >= chr)) ||
282	    ((0x0f80 <= chr) && (0x0f84 >= chr)) ||
283	    ((0x0f86 <= chr) && (0x0f87 >= chr)) ||
284	    ((0x0f90 <= chr) && (0x0f97 >= chr)) ||
285	    ((0x0f99 <= chr) && (0x0fbc >= chr)) || (0x0fc6 == chr) ||
286	    ((0x102d <= chr) && (0x1030 >= chr)) || (0x1032 == chr) ||
287	    ((0x1036 <= chr) && (0x1037 >= chr)) || (0x1039 == chr) ||
288	    ((0x1058 <= chr) && (0x1059 >= chr)) ||
289	    ((0x1160 <= chr) && (0x11ff >= chr)) || (0x135f == chr) ||
290	    ((0x1712 <= chr) && (0x1714 >= chr)) ||
291	    ((0x1732 <= chr) && (0x1734 >= chr)) ||
292	    ((0x1752 <= chr) && (0x1753 >= chr)) ||
293	    ((0x1772 <= chr) && (0x1773 >= chr)) ||
294	    ((0x17b4 <= chr) && (0x17b5 >= chr)) ||
295	    ((0x17b7 <= chr) && (0x17bd >= chr)) || (0x17c6 == chr) ||
296	    ((0x17c9 <= chr) && (0x17d3 >= chr)) || (0x17dd == chr) ||
297	    ((0x180b <= chr) && (0x180d >= chr)) || (0x18a9 == chr) ||
298	    ((0x1920 <= chr) && (0x1922 >= chr)) ||
299	    ((0x1927 <= chr) && (0x1928 >= chr)) || (0x1932 == chr) ||
300	    ((0x1939 <= chr) && (0x193b >= chr)) ||
301	    ((0x1a17 <= chr) && (0x1a18 >= chr)) ||
302	    ((0x1b00 <= chr) && (0x1b03 >= chr)) || (0x1b34 == chr) ||
303	    ((0x1b36 <= chr) && (0x1b3a >= chr)) || (0x1b3c == chr) ||
304	    (0x1b42 == chr) || ((0x1b6b <= chr) && (0x1b73 >= chr)) ||
305	    ((0x1dc0 <= chr) && (0x1dca >= chr)) ||
306	    ((0x1dfe <= chr) && (0x1dff >= chr)) ||
307	    ((0x200b <= chr) && (0x200f >= chr)) ||
308	    ((0x202a <= chr) && (0x202e >= chr)) ||
309	    ((0x2060 <= chr) && (0x2063 >= chr)) ||
310	    ((0x206a <= chr) && (0x206f >= chr)) ||
311	    ((0x20d0 <= chr) && (0x20ef >= chr)) ||
312	    ((0x302a <= chr) && (0x302f >= chr)) ||
313	    ((0x3099 <= chr) && (0x309a >= chr)) || (0xa806 == chr) ||
314	    (0xa80b == chr) || ((0xa825 <= chr) && (0xa826 >= chr)) ||
315	    (0xfb1e == chr) || ((0xfe00 <= chr) && (0xfe0f >= chr)) ||
316	    ((0xfe20 <= chr) && (0xfe23 >= chr)) || (0xfeff == chr) ||
317	    ((0xfff9 <= chr) && (0xfffb >= chr)) ||
318	    ((0x10a01 <= chr) && (0x10a03 >= chr)) ||
319	    ((0x10a05 <= chr) && (0x10a06 >= chr)) ||
320	    ((0x10a0c <= chr) && (0x10a0f >= chr)) ||
321	    ((0x10a38 <= chr) && (0x10a3a >= chr)) || (0x10a3f == chr) ||
322	    ((0x1d167 <= chr) && (0x1d169 >= chr)) ||
323	    ((0x1d173 <= chr) && (0x1d182 >= chr)) ||
324	    ((0x1d185 <= chr) && (0x1d18b >= chr)) ||
325	    ((0x1d1aa <= chr) && (0x1d1ad >= chr)) ||
326	    ((0x1d242 <= chr) && (0x1d244 >= chr)) || (0xe0001 == chr) ||
327	    ((0xe0020 <= chr) && (0xe007f >= chr)) ||
328	    ((0xe0100 <= chr) && (0xe01ef >= chr)))
329		return 0;
330
331	/* wide (double-width) codepoints, mostly East Asian scripts */
332	if ((0x1100 <= chr) &&
333	    (((0x1100 <= chr) && (0x115f >= chr)) || (0x2329 == chr) ||
334	     (0x232a == chr) ||
335	     (((0x2e80 <= chr) && (0xa4cf >= chr)) && (0x303f != chr)) ||
336	     ((0xac00 <= chr) && (0xd7a3 >= chr)) ||
337	     ((0xf900 <= chr) && (0xfaff >= chr)) ||
338	     ((0xfe10 <= chr) && (0xfe19 >= chr)) ||
339	     ((0xfe30 <= chr) && (0xfe6f >= chr)) ||
340	     ((0xff00 <= chr) && (0xff60 >= chr)) ||
341	     ((0xffe0 <= chr) && (0xffe6 >= chr)) ||
342	     ((0x20000 <= chr) && (0x2fffd >= chr)) ||
343	     ((0x30000 <= chr) && (0x3fffd >= chr))))
344		return 2;
345
346	return 1;
347}
348