Changeset: 84eacd66bfd7 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=84eacd66bfd7
Modified Files:
        monetdb5/modules/mal/pcre.c
Branch: Aug2018
Log Message:

Don't use mbsrtowcs and friends and wchar_t for ILIKE comparisons.
The reason is stated in a comment: the functions are locale-dependent,
and on Windows, wchar_t is too small to hold all Unicode code points.


diffs (truncated from 453 to 300 lines):

diff --git a/monetdb5/modules/mal/pcre.c b/monetdb5/modules/mal/pcre.c
--- a/monetdb5/modules/mal/pcre.c
+++ b/monetdb5/modules/mal/pcre.c
@@ -85,101 +85,182 @@ mal_export str ILIKEjoin1(bat *r1, bat *
 /* current implementation assumes simple %keyword% [keyw%]* */
 typedef struct RE {
        char *k;
-       wchar_t *w;
+       uint32_t *w;
        bool search;
-       ptrdiff_t len;
+       size_t len;
        struct RE *n;
 } RE;
 
-/* we cannot use strcasecmp and strncasecmp since they work byte for
- * byte and don't deal with multibyte encodings (such as UTF-8) */
-
-#ifdef _MSC_VER
-/* on Windows, we cannot set the UTF-8 locale, so we need to implement
- * our own version of mbrtowc and mbsrtowcs */
+/* We cannot use strcasecmp and strncasecmp since they work byte for
+ * byte and don't deal with multibyte encodings (such as UTF-8).
+ *
+ * We implement our own conversion from UTF-8 encoding to Unicode code
+ * points which we store in uint32_t.  The reason for this is,
+ * functions like mbsrtowcs are locale-dependent (so we need a UTF-8
+ * locale to use them), and on Windows, wchar_t is only 2 bytes and
+ * therefore cannot hold all Unicode code points.  We do use functions
+ * such as towlower to convert a Unicode code point to its lower-case
+ * equivalent, but again on Windows, if the code point doesn't fit in
+ * 2 bytes, we skip this conversion and compare the unconverted code
+ * points.
+ *
+ * Note, towlower is also locale-dependent, but we don't need a UTF-8
+ * locale in order to use it. */
 
+/* helper function to convert a UTF-8 multibyte character to a wide
+ * character */
 static size_t
-my_mbrtowc(wchar_t *dst, const char *src, size_t len)
+utfc8touc(uint32_t *restrict dest, const char *restrict src)
 {
-       if (len == 0)
-               return (size_t) -1;
        if ((src[0] & 0x80) == 0) {
-               *dst = src[0];
+               *dest = src[0];
                return src[0] != 0;
-       }
-       if (len == 1)
-               return (size_t) -1;
-       if ((src[0] & 0xE0) == 0xC0) {
-               *dst = ((src[0] & 0x1F) << 6) | (src[1] & 0x3F);
+       } else if ((src[0] & 0xE0) == 0xC0
+                  && (src[1] & 0xC0) == 0x80
+                  && (src[0] & 0x1E) != 0) {
+               *dest = (src[0] & 0x1F) << 6
+                       | (src[1] & 0x3F);
                return 2;
-       }
-       if (len == 2)
-               return (size_t) -1;
-       if ((src[0] & 0xF0) == 0xE0) {
-               *dst = ((src[0] & 0x0F) << 12) | ((src[1] & 0x3F) << 6) | 
(src[2] & 0x3F);
+       } else if ((src[0] & 0xF0) == 0xE0
+                  && (src[1] & 0xC0) == 0x80
+                  && (src[2] & 0xC0) == 0x80
+                  && ((src[0] & 0x0F) != 0
+                      || (src[1] & 0x20) != 0)) {
+               *dest = (src[0] & 0x0F) << 12
+                       | (src[1] & 0x3F) << 6
+                       | (src[2] & 0x3F);
                return 3;
-       }
-       if (len == 3)
-               return (size_t) -1;
-       if ((src[0] & 0xF8) == 0xF0) {
-               *dst = ((src[0] & 0x0F) << 18) | ((src[1] & 0x3F) << 12) | 
((src[2] & 0x3F) << 6) | (src[3] & 0x3F);
+       } else if ((src[0] & 0xF8) == 0xF0
+                  && (src[1] & 0xC0) == 0x80
+                  && (src[2] & 0xC0) == 0x80
+                  && (src[3] & 0xC0) == 0x80) {
+               uint32_t c = (src[0] & 0x07) << 18
+                       | (src[1] & 0x3F) << 12
+                       | (src[2] & 0x3F) << 6
+                       | (src[3] & 0x3F);
+               if (c < 0x10000
+                   || c > 0x10FFFF
+                   || (c & 0x1FF800) == 0x00D800)
+                       return (size_t) -1;
+               *dest = c;
                return 4;
        }
        return (size_t) -1;
 }
 
-static size_t
-my_mbsrtowcs(wchar_t *dst, const char **src, size_t len)
+/* helper function to convert a UTF-8 string to a wide character
+ * string, the wide character string is allocated */
+static uint32_t *
+utf8stoucs(const char *src)
 {
-       size_t i;
-       const char *s = *src;
+       uint32_t *dest;
+       size_t i = 0;
+       size_t j = 0;
 
-       for (i = 0; i < len; i++) {
-               if ((s[0] & 0x80) == 0) {
-                       dst[i] = s[0];
-                       s += 1;
-               } else if ((s[0] & 0xE0) == 0xC0) {
-                       dst[i] = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
-                       s += 2;
-               } else if ((s[0] & 0xF0) == 0xE0) {
-                       dst[i] = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | 
(s[2] & 0x3F);
-                       s += 3;
-               } else if ((s[0] & 0xF8) == 0xF0) {
-                       dst[i] = ((s[0] & 0x0F) << 18) | ((s[1] & 0x3F) << 12) 
| ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
-                       s += 4;
+       /* count how many uint32_t's we need, while also checking for
+        * correctness of the input */
+       while (src[j]) {
+               i++;
+               if ((src[j+0] & 0x80) == 0) {
+                       j += 1;
+               } else if ((src[j+0] & 0xE0) == 0xC0
+                          && (src[j+1] & 0xC0) == 0x80
+                          && (src[j+0] & 0x1E) != 0) {
+                       j += 2;
+               } else if ((src[j+0] & 0xF0) == 0xE0
+                          && (src[j+1] & 0xC0) == 0x80
+                          && (src[j+2] & 0xC0) == 0x80
+                          && ((src[j+0] & 0x0F) != 0
+                              || (src[j+1] & 0x20) != 0)) {
+                       j += 3;
+               } else if ((src[j+0] & 0xF8) == 0xF0
+                          && (src[j+1] & 0xC0) == 0x80
+                          && (src[j+2] & 0xC0) == 0x80
+                          && (src[j+3] & 0xC0) == 0x80) {
+                       uint32_t c = (src[j+0] & 0x07) << 18
+                               | (src[j+1] & 0x3F) << 12
+                               | (src[j+2] & 0x3F) << 6
+                               | (src[j+3] & 0x3F);
+                       if (c < 0x10000
+                           || c > 0x10FFFF
+                           || (c & 0x1FF800) == 0x00D800)
+                               return NULL;
+                       j += 4;
                } else {
-                       *src = s;
-                       return (size_t) -1;
-               }
-               if (dst[i] == 0) {
-                       *src = NULL;
-                       return i;
+                       return NULL;
                }
        }
-       *src = s;
+       dest = GDKmalloc((i + 1) * sizeof(uint32_t));
+       if (dest == NULL)
+               return NULL;
+       /* go through the source string again, this time we can skip
+        * the correctness tests */
+       i = j = 0;
+       while (src[j]) {
+               if ((src[j+0] & 0x80) == 0) {
+                       dest[i++] = src[j+0];
+                       j += 1;
+               } else if ((src[j+0] & 0xE0) == 0xC0) {
+                       dest[i++] = (src[j+0] & 0x1F) << 6
+                               | (src[j+1] & 0x3F);
+                       j += 2;
+               } else if ((src[j+0] & 0xF0) == 0xE0) {
+                       dest[i++] = (src[j+0] & 0x0F) << 12
+                               | (src[j+1] & 0x3F) << 6
+                               | (src[j+2] & 0x3F);
+                       j += 3;
+               } else if ((src[j+0] & 0xF8) == 0xF0) {
+                       dest[i++] = (src[j+0] & 0x07) << 18
+                               | (src[j+1] & 0x3F) << 12
+                               | (src[j+2] & 0x3F) << 6
+                               | (src[j+3] & 0x3F);
+                       j += 4;
+               }
+       }
+       dest[i] = 0;
+       return dest;
+}
+
+static uint32_t *
+myucschr(const uint32_t *ucs, uint32_t uc)
+{
+       while (*ucs) {
+               if (*ucs == uc)
+                       return (uint32_t *) ucs;
+               ucs++;
+       }
+       return NULL;
+}
+
+static size_t
+myucslen(const uint32_t *ucs)
+{
+       size_t i = 0;
+
+       while (ucs[i])
+               i++;
        return i;
 }
-#define mbrtowc(dst, src, len, ps)             my_mbrtowc(dst, src, len)
-#define mbsrtowcs(dst, src, len, ps)   my_mbsrtowcs(dst, src, len)
-#endif
 
 static int
-mywstrncasecmp(const char *s1, const wchar_t *s2, size_t n2)
+mywstrncasecmp(const char *restrict s1, const uint32_t *restrict s2, size_t n2)
 {
-       wchar_t c1;
+       uint32_t c1;
 
-#ifndef _MSC_VER
-       mbstate_t ps1;
-       memset(&ps1, 0, sizeof(ps1));
-#endif
        while (n2 > 0) {
-               size_t nn1 = mbrtowc(&c1, s1, 1000, &ps1);
-               if (nn1 == 0)
+               size_t nn1 = utfc8touc(&c1, s1);
+               if (nn1 == 0 || nn1 == (size_t) -1)
                        return -(*s2 != 0);
                if (*s2 == 0)
                        return 1;
                if (nn1 == (size_t) -1 || nn1 == (size_t) -2)
                        return 0;        /* actually an error that shouldn't 
happen */
+#if SIZEOF_WCHAR_T == 2
+               if (c1 > 0xFFFF || *s2 > 0xFFFF) {
+                       if (c1 != *s2)
+                               return c1 - *s2;
+               } else
+#endif
                if (towlower((wint_t) c1) != towlower((wint_t) *s2))
                        return towlower((wint_t) c1) - towlower((wint_t) *s2);
                s1 += nn1;
@@ -192,26 +273,24 @@ mywstrncasecmp(const char *s1, const wch
 static int
 mystrcasecmp(const char *s1, const char *s2)
 {
-       wchar_t c1, c2;
+       uint32_t c1, c2;
 
-#ifndef _MSC_VER
-       mbstate_t ps1, ps2;
-       memset(&ps1, 0, sizeof(ps1));
-       memset(&ps2, 0, sizeof(ps2));
-#endif
        for (;;) {
-               /* use some ridiculously high number as the length of the
-                * input strings: we will still not go beyond the terminating
-                * '\0' */
-               size_t nn1 = mbrtowc(&c1, s1, 1000, &ps1);
-               size_t nn2 = mbrtowc(&c2, s2, 1000, &ps2);
-               if (nn1 == 0)
-                       return -(nn2 != 0);
-               if (nn2 == 0)
+               size_t nn1 = utfc8touc(&c1, s1);
+               size_t nn2 = utfc8touc(&c2, s2);
+               if (nn1 == 0 || nn1 == (size_t) -1)
+                       return -(nn2 != 0 && nn2 != (size_t) -1);
+               if (nn2 == 0 || nn2 == (size_t) -1)
                        return 1;
                if (nn1 == (size_t) -1 || nn1 == (size_t) -2 ||
                        nn2 == (size_t) -1 || nn2 == (size_t) -2)
                        return 0;        /* actually an error that shouldn't 
happen */
+#if SIZEOF_WCHAR_T == 2
+               if (c1 > 0xFFFF || c2 > 0xFFFF) {
+                       if (c1 != c2)
+                               return c1 - c2;
+               } else
+#endif
                if (towlower((wint_t) c1) != towlower((wint_t) c2))
                        return towlower((wint_t) c1) - towlower((wint_t) c2);
                s1 += nn1;
@@ -220,25 +299,24 @@ mystrcasecmp(const char *s1, const char 
 }
 
 static int
-mywstrcasecmp(const char *s1, const wchar_t *s2)
+mywstrcasecmp(const char *restrict s1, const uint32_t *restrict s2)
 {
-       wchar_t c1;
+       uint32_t c1;
 
-#ifndef _MSC_VER
-       mbstate_t ps1;
-       memset(&ps1, 0, sizeof(ps1));
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to