intl/icu/source/tools/gennorm2/n2builder.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /*
michael@0 2 *******************************************************************************
michael@0 3 *
michael@0 4 * Copyright (C) 2009-2012, International Business Machines
michael@0 5 * Corporation and others. All Rights Reserved.
michael@0 6 *
michael@0 7 *******************************************************************************
michael@0 8 * file name: n2builder.cpp
michael@0 9 * encoding: US-ASCII
michael@0 10 * tab size: 8 (not used)
michael@0 11 * indentation:4
michael@0 12 *
michael@0 13 * created on: 2009nov25
michael@0 14 * created by: Markus W. Scherer
michael@0 15 *
michael@0 16 * Builds Normalizer2 data and writes a binary .nrm file.
michael@0 17 * For the file format see source/common/normalizer2impl.h.
michael@0 18 */
michael@0 19
michael@0 20 #include "unicode/utypes.h"
michael@0 21 #include "n2builder.h"
michael@0 22
michael@0 23 #include <stdio.h>
michael@0 24 #include <stdlib.h>
michael@0 25 #include <string.h>
michael@0 26 #if U_HAVE_STD_STRING
michael@0 27 #include <vector>
michael@0 28 #endif
michael@0 29 #include "unicode/errorcode.h"
michael@0 30 #include "unicode/localpointer.h"
michael@0 31 #include "unicode/putil.h"
michael@0 32 #include "unicode/udata.h"
michael@0 33 #include "unicode/uniset.h"
michael@0 34 #include "unicode/unistr.h"
michael@0 35 #include "unicode/ustring.h"
michael@0 36 #include "hash.h"
michael@0 37 #include "normalizer2impl.h"
michael@0 38 #include "toolutil.h"
michael@0 39 #include "unewdata.h"
michael@0 40 #include "utrie2.h"
michael@0 41 #include "uvectr32.h"
michael@0 42
michael@0 43 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
michael@0 44
michael@0 45 #if !UCONFIG_NO_NORMALIZATION
michael@0 46
michael@0 47 /* UDataInfo cf. udata.h */
michael@0 48 static UDataInfo dataInfo={
michael@0 49 sizeof(UDataInfo),
michael@0 50 0,
michael@0 51
michael@0 52 U_IS_BIG_ENDIAN,
michael@0 53 U_CHARSET_FAMILY,
michael@0 54 U_SIZEOF_UCHAR,
michael@0 55 0,
michael@0 56
michael@0 57 { 0x4e, 0x72, 0x6d, 0x32 }, /* dataFormat="Nrm2" */
michael@0 58 { 2, 0, 0, 0 }, /* formatVersion */
michael@0 59 { 5, 2, 0, 0 } /* dataVersion (Unicode version) */
michael@0 60 };
michael@0 61
michael@0 62 U_NAMESPACE_BEGIN
michael@0 63
michael@0 64 class HangulIterator {
michael@0 65 public:
michael@0 66 struct Range {
michael@0 67 UChar32 start, limit;
michael@0 68 uint16_t norm16;
michael@0 69 };
michael@0 70
michael@0 71 HangulIterator() : rangeIndex(0) {}
michael@0 72 const Range *nextRange() {
michael@0 73 if(rangeIndex<LENGTHOF(ranges)) {
michael@0 74 return ranges+rangeIndex++;
michael@0 75 } else {
michael@0 76 return NULL;
michael@0 77 }
michael@0 78 }
michael@0 79 void reset() { rangeIndex=0; }
michael@0 80 private:
michael@0 81 static const Range ranges[4];
michael@0 82 int32_t rangeIndex;
michael@0 83 };
michael@0 84
michael@0 85 const HangulIterator::Range HangulIterator::ranges[4]={
michael@0 86 { Hangul::JAMO_L_BASE, Hangul::JAMO_L_BASE+Hangul::JAMO_L_COUNT, 1 },
michael@0 87 { Hangul::JAMO_V_BASE, Hangul::JAMO_V_BASE+Hangul::JAMO_V_COUNT, Normalizer2Impl::JAMO_VT },
michael@0 88 // JAMO_T_BASE+1: not U+11A7
michael@0 89 { Hangul::JAMO_T_BASE+1, Hangul::JAMO_T_BASE+Hangul::JAMO_T_COUNT, Normalizer2Impl::JAMO_VT },
michael@0 90 { Hangul::HANGUL_BASE, Hangul::HANGUL_BASE+Hangul::HANGUL_COUNT, 0 }, // will become minYesNo
michael@0 91 };
michael@0 92
michael@0 93 struct CompositionPair {
michael@0 94 CompositionPair(UChar32 t, UChar32 c) : trail(t), composite(c) {}
michael@0 95 UChar32 trail, composite;
michael@0 96 };
michael@0 97
michael@0 98 struct Norm {
michael@0 99 enum MappingType { NONE, REMOVED, ROUND_TRIP, ONE_WAY };
michael@0 100
michael@0 101 UBool hasMapping() const { return mappingType>REMOVED; }
michael@0 102
michael@0 103 // Requires hasMapping() and well-formed mapping.
michael@0 104 void setMappingCP() {
michael@0 105 UChar32 c;
michael@0 106 if(!mapping->isEmpty() && mapping->length()==U16_LENGTH(c=mapping->char32At(0))) {
michael@0 107 mappingCP=c;
michael@0 108 } else {
michael@0 109 mappingCP=U_SENTINEL;
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 const CompositionPair *getCompositionPairs(int32_t &length) const {
michael@0 114 if(compositions==NULL) {
michael@0 115 length=0;
michael@0 116 return NULL;
michael@0 117 } else {
michael@0 118 length=compositions->size()/2;
michael@0 119 return reinterpret_cast<const CompositionPair *>(compositions->getBuffer());
michael@0 120 }
michael@0 121 }
michael@0 122
michael@0 123 UnicodeString *mapping;
michael@0 124 UnicodeString *rawMapping; // non-NULL if the mapping is further decomposed
michael@0 125 UChar32 mappingCP; // >=0 if mapping to 1 code point
michael@0 126 int32_t mappingPhase;
michael@0 127 MappingType mappingType;
michael@0 128
michael@0 129 UVector32 *compositions; // (trail, composite) pairs
michael@0 130 uint8_t cc;
michael@0 131 UBool combinesBack;
michael@0 132 UBool hasNoCompBoundaryAfter;
michael@0 133
michael@0 134 enum OffsetType {
michael@0 135 OFFSET_NONE,
michael@0 136 // Composition for back-combining character. Allowed, but not normally used.
michael@0 137 OFFSET_MAYBE_YES,
michael@0 138 // Composition for a starter that does not have a decomposition mapping.
michael@0 139 OFFSET_YES_YES,
michael@0 140 // Round-trip mapping & composition for a starter.
michael@0 141 OFFSET_YES_NO_MAPPING_AND_COMPOSITION,
michael@0 142 // Round-trip mapping for a starter that itself does not combine-forward.
michael@0 143 OFFSET_YES_NO_MAPPING_ONLY,
michael@0 144 // One-way mapping.
michael@0 145 OFFSET_NO_NO,
michael@0 146 // Delta for an algorithmic one-way mapping.
michael@0 147 OFFSET_DELTA
michael@0 148 };
michael@0 149 enum { OFFSET_SHIFT=4, OFFSET_MASK=(1<<OFFSET_SHIFT)-1 };
michael@0 150 int32_t offset;
michael@0 151 };
michael@0 152
michael@0 153 class Normalizer2DBEnumerator {
michael@0 154 public:
michael@0 155 Normalizer2DBEnumerator(Normalizer2DataBuilder &b) : builder(b) {}
michael@0 156 virtual ~Normalizer2DBEnumerator() {}
michael@0 157 virtual UBool rangeHandler(UChar32 start, UChar32 end, uint32_t value) = 0;
michael@0 158 Normalizer2DBEnumerator *ptr() { return this; }
michael@0 159 protected:
michael@0 160 Normalizer2DataBuilder &builder;
michael@0 161 };
michael@0 162
michael@0 163 U_CDECL_BEGIN
michael@0 164
michael@0 165 static UBool U_CALLCONV
michael@0 166 enumRangeHandler(const void *context, UChar32 start, UChar32 end, uint32_t value) {
michael@0 167 return ((Normalizer2DBEnumerator *)context)->rangeHandler(start, end, value);
michael@0 168 }
michael@0 169
michael@0 170 U_CDECL_END
michael@0 171
michael@0 172 Normalizer2DataBuilder::Normalizer2DataBuilder(UErrorCode &errorCode) :
michael@0 173 phase(0), overrideHandling(OVERRIDE_PREVIOUS), optimization(OPTIMIZE_NORMAL) {
michael@0 174 memset(unicodeVersion, 0, sizeof(unicodeVersion));
michael@0 175 normTrie=utrie2_open(0, 0, &errorCode);
michael@0 176 normMem=utm_open("gennorm2 normalization structs", 10000, 0x110100, sizeof(Norm));
michael@0 177 norms=allocNorm(); // unused Norm struct at index 0
michael@0 178 memset(indexes, 0, sizeof(indexes));
michael@0 179 memset(smallFCD, 0, sizeof(smallFCD));
michael@0 180 }
michael@0 181
michael@0 182 Normalizer2DataBuilder::~Normalizer2DataBuilder() {
michael@0 183 utrie2_close(normTrie);
michael@0 184 int32_t normsLength=utm_countItems(normMem);
michael@0 185 for(int32_t i=1; i<normsLength; ++i) {
michael@0 186 delete norms[i].mapping;
michael@0 187 delete norms[i].rawMapping;
michael@0 188 delete norms[i].compositions;
michael@0 189 }
michael@0 190 utm_close(normMem);
michael@0 191 utrie2_close(norm16Trie);
michael@0 192 }
michael@0 193
michael@0 194 void
michael@0 195 Normalizer2DataBuilder::setUnicodeVersion(const char *v) {
michael@0 196 UVersionInfo nullVersion={ 0, 0, 0, 0 };
michael@0 197 UVersionInfo version;
michael@0 198 u_versionFromString(version, v);
michael@0 199 if( 0!=memcmp(version, unicodeVersion, U_MAX_VERSION_LENGTH) &&
michael@0 200 0!=memcmp(nullVersion, unicodeVersion, U_MAX_VERSION_LENGTH)
michael@0 201 ) {
michael@0 202 char buffer[U_MAX_VERSION_STRING_LENGTH];
michael@0 203 u_versionToString(unicodeVersion, buffer);
michael@0 204 fprintf(stderr, "gennorm2 error: multiple inconsistent Unicode version numbers %s vs. %s\n",
michael@0 205 buffer, v);
michael@0 206 exit(U_ILLEGAL_ARGUMENT_ERROR);
michael@0 207 }
michael@0 208 memcpy(unicodeVersion, version, U_MAX_VERSION_LENGTH);
michael@0 209 }
michael@0 210
michael@0 211 Norm *Normalizer2DataBuilder::allocNorm() {
michael@0 212 Norm *p=(Norm *)utm_alloc(normMem);
michael@0 213 norms=(Norm *)utm_getStart(normMem); // in case it got reallocated
michael@0 214 return p;
michael@0 215 }
michael@0 216
michael@0 217 /* get an existing Norm unit */
michael@0 218 Norm *Normalizer2DataBuilder::getNorm(UChar32 c) {
michael@0 219 uint32_t i=utrie2_get32(normTrie, c);
michael@0 220 if(i==0) {
michael@0 221 return NULL;
michael@0 222 }
michael@0 223 return norms+i;
michael@0 224 }
michael@0 225
michael@0 226 const Norm &Normalizer2DataBuilder::getNormRef(UChar32 c) const {
michael@0 227 return norms[utrie2_get32(normTrie, c)];
michael@0 228 }
michael@0 229
michael@0 230 /*
michael@0 231 * get or create a Norm unit;
michael@0 232 * get or create the intermediate trie entries for it as well
michael@0 233 */
michael@0 234 Norm *Normalizer2DataBuilder::createNorm(UChar32 c) {
michael@0 235 uint32_t i=utrie2_get32(normTrie, c);
michael@0 236 if(i!=0) {
michael@0 237 return norms+i;
michael@0 238 } else {
michael@0 239 /* allocate Norm */
michael@0 240 Norm *p=allocNorm();
michael@0 241 IcuToolErrorCode errorCode("gennorm2/createNorm()");
michael@0 242 utrie2_set32(normTrie, c, (uint32_t)(p-norms), errorCode);
michael@0 243 return p;
michael@0 244 }
michael@0 245 }
michael@0 246
michael@0 247 Norm *Normalizer2DataBuilder::checkNormForMapping(Norm *p, UChar32 c) {
michael@0 248 if(p!=NULL) {
michael@0 249 if(p->mappingType!=Norm::NONE) {
michael@0 250 if( overrideHandling==OVERRIDE_NONE ||
michael@0 251 (overrideHandling==OVERRIDE_PREVIOUS && p->mappingPhase==phase)
michael@0 252 ) {
michael@0 253 fprintf(stderr,
michael@0 254 "error in gennorm2 phase %d: "
michael@0 255 "not permitted to override mapping for U+%04lX from phase %d\n",
michael@0 256 (int)phase, (long)c, (int)p->mappingPhase);
michael@0 257 exit(U_INVALID_FORMAT_ERROR);
michael@0 258 }
michael@0 259 delete p->mapping;
michael@0 260 p->mapping=NULL;
michael@0 261 }
michael@0 262 p->mappingPhase=phase;
michael@0 263 }
michael@0 264 return p;
michael@0 265 }
michael@0 266
michael@0 267 void Normalizer2DataBuilder::setOverrideHandling(OverrideHandling oh) {
michael@0 268 overrideHandling=oh;
michael@0 269 ++phase;
michael@0 270 }
michael@0 271
michael@0 272 void Normalizer2DataBuilder::setCC(UChar32 c, uint8_t cc) {
michael@0 273 createNorm(c)->cc=cc;
michael@0 274 }
michael@0 275
michael@0 276 uint8_t Normalizer2DataBuilder::getCC(UChar32 c) const {
michael@0 277 return getNormRef(c).cc;
michael@0 278 }
michael@0 279
michael@0 280 static UBool isWellFormed(const UnicodeString &s) {
michael@0 281 UErrorCode errorCode=U_ZERO_ERROR;
michael@0 282 u_strToUTF8(NULL, 0, NULL, s.getBuffer(), s.length(), &errorCode);
michael@0 283 return U_SUCCESS(errorCode) || errorCode==U_BUFFER_OVERFLOW_ERROR;
michael@0 284 }
michael@0 285
michael@0 286 void Normalizer2DataBuilder::setOneWayMapping(UChar32 c, const UnicodeString &m) {
michael@0 287 if(!isWellFormed(m)) {
michael@0 288 fprintf(stderr,
michael@0 289 "error in gennorm2 phase %d: "
michael@0 290 "illegal one-way mapping from U+%04lX to malformed string\n",
michael@0 291 (int)phase, (long)c);
michael@0 292 exit(U_INVALID_FORMAT_ERROR);
michael@0 293 }
michael@0 294 Norm *p=checkNormForMapping(createNorm(c), c);
michael@0 295 p->mapping=new UnicodeString(m);
michael@0 296 p->mappingType=Norm::ONE_WAY;
michael@0 297 p->setMappingCP();
michael@0 298 }
michael@0 299
michael@0 300 void Normalizer2DataBuilder::setRoundTripMapping(UChar32 c, const UnicodeString &m) {
michael@0 301 if(U_IS_SURROGATE(c)) {
michael@0 302 fprintf(stderr,
michael@0 303 "error in gennorm2 phase %d: "
michael@0 304 "illegal round-trip mapping from surrogate code point U+%04lX\n",
michael@0 305 (int)phase, (long)c);
michael@0 306 exit(U_INVALID_FORMAT_ERROR);
michael@0 307 }
michael@0 308 if(!isWellFormed(m)) {
michael@0 309 fprintf(stderr,
michael@0 310 "error in gennorm2 phase %d: "
michael@0 311 "illegal round-trip mapping from U+%04lX to malformed string\n",
michael@0 312 (int)phase, (long)c);
michael@0 313 exit(U_INVALID_FORMAT_ERROR);
michael@0 314 }
michael@0 315 int32_t numCP=u_countChar32(m.getBuffer(), m.length());
michael@0 316 if(numCP!=2) {
michael@0 317 fprintf(stderr,
michael@0 318 "error in gennorm2 phase %d: "
michael@0 319 "illegal round-trip mapping from U+%04lX to %d!=2 code points\n",
michael@0 320 (int)phase, (long)c, (int)numCP);
michael@0 321 exit(U_INVALID_FORMAT_ERROR);
michael@0 322 }
michael@0 323 Norm *p=checkNormForMapping(createNorm(c), c);
michael@0 324 p->mapping=new UnicodeString(m);
michael@0 325 p->mappingType=Norm::ROUND_TRIP;
michael@0 326 p->mappingCP=U_SENTINEL;
michael@0 327 }
michael@0 328
michael@0 329 void Normalizer2DataBuilder::removeMapping(UChar32 c) {
michael@0 330 Norm *p=checkNormForMapping(getNorm(c), c);
michael@0 331 if(p!=NULL) {
michael@0 332 p->mappingType=Norm::REMOVED;
michael@0 333 }
michael@0 334 }
michael@0 335
michael@0 336 class CompositionBuilder : public Normalizer2DBEnumerator {
michael@0 337 public:
michael@0 338 CompositionBuilder(Normalizer2DataBuilder &b) : Normalizer2DBEnumerator(b) {}
michael@0 339 virtual UBool rangeHandler(UChar32 start, UChar32 end, uint32_t value) {
michael@0 340 builder.addComposition(start, end, value);
michael@0 341 return TRUE;
michael@0 342 }
michael@0 343 };
michael@0 344
michael@0 345 void
michael@0 346 Normalizer2DataBuilder::addComposition(UChar32 start, UChar32 end, uint32_t value) {
michael@0 347 if(norms[value].mappingType==Norm::ROUND_TRIP) {
michael@0 348 if(start!=end) {
michael@0 349 fprintf(stderr,
michael@0 350 "gennorm2 error: same round-trip mapping for "
michael@0 351 "more than 1 code point U+%04lX..U+%04lX\n",
michael@0 352 (long)start, (long)end);
michael@0 353 exit(U_INVALID_FORMAT_ERROR);
michael@0 354 }
michael@0 355 if(norms[value].cc!=0) {
michael@0 356 fprintf(stderr,
michael@0 357 "gennorm2 error: "
michael@0 358 "U+%04lX has a round-trip mapping and ccc!=0, "
michael@0 359 "not possible in Unicode normalization\n",
michael@0 360 (long)start);
michael@0 361 exit(U_INVALID_FORMAT_ERROR);
michael@0 362 }
michael@0 363 // setRoundTripMapping() ensured that there are exactly two code points.
michael@0 364 const UnicodeString &m=*norms[value].mapping;
michael@0 365 UChar32 lead=m.char32At(0);
michael@0 366 UChar32 trail=m.char32At(m.length()-1);
michael@0 367 if(getCC(lead)!=0) {
michael@0 368 fprintf(stderr,
michael@0 369 "gennorm2 error: "
michael@0 370 "U+%04lX's round-trip mapping's starter U+%04lX has ccc!=0, "
michael@0 371 "not possible in Unicode normalization\n",
michael@0 372 (long)start, (long)lead);
michael@0 373 exit(U_INVALID_FORMAT_ERROR);
michael@0 374 }
michael@0 375 // Flag for trailing character.
michael@0 376 createNorm(trail)->combinesBack=TRUE;
michael@0 377 // Insert (trail, composite) pair into compositions list for the lead character.
michael@0 378 IcuToolErrorCode errorCode("gennorm2/addComposition()");
michael@0 379 Norm *leadNorm=createNorm(lead);
michael@0 380 UVector32 *compositions=leadNorm->compositions;
michael@0 381 int32_t i;
michael@0 382 if(compositions==NULL) {
michael@0 383 compositions=leadNorm->compositions=new UVector32(errorCode);
michael@0 384 i=0; // "insert" the first pair at index 0
michael@0 385 } else {
michael@0 386 // Insertion sort, and check for duplicate trail characters.
michael@0 387 int32_t length;
michael@0 388 const CompositionPair *pairs=leadNorm->getCompositionPairs(length);
michael@0 389 for(i=0; i<length; ++i) {
michael@0 390 if(trail==pairs[i].trail) {
michael@0 391 fprintf(stderr,
michael@0 392 "gennorm2 error: same round-trip mapping for "
michael@0 393 "more than 1 code point (e.g., U+%04lX) to U+%04lX + U+%04lX\n",
michael@0 394 (long)start, (long)lead, (long)trail);
michael@0 395 exit(U_INVALID_FORMAT_ERROR);
michael@0 396 }
michael@0 397 if(trail<pairs[i].trail) {
michael@0 398 break;
michael@0 399 }
michael@0 400 }
michael@0 401 }
michael@0 402 compositions->insertElementAt(trail, 2*i, errorCode);
michael@0 403 compositions->insertElementAt(start, 2*i+1, errorCode);
michael@0 404 }
michael@0 405 }
michael@0 406
michael@0 407 UBool Normalizer2DataBuilder::combinesWithCCBetween(const Norm &norm,
michael@0 408 uint8_t lowCC, uint8_t highCC) const {
michael@0 409 if((highCC-lowCC)>=2) {
michael@0 410 int32_t length;
michael@0 411 const CompositionPair *pairs=norm.getCompositionPairs(length);
michael@0 412 for(int32_t i=0; i<length; ++i) {
michael@0 413 uint8_t trailCC=getCC(pairs[i].trail);
michael@0 414 if(lowCC<trailCC && trailCC<highCC) {
michael@0 415 return TRUE;
michael@0 416 }
michael@0 417 }
michael@0 418 }
michael@0 419 return FALSE;
michael@0 420 }
michael@0 421
michael@0 422 UChar32 Normalizer2DataBuilder::combine(const Norm &norm, UChar32 trail) const {
michael@0 423 int32_t length;
michael@0 424 const CompositionPair *pairs=norm.getCompositionPairs(length);
michael@0 425 for(int32_t i=0; i<length; ++i) {
michael@0 426 if(trail==pairs[i].trail) {
michael@0 427 return pairs[i].composite;
michael@0 428 }
michael@0 429 if(trail<pairs[i].trail) {
michael@0 430 break;
michael@0 431 }
michael@0 432 }
michael@0 433 return U_SENTINEL;
michael@0 434 }
michael@0 435
michael@0 436 class Decomposer : public Normalizer2DBEnumerator {
michael@0 437 public:
michael@0 438 Decomposer(Normalizer2DataBuilder &b) : Normalizer2DBEnumerator(b), didDecompose(FALSE) {}
michael@0 439 virtual UBool rangeHandler(UChar32 start, UChar32 end, uint32_t value) {
michael@0 440 didDecompose|=builder.decompose(start, end, value);
michael@0 441 return TRUE;
michael@0 442 }
michael@0 443 UBool didDecompose;
michael@0 444 };
michael@0 445
michael@0 446 UBool
michael@0 447 Normalizer2DataBuilder::decompose(UChar32 start, UChar32 end, uint32_t value) {
michael@0 448 if(norms[value].hasMapping()) {
michael@0 449 Norm &norm=norms[value];
michael@0 450 const UnicodeString &m=*norm.mapping;
michael@0 451 UnicodeString *decomposed=NULL;
michael@0 452 const UChar *s=m.getBuffer();
michael@0 453 int32_t length=m.length();
michael@0 454 int32_t prev, i=0;
michael@0 455 UChar32 c;
michael@0 456 while(i<length) {
michael@0 457 prev=i;
michael@0 458 U16_NEXT(s, i, length, c);
michael@0 459 if(start<=c && c<=end) {
michael@0 460 fprintf(stderr,
michael@0 461 "gennorm2 error: U+%04lX maps to itself directly or indirectly\n",
michael@0 462 (long)c);
michael@0 463 exit(U_INVALID_FORMAT_ERROR);
michael@0 464 }
michael@0 465 const Norm &cNorm=getNormRef(c);
michael@0 466 if(cNorm.hasMapping()) {
michael@0 467 if(norm.mappingType==Norm::ROUND_TRIP) {
michael@0 468 if(prev==0) {
michael@0 469 if(cNorm.mappingType!=Norm::ROUND_TRIP) {
michael@0 470 fprintf(stderr,
michael@0 471 "gennorm2 error: "
michael@0 472 "U+%04lX's round-trip mapping's starter "
michael@0 473 "U+%04lX one-way-decomposes, "
michael@0 474 "not possible in Unicode normalization\n",
michael@0 475 (long)start, (long)c);
michael@0 476 exit(U_INVALID_FORMAT_ERROR);
michael@0 477 }
michael@0 478 uint8_t myTrailCC=getCC(m.char32At(i));
michael@0 479 UChar32 cTrailChar=cNorm.mapping->char32At(cNorm.mapping->length()-1);
michael@0 480 uint8_t cTrailCC=getCC(cTrailChar);
michael@0 481 if(cTrailCC>myTrailCC) {
michael@0 482 fprintf(stderr,
michael@0 483 "gennorm2 error: "
michael@0 484 "U+%04lX's round-trip mapping's starter "
michael@0 485 "U+%04lX decomposes and the "
michael@0 486 "inner/earlier tccc=%hu > outer/following tccc=%hu, "
michael@0 487 "not possible in Unicode normalization\n",
michael@0 488 (long)start, (long)c,
michael@0 489 (short)cTrailCC, (short)myTrailCC);
michael@0 490 exit(U_INVALID_FORMAT_ERROR);
michael@0 491 }
michael@0 492 } else {
michael@0 493 fprintf(stderr,
michael@0 494 "gennorm2 error: "
michael@0 495 "U+%04lX's round-trip mapping's non-starter "
michael@0 496 "U+%04lX decomposes, "
michael@0 497 "not possible in Unicode normalization\n",
michael@0 498 (long)start, (long)c);
michael@0 499 exit(U_INVALID_FORMAT_ERROR);
michael@0 500 }
michael@0 501 }
michael@0 502 if(decomposed==NULL) {
michael@0 503 decomposed=new UnicodeString(m, 0, prev);
michael@0 504 }
michael@0 505 decomposed->append(*cNorm.mapping);
michael@0 506 } else if(Hangul::isHangul(c)) {
michael@0 507 UChar buffer[3];
michael@0 508 int32_t hangulLength=Hangul::decompose(c, buffer);
michael@0 509 if(norm.mappingType==Norm::ROUND_TRIP && prev!=0) {
michael@0 510 fprintf(stderr,
michael@0 511 "gennorm2 error: "
michael@0 512 "U+%04lX's round-trip mapping's non-starter "
michael@0 513 "U+%04lX decomposes, "
michael@0 514 "not possible in Unicode normalization\n",
michael@0 515 (long)start, (long)c);
michael@0 516 exit(U_INVALID_FORMAT_ERROR);
michael@0 517 }
michael@0 518 if(decomposed==NULL) {
michael@0 519 decomposed=new UnicodeString(m, 0, prev);
michael@0 520 }
michael@0 521 decomposed->append(buffer, hangulLength);
michael@0 522 } else if(decomposed!=NULL) {
michael@0 523 decomposed->append(m, prev, i-prev);
michael@0 524 }
michael@0 525 }
michael@0 526 if(decomposed!=NULL) {
michael@0 527 if(norm.rawMapping==NULL) {
michael@0 528 // Remember the original mapping when decomposing recursively.
michael@0 529 norm.rawMapping=norm.mapping;
michael@0 530 } else {
michael@0 531 delete norm.mapping;
michael@0 532 }
michael@0 533 norm.mapping=decomposed;
michael@0 534 // Not norm.setMappingCP(); because the original mapping
michael@0 535 // is most likely to be encodable as a delta.
michael@0 536 return TRUE;
michael@0 537 }
michael@0 538 }
michael@0 539 return FALSE;
michael@0 540 }
michael@0 541
michael@0 542 class BuilderReorderingBuffer {
michael@0 543 public:
michael@0 544 BuilderReorderingBuffer() : fLength(0), fLastStarterIndex(-1), fDidReorder(FALSE) {}
michael@0 545 void reset() {
michael@0 546 fLength=0;
michael@0 547 fLastStarterIndex=-1;
michael@0 548 fDidReorder=FALSE;
michael@0 549 }
michael@0 550 int32_t length() const { return fLength; }
michael@0 551 UBool isEmpty() const { return fLength==0; }
michael@0 552 int32_t lastStarterIndex() const { return fLastStarterIndex; }
michael@0 553 UChar32 charAt(int32_t i) const { return fArray[i]>>8; }
michael@0 554 uint8_t ccAt(int32_t i) const { return (uint8_t)fArray[i]; }
michael@0 555 UBool didReorder() const { return fDidReorder; }
michael@0 556 void append(UChar32 c, uint8_t cc) {
michael@0 557 if(cc==0 || fLength==0 || ccAt(fLength-1)<=cc) {
michael@0 558 if(cc==0) {
michael@0 559 fLastStarterIndex=fLength;
michael@0 560 }
michael@0 561 fArray[fLength++]=(c<<8)|cc;
michael@0 562 return;
michael@0 563 }
michael@0 564 // Let this character bubble back to its canonical order.
michael@0 565 int32_t i=fLength-1;
michael@0 566 while(i>fLastStarterIndex && ccAt(i)>cc) {
michael@0 567 --i;
michael@0 568 }
michael@0 569 ++i; // after the last starter or prevCC<=cc
michael@0 570 // Move this and the following characters forward one to make space.
michael@0 571 for(int32_t j=fLength; i<j; --j) {
michael@0 572 fArray[j]=fArray[j-1];
michael@0 573 }
michael@0 574 fArray[i]=(c<<8)|cc;
michael@0 575 ++fLength;
michael@0 576 fDidReorder=TRUE;
michael@0 577 }
michael@0 578 void toString(UnicodeString &dest) {
michael@0 579 dest.remove();
michael@0 580 for(int32_t i=0; i<fLength; ++i) {
michael@0 581 dest.append(charAt(i));
michael@0 582 }
michael@0 583 }
michael@0 584 void setComposite(UChar32 composite, int32_t combMarkIndex) {
michael@0 585 fArray[fLastStarterIndex]=composite<<8;
michael@0 586 // Remove the combining mark that contributed to the composite.
michael@0 587 --fLength;
michael@0 588 while(combMarkIndex<fLength) {
michael@0 589 fArray[combMarkIndex]=fArray[combMarkIndex+1];
michael@0 590 ++combMarkIndex;
michael@0 591 }
michael@0 592 }
michael@0 593 private:
michael@0 594 int32_t fArray[Normalizer2Impl::MAPPING_LENGTH_MASK];
michael@0 595 int32_t fLength;
michael@0 596 int32_t fLastStarterIndex;
michael@0 597 UBool fDidReorder;
michael@0 598 };
michael@0 599
michael@0 600 void
michael@0 601 Normalizer2DataBuilder::reorder(Norm *p, BuilderReorderingBuffer &buffer) {
michael@0 602 UnicodeString &m=*p->mapping;
michael@0 603 int32_t length=m.length();
michael@0 604 if(length>Normalizer2Impl::MAPPING_LENGTH_MASK) {
michael@0 605 return; // writeMapping() will complain about it and print the code point.
michael@0 606 }
michael@0 607 const UChar *s=m.getBuffer();
michael@0 608 int32_t i=0;
michael@0 609 UChar32 c;
michael@0 610 while(i<length) {
michael@0 611 U16_NEXT(s, i, length, c);
michael@0 612 buffer.append(c, getCC(c));
michael@0 613 }
michael@0 614 if(buffer.didReorder()) {
michael@0 615 buffer.toString(m);
michael@0 616 }
michael@0 617 }
michael@0 618
michael@0 619 /*
michael@0 620 * Computes the flag for the last code branch in Normalizer2Impl::hasCompBoundaryAfter().
michael@0 621 * A starter character with a mapping does not have a composition boundary after it
michael@0 622 * if the character itself combines-forward (which is tested by the caller of this function),
michael@0 623 * or it is deleted (mapped to the empty string),
michael@0 624 * or its mapping contains no starter,
michael@0 625 * or the last starter combines-forward.
michael@0 626 */
michael@0 627 UBool Normalizer2DataBuilder::hasNoCompBoundaryAfter(BuilderReorderingBuffer &buffer) {
michael@0 628 if(buffer.isEmpty()) {
michael@0 629 return TRUE; // maps-to-empty-string is no boundary of any kind
michael@0 630 }
michael@0 631 int32_t lastStarterIndex=buffer.lastStarterIndex();
michael@0 632 if(lastStarterIndex<0) {
michael@0 633 return TRUE; // no starter
michael@0 634 }
michael@0 635 UChar32 starter=buffer.charAt(lastStarterIndex);
michael@0 636 if( Hangul::isJamoL(starter) ||
michael@0 637 (Hangul::isJamoV(starter) &&
michael@0 638 0<lastStarterIndex && Hangul::isJamoL(buffer.charAt(lastStarterIndex-1)))
michael@0 639 ) {
michael@0 640 // A Jamo leading consonant or an LV pair combines-forward if it is at the end,
michael@0 641 // otherwise it is blocked.
michael@0 642 return lastStarterIndex==buffer.length()-1;
michael@0 643 }
michael@0 644 // Note: There can be no Hangul syllable in the fully decomposed mapping.
michael@0 645 const Norm *starterNorm=&getNormRef(starter);
michael@0 646 if(starterNorm->compositions==NULL) {
michael@0 647 return FALSE; // the last starter does not combine forward
michael@0 648 }
michael@0 649 // Compose as far as possible, and see if further compositions are possible.
michael@0 650 uint8_t prevCC=0;
michael@0 651 for(int32_t combMarkIndex=lastStarterIndex+1; combMarkIndex<buffer.length();) {
michael@0 652 uint8_t cc=buffer.ccAt(combMarkIndex); // !=0 because after last starter
michael@0 653 if(combinesWithCCBetween(*starterNorm, prevCC, cc)) {
michael@0 654 return TRUE;
michael@0 655 }
michael@0 656 if( prevCC<cc &&
michael@0 657 (starter=combine(*starterNorm, buffer.charAt(combMarkIndex)))>=0
michael@0 658 ) {
michael@0 659 buffer.setComposite(starter, combMarkIndex);
michael@0 660 starterNorm=&getNormRef(starter);
michael@0 661 if(starterNorm->compositions==NULL) {
michael@0 662 return FALSE; // the composite does not combine further
michael@0 663 }
michael@0 664 } else {
michael@0 665 prevCC=cc;
michael@0 666 ++combMarkIndex;
michael@0 667 }
michael@0 668 }
michael@0 669 // TRUE if the final, forward-combining starter is at the end.
michael@0 670 return prevCC==0;
michael@0 671 }
michael@0 672
michael@0 673 // Requires p->hasMapping().
michael@0 674 // Returns the offset of the "first unit" from the beginning of the extraData for c.
michael@0 675 // That is the same as the length of the optional data for the raw mapping and the ccc/lccc word.
michael@0 676 int32_t Normalizer2DataBuilder::writeMapping(UChar32 c, const Norm *p, UnicodeString &dataString) {
michael@0 677 UnicodeString &m=*p->mapping;
michael@0 678 int32_t length=m.length();
michael@0 679 if(length>Normalizer2Impl::MAPPING_LENGTH_MASK) {
michael@0 680 fprintf(stderr,
michael@0 681 "gennorm2 error: "
michael@0 682 "mapping for U+%04lX longer than maximum of %d\n",
michael@0 683 (long)c, Normalizer2Impl::MAPPING_LENGTH_MASK);
michael@0 684 exit(U_INVALID_FORMAT_ERROR);
michael@0 685 }
michael@0 686 int32_t leadCC, trailCC;
michael@0 687 if(length==0) {
michael@0 688 leadCC=trailCC=0;
michael@0 689 } else {
michael@0 690 leadCC=getCC(m.char32At(0));
michael@0 691 trailCC=getCC(m.char32At(length-1));
michael@0 692 }
michael@0 693 if(c<Normalizer2Impl::MIN_CCC_LCCC_CP && (p->cc!=0 || leadCC!=0)) {
michael@0 694 fprintf(stderr,
michael@0 695 "gennorm2 error: "
michael@0 696 "U+%04lX below U+0300 has ccc!=0 or lccc!=0, not supported by ICU\n",
michael@0 697 (long)c);
michael@0 698 exit(U_INVALID_FORMAT_ERROR);
michael@0 699 }
michael@0 700 // Write small-FCD data.
michael@0 701 if((leadCC|trailCC)!=0) {
michael@0 702 UChar32 lead= c<=0xffff ? c : U16_LEAD(c);
michael@0 703 smallFCD[lead>>8]|=(uint8_t)1<<((lead>>5)&7);
michael@0 704 }
michael@0 705 // Write the mapping & raw mapping extraData.
michael@0 706 int32_t firstUnit=length|(trailCC<<8);
michael@0 707 int32_t preMappingLength=0;
michael@0 708 if(p->rawMapping!=NULL) {
michael@0 709 UnicodeString &rm=*p->rawMapping;
michael@0 710 int32_t rmLength=rm.length();
michael@0 711 if(rmLength>Normalizer2Impl::MAPPING_LENGTH_MASK) {
michael@0 712 fprintf(stderr,
michael@0 713 "gennorm2 error: "
michael@0 714 "raw mapping for U+%04lX longer than maximum of %d\n",
michael@0 715 (long)c, Normalizer2Impl::MAPPING_LENGTH_MASK);
michael@0 716 exit(U_INVALID_FORMAT_ERROR);
michael@0 717 }
michael@0 718 UChar rm0=rm.charAt(0);
michael@0 719 if( rmLength==length-1 &&
michael@0 720 // 99: overlong substring lengths get pinned to remainder lengths anyway
michael@0 721 0==rm.compare(1, 99, m, 2, 99) &&
michael@0 722 rm0>Normalizer2Impl::MAPPING_LENGTH_MASK
michael@0 723 ) {
michael@0 724 // Compression:
michael@0 725 // rawMapping=rm0+mapping.substring(2) -> store only rm0
michael@0 726 //
michael@0 727 // The raw mapping is the same as the final mapping after replacing
michael@0 728 // the final mapping's first two code units with the raw mapping's first one.
michael@0 729 // In this case, we store only that first unit, rm0.
michael@0 730 // This helps with a few hundred mappings.
michael@0 731 dataString.append(rm0);
michael@0 732 preMappingLength=1;
michael@0 733 } else {
michael@0 734 // Store the raw mapping with its length.
michael@0 735 dataString.append(rm);
michael@0 736 dataString.append((UChar)rmLength);
michael@0 737 preMappingLength=rmLength+1;
michael@0 738 }
michael@0 739 firstUnit|=Normalizer2Impl::MAPPING_HAS_RAW_MAPPING;
michael@0 740 }
michael@0 741 int32_t cccLccc=p->cc|(leadCC<<8);
michael@0 742 if(cccLccc!=0) {
michael@0 743 dataString.append((UChar)cccLccc);
michael@0 744 ++preMappingLength;
michael@0 745 firstUnit|=Normalizer2Impl::MAPPING_HAS_CCC_LCCC_WORD;
michael@0 746 }
michael@0 747 if(p->hasNoCompBoundaryAfter) {
michael@0 748 firstUnit|=Normalizer2Impl::MAPPING_NO_COMP_BOUNDARY_AFTER;
michael@0 749 }
michael@0 750 dataString.append((UChar)firstUnit);
michael@0 751 dataString.append(m);
michael@0 752 return preMappingLength;
michael@0 753 }
michael@0 754
michael@0 755 // Requires p->compositions!=NULL.
michael@0 756 void Normalizer2DataBuilder::writeCompositions(UChar32 c, const Norm *p, UnicodeString &dataString) {
michael@0 757 if(p->cc!=0) {
michael@0 758 fprintf(stderr,
michael@0 759 "gennorm2 error: "
michael@0 760 "U+%04lX combines-forward and has ccc!=0, not possible in Unicode normalization\n",
michael@0 761 (long)c);
michael@0 762 exit(U_INVALID_FORMAT_ERROR);
michael@0 763 }
michael@0 764 int32_t length;
michael@0 765 const CompositionPair *pairs=p->getCompositionPairs(length);
michael@0 766 for(int32_t i=0; i<length; ++i) {
michael@0 767 const CompositionPair &pair=pairs[i];
michael@0 768 // 22 bits for the composite character and whether it combines forward.
michael@0 769 UChar32 compositeAndFwd=pair.composite<<1;
michael@0 770 if(getNormRef(pair.composite).compositions!=NULL) {
michael@0 771 compositeAndFwd|=1; // The composite character also combines-forward.
michael@0 772 }
michael@0 773 // Encode most pairs in two units and some in three.
michael@0 774 int32_t firstUnit, secondUnit, thirdUnit;
michael@0 775 if(pair.trail<Normalizer2Impl::COMP_1_TRAIL_LIMIT) {
michael@0 776 if(compositeAndFwd<=0xffff) {
michael@0 777 firstUnit=pair.trail<<1;
michael@0 778 secondUnit=compositeAndFwd;
michael@0 779 thirdUnit=-1;
michael@0 780 } else {
michael@0 781 firstUnit=(pair.trail<<1)|Normalizer2Impl::COMP_1_TRIPLE;
michael@0 782 secondUnit=compositeAndFwd>>16;
michael@0 783 thirdUnit=compositeAndFwd;
michael@0 784 }
michael@0 785 } else {
michael@0 786 firstUnit=(Normalizer2Impl::COMP_1_TRAIL_LIMIT+
michael@0 787 (pair.trail>>Normalizer2Impl::COMP_1_TRAIL_SHIFT))|
michael@0 788 Normalizer2Impl::COMP_1_TRIPLE;
michael@0 789 secondUnit=(pair.trail<<Normalizer2Impl::COMP_2_TRAIL_SHIFT)|
michael@0 790 (compositeAndFwd>>16);
michael@0 791 thirdUnit=compositeAndFwd;
michael@0 792 }
michael@0 793 // Set the high bit of the first unit if this is the last composition pair.
michael@0 794 if(i==(length-1)) {
michael@0 795 firstUnit|=Normalizer2Impl::COMP_1_LAST_TUPLE;
michael@0 796 }
michael@0 797 dataString.append((UChar)firstUnit).append((UChar)secondUnit);
michael@0 798 if(thirdUnit>=0) {
michael@0 799 dataString.append((UChar)thirdUnit);
michael@0 800 }
michael@0 801 }
michael@0 802 }
michael@0 803
michael@0 804 class ExtraDataWriter : public Normalizer2DBEnumerator {
michael@0 805 public:
michael@0 806 ExtraDataWriter(Normalizer2DataBuilder &b) :
michael@0 807 Normalizer2DBEnumerator(b),
michael@0 808 yesYesCompositions(1000, (UChar32)0xffff, 2), // 0=inert, 1=Jamo L, 2=start of compositions
michael@0 809 yesNoMappingsAndCompositions(1000, (UChar32)0, 1) {} // 0=Hangul, 1=start of normal data
michael@0 810 virtual UBool rangeHandler(UChar32 start, UChar32 end, uint32_t value) {
michael@0 811 if(value!=0) {
michael@0 812 if(start!=end) {
michael@0 813 fprintf(stderr,
michael@0 814 "gennorm2 error: unexpected shared data for "
michael@0 815 "multiple code points U+%04lX..U+%04lX\n",
michael@0 816 (long)start, (long)end);
michael@0 817 exit(U_INTERNAL_PROGRAM_ERROR);
michael@0 818 }
michael@0 819 builder.writeExtraData(start, value, *this);
michael@0 820 }
michael@0 821 return TRUE;
michael@0 822 }
michael@0 823 UnicodeString maybeYesCompositions;
michael@0 824 UnicodeString yesYesCompositions;
michael@0 825 UnicodeString yesNoMappingsAndCompositions;
michael@0 826 UnicodeString yesNoMappingsOnly;
michael@0 827 UnicodeString noNoMappings;
michael@0 828 Hashtable previousNoNoMappings; // If constructed in runtime code, pass in UErrorCode.
michael@0 829 };
michael@0 830
michael@0 831 void Normalizer2DataBuilder::writeExtraData(UChar32 c, uint32_t value, ExtraDataWriter &writer) {
michael@0 832 Norm *p=norms+value;
michael@0 833 if(!p->hasMapping()) {
michael@0 834 // Write small-FCD data.
michael@0 835 // There is similar code in writeMapping() for characters that do have a mapping.
michael@0 836 if(c<Normalizer2Impl::MIN_CCC_LCCC_CP && p->cc!=0) {
michael@0 837 fprintf(stderr,
michael@0 838 "gennorm2 error: "
michael@0 839 "U+%04lX below U+0300 has ccc!=0, not supported by ICU\n",
michael@0 840 (long)c);
michael@0 841 exit(U_INVALID_FORMAT_ERROR);
michael@0 842 }
michael@0 843 if(p->cc!=0) {
michael@0 844 UChar32 lead= c<=0xffff ? c : U16_LEAD(c);
michael@0 845 smallFCD[lead>>8]|=(uint8_t)1<<((lead>>5)&7);
michael@0 846 }
michael@0 847 }
michael@0 848 if(p->combinesBack) {
michael@0 849 if(p->hasMapping()) {
michael@0 850 fprintf(stderr,
michael@0 851 "gennorm2 error: "
michael@0 852 "U+%04lX combines-back and decomposes, not possible in Unicode normalization\n",
michael@0 853 (long)c);
michael@0 854 exit(U_INVALID_FORMAT_ERROR);
michael@0 855 }
michael@0 856 if(p->compositions!=NULL) {
michael@0 857 p->offset=
michael@0 858 (writer.maybeYesCompositions.length()<<Norm::OFFSET_SHIFT)|
michael@0 859 Norm::OFFSET_MAYBE_YES;
michael@0 860 writeCompositions(c, p, writer.maybeYesCompositions);
michael@0 861 }
michael@0 862 } else if(!p->hasMapping()) {
michael@0 863 if(p->compositions!=NULL) {
michael@0 864 p->offset=
michael@0 865 (writer.yesYesCompositions.length()<<Norm::OFFSET_SHIFT)|
michael@0 866 Norm::OFFSET_YES_YES;
michael@0 867 writeCompositions(c, p, writer.yesYesCompositions);
michael@0 868 }
michael@0 869 } else if(p->mappingType==Norm::ROUND_TRIP) {
michael@0 870 if(p->compositions!=NULL) {
michael@0 871 int32_t offset=writer.yesNoMappingsAndCompositions.length()+
michael@0 872 writeMapping(c, p, writer.yesNoMappingsAndCompositions);
michael@0 873 p->offset=(offset<<Norm::OFFSET_SHIFT)|Norm::OFFSET_YES_NO_MAPPING_AND_COMPOSITION;
michael@0 874 writeCompositions(c, p, writer.yesNoMappingsAndCompositions);
michael@0 875 } else {
michael@0 876 int32_t offset=writer.yesNoMappingsOnly.length()+
michael@0 877 writeMapping(c, p, writer.yesNoMappingsOnly);
michael@0 878 p->offset=(offset<<Norm::OFFSET_SHIFT)|Norm::OFFSET_YES_NO_MAPPING_ONLY;
michael@0 879 }
michael@0 880 } else /* one-way */ {
michael@0 881 if(p->compositions!=NULL) {
michael@0 882 fprintf(stderr,
michael@0 883 "gennorm2 error: "
michael@0 884 "U+%04lX combines-forward and has a one-way mapping, "
michael@0 885 "not possible in Unicode normalization\n",
michael@0 886 (long)c);
michael@0 887 exit(U_INVALID_FORMAT_ERROR);
michael@0 888 }
michael@0 889 if(p->cc==0 && optimization!=OPTIMIZE_FAST) {
michael@0 890 // Try a compact, algorithmic encoding.
michael@0 891 // Only for ccc=0, because we can't store additional information
michael@0 892 // and we do not recursively follow an algorithmic encoding for access to the ccc.
michael@0 893 //
michael@0 894 // Also, if hasNoCompBoundaryAfter is set, we can only use the algorithmic encoding
michael@0 895 // if the mappingCP decomposes further, to ensure that there is a place to store it.
michael@0 896 // We want to see that the final mapping does not have exactly 1 code point,
michael@0 897 // or else we would have to recursively ensure that the final mapping is stored
michael@0 898 // in normal extraData.
michael@0 899 if(p->mappingCP>=0 && (!p->hasNoCompBoundaryAfter || 1!=p->mapping->countChar32())) {
michael@0 900 int32_t delta=p->mappingCP-c;
michael@0 901 if(-Normalizer2Impl::MAX_DELTA<=delta && delta<=Normalizer2Impl::MAX_DELTA) {
michael@0 902 p->offset=(delta<<Norm::OFFSET_SHIFT)|Norm::OFFSET_DELTA;
michael@0 903 }
michael@0 904 }
michael@0 905 }
michael@0 906 if(p->offset==0) {
michael@0 907 int32_t oldNoNoLength=writer.noNoMappings.length();
michael@0 908 int32_t offset=oldNoNoLength+writeMapping(c, p, writer.noNoMappings);
michael@0 909 UnicodeString newMapping=writer.noNoMappings.tempSubString(oldNoNoLength);
michael@0 910 int32_t previousOffset=writer.previousNoNoMappings.geti(newMapping);
michael@0 911 if(previousOffset!=0) {
michael@0 912 // Duplicate, remove the new units and point to the old ones.
michael@0 913 writer.noNoMappings.truncate(oldNoNoLength);
michael@0 914 p->offset=((previousOffset-1)<<Norm::OFFSET_SHIFT)|Norm::OFFSET_NO_NO;
michael@0 915 } else {
michael@0 916 // Enter this new mapping into the hashtable, avoiding value 0 which is "not found".
michael@0 917 IcuToolErrorCode errorCode("gennorm2/writeExtraData()/Hashtable.puti()");
michael@0 918 writer.previousNoNoMappings.puti(newMapping, offset+1, errorCode);
michael@0 919 p->offset=(offset<<Norm::OFFSET_SHIFT)|Norm::OFFSET_NO_NO;
michael@0 920 }
michael@0 921 }
michael@0 922 }
michael@0 923 }
michael@0 924
michael@0 925 class Norm16Writer : public Normalizer2DBEnumerator {
michael@0 926 public:
michael@0 927 Norm16Writer(Normalizer2DataBuilder &b) : Normalizer2DBEnumerator(b) {}
michael@0 928 virtual UBool rangeHandler(UChar32 start, UChar32 end, uint32_t value) {
michael@0 929 builder.writeNorm16(start, end, value);
michael@0 930 return TRUE;
michael@0 931 }
michael@0 932 };
michael@0 933
michael@0 934 void Normalizer2DataBuilder::writeNorm16(UChar32 start, UChar32 end, uint32_t value) {
michael@0 935 if(value!=0) {
michael@0 936 const Norm *p=norms+value;
michael@0 937 int32_t offset=p->offset>>Norm::OFFSET_SHIFT;
michael@0 938 int32_t norm16=0;
michael@0 939 UBool isDecompNo=FALSE;
michael@0 940 UBool isCompNoMaybe=FALSE;
michael@0 941 switch(p->offset&Norm::OFFSET_MASK) {
michael@0 942 case Norm::OFFSET_NONE:
michael@0 943 // No mapping, no compositions list.
michael@0 944 if(p->combinesBack) {
michael@0 945 norm16=Normalizer2Impl::MIN_NORMAL_MAYBE_YES+p->cc;
michael@0 946 isDecompNo=(UBool)(p->cc!=0);
michael@0 947 isCompNoMaybe=TRUE;
michael@0 948 } else if(p->cc!=0) {
michael@0 949 norm16=Normalizer2Impl::MIN_YES_YES_WITH_CC-1+p->cc;
michael@0 950 isDecompNo=isCompNoMaybe=TRUE;
michael@0 951 }
michael@0 952 break;
michael@0 953 case Norm::OFFSET_MAYBE_YES:
michael@0 954 norm16=indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]+offset;
michael@0 955 isCompNoMaybe=TRUE;
michael@0 956 break;
michael@0 957 case Norm::OFFSET_YES_YES:
michael@0 958 norm16=offset;
michael@0 959 break;
michael@0 960 case Norm::OFFSET_YES_NO_MAPPING_AND_COMPOSITION:
michael@0 961 norm16=indexes[Normalizer2Impl::IX_MIN_YES_NO]+offset;
michael@0 962 isDecompNo=TRUE;
michael@0 963 break;
michael@0 964 case Norm::OFFSET_YES_NO_MAPPING_ONLY:
michael@0 965 norm16=indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]+offset;
michael@0 966 isDecompNo=TRUE;
michael@0 967 break;
michael@0 968 case Norm::OFFSET_NO_NO:
michael@0 969 norm16=indexes[Normalizer2Impl::IX_MIN_NO_NO]+offset;
michael@0 970 isDecompNo=isCompNoMaybe=TRUE;
michael@0 971 break;
michael@0 972 case Norm::OFFSET_DELTA:
michael@0 973 norm16=getCenterNoNoDelta()+offset;
michael@0 974 isDecompNo=isCompNoMaybe=TRUE;
michael@0 975 break;
michael@0 976 default: // Should not occur.
michael@0 977 exit(U_INTERNAL_PROGRAM_ERROR);
michael@0 978 }
michael@0 979 IcuToolErrorCode errorCode("gennorm2/writeNorm16()");
michael@0 980 utrie2_setRange32(norm16Trie, start, end, (uint32_t)norm16, TRUE, errorCode);
michael@0 981 if(isDecompNo && start<indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]) {
michael@0 982 indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=start;
michael@0 983 }
michael@0 984 if(isCompNoMaybe && start<indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]) {
michael@0 985 indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=start;
michael@0 986 }
michael@0 987 }
michael@0 988 }
michael@0 989
michael@0 990 void Normalizer2DataBuilder::setHangulData() {
michael@0 991 HangulIterator hi;
michael@0 992 const HangulIterator::Range *range;
michael@0 993 // Check that none of the Hangul/Jamo code points have data.
michael@0 994 while((range=hi.nextRange())!=NULL) {
michael@0 995 for(UChar32 c=range->start; c<range->limit; ++c) {
michael@0 996 if(utrie2_get32(norm16Trie, c)!=0) {
michael@0 997 fprintf(stderr,
michael@0 998 "gennorm2 error: "
michael@0 999 "illegal mapping/composition/ccc data for Hangul or Jamo U+%04lX\n",
michael@0 1000 (long)c);
michael@0 1001 exit(U_INVALID_FORMAT_ERROR);
michael@0 1002 }
michael@0 1003 }
michael@0 1004 }
michael@0 1005 // Set data for algorithmic runtime handling.
michael@0 1006 IcuToolErrorCode errorCode("gennorm2/setHangulData()");
michael@0 1007 hi.reset();
michael@0 1008 while((range=hi.nextRange())!=NULL) {
michael@0 1009 uint16_t norm16=range->norm16;
michael@0 1010 if(norm16==0) {
michael@0 1011 norm16=(uint16_t)indexes[Normalizer2Impl::IX_MIN_YES_NO]; // Hangul LV/LVT encoded as minYesNo
michael@0 1012 if(range->start<indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]) {
michael@0 1013 indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=range->start;
michael@0 1014 }
michael@0 1015 } else {
michael@0 1016 if(range->start<indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]) { // Jamo V/T are maybeYes
michael@0 1017 indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=range->start;
michael@0 1018 }
michael@0 1019 }
michael@0 1020 utrie2_setRange32(norm16Trie, range->start, range->limit-1, norm16, TRUE, errorCode);
michael@0 1021 errorCode.assertSuccess();
michael@0 1022 }
michael@0 1023 }
michael@0 1024
michael@0 1025 U_CDECL_BEGIN
michael@0 1026
michael@0 1027 static UBool U_CALLCONV
michael@0 1028 enumRangeMaxValue(const void *context, UChar32 /*start*/, UChar32 /*end*/, uint32_t value) {
michael@0 1029 uint32_t *pMaxValue=(uint32_t *)context;
michael@0 1030 if(value>*pMaxValue) {
michael@0 1031 *pMaxValue=value;
michael@0 1032 }
michael@0 1033 return TRUE;
michael@0 1034 }
michael@0 1035
michael@0 1036 U_CDECL_END
michael@0 1037
michael@0 1038 void Normalizer2DataBuilder::processData() {
michael@0 1039 IcuToolErrorCode errorCode("gennorm2/processData()");
michael@0 1040 norm16Trie=utrie2_open(0, 0, errorCode);
michael@0 1041 errorCode.assertSuccess();
michael@0 1042
michael@0 1043 utrie2_enum(normTrie, NULL, enumRangeHandler, CompositionBuilder(*this).ptr());
michael@0 1044
michael@0 1045 Decomposer decomposer(*this);
michael@0 1046 do {
michael@0 1047 decomposer.didDecompose=FALSE;
michael@0 1048 utrie2_enum(normTrie, NULL, enumRangeHandler, &decomposer);
michael@0 1049 } while(decomposer.didDecompose);
michael@0 1050
michael@0 1051 BuilderReorderingBuffer buffer;
michael@0 1052 int32_t normsLength=utm_countItems(normMem);
michael@0 1053 for(int32_t i=1; i<normsLength; ++i) {
michael@0 1054 // Set the hasNoCompBoundaryAfter flag for use by the last code branch
michael@0 1055 // in Normalizer2Impl::hasCompBoundaryAfter().
michael@0 1056 // For details see the comments on hasNoCompBoundaryAfter(buffer).
michael@0 1057 const Norm &norm=norms[i];
michael@0 1058 if(norm.hasMapping()) {
michael@0 1059 if(norm.compositions!=NULL) {
michael@0 1060 norms[i].hasNoCompBoundaryAfter=TRUE;
michael@0 1061 } else {
michael@0 1062 buffer.reset();
michael@0 1063 reorder(norms+i, buffer);
michael@0 1064 norms[i].hasNoCompBoundaryAfter=hasNoCompBoundaryAfter(buffer);
michael@0 1065 }
michael@0 1066 }
michael@0 1067 }
michael@0 1068
michael@0 1069 indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=0x110000;
michael@0 1070 indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=0x110000;
michael@0 1071
michael@0 1072 ExtraDataWriter extraDataWriter(*this);
michael@0 1073 utrie2_enum(normTrie, NULL, enumRangeHandler, &extraDataWriter);
michael@0 1074
michael@0 1075 extraData=extraDataWriter.maybeYesCompositions;
michael@0 1076 extraData.append(extraDataWriter.yesYesCompositions).
michael@0 1077 append(extraDataWriter.yesNoMappingsAndCompositions).
michael@0 1078 append(extraDataWriter.yesNoMappingsOnly).
michael@0 1079 append(extraDataWriter.noNoMappings);
michael@0 1080 // Pad to even length for 4-byte alignment of following data.
michael@0 1081 if(extraData.length()&1) {
michael@0 1082 extraData.append((UChar)0);
michael@0 1083 }
michael@0 1084
michael@0 1085 indexes[Normalizer2Impl::IX_MIN_YES_NO]=
michael@0 1086 extraDataWriter.yesYesCompositions.length();
michael@0 1087 indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]=
michael@0 1088 indexes[Normalizer2Impl::IX_MIN_YES_NO]+
michael@0 1089 extraDataWriter.yesNoMappingsAndCompositions.length();
michael@0 1090 indexes[Normalizer2Impl::IX_MIN_NO_NO]=
michael@0 1091 indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]+
michael@0 1092 extraDataWriter.yesNoMappingsOnly.length();
michael@0 1093 indexes[Normalizer2Impl::IX_LIMIT_NO_NO]=
michael@0 1094 indexes[Normalizer2Impl::IX_MIN_NO_NO]+
michael@0 1095 extraDataWriter.noNoMappings.length();
michael@0 1096 indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]=
michael@0 1097 Normalizer2Impl::MIN_NORMAL_MAYBE_YES-
michael@0 1098 extraDataWriter.maybeYesCompositions.length();
michael@0 1099
michael@0 1100 int32_t minNoNoDelta=getCenterNoNoDelta()-Normalizer2Impl::MAX_DELTA;
michael@0 1101 if(indexes[Normalizer2Impl::IX_LIMIT_NO_NO]>minNoNoDelta) {
michael@0 1102 fprintf(stderr,
michael@0 1103 "gennorm2 error: "
michael@0 1104 "data structure overflow, too much mapping composition data\n");
michael@0 1105 exit(U_BUFFER_OVERFLOW_ERROR);
michael@0 1106 }
michael@0 1107
michael@0 1108 utrie2_enum(normTrie, NULL, enumRangeHandler, Norm16Writer(*this).ptr());
michael@0 1109
michael@0 1110 setHangulData();
michael@0 1111
michael@0 1112 // Look for the "worst" norm16 value of any supplementary code point
michael@0 1113 // corresponding to a lead surrogate, and set it as that surrogate's value.
michael@0 1114 // Enables quick check inner loops to look at only code units.
michael@0 1115 //
michael@0 1116 // We could be more sophisticated:
michael@0 1117 // We could collect a bit set for whether there are values in the different
michael@0 1118 // norm16 ranges (yesNo, maybeYes, yesYesWithCC etc.)
michael@0 1119 // and select the best value that only breaks the composition and/or decomposition
michael@0 1120 // inner loops if necessary.
michael@0 1121 // However, that seems like overkill for an optimization for supplementary characters.
michael@0 1122 for(UChar lead=0xd800; lead<0xdc00; ++lead) {
michael@0 1123 uint32_t maxValue=utrie2_get32(norm16Trie, lead);
michael@0 1124 utrie2_enumForLeadSurrogate(norm16Trie, lead, NULL, enumRangeMaxValue, &maxValue);
michael@0 1125 if( maxValue>=(uint32_t)indexes[Normalizer2Impl::IX_LIMIT_NO_NO] &&
michael@0 1126 maxValue>(uint32_t)indexes[Normalizer2Impl::IX_MIN_NO_NO]
michael@0 1127 ) {
michael@0 1128 // Set noNo ("worst" value) if it got into "less-bad" maybeYes or ccc!=0.
michael@0 1129 // Otherwise it might end up at something like JAMO_VT which stays in
michael@0 1130 // the inner decomposition quick check loop.
michael@0 1131 maxValue=(uint32_t)indexes[Normalizer2Impl::IX_LIMIT_NO_NO]-1;
michael@0 1132 }
michael@0 1133 utrie2_set32ForLeadSurrogateCodeUnit(norm16Trie, lead, maxValue, errorCode);
michael@0 1134 }
michael@0 1135
michael@0 1136 // Adjust supplementary minimum code points to break quick check loops at their lead surrogates.
michael@0 1137 // For an empty data file, minCP=0x110000 turns into 0xdc00 (first trail surrogate)
michael@0 1138 // which is harmless.
michael@0 1139 // As a result, the minimum code points are always BMP code points.
michael@0 1140 int32_t minCP=indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP];
michael@0 1141 if(minCP>=0x10000) {
michael@0 1142 indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=U16_LEAD(minCP);
michael@0 1143 }
michael@0 1144 minCP=indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP];
michael@0 1145 if(minCP>=0x10000) {
michael@0 1146 indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=U16_LEAD(minCP);
michael@0 1147 }
michael@0 1148 }
michael@0 1149
michael@0 1150 void Normalizer2DataBuilder::writeBinaryFile(const char *filename) {
michael@0 1151 processData();
michael@0 1152
michael@0 1153 IcuToolErrorCode errorCode("gennorm2/writeBinaryFile()");
michael@0 1154 utrie2_freeze(norm16Trie, UTRIE2_16_VALUE_BITS, errorCode);
michael@0 1155 int32_t norm16TrieLength=utrie2_serialize(norm16Trie, NULL, 0, errorCode);
michael@0 1156 if(errorCode.get()!=U_BUFFER_OVERFLOW_ERROR) {
michael@0 1157 fprintf(stderr, "gennorm2 error: unable to freeze/serialize the normalization trie - %s\n",
michael@0 1158 errorCode.errorName());
michael@0 1159 exit(errorCode.reset());
michael@0 1160 }
michael@0 1161 errorCode.reset();
michael@0 1162 LocalArray<uint8_t> norm16TrieBytes(new uint8_t[norm16TrieLength]);
michael@0 1163 utrie2_serialize(norm16Trie, norm16TrieBytes.getAlias(), norm16TrieLength, errorCode);
michael@0 1164 errorCode.assertSuccess();
michael@0 1165
michael@0 1166 int32_t offset=(int32_t)sizeof(indexes);
michael@0 1167 indexes[Normalizer2Impl::IX_NORM_TRIE_OFFSET]=offset;
michael@0 1168 offset+=norm16TrieLength;
michael@0 1169 indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET]=offset;
michael@0 1170 offset+=extraData.length()*2;
michael@0 1171 indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET]=offset;
michael@0 1172 offset+=sizeof(smallFCD);
michael@0 1173 int32_t totalSize=offset;
michael@0 1174 for(int32_t i=Normalizer2Impl::IX_RESERVED3_OFFSET; i<=Normalizer2Impl::IX_TOTAL_SIZE; ++i) {
michael@0 1175 indexes[i]=totalSize;
michael@0 1176 }
michael@0 1177
michael@0 1178 if(beVerbose) {
michael@0 1179 printf("size of normalization trie: %5ld bytes\n", (long)norm16TrieLength);
michael@0 1180 printf("size of 16-bit extra data: %5ld uint16_t\n", (long)extraData.length());
michael@0 1181 printf("size of small-FCD data: %5ld bytes\n", (long)sizeof(smallFCD));
michael@0 1182 printf("size of binary data file contents: %5ld bytes\n", (long)totalSize);
michael@0 1183 printf("minDecompNoCodePoint: U+%04lX\n", (long)indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]);
michael@0 1184 printf("minCompNoMaybeCodePoint: U+%04lX\n", (long)indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]);
michael@0 1185 printf("minYesNo: 0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_YES_NO]);
michael@0 1186 printf("minYesNoMappingsOnly: 0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]);
michael@0 1187 printf("minNoNo: 0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_NO_NO]);
michael@0 1188 printf("limitNoNo: 0x%04x\n", (int)indexes[Normalizer2Impl::IX_LIMIT_NO_NO]);
michael@0 1189 printf("minMaybeYes: 0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]);
michael@0 1190 }
michael@0 1191
michael@0 1192 UVersionInfo nullVersion={ 0, 0, 0, 0 };
michael@0 1193 if(0==memcmp(nullVersion, unicodeVersion, 4)) {
michael@0 1194 u_versionFromString(unicodeVersion, U_UNICODE_VERSION);
michael@0 1195 }
michael@0 1196 memcpy(dataInfo.dataVersion, unicodeVersion, 4);
michael@0 1197 UNewDataMemory *pData=
michael@0 1198 udata_create(NULL, NULL, filename, &dataInfo,
michael@0 1199 haveCopyright ? U_COPYRIGHT_STRING : NULL, errorCode);
michael@0 1200 if(errorCode.isFailure()) {
michael@0 1201 fprintf(stderr, "gennorm2 error: unable to create the output file %s - %s\n",
michael@0 1202 filename, errorCode.errorName());
michael@0 1203 exit(errorCode.reset());
michael@0 1204 }
michael@0 1205 udata_writeBlock(pData, indexes, sizeof(indexes));
michael@0 1206 udata_writeBlock(pData, norm16TrieBytes.getAlias(), norm16TrieLength);
michael@0 1207 udata_writeUString(pData, extraData.getBuffer(), extraData.length());
michael@0 1208 udata_writeBlock(pData, smallFCD, sizeof(smallFCD));
michael@0 1209 int32_t writtenSize=udata_finish(pData, errorCode);
michael@0 1210 if(errorCode.isFailure()) {
michael@0 1211 fprintf(stderr, "gennorm2: error %s writing the output file\n", errorCode.errorName());
michael@0 1212 exit(errorCode.reset());
michael@0 1213 }
michael@0 1214 if(writtenSize!=totalSize) {
michael@0 1215 fprintf(stderr, "gennorm2 error: written size %ld != calculated size %ld\n",
michael@0 1216 (long)writtenSize, (long)totalSize);
michael@0 1217 exit(U_INTERNAL_PROGRAM_ERROR);
michael@0 1218 }
michael@0 1219 }
michael@0 1220
michael@0 1221 U_NAMESPACE_END
michael@0 1222
michael@0 1223 #endif /* #if !UCONFIG_NO_NORMALIZATION */
michael@0 1224
michael@0 1225 /*
michael@0 1226 * Hey, Emacs, please set the following:
michael@0 1227 *
michael@0 1228 * Local Variables:
michael@0 1229 * indent-tabs-mode: nil
michael@0 1230 * End:
michael@0 1231 */

mercurial