From: Alexander Barkov Date: March 11 2011 2:16pm Subject: bzr commit into mysql-trunk branch (alexander.barkov:3754) WL#896 WL#5821 List-Archive: http://lists.mysql.com/commits/132837 Message-Id: <201103111416.p2BEGuCc012240@bar.myoffice.izhnet.ru> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============4115815016237760220==" --===============4115815016237760220== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/bar/mysql-bzr/mysql-trunk/ based on revid:bjorn.munch@stripped 3754 Alexander Barkov 2011-03-11 Preparation for WL#896 and WL#5821 - Adding various command line options: --levels=4, --contractions=1, etc - New code to dump built-in contractions (when --contractions=1) modified: strings/uca-dump.c === modified file 'strings/uca-dump.c' --- a/strings/uca-dump.c 2011-01-19 13:35:54 +0000 +++ b/strings/uca-dump.c 2011-03-11 14:16:44 +0000 @@ -19,12 +19,15 @@ typedef unsigned char uchar; typedef unsigned short uint16; +typedef unsigned int uint; #define MY_UCA_MAXWEIGHT_TO_PARSE 64 #define MY_UCA_MAXWEIGHT_TO_DUMP 8 #define MY_UCA_MAXLEVEL 4 #define MY_UCA_VERSION_SIZE 32 +#define MY_UCA_MAX_CONTRACTION 6 +#define MY_UCA_NCONTRACTIONS 1024 #define MY_UCA_MAXCHAR (0x10FFFF+1) #define MY_UCA_NCHARS 256 #define MY_UCA_CMASK 255 @@ -38,10 +41,20 @@ typedef struct uca_item_st } MY_UCA_ITEM; +typedef struct uca_contraction_st +{ + uint ch[MY_UCA_MAX_CONTRACTION]; + MY_UCA_ITEM item; +} MY_UCA_CONTRACTION; + typedef struct uca_info_st { char version[MY_UCA_VERSION_SIZE]; MY_UCA_ITEM item[MY_UCA_MAXCHAR]; + size_t ncontractions; + MY_UCA_CONTRACTION contraction[MY_UCA_NCONTRACTIONS]; + int optimize_contractions; + int debug; } MY_UCA; @@ -59,7 +72,7 @@ static int load_uca_file(MY_UCA *uca, { char *comment; char *weight; - char *s; + char *s, *ch[MY_UCA_MAX_CONTRACTION]; size_t codenum, i, code; MY_UCA_ITEM *item= NULL; @@ -83,7 +96,6 @@ static int load_uca_file(MY_UCA *uca, out_of_range_chars++; continue; } - item= &uca->item[code]; if ((comment= strchr(str,'#'))) @@ -110,20 +122,48 @@ static int load_uca_file(MY_UCA *uca, continue; } - codenum= 0; - s= strtok(str, " \t"); - while (s) + for (codenum= 0, s= strtok(str, " \t"); s; + codenum++, s= strtok(NULL, " \t")) { - s= strtok(NULL, " \t"); - codenum++; + if (codenum == MY_UCA_MAX_CONTRACTION) + { + fprintf(stderr, "Contraction length is too long (%d) line #%d", + codenum, lineno); + exit(1); + } + ch[codenum]= s; + ch[codenum + 1]= 0; } if (codenum > 1) { + MY_UCA_CONTRACTION *c= &uca->contraction[uca->ncontractions++]; + size_t i; /* Multi-character weight (contraction) - not supported yet. */ - continue; + + if (uca->ncontractions >= MY_UCA_NCONTRACTIONS) + { + fprintf(stderr, + "Too many contractions (%d) at line #%d\n" + "Rebuild with a bigger MY_UCA_MAXCONTRACTIONS value\n", + uca->ncontractions, lineno); + exit(1); + } + /* Copy codepoints of the contraction parts */ + for (i= 0; i < MY_UCA_MAX_CONTRACTION; i++) + { + c->ch[i]= (i < codenum) ? (uint) strtol(ch[i], NULL, 16) : 0; + } + + if (uca->debug) + fprintf(stderr, "Contraction: %04X-%04X-%04X\n", + c->ch[0], c->ch[1], c->ch[2]); + item= &c->item; } - + else + { + item= &uca->item[code]; + } /* Split weight string into separate weights @@ -173,7 +213,8 @@ static int load_uca_file(MY_UCA *uca, } else { - fprintf(stderr, "Too many weights: %d\n", i); + fprintf(stderr, "Too many weights (%d) at line %d\n", i, lineno); + exit(1); } s= endptr; level++; @@ -277,8 +318,8 @@ get_page_statistics(MY_UCA *uca, size_t } -static const char *pname[]= {"", "l2", "l3", "l4"}; - +static const char *pname[]= {"", "_s", "_t", "_q"}; +static const char *lname[]= {"primary", "secondary", "tertiary", "quaternary"}; static char * prefix_name(MY_UCA *uca) @@ -309,6 +350,50 @@ page_name(MY_UCA *uca, size_t page, size } +/* + "weight" must be [MY_UCA_MAXWEIGHT_TO_DUMP+1] elements long +*/ +static size_t +normalize_weight(MY_UCA_ITEM *item, size_t level, + uint16 *weight, size_t weight_elements) +{ + size_t num, i; + + bzero(weight, weight_elements * sizeof(*weight)); + + /* + Copy non-zero weights only. For example: + + [.17A6.0020.0004.00DF][.0000.015F.0004.00DF][.17A6.0020.001F.00DF] + + makes [17A6][0000][17A6] on the primary level + + pack it to [17A6][17A7] + */ + + for (num= 0, i= 0; i < item->num && i < MY_UCA_MAXWEIGHT_TO_DUMP; i++) + { + if (item->weight[level][i]) + { + weight[num]= item->weight[level][i]; +#ifdef INVERT_TERTIARY_WEIGHTS + if (level == 2) + { + /* + Invert tertiary weights to sort upper case letters + before their lower case counterparts. + */ + if (weight[num] >= 0x20) + fprintf(stderr, "Tertiary weight is too big: %02X\n", weight[num]); + weight[num]= (uint) (0x20) - weight[num]; + } +#endif + num++; + } + } + return num; +} + static void print_one_page(MY_UCA *uca, size_t level, @@ -336,37 +421,19 @@ print_one_page(MY_UCA *uca, size_t level /* Print the page */ for (offs=0; offs < MY_UCA_NCHARS; offs++) { - uint16 weight[9]; + uint16 weight[MY_UCA_MAXWEIGHT_TO_DUMP + 1]; size_t num, i, code= page * MY_UCA_NCHARS + offs; MY_UCA_ITEM *item= &uca->item[code]; - - bzero(weight, sizeof(weight)); - - /* Copy non-zero weights */ - for (num= 0, i= 0; i < item->num && i < MY_UCA_MAXWEIGHT_TO_DUMP; i++) - { - if (item->weight[level][i]) - { - weight[num]= item->weight[level][i]; - num++; - } - } - + + normalize_weight(item, level, weight, sizeof(weight)/sizeof(weight[0])); + /* Print weights */ for (i= 0; i < maxnum; i++) { - /* - Invert weights for secondary level to - sort upper case letters before their - lower case counter part. - */ int tmp= weight[i]; - if (level == 2 && tmp) - tmp= (int) (0x20 - weight[i]); printf("0x%04X", tmp); - - + if (tmp > 0xFFFF || tmp < 0) { fprintf(stderr, @@ -374,8 +441,7 @@ print_one_page(MY_UCA *uca, size_t level code, level, tmp); exit(1); } - - + if ((offs + 1 != MY_UCA_NCHARS) || (i + 1 != maxnum)) printf(","); else @@ -398,15 +464,199 @@ print_one_page(MY_UCA *uca, size_t level } +/* + Compare two weight strings. + Return 1 if weight string differ + Return 0 if weigh string are equal +*/ +static int +weight_cmp(uint16 *w1, uint16 *w2, size_t len) +{ + size_t i; + for (i= 0; i < len; i++) + { + if (w1[i] != w2[i]) + return 1; + } + return 0; +} + + +static void +print_contraction(MY_UCA *uca, MY_UCA_CONTRACTION *c, size_t level) +{ + size_t ch; + uint16 weight[MY_UCA_MAXWEIGHT_TO_DUMP + 1]; + int optimize= 0; + + if (c) + { + normalize_weight(&c->item, level, + weight, sizeof(weight)/sizeof(weight[0])); + + if (uca->optimize_contractions) + { + /* + Some contraction can be optimized away on certain levels. + For example, in Unicode-6.0: + + 0E40 0E01 ; [.2395.0020.0002.0E01][.23CF.0020.001F.0E40] # + + Its part weights are: + + 0E40 ; [.23CF.0020.0002.0E40] # THAI CHARACTER SARA E + 0E01 ; [.2395.0020.0002.0E01] # THAI CHARACTER KO KAI + + On the secondary level weights for the contraction + and for the two characters in a sequence are: 0020-0020. + + So "0E40 0E01" can be optimized away of the secondary level. + + This optimization is OFF by default, as it's better to optimize + this at collation initialization time rather than at dump time, + to preserve all available DUCET data. + + Also, this does not seem to ever happen on the primary level, + so this optimization will not bring any serious performance + improvement. + */ + size_t i; + uint16 sweight[MY_UCA_MAXWEIGHT_TO_DUMP*MY_UCA_MAX_CONTRACTION + 1], *sw; + + /* Concatenate weight arrays for the contraction parts */ + for (sw= sweight, i= 0; c->ch[i]; i++) + { + MY_UCA_ITEM *item= &uca->item[c->ch[i]]; + sw+= normalize_weight(item, level, sw, MY_UCA_MAXWEIGHT_TO_DUMP); + } + + if (sw - sweight < MY_UCA_MAXWEIGHT_TO_DUMP && + !weight_cmp(sweight, weight, MY_UCA_MAXWEIGHT_TO_DUMP)) + { + if (uca->debug) + fprintf(stderr, "Equal[%d]: %04X [%04X-%04X-%04X] == {%04X,%04X,%04X} [%04X-%04X-%04X]\n", + level, + c->ch[0], sweight[0], sweight[1], sweight[2], + c->ch[0], c->ch[1], c->ch[2], + weight[0], weight[1], weight[2]); + optimize= 1; + } + } + } + + printf("%s{", optimize ? "/* " : ""); + for (ch= 0; ch < MY_UCA_MAX_CONTRACTION; ch++) + { + uint codepoint= c ? c->ch[ch] : 0; /* Real character or terminator line */ + printf("%s", ch > 0 ? "," : ""); + if (codepoint) + printf("0x%04X", codepoint); + else + printf("0"); + } + printf("},"); + printf("{"); + for (ch= 0; ch < MY_UCA_MAXWEIGHT_TO_DUMP; ch++) + { + uint w= c ? weight[ch] : 0; /* Real chr or terminator */ + printf("%s", ch > 0 ? "," : ""); + if (w) + printf("0x%04X", w); + else + printf("0"); + } + printf("}"); + printf(",0"); + printf("},%s\n", optimize ? " */" : ""); +} + + +static void +print_contractions(MY_UCA *uca, size_t level) +{ + size_t i; + + printf("\n\n"); + printf("/* Contractions, %s level */\n", lname[level]); + printf("MY_CONTRACTION %s_default_contraction%s[]= {\n", + prefix_name(uca), pname[level]); + for (i= 0; i < uca->ncontractions; i++) + { + MY_UCA_CONTRACTION *c= &uca->contraction[i]; + print_contraction(uca, c, level); + } + print_contraction(uca, NULL, level); + printf("};\n"); +} + + +static int contractions= 0; +static int nlevels= 1; + +static void +usage(FILE *file, int rc) +{ + fprintf(file, "Usage:\n"); + fprintf(file, "uca-dump [options...] < /path/to/allkeys.txt\n"); + fprintf(file, "\n"); + fprintf(file, "Options:\n"); + fprintf(file, "--levels=NUM How many levels to dump, 1-4, default 1.\n"); + fprintf(file, "--contractions=NUM Whether to dump comtractions, 0-1, default 0.\n"); + fprintf(file, "--optimize-contractions=NUM Whether to optimize contractions, 0-1, default 0.\n"); + fprintf(file, "--debug=NUM Print debug information, 0-1, default 0.\n"); + fprintf(file, "\n\n"); + exit(rc); +} + + +static int +get_int_option(const char *str, const char *name, int *num) +{ + size_t namelen= strlen(name); + if (!strncmp(str, name, namelen)) + { + *num= atoi(str + namelen); + if (*num == 0 && str[namelen] !='0') + { + fprintf(stderr, "\nBad numeric option value: %s\n\n", str); + usage(stderr, 1); + } + return 1; + } + return 0; +} + + +static void +process_options(int ac, char **av, MY_UCA *uca) +{ + size_t i; + for (i= 1; i < ac ; i++) + { + /*printf("[%d]=%s\n", i, av[i]);*/ + if (!get_int_option(av[i], "--levels=", &nlevels) && + !get_int_option(av[i], "--contractions=", &contractions) && + !get_int_option(av[i], "--debug=", &uca->debug) && + !get_int_option(av[i], "--optimize-contractions=", &uca->optimize_contractions)) + { + fprintf(stderr, "\nUnknown option: %s\n\n", av[i]); + usage(stderr, 1); + } + } +} + + int -main(int ac __attribute__((unused)), char **av __attribute__((unused))) +main(int ac, char **av) { static MY_UCA uca; size_t level, maxchar= MY_UCA_MAXCHAR; static int pageloaded[MY_UCA_NPAGES]; - size_t nlevels= 1; - + bzero(&uca, sizeof(uca)); + + process_options(ac, av, &uca); + bzero(pageloaded, sizeof(pageloaded)); load_uca_file(&uca, maxchar, pageloaded); @@ -491,6 +741,10 @@ main(int ac __attribute__((unused)), cha printf("%s%s%s", page_name(&uca, page, level), comma, nline); } printf("};\n"); + + /* Print contractions */ + if (contractions) + print_contractions(&uca, level); } --===============4115815016237760220== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/alexander.barkov@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: alexander.barkov@stripped\ # 85rz3i8s4wmyi1x2 # target_branch: file:///home/bar/mysql-bzr/mysql-trunk/ # testament_sha1: c7b4b35e196b302751a92a60443baa7608c14e5f # timestamp: 2011-03-11 17:16:56 +0300 # source_branch: file:///home/bar/mysql-bzr/mysql-5.5.b31384/ # base_revision_id: bjorn.munch@stripped\ # 848ir9njh66pkshu # # Begin bundle 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 kCXHshSSnDo1AoUREvcYIP4u5IpwoSBsQ8kk --===============4115815016237760220==--