chiark / gitweb /
distinguish the different decompositions via a flag bit
[disorder] / scripts / make-unidata
index 259e1148a0201dbedd56827479ebdf0f0af3b944..ce641ed55f2e16c92c685776292373fb44c4af24 100755 (executable)
@@ -31,7 +31,6 @@
 #  - SpecialCasing.txt data for case mapping
 #  - Title case offsets
 #  - Some kind of hinting for composition
-#  - Word boundary support
 #  - ...
 #
 # NB the generated files DO NOT offer a stable ABI and so are not immediately
@@ -60,7 +59,13 @@ sub key {
 # Size of a subtable
 #
 # This can be varied to trade off the number of subtables against their size.
-our $modulus = 128;
+# 16 gave the smallest results last time I checked (on a Mac with a 32-bit
+# build).
+our $modulus = 16;
+
+if(@ARGV) {
+    $modulus = shift;
+}
 
 # Where to break the table.  There is a huge empty section of the Unicode
 # code space and we deal with this by simply leaving it out of the table.
@@ -131,27 +136,24 @@ while(<>) {
     $maxud = $ud if $ud > $maxud;
     $minld = $ld if $ld < $minld;
     $maxld = $ld if $ld > $maxld;
-    my $d = {
-       "gc" => $gc,
-       "ccc" => $ccc,
-       "ud" => $ud,
-       "ld" => $ld,
-    };
-    if($dm ne '') {
-       if($dm !~ /</) {
-           # This is a canonical decomposition
-           $d->{canon} = $dm;
-           $d->{compat} = $dm;
-       } else {
-           # This is only a compatibility decomposition
-           $dm =~ s/^<.*>\s*//;
-           $d->{compat} = $dm;
-       }
-    }
     if($start != $end) {
-       printf STDERR "> range %04X-%04X is %s\n", $start, $end, $d->{gc};
+       printf STDERR "> range %04X-%04X is %s\n", $start, $end, $gc;
     }
     for($c = $start; $c <= $end; ++$c) {
+       my $d = {
+           "gc" => $gc,
+           "ccc" => $ccc,
+           "ud" => $ud,
+           "ld" => $ld,
+       };
+       if($dm ne '') {
+           if($dm =~ /</) {
+               # This is a compatibility decomposition
+               $dm =~ s/^<.*>\s*//;
+               $d->{compat} = 1;
+           }
+           $d->{decomp} = $dm;
+       }
        $data{$c} = $d;
     }
     $cats{$gc} = 1;
@@ -169,22 +171,17 @@ sub read_prop_with_ranges {
        my ($range, $propval) = split(/\s*;\s*/, $_);
        if($range =~ /(.*)\.\.(.*)/) {
            for my $c (hex($1) .. hex($2)) {
-               if(exists $data{$c}) {
-                   $data{$c}->{$propkey} = $propval;
-               }
+               die "($range)\n" if($c == 0xAC00 and $propkey eq 'gbreak');
+               $data{$c}->{$propkey} = $propval;
            }
        } else {
            my $c = hex($range);
-           if(exists $data{$c}) {
-               $data{$c}->{$propkey} = $propval;
-           }
+           $data{$c}->{$propkey} = $propval;
        }
     }
 }
 
 # Grapheme_Break etc
-# NB we do this BEFORE filling in blanks so that the Hangul characters
-# don't get filled in; we can compute their properties mechanically.
 read_prop_with_ranges("auxiliary/GraphemeBreakProperty.txt", "gbreak");
 read_prop_with_ranges("auxiliary/WordBreakProperty.txt", "wbreak");
 read_prop_with_ranges("auxiliary/SentenceBreakProperty.txt", "sbreak");
@@ -331,7 +328,8 @@ out("enum unicode_Sentence_Break {\n",
 out("extern const char *const unicode_Sentence_Break_names[];\n");
 
 out("enum unicode_flags {\n",
-    "  unicode_normalize_before_casefold = 1\n",
+    "  unicode_normalize_before_casefold = 1,\n",
+    "  unicode_compatibility_decomposition = 2\n",
     "};\n",
     "\n");
 
@@ -352,8 +350,7 @@ sub choosetype {
 }
 
 out("struct unidata {\n",
-    "  const uint32_t *compat;\n",
-    "  const uint32_t *canon;\n",
+    "  const uint32_t *decomp;\n",
     "  const uint32_t *casefold;\n",
 #    "  ".choosetype($minud, $maxud)." upper_offset;\n",
 #    "  ".choosetype($minld, $maxld)." lower_offset;\n",
@@ -364,13 +361,18 @@ out("struct unidata {\n",
     "  char word_break;\n",
     "  char sentence_break;\n",
     "};\n");
-# compat, canon and casefold do have have non-BMP characters, so we
+# decomp and  casefold do have have non-BMP characters, so we
 # can't use a simple 16-bit table.  We could use UTF-8 or UTF-16
 # though, saving a bit of space (probably not that much...) at the
 # cost of marginally reduced performance and additional complexity
 
 out("extern const struct unidata *const unidata[];\n");
 
+out("extern const struct unicode_utf8_row {\n",
+    "  uint8_t count;\n",
+    "  uint8_t min2, max2;\n",
+    "} unicode_utf8_valid[];\n");
+
 out("#define UNICODE_NCHARS ", ($max + 1), "\n");
 out("#define UNICODE_MODULUS $modulus\n");
 out("#define UNICODE_BREAK_START $break_start\n");
@@ -422,12 +424,9 @@ my %decompnums = ();
 my $decompsaved = 0;
 out("static const uint32_t ");
 for(my $c = 0; $c <= $max; ++$c) {
-    # If canon is set then compat will be too and will be identical.
-    # If compat is set the canon might be clear.  So we use the
-    # compat version and fix up the symbols after.
-    if(exists $data{$c} && exists $data{$c}->{compat}) {
+    if(exists $data{$c} && exists $data{$c}->{decomp}) {
        my $s = join(",",
-                    (map(hex($_), split(/\s+/, $data{$c}->{compat})), 0));
+                    (map(hex($_), split(/\s+/, $data{$c}->{decomp})), 0));
        if(!exists $decompnums{$s}) {
            out(",\n") if $decompnum != 0;
            out("cd$decompnum\[]={$s}");
@@ -435,10 +434,7 @@ for(my $c = 0; $c <= $max; ++$c) {
        } else {
            ++$decompsaved;
        }
-       $data{$c}->{compatsym} = "cd$decompnums{$s}";
-       if(exists $data{$c}->{canon}) {
-           $data{$c}->{canonsym} = "cd$decompnums{$s}";
-       }
+       $data{$c}->{decompsym} = "cd$decompnums{$s}";
     }
 }
 out(";\n");
@@ -482,18 +478,19 @@ for(my $base = 0; $base <= $max; $base += $modulus) {
     my @t;
     for(my $c = $base; $c < $base + $modulus; ++$c) {
        my $d = $data{$c};
-       my $canonsym = ($data{$c}->{canonsym} or "0");
-       my $compatsym = ($data{$c}->{compatsym} or "0");
+       my $decompsym = ($data{$c}->{decompsym} or "0");
        my $cfsym = ($data{$c}->{cfsym} or "0");
        my @flags = ();
        if($data{$c}->{ypogegrammeni}) {
            push(@flags, "unicode_normalize_before_casefold");
        }
+       if($data{$c}->{compat}) {
+           push(@flags, "unicode_compatibility_decomposition");
+       }
        my $flags = @flags ? join("|", @flags) : 0;
        push(@t, "{".
             join(",",
-                 $compatsym,
-                 $canonsym,
+                 $decompsym,
                  $cfsym,
 #                $d->{ud},
 #                $d->{ld},
@@ -507,6 +504,7 @@ for(my $base = 0; $base <= $max; $base += $modulus) {
     }
     my $t = join(",\n", @t);
     if(!exists $subtable{$t}) {
+       out(sprintf("/* %04X-%04X */\n", $base, $base + $modulus - 1));
        out("static const struct unidata st$subtablecounter\[] = {\n",
            "$t\n",
            "};\n");
@@ -526,8 +524,45 @@ for(my $base = 0; $base <= $max; $base += $modulus) {
 }
 out("};\n");
 
+out("const struct unicode_utf8_row unicode_utf8_valid[] = {\n");
+for(my $c = 0; $c <= 0x7F; ++$c) {
+    out(" { 1, 0, 0 }, /* $c */\n");
+}
+for(my $c = 0x80; $c < 0xC2; ++$c) {
+    out(" { 0, 0, 0 }, /* $c */\n");
+}
+for(my $c = 0xC2; $c <= 0xDF; ++$c) {
+    out(" { 2, 0x80, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xE0; $c <= 0xE0; ++$c) {
+    out(" { 3, 0xA0, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xE1; $c <= 0xEC; ++$c) {
+    out(" { 3, 0x80, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xED; $c <= 0xED; ++$c) {
+    out(" { 3, 0x80, 0x9F }, /* $c */\n");
+}
+for(my $c = 0xEE; $c <= 0xEF; ++$c) {
+    out(" { 3, 0x80, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xF0; $c <= 0xF0; ++$c) {
+    out(" { 4, 0x90, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xF1; $c <= 0xF3; ++$c) {
+    out(" { 4, 0x80, 0xBF }, /* $c */\n");
+}
+for(my $c = 0xF4; $c <= 0xF4; ++$c) {
+    out(" { 4, 0x80, 0x8F }, /* $c */\n");
+}
+for(my $c = 0xF5; $c <= 0xFF; ++$c) {
+    out(" { 0, 0, 0 }, /* $c */\n");
+}
+out("};\n");
+
 close STDOUT or die "unidata.c: $!\n";
 
+printf STDERR "modulus=%d\n", $modulus;
 printf STDERR "max=%04X\n", $max;
 print STDERR "subtables=$subtablecounter, subtablessaved=$subtablessaved\n";
 print STDERR "decompsaved=$decompsaved cfsaved=$cfsaved\n";