Fix #34 handle 66 Unicode non-characters, also improve performance and surrogate...
authorScott Paul Jones <scottjones@alum.mit.edu>
Sat, 9 May 2015 15:45:03 +0000 (11:45 -0400)
committerScottPJones <scottjones@alum.mit.edu>
Fri, 29 May 2015 17:50:03 +0000 (19:50 +0200)
test/charwidth.c
test/graphemetest.c
test/tests.h
utf8proc.c
utf8proc.h

index bdae9fadc16acd6cde6c263d0c4a3d4d61e7f4ab..28554a42001cb44a9f1f380a2b027d6eae5280a7 100644 (file)
@@ -10,7 +10,7 @@ int my_isprint(int c) {
 
 int main(int argc, char **argv)
 {
-     int prevc, c, error = 0;
+     int c, error = 0;
 
      (void) argc; /* unused */
      (void) argv; /* unused */
index a05b6e2003f789c65f69ef736d321b73576cbcab..eb3645b9a0ceb06aa9858237443ce2df2b3d076d 100644 (file)
@@ -6,6 +6,7 @@ int main(int argc, char **argv)
     size_t bufsize = 0;
     FILE *f = argc > 1 ? fopen(argv[1], "r") : NULL;
     utf8proc_uint8_t src[1024];
+    int len;
     
     check(f != NULL, "error opening GraphemeBreakTest.txt");
     while (getline(&buf, &bufsize, f) > 0) {
@@ -29,9 +30,10 @@ int main(int argc, char **argv)
             else if (buf[bi] == '#') { /* start of comments */
                 break;
             }
-            else { /* hex-encoded codepoint */
-                bi += encode((char*) (src + si), buf + bi) - 1;
+           else { /* hex-encoded codepoint */
+                len = encode((char*) (src + si), buf + bi) - 1;
                 while (src[si]) ++si; /* advance to NUL termination */
+                bi += len;
             }
         }
         if (si && src[si-1] == '/')
@@ -59,7 +61,7 @@ int main(int argc, char **argv)
                        utf8proc_errmsg(glen));
                  for (i = 0; i <= glen; ++i)
                       if (g[i] == 0xff)
-                           g[i] = '/'; /* easier-to-read output (/ is not in test strings) */
+                          g[i] = '/'; /* easier-to-read output (/ is not in test strings) */
                  check(!strcmp((char*)g, (char*)src),
                        "grapheme mismatch: \"%s\" instead of \"%s\"", (char*)g, (char*)src);
             }
index c27185d831fc43dc65064661d6ed553aa129f71d..6eb5457d2822d90ac28b2c16911a76e3d817bf7e 100644 (file)
@@ -33,10 +33,11 @@ size_t skipspaces(const char *buf, size_t i)
    separated by whitespace, and terminated by any character not in
    [0-9a-fA-F] or whitespace, then stores the corresponding utf8 string
    in dest, returning the number of bytes read from buf */
+utf8proc_ssize_t unsafe_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst);
 size_t encode(char *dest, const char *buf)
 {
      size_t i = 0, j, d = 0;
-     do {
+     for (;;) {
           int c;
           i = skipspaces(buf, i);
           for (j=i; buf[j] && strchr("0123456789abcdef", tolower(buf[j])); ++j)
@@ -45,9 +46,9 @@ size_t encode(char *dest, const char *buf)
                dest[d] = 0; /* NUL-terminate destination string */
                return i + 1;
           }
-          check(sscanf(buf + i, "%x", &c) == 1, "invalid hex input %s", buf+i);
+          check(sscanf(buf + i, "%x", (unsigned int *)&c) == 1, "invalid hex input %s", buf+i);
           i = j; /* skip to char after hex input */
-          d += utf8proc_encode_char(c, (utf8proc_uint8_t *) (dest + d));
-     } while (1);
+          d += unsafe_encode_char(c, (utf8proc_uint8_t *) (dest + d));
+     }
 }
 
index a1d0c4b2c52b286e6e325147fa15fd4f705cc515..971b87ad18b39a0ce1e6d449b35019038e368071 100644 (file)
@@ -108,51 +108,57 @@ UTF8PROC_DLLEXPORT const char *utf8proc_errmsg(utf8proc_ssize_t errcode) {
   }
 }
 
+#define utf_cont(ch)  (((ch) & 0xc0) == 0x80)
 UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate(
   const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *dst
 ) {
-  int length;
-  int i;
-  utf8proc_int32_t uc = -1;
+  utf8proc_uint32_t uc;
+  const utf8proc_uint8_t *end;
+
   *dst = -1;
   if (!strlen) return 0;
-  length = utf8proc_utf8class[str[0]];
-  if (!length) return UTF8PROC_ERROR_INVALIDUTF8;
-  if (strlen >= 0 && length > strlen) return UTF8PROC_ERROR_INVALIDUTF8;
-  for (i=1; i<length; i++) {
-    if ((str[i] & 0xC0) != 0x80) return UTF8PROC_ERROR_INVALIDUTF8;
+  end = str + ((strlen < 0) ? 4 : strlen);
+  uc = *str++;
+  if (uc < 0x80) {
+    *dst = uc;
+    return 1;
+  }
+  // Must be between 0xc2 and 0xf4 inclusive to be valid
+  if ((uc - 0xc2) > (0xf4-0xc2)) return UTF8PROC_ERROR_INVALIDUTF8;
+  if (uc < 0xe0) {         // 2-byte sequence
+     // Must have valid continuation character
+     if (!utf_cont(*str)) return UTF8PROC_ERROR_INVALIDUTF8;
+     *dst = ((uc & 0x1f)<<6) | (*str & 0x3f);
+     return 2;
+  }
+  if (uc < 0xf0) {        // 3-byte sequence
+     if ((str + 1 >= end) || !utf_cont(*str) || !utf_cont(str[1]))
+        return UTF8PROC_ERROR_INVALIDUTF8;
+     // Check for surrogate chars
+     if (uc == 0xed && *str > 0x9f)
+         return UTF8PROC_ERROR_INVALIDUTF8;
+     uc = ((uc & 0xf)<<12) | ((*str & 0x3f)<<6) | (str[1] & 0x3f);
+     if (uc < 0x800)
+         return UTF8PROC_ERROR_INVALIDUTF8;
+     *dst = uc;
+     return 3;
   }
-  switch (length) {
-    case 1:
-    uc = str[0];
-    break;
-    case 2:
-    uc = ((str[0] & 0x1F) <<  6) + (str[1] & 0x3F);
-    if (uc < 0x80) uc = -1;
-    break;
-    case 3:
-    uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) <<  6)
-      + (str[2] & 0x3F);
-    if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
-      (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
-    break;
-    case 4:
-    uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
-      + ((str[2] & 0x3F) <<  6) + (str[3] & 0x3F);
-    if (uc < 0x10000 || uc >= 0x110000) uc = -1;
-    break;
+  // 4-byte sequence
+  // Must have 3 valid continuation characters
+  if ((str + 2 >= end) || !utf_cont(*str) || !utf_cont(str[1]) || !utf_cont(str[2]))
+     return UTF8PROC_ERROR_INVALIDUTF8;
+  // Make sure in correct range (0x10000 - 0x10ffff)
+  if (uc == 0xf0) {
+    if (*str < 0x90) return UTF8PROC_ERROR_INVALIDUTF8;
+  } else if (uc == 0xf4) {
+    if (*str > 0x8f) return UTF8PROC_ERROR_INVALIDUTF8;
   }
-  if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
-    return UTF8PROC_ERROR_INVALIDUTF8;
-  *dst = uc;
-  return length;
+  *dst = ((uc & 7)<<18) | ((*str & 0x3f)<<12) | ((str[1] & 0x3f)<<6) | (str[2] & 0x3f);
+  return 4;
 }
 
 UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_codepoint_valid(utf8proc_int32_t uc) {
-  if (uc < 0 || uc >= 0x110000 ||
-    ((uc & 0xFFFF) >= 0xFFFE) || (uc >= 0xD800 && uc < 0xE000) ||
-    (uc >= 0xFDD0 && uc < 0xFDF0)) return false;
-  else return true;
+    return (((utf8proc_uint32_t)uc)-0xd800 > 0x07ff) && ((utf8proc_uint32_t)uc < 0x110000);
 }
 
 UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) {
@@ -165,12 +171,8 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, ut
     dst[0] = 0xC0 + (uc >> 6);
     dst[1] = 0x80 + (uc & 0x3F);
     return 2;
-  } else if (uc == 0xFFFF) {
-    dst[0] = 0xFF;
-    return 1;
-  } else if (uc == 0xFFFE) {
-    dst[0] = 0xFE;
-    return 1;
+  // Note: we allow encoding 0xd800-0xdfff here, so as not to change
+  // the API, however, these are actually invalid in UTF-8
   } else if (uc < 0x10000) {
     dst[0] = 0xE0 + (uc >> 12);
     dst[1] = 0x80 + ((uc >> 6) & 0x3F);
@@ -186,7 +188,38 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, ut
 }
 
 /* internal "unsafe" version that does not check whether uc is in range */
-static const utf8proc_property_t *get_property(utf8proc_int32_t uc) {
+utf8proc_ssize_t unsafe_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) {
+   if (uc < 0x00) {
+      return 0;
+   } else if (uc < 0x80) {
+      dst[0] = uc;
+      return 1;
+   } else if (uc < 0x800) {
+      dst[0] = 0xC0 + (uc >> 6);
+      dst[1] = 0x80 + (uc & 0x3F);
+      return 2;
+   } else if (uc == 0xFFFF) {
+       dst[0] = 0xFF;
+       return 1;
+   } else if (uc == 0xFFFE) {
+       dst[0] = 0xFE;
+       return 1;
+   } else if (uc < 0x10000) {
+      dst[0] = 0xE0 + (uc >> 12);
+      dst[1] = 0x80 + ((uc >> 6) & 0x3F);
+      dst[2] = 0x80 + (uc & 0x3F);
+      return 3;
+   } else if (uc < 0x110000) {
+      dst[0] = 0xF0 + (uc >> 18);
+      dst[1] = 0x80 + ((uc >> 12) & 0x3F);
+      dst[2] = 0x80 + ((uc >> 6) & 0x3F);
+      dst[3] = 0x80 + (uc & 0x3F);
+      return 4;
+   } else return 0;
+}
+
+/* internal "unsafe" version that does not check whether uc is in range */
+static const utf8proc_property_t *unsafe_get_property(utf8proc_int32_t uc) {
   /* ASSERT: uc >= 0 && uc < 0x110000 */
   return utf8proc_properties + (
     utf8proc_stage2table[
@@ -196,7 +229,7 @@ static const utf8proc_property_t *get_property(utf8proc_int32_t uc) {
 }
 
 UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int32_t uc) {
-  return uc < 0 || uc >= 0x110000 ? utf8proc_properties : get_property(uc);
+  return uc < 0 || uc >= 0x110000 ? utf8proc_properties : unsafe_get_property(uc);
 }
 
 /* return whether there is a grapheme break between boundclasses lbc and tbc */
@@ -255,7 +288,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc,
   utf8proc_propval_t category;
   utf8proc_int32_t hangul_sindex;
   if (uc < 0 || uc >= 0x110000) return UTF8PROC_ERROR_NOTASSIGNED;
-  property = get_property(uc);
+  property = unsafe_get_property(uc);
   category = property->category;
   hangul_sindex = uc - UTF8PROC_HANGUL_SBASE;
   if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
@@ -401,8 +434,8 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose(
       const utf8proc_property_t *property1, *property2;
       uc1 = buffer[pos];
       uc2 = buffer[pos+1];
-      property1 = get_property(uc1);
-      property2 = get_property(uc2);
+      property1 = unsafe_get_property(uc1);
+      property2 = unsafe_get_property(uc2);
       if (property1->combining_class > property2->combining_class &&
           property2->combining_class > 0) {
         buffer[pos] = uc2;
@@ -460,7 +493,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
     utf8proc_int32_t composition;
     for (rpos = 0; rpos < length; rpos++) {
       current_char = buffer[rpos];
-      current_property = get_property(current_char);
+      current_property = unsafe_get_property(current_char);
       if (starter && current_property->combining_class > max_combining_class) {
         /* combination perhaps possible */
         utf8proc_int32_t hangul_lindex;
@@ -489,7 +522,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
           }
         }
         if (!starter_property) {
-          starter_property = get_property(*starter);
+          starter_property = unsafe_get_property(*starter);
         }
         if (starter_property->comb1st_index >= 0 &&
             current_property->comb2nd_index >= 0) {
@@ -498,7 +531,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
             current_property->comb2nd_index
           ];
           if (composition >= 0 && (!(options & UTF8PROC_STABLE) ||
-              !(get_property(composition)->comp_exclusion))) {
+              !(unsafe_get_property(composition)->comp_exclusion))) {
             *starter = composition;
             starter_property = NULL;
             continue;
@@ -522,9 +555,16 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
   {
     utf8proc_ssize_t rpos, wpos = 0;
     utf8proc_int32_t uc;
-    for (rpos = 0; rpos < length; rpos++) {
-      uc = buffer[rpos];
-      wpos += utf8proc_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos);
+    if (options & UTF8PROC_CHARBOUND) {
+        for (rpos = 0; rpos < length; rpos++) {
+            uc = buffer[rpos];
+            wpos += unsafe_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos);
+        }
+    } else {
+        for (rpos = 0; rpos < length; rpos++) {
+            uc = buffer[rpos];
+            wpos += utf8proc_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos);
+        }
     }
     ((utf8proc_uint8_t *)buffer)[wpos] = 0;
     return wpos;
index 69c3213e81ac22c8bda11a3ffa14d64d280dfc05..710821524f6621694623b24eddec9d6c4e0c96f0 100644 (file)
@@ -82,10 +82,13 @@ typedef unsigned char utf8proc_uint8_t;
 typedef short utf8proc_int16_t;
 typedef unsigned short utf8proc_uint16_t;
 typedef int utf8proc_int32_t;
+typedef unsigned int utf8proc_uint32_t;
 #  ifdef _WIN64
 typedef __int64 utf8proc_ssize_t;
+typedef unsigned __int64 utf8proc_size_t;
 #  else
 typedef int utf8proc_ssize_t;
+typedef unsigned int utf8proc_size_t;
 #  endif
 #  ifndef __cplusplus
 typedef unsigned char utf8proc_bool;
@@ -101,6 +104,8 @@ typedef uint8_t utf8proc_uint8_t;
 typedef int16_t utf8proc_int16_t;
 typedef uint16_t utf8proc_uint16_t;
 typedef int32_t utf8proc_int32_t;
+typedef uint32_t utf8proc_uint32_t;
+typedef size_t utf8proc_size_t;
 typedef ssize_t utf8proc_ssize_t;
 typedef bool utf8proc_bool;
 #endif