sl@0: # Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: # All rights reserved. sl@0: # This component and the accompanying materials are made available sl@0: # under the terms of the License "Eclipse Public License v1.0" sl@0: # which accompanies this distribution, and is available sl@0: # at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: # sl@0: # Initial Contributors: sl@0: # Nokia Corporation - initial contribution. sl@0: # sl@0: # Contributors: sl@0: # sl@0: # Description: sl@0: # Creates C++ code describing how to decompose, compose and fold each character. sl@0: # Usage: sl@0: # perl -w FoldAndDecompTables.pl < sl@0: # Tables we want to create: sl@0: # A: Ordered list of non-excluded decompositions sl@0: # B: List of folded decompositions matching A sl@0: # C: List of decompositions not listed in A of length > 1 sl@0: # D: List of folded decompositions matching C sl@0: # E: List of decompositions of length = 1 whose matching folded decompositions sl@0: # are of length > 1 sl@0: # F: List of folded decompositions matching E sl@0: # G: List of decompositions of length = 1 with matching folded decompositions sl@0: # H: List of folded decompostions matching G sl@0: # I: List of folded decompositions that do not have matching decompositions sl@0: # J: List of decompositions (folding and otherwise) of length > 2 sl@0: # K: Hash table mapping Unicode value to its folded decomposition value in the sl@0: # concatenated list B-D-F-H-I sl@0: # L: List of hash slots in K matching A (providing a mapping from non-excluded sl@0: # decompositions to Unicode value) sl@0: # [all lengths are of UTF16 strings] sl@0: # sl@0: # sl@0: sl@0: use strict; sl@0: sl@0: # sl@0: # Hash table: sl@0: # sl@0: sl@0: # Size of hashing table = 1 to the power $LgHashTableSize sl@0: my $LgHashTableSize = 12; sl@0: sl@0: # Do not change these next two values! sl@0: my $HashTableSize = 1 << $LgHashTableSize; sl@0: my $HashTableBitmaskCpp = sprintf('0x%x', $HashTableSize - 1); sl@0: sl@0: # Hashing function in Perl: Getting the initial search position sl@0: sub HashStart sl@0: { sl@0: return $_[0] & ($HashTableSize - 1); sl@0: } sl@0: # How far to step through each time sl@0: sub HashStep sl@0: { sl@0: my ($code) = @_; sl@0: $code *= $code >> $LgHashTableSize; sl@0: return ($code * 2 + 1) & ($HashTableSize - 1); sl@0: } sl@0: sl@0: # Make sure input string is all hex numbers separated by single spaces with sl@0: # each hex number having 4 digits and decomposed into UTF16 sl@0: sub Normalize sl@0: { sl@0: my ($string) = @_; sl@0: if ($string =~ /^([0-9A-F]{4}( [0-9A-F]{4})*)?$/) sl@0: { sl@0: return $string; sl@0: } sl@0: my $norm = ''; sl@0: foreach my $elt (split(' ', $string)) sl@0: { sl@0: if ($elt) sl@0: { sl@0: die "'$elt' is not a hex number" sl@0: unless $elt =~ /[0-9a-fA-F]+/; sl@0: $norm = $norm.' ' sl@0: unless $norm eq ''; sl@0: $elt = hex $elt; sl@0: if ($elt < 0x10000) sl@0: { sl@0: $norm = $norm.(sprintf('%04X', $elt)); sl@0: } sl@0: else sl@0: { sl@0: # Add a surrogate pair sl@0: $norm = $norm.(sprintf('%04X %04X', sl@0: ($elt / 0x400) + 0xD7C0, ($elt % 0x400) + 0xDC00)); sl@0: } sl@0: } sl@0: } sl@0: #print STDERR "'$string' normalized to '$norm'\n"; sl@0: return $norm; sl@0: } sl@0: sl@0: # First stage: sl@0: # Hash of Unicode values to normalised decomposition and folded strings sl@0: my %Decomp = (); sl@0: my %Folded = (); sl@0: # Mapping from decomposition->char, if not excluded sl@0: my %Composition = (); sl@0: # characters with non-excluded decompositions sl@0: my @IncludedDecomps = (); sl@0: # characters with long (>1 UTF16) excluded decompositions sl@0: my @LongExcludedDecomps = (); sl@0: # characters with singleton decompositions but long folds sl@0: my @ShortDecompsLongFolds = (); sl@0: # characters with singleton folds and singleton sl@0: my @ShortDecompsShortFolds = (); sl@0: # characters with singleton folds but no decomps sl@0: my @ShortFoldsOnly = (); sl@0: sl@0: # A mapping from decompositions of length greater than two sl@0: # to the code that produced them. sl@0: my %VeryLongDecompositions = (); sl@0: sl@0: # A list of characters containing all decompositions of length >2 as slices sl@0: my @VeryLongDecompData = (); sl@0: # Mapping from decomposition->index into VeryLongDecompData sl@0: my %VeryLongDecompMap = (); sl@0: sl@0: # There will be a hash table mapping Unicode values to indices into the other sl@0: # tables. %Index maps the same thing in Perl. sl@0: my %Index = (); sl@0: # %HashTableEntryContents maps the table entries to the Unicode values they sl@0: # contain. sl@0: my %HashTableEntryContents = (); sl@0: # %HashTableEntry maps Unicode value to the entry in the hash table sl@0: my %HashTableEntry = (); sl@0: sl@0: # Bind a unicode value to an index into the tables sl@0: sub AddHashValue sl@0: { sl@0: my ($unicode, $index) = @_; sl@0: $Index{$unicode} = $index; sl@0: my $pos = HashStart($unicode); sl@0: my $step = HashStep($unicode); sl@0: while (exists $HashTableEntryContents{$pos}) sl@0: { sl@0: $pos += $step; sl@0: if ($HashTableSize <= $pos) sl@0: { sl@0: $pos %= $HashTableSize; sl@0: } sl@0: } sl@0: $HashTableEntryContents{$pos} = $unicode; sl@0: $HashTableEntry{$unicode} = $pos; sl@0: } sl@0: sl@0: # Bind a whole array to the indices starting from that given as the first sl@0: # argument. Returns the index of the next slot to be filled. sl@0: sub AddListToHash sl@0: { sl@0: my ($index, @unicodes) = @_; sl@0: while (@unicodes) sl@0: { sl@0: AddHashValue(shift @unicodes, $index); sl@0: $index++; sl@0: } sl@0: return $index; sl@0: } sl@0: sl@0: # put the results of a read line into the data structures sl@0: sub AddCode sl@0: { sl@0: my ($code, $excluded, $decomposition, $folded) = @_; sl@0: return if ($decomposition eq '' && $folded eq ''); sl@0: $Decomp{$code} = $decomposition; sl@0: $Folded{$code} = $folded; sl@0: sl@0: if (!$excluded && $decomposition ne '') sl@0: { sl@0: push @IncludedDecomps, $code; sl@0: $Composition{$decomposition} = $code; sl@0: } sl@0: elsif (4 < length $decomposition) sl@0: { sl@0: push @LongExcludedDecomps, $code; sl@0: } sl@0: elsif (4 < length $folded) sl@0: { sl@0: push @ShortDecompsLongFolds, $code; sl@0: } sl@0: elsif ($decomposition ne '') sl@0: { sl@0: push @ShortDecompsShortFolds, $code; sl@0: } sl@0: elsif ($folded ne '') sl@0: { sl@0: push @ShortFoldsOnly, $code; sl@0: } sl@0: sl@0: $VeryLongDecompositions{$decomposition} = $code sl@0: if (9 < length $decomposition); sl@0: $VeryLongDecompositions{$folded} = $code sl@0: if (9 < length $folded); sl@0: } sl@0: sl@0: if (scalar(@ARGV) != 0) sl@0: { sl@0: print (STDERR "Usage:\nperl -w FoldAndDecompTables.pl < \n"); sl@0: exit 1; sl@0: } sl@0: sl@0: my $lineNo = 0; sl@0: my $inBlock = 0; sl@0: while() sl@0: { sl@0: $lineNo++; sl@0: if (/^(1?[0-9a-fA-F]{4,5});([^;]*);.*symbian:(E?);[^;]*;([0-9a-fA-F \t]*);([0-9a-fA-F \t]*)[ \t]*$/i) sl@0: { sl@0: my $code = hex $1; sl@0: my $description = $2; sl@0: my $excluded = $3; sl@0: my $decomposition = Normalize($4); sl@0: my $folded = Normalize($5); sl@0: sl@0: die ("Value $1 too large to be Unicode at line $lineNo.") sl@0: if (0x110000 <= $code); sl@0: sl@0: die("Normalisation failed with '$decomposition' at line $lineNo.") sl@0: unless (length $decomposition) == 0 || (length $decomposition) % 5 == 4; sl@0: die("Normalisation failed with '$folded' at line $lineNo.") sl@0: unless (length $folded) == 0 || (length $folded) % 5 == 4; sl@0: sl@0: AddCode($code, $excluded, $decomposition, $folded); sl@0: sl@0: if ($description =~ /^<.*Last>$/i) sl@0: { sl@0: die("End of block without start at line $lineNo!") sl@0: if !$inBlock; sl@0: while ($inBlock <= $code) sl@0: { sl@0: AddCode($inBlock, $excluded, $decomposition, $folded); sl@0: $inBlock++; sl@0: } sl@0: $inBlock = 0; sl@0: } sl@0: elsif ($description =~ /^<.*First>$/i) sl@0: { sl@0: die("Block within block at line $lineNo!") sl@0: if $inBlock; sl@0: $inBlock = $code + 1; sl@0: } sl@0: } sl@0: elsif (!/^[ \t]*$/) sl@0: { sl@0: die("Did not understand line $lineNo."); sl@0: } sl@0: } sl@0: sl@0: # We need to construct the data for the table of decompositions of length > 2. sl@0: foreach my $decomp (sort {length $::b <=> length $::a} keys %VeryLongDecompositions) sl@0: { sl@0: if (!exists $VeryLongDecompMap{$decomp}) sl@0: { sl@0: # Does not already exist sl@0: my $newPos = scalar @VeryLongDecompData; sl@0: $VeryLongDecompMap{$decomp} = $newPos; sl@0: foreach my $code (split(' ', $decomp)) sl@0: { sl@0: push @VeryLongDecompData, $code; sl@0: } sl@0: while ($decomp =~ /^([0-9A-F]{4}( [0-9A-F]{4}){2,}) [0-9A-F]{4}$/) sl@0: { sl@0: $decomp = $1; sl@0: $VeryLongDecompMap{$decomp} = $newPos; sl@0: } sl@0: } sl@0: } sl@0: sl@0: # We need to sort the codes for included decompositions into lexicographic sl@0: # order of their decompositions. sl@0: # This, luckily, is the same as sorting the strings that represent their sl@0: # decompositions in hex lexicographically. sl@0: @IncludedDecomps = sort {$Decomp{$::a} cmp $Decomp{$::b}} @IncludedDecomps; sl@0: sl@0: print (STDERR 'Included: ', scalar(@IncludedDecomps), "\nLong: ", scalar(@LongExcludedDecomps)); sl@0: print(STDERR "\nLongFolds: ", scalar(@ShortDecompsLongFolds), "\nShort: ", scalar(@ShortDecompsShortFolds)); sl@0: print(STDERR "\nShortFoldsOnly: ", scalar(@ShortFoldsOnly), "\nTOTAL: "); sl@0: print STDERR (scalar(@IncludedDecomps) + scalar(@LongExcludedDecomps) + scalar(@ShortDecompsLongFolds) + scalar(@ShortDecompsShortFolds) + scalar(@ShortFoldsOnly)); sl@0: print STDERR "\n"; sl@0: sl@0: # Analyse the hash table to find out the maximum and average time sl@0: # taken to find each ASCII character sl@0: my $maxAsciiTime = 0; sl@0: my $totalAsciiTime = 0; sl@0: my $mostDifficultCode = undef; sl@0: my $asciiFoundWithoutStepCount = 0; sl@0: for (32..126) sl@0: { sl@0: my $code = $_; sl@0: my $pos = HashStart($code); sl@0: my $step = HashStep($code); sl@0: my $stepCount = 1; sl@0: if ($HashTableEntry{$code}) sl@0: { sl@0: my $posRequired = $HashTableEntry{$code}; sl@0: while ($pos != $posRequired) sl@0: { sl@0: $pos = ($pos + $step) % $HashTableSize; sl@0: $stepCount++; sl@0: } sl@0: } sl@0: $totalAsciiTime += $stepCount; sl@0: if ($maxAsciiTime < $stepCount) sl@0: { sl@0: $maxAsciiTime = $stepCount; sl@0: $mostDifficultCode = $code; sl@0: } sl@0: if ($stepCount == 1) sl@0: { sl@0: $asciiFoundWithoutStepCount++; sl@0: } sl@0: } sl@0: printf (STDERR "Average ASCII search: %f\n", $totalAsciiTime / 95); sl@0: printf (STDERR "Maximum ASCII search %d for %x: '%c'.\n", $maxAsciiTime, $mostDifficultCode, $mostDifficultCode); sl@0: sl@0: # Now we populate the hash table sl@0: my $index = 0; sl@0: sl@0: $index = AddListToHash($index, @IncludedDecomps); sl@0: my $hashIndexAfterIncludedDecomps = $index; sl@0: printf (STDERR "after IncludedDecomps index= %d\n", $hashIndexAfterIncludedDecomps); sl@0: sl@0: $index = AddListToHash($index, @LongExcludedDecomps); sl@0: my $hashIndexAfterLongExcludeDecomps = $index; sl@0: printf (STDERR "after LongExcludedDecomps index= %d\n", $hashIndexAfterLongExcludeDecomps); sl@0: sl@0: $index = AddListToHash($index, @ShortDecompsLongFolds); sl@0: my $hashIndexAfterShortDecompsLongFolds = $index; sl@0: printf (STDERR "after ShortDecompsLongFolds index= %d\n", $hashIndexAfterShortDecompsLongFolds); sl@0: sl@0: $index = AddListToHash($index, @ShortDecompsShortFolds); sl@0: my $hashIndexAfterShortDecompsShortFolds = $index; sl@0: printf (STDERR "after ShortDecompsShortFolds index= %d\n", $hashIndexAfterShortDecompsShortFolds); sl@0: sl@0: $index = AddListToHash($index, @ShortFoldsOnly); sl@0: my $hashIndexAfterShortFoldsOnly = $index; sl@0: printf (STDERR "after ShortFoldsOnly index= %d\n", $hashIndexAfterShortFoldsOnly); sl@0: sl@0: # sl@0: # Output C++ File sl@0: # sl@0: my $totalBytes = 0; sl@0: sl@0: print "// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).\n"; sl@0: print "// All rights reserved.\n"; sl@0: print "// This component and the accompanying materials are made available\n"; sl@0: print "// under the terms of the License \"Eclipse Public License v1.0\"\n"; sl@0: print "// which accompanies this distribution, and is available\n"; sl@0: print "// at the URL \"http://www.eclipse.org/legal/epl-v10.html\".\n"; sl@0: print "//\n"; sl@0: print "// Initial Contributors:\n"; sl@0: print "// Nokia Corporation - initial contribution.\n"; sl@0: print "//\n"; sl@0: print "// Contributors:\n"; sl@0: print "//\n"; sl@0: print "// Description:\n"; sl@0: print "//\n"; sl@0: print "// Fold and decomposition tables.\n"; sl@0: print "//\n"; sl@0: print "// These tables are linked in the following way:\n"; sl@0: print "// KUnicodeToIndexHash is a hash table using double hashing for\n"; sl@0: print "// conflict resolution. The functions DecompositionHashStart and\n"; sl@0: print "// DecompositionHashStep give the start and step values for accessing\n"; sl@0: print "// the table. The first probe is at DecompositionHashStart and each\n"; sl@0: print "// subsequent probe is offset by DecompositionHashStep. Probes\n"; sl@0: print "// continue until either 0 is found (indicating that the Unicode value\n"; sl@0: print "// sought has no decompostion (i.e. decomposes to itself)) or a value\n"; sl@0: print "// is found that has the sought Unicode value in its lower 20 bits.\n"; sl@0: print "//\n"; sl@0: print "// In this latter case, the upper 12 bits contain an index into\n"; sl@0: print "// one of the following tables, according to the following rules:\n"; sl@0: print "//\n"; sl@0: print "// In the case of folding:\n"; sl@0: print "// If the Index is less than the length of KNonSingletonFolds / 2,\n"; sl@0: print "// it is an index into KNonSingletonFolds. If the Index is\n"; sl@0: print "// greater than the length of KNonSingletonFolds / 2, then it is an\n"; sl@0: print "// index into KSingletonFolds.\n"; sl@0: print "//\n"; sl@0: print "// In the case of decomposition:\n"; sl@0: print "// If the Index is less than the length of KNonSingletonDecompositions / 2,\n"; sl@0: print "// it is an index into KNonSingletonDecompositions. If the Index is\n"; sl@0: print "// greater than the length of KNonSingletonDecompositions / 2, then it is an\n"; sl@0: print "// index into KSingletonDecompositions.\n"; sl@0: print "//\n"; sl@0: print "// In summary:\n"; sl@0: print "// Let Knsf be the length of KNonSingletonFolds / 2,\n"; sl@0: print "// let Knsd be the length of KNonSingletonDecompositions / 2,\n"; sl@0: print "// let Ksd be the length of KSingletonDecompositions and\n"; sl@0: print "// let Ksf be the length of KSingletonFolds.\n"; sl@0: print "// Now if you want to fold a character and you have found\n"; sl@0: print "// its index 'i' from the KUnicodeToIndexHash, then;\n"; sl@0: print "// if (i < Knsf) then look up\n"; sl@0: print "//\t\tKNonSingletonFolds[i * 2] and KNonSingletonFolds[i * 2 + 1]\n"; sl@0: print "// else if (Knsf <= i < Knsf + Ksf) look up KSingletonFolds[i - Knsf]\n"; sl@0: print "// else there is no fold for this character.\n"; sl@0: print "//\n"; sl@0: print "// Or if you want to decompose the same character, then;\n"; sl@0: print "// if (i < Knsd) then look up KNonSingletonDecompositions[i * 2]\n"; sl@0: print "//\t\tand KNonSingletonDecompositions[i * 2 + 1]\n"; sl@0: print "// else if (Knsd <= i < Knsd + Ksd) look up KSingletonDecompositions[i - Knsd]\n"; sl@0: print "// else there is no decomposition for this character.\n"; sl@0: print "//\n"; sl@0: print "// Your index into KSingletonDecompositions or KSingletonFolds\n"; sl@0: print "// yields a single value which is the decomposition or fold.\n"; sl@0: print "//\n"; sl@0: print "// The KNonSingletonFolds and KNonSingletonDecomposition\n"; sl@0: print "// tables are made up of pairs of values. Each pair is either a pair\n"; sl@0: print "// of Unicode values that constitute the fold or decomposition, or\n"; sl@0: print "// the first value is KLongD and the second has its top 4 bits as the\n"; sl@0: print "// length of the decomposition (or folded decomposition) minus 3,\n"; sl@0: print "// and its bottom 12 bits as the index into KLongDecompositions\n"; sl@0: print "// of where you can find this decomposition.\n"; sl@0: print "//\n"; sl@0: print "// KLongDecompositions simply contains UTF-16 (Unicode) for\n"; sl@0: print "// all the decomposed and folded sequences longer than 4 bytes long.\n"; sl@0: print "\n"; sl@0: print "// Hash table mapping unicode values to indices into the other tables\n"; sl@0: print "// in use = ".$hashIndexAfterShortFoldsOnly." entries\n"; sl@0: print "const unsigned long KUnicodeToIndexHash[$HashTableSize] =\n\t{\n\t"; sl@0: my @HashTableOutput; sl@0: for (0..($HashTableSize - 1)) sl@0: { sl@0: my $v = 0; sl@0: if (exists $HashTableEntryContents{$_}) sl@0: { sl@0: $v = $HashTableEntryContents{$_}; sl@0: die ('Did not expect a Unicode value > 0xFFFFF') sl@0: if 0xFFFFF < $v; sl@0: $v |= ($Index{$v}) << 20; sl@0: } sl@0: push @HashTableOutput, sprintf('0x%08x', $v); sl@0: $totalBytes += 4; sl@0: } sl@0: print (shift @HashTableOutput); sl@0: my $valueCount = 0; sl@0: foreach my $v (@HashTableOutput) sl@0: { sl@0: print (((++$valueCount & 7) == 0)? ",\n\t" : ', '); sl@0: print $v; sl@0: } sl@0: print "\n\t};\n\n"; sl@0: print "// Hash table access functions\n"; sl@0: print "const int KDecompositionHashBitmask = $HashTableBitmaskCpp;\n\n"; sl@0: print "inline int DecompositionHashStart(long a)\n"; sl@0: print "\t{\n\treturn a & $HashTableBitmaskCpp;\n\t}\n\n"; sl@0: print "inline int DecompositionHashStep(long a)\n"; sl@0: print "\t{\n\ta *= a >> $LgHashTableSize;\n"; sl@0: print "\treturn ((a<<1) + 1) & $HashTableBitmaskCpp;\n\t}\n\n"; sl@0: sl@0: print "// Table mapping KNonSingletonDecompositions to the hash table entry that\n"; sl@0: print "// indexes it\n"; sl@0: print "const unsigned short KCompositionMapping[] =\n\t{\n\t"; sl@0: for (0..(scalar(@IncludedDecomps - 1))) sl@0: { sl@0: if ($_ != 0) sl@0: {print (($_ & 7) == 0? ",\n\t" : ', ')} sl@0: printf( '0x%04x', $HashTableEntry{$IncludedDecomps[$_]} ); sl@0: $totalBytes += 2; sl@0: } sl@0: print "\n\t};\n\n"; sl@0: sl@0: print "// Table containing all the decomposition and folding strings longer\n"; sl@0: print "// than 2 UTF16 characters\n"; sl@0: print "const unsigned short KLongDecompositions[] =\n\t{\n\t0x"; sl@0: for(0..(scalar(@VeryLongDecompData) - 1)) sl@0: { sl@0: if ($_ != 0) sl@0: {print (($_ & 7) == 0?",\n\t0x" : ', 0x')} sl@0: print $VeryLongDecompData[$_]; sl@0: $totalBytes += 2; sl@0: } sl@0: print "\n\t};\n\n"; sl@0: sl@0: print "// Table containing decompositions longer than one UTF16 character.\n"; sl@0: print "// The top of the table contains all compositions, sorted lexicographically.\n"; sl@0: print "// Any decompositions of length 2 are in the table as a pair of values,\n"; sl@0: print "// decompositions longer than that are represented by a KLongD followed by\n"; sl@0: print "// a value whose top four bits indicate the length of the decomposition minus\n"; sl@0: print "// three and whose bottom 12 bits indicate an index into the KLongDecompositions\n"; sl@0: print "// array where the decomposition starts.\n"; sl@0: print "const long KLongD = 0;\n"; sl@0: print "// sizeof/2 = ".$hashIndexAfterLongExcludeDecomps."\n"; sl@0: print "const unsigned short KNonSingletonDecompositions[] =\n\t{\n\t"; sl@0: sl@0: sub PrintNonsingletonDecompTableEntry sl@0: { sl@0: my ($decomp) = @_; sl@0: if (length $decomp < 10) sl@0: { sl@0: if ($decomp =~ /([0-9A-F]{4}) ([0-9A-F]{4})/) sl@0: { sl@0: print '0x'.$1.', 0x'.$2; sl@0: } sl@0: else sl@0: { sl@0: die("$decomp expected to be normalized and of length 1 or 2") sl@0: if $decomp !~ /[0-9A-F]{4}/; sl@0: print '0x'.$decomp.', 0xFFFF'; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: printf ('KLongD, 0x%1X%03X', ((length $decomp) - 14)/5, $VeryLongDecompMap{$decomp}); sl@0: } sl@0: } sl@0: sl@0: {my $entryNo = 0; sl@0: foreach my $code (@IncludedDecomps) sl@0: { sl@0: if ($entryNo != 0) sl@0: {print (($entryNo & 3) == 0?",\n\t" : ', ')} sl@0: PrintNonsingletonDecompTableEntry($Decomp{$code}); sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: foreach my $code (@LongExcludedDecomps) sl@0: { sl@0: print (($entryNo & 3) == 0?",\n\t" : ', '); sl@0: PrintNonsingletonDecompTableEntry($Decomp{$code}); sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: } sl@0: print "\n\t};\n\n"; sl@0: sl@0: print "// Table of folded decompositions which either have more than one UTF16, or\n"; sl@0: print "// their normal decompositions have more than one UTF16\n"; sl@0: print "// sizeof/2 = ".$hashIndexAfterShortDecompsLongFolds."\n"; sl@0: print "const unsigned short KNonSingletonFolds[] =\n\t{\n\t"; sl@0: {my $entryNo = 0; sl@0: foreach my $code (@IncludedDecomps) sl@0: { sl@0: if ($entryNo != 0) sl@0: {print (($entryNo & 3) == 0?",\n\t" : ', ')} sl@0: PrintNonsingletonDecompTableEntry($Folded{$code}); sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: foreach my $code (@LongExcludedDecomps) sl@0: { sl@0: print (($entryNo & 3) == 0?",\n\t" : ', '); sl@0: PrintNonsingletonDecompTableEntry($Folded{$code}); sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: foreach my $code (@ShortDecompsLongFolds) sl@0: { sl@0: print (($entryNo & 3) == 0?",\n\t" : ', '); sl@0: PrintNonsingletonDecompTableEntry($Folded{$code}); sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: } sl@0: print "\n\t};\n\n"; sl@0: sl@0: print "// Table of singleton decompositions and characters with singleton folds\n"; sl@0: print "// Note for Unicode 5.0:\n"; sl@0: print "// Unicode 5.0 contains some non-BMP characters have non-BMP \"singleton\" folds.\n"; sl@0: print "// As per the algorithm of this file, the non-BMP character should be stored in \n"; sl@0: print "// this table. \"Unsigned short\" is not big enough to hold them. However, this \n"; sl@0: print "// \"character\" information is not useful. So we just store 0xFFFF instead. \n"; sl@0: print "// Please do check 0xFFFF when access this table. If meet 0xFFFF, that means \n"; sl@0: print "// your character has no decomposition.\n"; sl@0: print "// See the variable \"ShortDecompsLongFolds\" in FoldAndDecompTables.pl if you \n"; sl@0: print "// want to know more.\n"; sl@0: print "// sizeof = ".($hashIndexAfterShortDecompsShortFolds-$hashIndexAfterLongExcludeDecomps)."\n"; sl@0: print "const unsigned short KSingletonDecompositions[] =\n\t{\n\t0x"; sl@0: {my $entryNo = 0; sl@0: foreach my $code (@ShortDecompsLongFolds) sl@0: { sl@0: if ($entryNo != 0) sl@0: {print (($entryNo & 7) == 0?",\n\t0x" : ', 0x')} sl@0: if (exists $Decomp{$code} && $Decomp{$code} ne '') sl@0: { sl@0: print $Decomp{$code}; sl@0: } sl@0: else sl@0: { sl@0: # Don't take these 0xFFFF as character. sl@0: #printf ('%04X', $code); sl@0: printf ("FFFF"); sl@0: } sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: foreach my $code (@ShortDecompsShortFolds) sl@0: { sl@0: if ($entryNo != 0) sl@0: {print (($entryNo & 7) == 0?",\n\t0x" : ', 0x')} sl@0: print $Decomp{$code}; sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: } sl@0: print "\n\t};\n\n"; sl@0: sl@0: print "// Table of singleton folds\n"; sl@0: print "// sizeof = ".($hashIndexAfterShortFoldsOnly-$hashIndexAfterShortDecompsLongFolds)."\n"; sl@0: print "const unsigned short KSingletonFolds[] =\n\t{\n\t0x"; sl@0: {my $entryNo = 0; sl@0: foreach my $code (@ShortDecompsShortFolds) sl@0: { sl@0: if ($entryNo != 0) sl@0: {print (($entryNo & 7) == 0?",\n\t0x" : ', 0x')} sl@0: print $Folded{$code}; sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: foreach my $code (@ShortFoldsOnly) sl@0: { sl@0: print (($entryNo & 7) == 0?",\n\t0x" : ', 0x'); sl@0: print $Folded{$code}; sl@0: $entryNo++; sl@0: $totalBytes += 4; sl@0: } sl@0: } sl@0: print "\n\t};\n"; sl@0: sl@0: print "\n// Total size: $totalBytes bytes\n"; sl@0: print STDERR $totalBytes, " bytes\n";