1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4*******************************************************************************
5*
6*   Copyright (C) 2009-2016, International Business Machines
7*   Corporation and others.  All Rights Reserved.
8*
9*******************************************************************************
10*   file name:  n2builder.cpp
11*   encoding:   UTF-8
12*   tab size:   8 (not used)
13*   indentation:4
14*
15*   created on: 2009nov25
16*   created by: Markus W. Scherer
17*
18* Builds Normalizer2 data and writes a binary .nrm file.
19* For the file format see source/common/normalizer2impl.h.
20*/
21
22#include "unicode/utypes.h"
23#include "n2builder.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <vector>
29#include "unicode/errorcode.h"
30#include "unicode/localpointer.h"
31#include "unicode/putil.h"
32#include "unicode/udata.h"
33#include "unicode/uniset.h"
34#include "unicode/unistr.h"
35#include "unicode/usetiter.h"
36#include "unicode/ustring.h"
37#include "charstr.h"
38#include "extradata.h"
39#include "hash.h"
40#include "normalizer2impl.h"
41#include "norms.h"
42#include "toolutil.h"
43#include "unewdata.h"
44#include "utrie2.h"
45#include "uvectr32.h"
46#include "writesrc.h"
47
48#if !UCONFIG_NO_NORMALIZATION
49
50/* UDataInfo cf. udata.h */
51static UDataInfo dataInfo={
52    sizeof(UDataInfo),
53    0,
54
55    U_IS_BIG_ENDIAN,
56    U_CHARSET_FAMILY,
57    U_SIZEOF_UCHAR,
58    0,
59
60    { 0x4e, 0x72, 0x6d, 0x32 }, /* dataFormat="Nrm2" */
61    { 3, 0, 0, 0 },             /* formatVersion */
62    { 10, 0, 0, 0 }             /* dataVersion (Unicode version) */
63};
64
65U_NAMESPACE_BEGIN
66
67class HangulIterator {
68public:
69    struct Range {
70        UChar32 start, end;
71    };
72
73    HangulIterator() : rangeIndex(0) {}
74    const Range *nextRange() {
75        if(rangeIndex<UPRV_LENGTHOF(ranges)) {
76            return ranges+rangeIndex++;
77        } else {
78            return NULL;
79        }
80    }
81private:
82    static const Range ranges[4];
83    int32_t rangeIndex;
84};
85
86const HangulIterator::Range HangulIterator::ranges[4]={
87    { Hangul::JAMO_L_BASE, Hangul::JAMO_L_END },
88    { Hangul::JAMO_V_BASE, Hangul::JAMO_V_END },
89    // JAMO_T_BASE+1: not U+11A7
90    { Hangul::JAMO_T_BASE+1, Hangul::JAMO_T_END },
91    { Hangul::HANGUL_BASE, Hangul::HANGUL_END },
92};
93
94Normalizer2DataBuilder::Normalizer2DataBuilder(UErrorCode &errorCode) :
95        norms(errorCode),
96        phase(0), overrideHandling(OVERRIDE_PREVIOUS), optimization(OPTIMIZE_NORMAL),
97        norm16Trie(nullptr), norm16TrieLength(0) {
98    memset(unicodeVersion, 0, sizeof(unicodeVersion));
99    memset(indexes, 0, sizeof(indexes));
100    memset(smallFCD, 0, sizeof(smallFCD));
101}
102
103Normalizer2DataBuilder::~Normalizer2DataBuilder() {
104    utrie2_close(norm16Trie);
105}
106
107void
108Normalizer2DataBuilder::setUnicodeVersion(const char *v) {
109    UVersionInfo nullVersion={ 0, 0, 0, 0 };
110    UVersionInfo version;
111    u_versionFromString(version, v);
112    if( 0!=memcmp(version, unicodeVersion, U_MAX_VERSION_LENGTH) &&
113        0!=memcmp(nullVersion, unicodeVersion, U_MAX_VERSION_LENGTH)
114    ) {
115        char buffer[U_MAX_VERSION_STRING_LENGTH];
116        u_versionToString(unicodeVersion, buffer);
117        fprintf(stderr, "gennorm2 error: multiple inconsistent Unicode version numbers %s vs. %s\n",
118                buffer, v);
119        exit(U_ILLEGAL_ARGUMENT_ERROR);
120    }
121    memcpy(unicodeVersion, version, U_MAX_VERSION_LENGTH);
122}
123
124Norm *Normalizer2DataBuilder::checkNormForMapping(Norm *p, UChar32 c) {
125    if(p!=NULL) {
126        if(p->mappingType!=Norm::NONE) {
127            if( overrideHandling==OVERRIDE_NONE ||
128                (overrideHandling==OVERRIDE_PREVIOUS && p->mappingPhase==phase)
129            ) {
130                fprintf(stderr,
131                        "error in gennorm2 phase %d: "
132                        "not permitted to override mapping for U+%04lX from phase %d\n",
133                        (int)phase, (long)c, (int)p->mappingPhase);
134                exit(U_INVALID_FORMAT_ERROR);
135            }
136            delete p->mapping;
137            p->mapping=NULL;
138        }
139        p->mappingPhase=phase;
140    }
141    return p;
142}
143
144void Normalizer2DataBuilder::setOverrideHandling(OverrideHandling oh) {
145    overrideHandling=oh;
146    ++phase;
147}
148
149void Normalizer2DataBuilder::setCC(UChar32 c, uint8_t cc) {
150    norms.createNorm(c)->cc=cc;
151    norms.ccSet.add(c);
152}
153
154static UBool isWellFormed(const UnicodeString &s) {
155    UErrorCode errorCode=U_ZERO_ERROR;
156    u_strToUTF8(NULL, 0, NULL, toUCharPtr(s.getBuffer()), s.length(), &errorCode);
157    return U_SUCCESS(errorCode) || errorCode==U_BUFFER_OVERFLOW_ERROR;
158}
159
160void Normalizer2DataBuilder::setOneWayMapping(UChar32 c, const UnicodeString &m) {
161    if(!isWellFormed(m)) {
162        fprintf(stderr,
163                "error in gennorm2 phase %d: "
164                "illegal one-way mapping from U+%04lX to malformed string\n",
165                (int)phase, (long)c);
166        exit(U_INVALID_FORMAT_ERROR);
167    }
168    Norm *p=checkNormForMapping(norms.createNorm(c), c);
169    p->mapping=new UnicodeString(m);
170    p->mappingType=Norm::ONE_WAY;
171    p->setMappingCP();
172    norms.mappingSet.add(c);
173}
174
175void Normalizer2DataBuilder::setRoundTripMapping(UChar32 c, const UnicodeString &m) {
176    if(U_IS_SURROGATE(c)) {
177        fprintf(stderr,
178                "error in gennorm2 phase %d: "
179                "illegal round-trip mapping from surrogate code point U+%04lX\n",
180                (int)phase, (long)c);
181        exit(U_INVALID_FORMAT_ERROR);
182    }
183    if(!isWellFormed(m)) {
184        fprintf(stderr,
185                "error in gennorm2 phase %d: "
186                "illegal round-trip mapping from U+%04lX to malformed string\n",
187                (int)phase, (long)c);
188        exit(U_INVALID_FORMAT_ERROR);
189    }
190    int32_t numCP=u_countChar32(toUCharPtr(m.getBuffer()), m.length());
191    if(numCP!=2) {
192        fprintf(stderr,
193                "error in gennorm2 phase %d: "
194                "illegal round-trip mapping from U+%04lX to %d!=2 code points\n",
195                (int)phase, (long)c, (int)numCP);
196        exit(U_INVALID_FORMAT_ERROR);
197    }
198    Norm *p=checkNormForMapping(norms.createNorm(c), c);
199    p->mapping=new UnicodeString(m);
200    p->mappingType=Norm::ROUND_TRIP;
201    p->mappingCP=U_SENTINEL;
202    norms.mappingSet.add(c);
203}
204
205void Normalizer2DataBuilder::removeMapping(UChar32 c) {
206    // createNorm(c), not getNorm(c), to record a non-mapping and detect conflicting data.
207    Norm *p=checkNormForMapping(norms.createNorm(c), c);
208    p->mappingType=Norm::REMOVED;
209    norms.mappingSet.add(c);
210}
211
212UBool Normalizer2DataBuilder::mappingHasCompBoundaryAfter(const BuilderReorderingBuffer &buffer,
213                                                          Norm::MappingType mappingType) const {
214    if(buffer.isEmpty()) {
215        return FALSE;  // Maps-to-empty-string is no boundary of any kind.
216    }
217    int32_t lastStarterIndex=buffer.lastStarterIndex();
218    if(lastStarterIndex<0) {
219        return FALSE;  // no starter
220    }
221    const int32_t lastIndex=buffer.length()-1;
222    if(mappingType==Norm::ONE_WAY && lastStarterIndex<lastIndex && buffer.ccAt(lastIndex)>1) {
223        // One-way mapping where after the last starter is at least one combining mark
224        // with a combining class greater than 1,
225        // which means that another combining mark can reorder before it.
226        // By contrast, in a round-trip mapping this does not prevent a boundary as long as
227        // the starter or composite does not combine-forward with a following combining mark.
228        return FALSE;
229    }
230    UChar32 starter=buffer.charAt(lastStarterIndex);
231    if(lastStarterIndex==0 && norms.combinesBack(starter)) {
232        // The last starter is at the beginning of the mapping and combines backward.
233        return FALSE;
234    }
235    if(Hangul::isJamoL(starter) ||
236            (Hangul::isJamoV(starter) &&
237            0<lastStarterIndex && Hangul::isJamoL(buffer.charAt(lastStarterIndex-1)))) {
238        // A Jamo leading consonant or an LV pair combines-forward if it is at the end,
239        // otherwise it is blocked.
240        return lastStarterIndex!=lastIndex;
241    }
242    // Note: There can be no Hangul syllable in the fully decomposed mapping.
243
244    // Multiple starters can combine into one.
245    // Look for the first of the last sequence of starters, excluding Jamos.
246    int32_t i=lastStarterIndex;
247    UChar32 c;
248    while(0<i && buffer.ccAt(i-1)==0 && !Hangul::isJamo(c=buffer.charAt(i-1))) {
249        starter=c;
250        --i;
251    }
252    // Compose as far as possible, and see if further compositions with
253    // characters following this mapping are possible.
254    const Norm *starterNorm=norms.getNorm(starter);
255    if(i==lastStarterIndex &&
256            (starterNorm==nullptr || starterNorm->compositions==nullptr)) {
257        return TRUE;  // The last starter does not combine forward.
258    }
259    uint8_t prevCC=0;
260    while(++i<buffer.length()) {
261        uint8_t cc=buffer.ccAt(i);  // !=0 if after last starter
262        if(i>lastStarterIndex && norms.combinesWithCCBetween(*starterNorm, prevCC, cc)) {
263            // The starter combines with a mark that reorders before the current one.
264            return FALSE;
265        }
266        UChar32 c=buffer.charAt(i);
267        if(starterNorm!=nullptr && (prevCC<cc || prevCC==0) &&
268                norms.getNormRef(c).combinesBack && (starter=starterNorm->combine(c))>=0) {
269            // The starter combines with c into a composite replacement starter.
270            starterNorm=norms.getNorm(starter);
271            if(i>=lastStarterIndex &&
272                    (starterNorm==nullptr || starterNorm->compositions==nullptr)) {
273                return TRUE;  // The composite does not combine further.
274            }
275            // Keep prevCC because we "removed" the combining mark.
276        } else if(cc==0) {
277            starterNorm=norms.getNorm(c);
278            if(i==lastStarterIndex &&
279                    (starterNorm==nullptr || starterNorm->compositions==nullptr)) {
280                return TRUE;  // The new starter does not combine forward.
281            }
282            prevCC=0;
283        } else {
284            prevCC=cc;
285        }
286    }
287    if(prevCC==0) {
288        return FALSE;  // forward-combining starter at the very end
289    }
290    if(norms.combinesWithCCBetween(*starterNorm, prevCC, 256)) {
291        // The starter combines with another mark.
292        return FALSE;
293    }
294    return TRUE;
295}
296
297UBool Normalizer2DataBuilder::mappingRecomposes(const BuilderReorderingBuffer &buffer) const {
298    if(buffer.lastStarterIndex()<0) {
299        return FALSE;  // no starter
300    }
301    const Norm *starterNorm=nullptr;
302    uint8_t prevCC=0;
303    for(int32_t i=0; i<buffer.length(); ++i) {
304        UChar32 c=buffer.charAt(i);
305        uint8_t cc=buffer.ccAt(i);
306        if(starterNorm!=nullptr && (prevCC<cc || prevCC==0) &&
307                norms.getNormRef(c).combinesBack && starterNorm->combine(c)>=0) {
308            return TRUE;  // normal composite
309        } else if(cc==0) {
310            if(Hangul::isJamoL(c)) {
311                if((i+1)<buffer.length() && Hangul::isJamoV(buffer.charAt(i+1))) {
312                    return TRUE;  // Hangul syllable
313                }
314                starterNorm=nullptr;
315            } else {
316                starterNorm=norms.getNorm(c);
317            }
318        }
319        prevCC=cc;
320    }
321    return FALSE;
322}
323
324void Normalizer2DataBuilder::postProcess(Norm &norm) {
325    // Prerequisites: Compositions are built, mappings are recursively decomposed.
326    // Mappings are not yet in canonical order.
327    //
328    // This function works on a Norm struct. We do not know which code point(s) map(s) to it.
329    // Therefore, we cannot compute algorithmic mapping deltas here.
330    // Error conditions are checked, but printed later when we do know the offending code point.
331    if(norm.hasMapping()) {
332        if(norm.mapping->length()>Normalizer2Impl::MAPPING_LENGTH_MASK) {
333            norm.error="mapping longer than maximum of 31";
334            return;
335        }
336        // Ensure canonical order.
337        BuilderReorderingBuffer buffer;
338        if(norm.rawMapping!=nullptr) {
339            norms.reorder(*norm.rawMapping, buffer);
340            buffer.reset();
341        }
342        norms.reorder(*norm.mapping, buffer);
343        if(buffer.isEmpty()) {
344            // A character that is deleted (maps to an empty string) must
345            // get the worst-case lccc and tccc values because arbitrary
346            // characters on both sides will become adjacent.
347            norm.leadCC=1;
348            norm.trailCC=0xff;
349        } else {
350            norm.leadCC=buffer.ccAt(0);
351            norm.trailCC=buffer.ccAt(buffer.length()-1);
352        }
353
354        norm.hasCompBoundaryBefore=
355            !buffer.isEmpty() && norm.leadCC==0 && !norms.combinesBack(buffer.charAt(0));
356        norm.hasCompBoundaryAfter=
357            norm.compositions==nullptr && mappingHasCompBoundaryAfter(buffer, norm.mappingType);
358
359        if(norm.combinesBack) {
360            norm.error="combines-back and decomposes, not possible in Unicode normalization";
361        } else if(norm.mappingType==Norm::ROUND_TRIP) {
362            if(norm.compositions!=NULL) {
363                norm.type=Norm::YES_NO_COMBINES_FWD;
364            } else {
365                norm.type=Norm::YES_NO_MAPPING_ONLY;
366            }
367        } else {  // one-way mapping
368            if(norm.compositions!=NULL) {
369                norm.error="combines-forward and has a one-way mapping, "
370                           "not possible in Unicode normalization";
371            } else if(buffer.isEmpty()) {
372                norm.type=Norm::NO_NO_EMPTY;
373            } else if(!norm.hasCompBoundaryBefore) {
374                norm.type=Norm::NO_NO_COMP_NO_MAYBE_CC;
375            } else if(mappingRecomposes(buffer)) {
376                norm.type=Norm::NO_NO_COMP_BOUNDARY_BEFORE;
377            } else {
378                // The mapping is comp-normalized.
379                norm.type=Norm::NO_NO_COMP_YES;
380            }
381        }
382    } else {  // no mapping
383        norm.leadCC=norm.trailCC=norm.cc;
384
385        norm.hasCompBoundaryBefore=
386            norm.cc==0 && !norm.combinesBack;
387        norm.hasCompBoundaryAfter=
388            norm.cc==0 && !norm.combinesBack && norm.compositions==nullptr;
389
390        if(norm.combinesBack) {
391            if(norm.compositions!=nullptr) {
392                // Earlier code checked ccc=0.
393                norm.type=Norm::MAYBE_YES_COMBINES_FWD;
394            } else {
395                norm.type=Norm::MAYBE_YES_SIMPLE;  // any ccc
396            }
397        } else if(norm.compositions!=nullptr) {
398            // Earlier code checked ccc=0.
399            norm.type=Norm::YES_YES_COMBINES_FWD;
400        } else if(norm.cc!=0) {
401            norm.type=Norm::YES_YES_WITH_CC;
402        } else {
403            norm.type=Norm::INERT;
404        }
405    }
406}
407
408class Norm16Writer : public Norms::Enumerator {
409public:
410    Norm16Writer(Norms &n, Normalizer2DataBuilder &b) : Norms::Enumerator(n), builder(b) {}
411    void rangeHandler(UChar32 start, UChar32 end, Norm &norm) U_OVERRIDE {
412        builder.writeNorm16(start, end, norm);
413    }
414    Normalizer2DataBuilder &builder;
415};
416
417void Normalizer2DataBuilder::setSmallFCD(UChar32 c) {
418    UChar32 lead= c<=0xffff ? c : U16_LEAD(c);
419    smallFCD[lead>>8]|=(uint8_t)1<<((lead>>5)&7);
420}
421
422void Normalizer2DataBuilder::writeNorm16(UChar32 start, UChar32 end, Norm &norm) {
423    if((norm.leadCC|norm.trailCC)!=0) {
424        for(UChar32 c=start; c<=end; ++c) {
425            setSmallFCD(c);
426        }
427    }
428
429    int32_t norm16;
430    switch(norm.type) {
431    case Norm::INERT:
432        norm16=Normalizer2Impl::INERT;
433        break;
434    case Norm::YES_YES_COMBINES_FWD:
435        norm16=norm.offset*2;
436        break;
437    case Norm::YES_NO_COMBINES_FWD:
438        norm16=indexes[Normalizer2Impl::IX_MIN_YES_NO]+norm.offset*2;
439        break;
440    case Norm::YES_NO_MAPPING_ONLY:
441        norm16=indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]+norm.offset*2;
442        break;
443    case Norm::NO_NO_COMP_YES:
444        norm16=indexes[Normalizer2Impl::IX_MIN_NO_NO]+norm.offset*2;
445        break;
446    case Norm::NO_NO_COMP_BOUNDARY_BEFORE:
447        norm16=indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]+norm.offset*2;
448        break;
449    case Norm::NO_NO_COMP_NO_MAYBE_CC:
450        norm16=indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_NO_MAYBE_CC]+norm.offset*2;
451        break;
452    case Norm::NO_NO_EMPTY:
453        norm16=indexes[Normalizer2Impl::IX_MIN_NO_NO_EMPTY]+norm.offset*2;
454        break;
455    case Norm::NO_NO_DELTA:
456        {
457            // Positive offset from minNoNoDelta, shifted left for additional bits.
458            int32_t offset=(norm.offset+Normalizer2Impl::MAX_DELTA)<<Normalizer2Impl::DELTA_SHIFT;
459            if(norm.trailCC==0) {
460                // DELTA_TCCC_0==0
461            } else if(norm.trailCC==1) {
462                offset|=Normalizer2Impl::DELTA_TCCC_1;
463            } else {
464                offset|=Normalizer2Impl::DELTA_TCCC_GT_1;
465            }
466            norm16=getMinNoNoDelta()+offset;
467            break;
468        }
469    case Norm::MAYBE_YES_COMBINES_FWD:
470        norm16=indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]+norm.offset*2;
471        break;
472    case Norm::MAYBE_YES_SIMPLE:
473        norm16=Normalizer2Impl::MIN_NORMAL_MAYBE_YES+norm.cc*2;  // ccc=0..255
474        break;
475    case Norm::YES_YES_WITH_CC:
476        U_ASSERT(norm.cc!=0);
477        norm16=Normalizer2Impl::MIN_YES_YES_WITH_CC-2+norm.cc*2;  // ccc=1..255
478        break;
479    default:  // Should not occur.
480        exit(U_INTERNAL_PROGRAM_ERROR);
481    }
482    U_ASSERT((norm16&1)==0);
483    if(norm.hasCompBoundaryAfter) {
484        norm16|=Normalizer2Impl::HAS_COMP_BOUNDARY_AFTER;
485    }
486    IcuToolErrorCode errorCode("gennorm2/writeNorm16()");
487    utrie2_setRange32(norm16Trie, start, end, (uint32_t)norm16, TRUE, errorCode);
488
489    // Set the minimum code points for real data lookups in the quick check loops.
490    UBool isDecompNo=
491            (Norm::YES_NO_COMBINES_FWD<=norm.type && norm.type<=Norm::NO_NO_DELTA) ||
492            norm.cc!=0;
493    if(isDecompNo && start<indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]) {
494        indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=start;
495    }
496    UBool isCompNoMaybe= norm.type>=Norm::NO_NO_COMP_YES;
497    if(isCompNoMaybe && start<indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]) {
498        indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=start;
499    }
500    if(norm.leadCC!=0 && start<indexes[Normalizer2Impl::IX_MIN_LCCC_CP]) {
501        indexes[Normalizer2Impl::IX_MIN_LCCC_CP]=start;
502    }
503}
504
505void Normalizer2DataBuilder::setHangulData() {
506    HangulIterator hi;
507    const HangulIterator::Range *range;
508    // Check that none of the Hangul/Jamo code points have data.
509    while((range=hi.nextRange())!=NULL) {
510        for(UChar32 c=range->start; c<=range->end; ++c) {
511            if(utrie2_get32(norm16Trie, c)>Normalizer2Impl::INERT) {
512                fprintf(stderr,
513                        "gennorm2 error: "
514                        "illegal mapping/composition/ccc data for Hangul or Jamo U+%04lX\n",
515                        (long)c);
516                exit(U_INVALID_FORMAT_ERROR);
517            }
518        }
519    }
520    // Set data for algorithmic runtime handling.
521    IcuToolErrorCode errorCode("gennorm2/setHangulData()");
522
523    // Jamo V/T are maybeYes
524    if(Hangul::JAMO_V_BASE<indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]) {
525        indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=Hangul::JAMO_V_BASE;
526    }
527    utrie2_setRange32(norm16Trie, Hangul::JAMO_L_BASE, Hangul::JAMO_L_END,
528                      Normalizer2Impl::JAMO_L, TRUE, errorCode);
529    utrie2_setRange32(norm16Trie, Hangul::JAMO_V_BASE, Hangul::JAMO_V_END,
530                      Normalizer2Impl::JAMO_VT, TRUE, errorCode);
531    // JAMO_T_BASE+1: not U+11A7
532    utrie2_setRange32(norm16Trie, Hangul::JAMO_T_BASE+1, Hangul::JAMO_T_END,
533                      Normalizer2Impl::JAMO_VT, TRUE, errorCode);
534
535    // Hangul LV encoded as minYesNo
536    uint32_t lv=indexes[Normalizer2Impl::IX_MIN_YES_NO];
537    // Hangul LVT encoded as minYesNoMappingsOnly|HAS_COMP_BOUNDARY_AFTER
538    uint32_t lvt=indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]|
539        Normalizer2Impl::HAS_COMP_BOUNDARY_AFTER;
540    if(Hangul::HANGUL_BASE<indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]) {
541        indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=Hangul::HANGUL_BASE;
542    }
543    // Set the first LV, then write all other Hangul syllables as LVT,
544    // then overwrite the remaining LV.
545    // The UTrie2 should be able to compact this into 7 32-item blocks
546    // because JAMO_T_COUNT is 28 and the UTrie2 granularity is 4.
547    // (7*32=8*28 smallest common multiple)
548    utrie2_set32(norm16Trie, Hangul::HANGUL_BASE, lv, errorCode);
549    utrie2_setRange32(norm16Trie, Hangul::HANGUL_BASE+1, Hangul::HANGUL_END,
550                      lvt, TRUE, errorCode);
551    UChar32 c=Hangul::HANGUL_BASE;
552    while((c+=Hangul::JAMO_T_COUNT)<=Hangul::HANGUL_END) {
553        utrie2_set32(norm16Trie, c, lv, errorCode);
554    }
555    errorCode.assertSuccess();
556}
557
558namespace {
559
560struct Norm16Summary {
561    uint32_t maxNorm16;
562    // ANDing values yields 0 bits where any value has a 0.
563    // Used for worst-case HAS_COMP_BOUNDARY_AFTER.
564    uint32_t andedNorm16;
565};
566
567}  // namespace
568
569U_CDECL_BEGIN
570
571static UBool U_CALLCONV
572enumRangeMaxValue(const void *context, UChar32 /*start*/, UChar32 /*end*/, uint32_t value) {
573    Norm16Summary *p=(Norm16Summary *)context;
574    if(value>p->maxNorm16) {
575        p->maxNorm16=value;
576    }
577    p->andedNorm16&=value;
578    return TRUE;
579}
580
581U_CDECL_END
582
583void Normalizer2DataBuilder::processData() {
584    IcuToolErrorCode errorCode("gennorm2/processData()");
585    norm16Trie=utrie2_open(Normalizer2Impl::INERT, Normalizer2Impl::INERT, errorCode);
586    errorCode.assertSuccess();
587
588    // Build composition lists before recursive decomposition,
589    // so that we still have the raw, pair-wise mappings.
590    CompositionBuilder compBuilder(norms);
591    norms.enumRanges(compBuilder);
592
593    // Recursively decompose all mappings.
594    Decomposer decomposer(norms);
595    do {
596        decomposer.didDecompose=FALSE;
597        norms.enumRanges(decomposer);
598    } while(decomposer.didDecompose);
599
600    // Set the Norm::Type and other properties.
601    int32_t normsLength=norms.length();
602    for(int32_t i=1; i<normsLength; ++i) {
603        postProcess(norms.getNormRefByIndex(i));
604    }
605
606    // Write the properties, mappings and composition lists to
607    // appropriate parts of the "extra data" array.
608    ExtraData extra(norms, optimization==OPTIMIZE_FAST);
609    norms.enumRanges(extra);
610
611    extraData=extra.yesYesCompositions;
612    indexes[Normalizer2Impl::IX_MIN_YES_NO]=extraData.length()*2;
613    extraData.append(extra.yesNoMappingsAndCompositions);
614    indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]=extraData.length()*2;
615    extraData.append(extra.yesNoMappingsOnly);
616    indexes[Normalizer2Impl::IX_MIN_NO_NO]=extraData.length()*2;
617    extraData.append(extra.noNoMappingsCompYes);
618    indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]=extraData.length()*2;
619    extraData.append(extra.noNoMappingsCompBoundaryBefore);
620    indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_NO_MAYBE_CC]=extraData.length()*2;
621    extraData.append(extra.noNoMappingsCompNoMaybeCC);
622    indexes[Normalizer2Impl::IX_MIN_NO_NO_EMPTY]=extraData.length()*2;
623    extraData.append(extra.noNoMappingsEmpty);
624    indexes[Normalizer2Impl::IX_LIMIT_NO_NO]=extraData.length()*2;
625
626    // Pad the maybeYesCompositions length to a multiple of 4,
627    // so that NO_NO_DELTA bits 2..1 can be used without subtracting the center.
628    while(extra.maybeYesCompositions.length()&3) {
629        extra.maybeYesCompositions.append((UChar)0);
630    }
631    extraData.insert(0, extra.maybeYesCompositions);
632    indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]=
633        Normalizer2Impl::MIN_NORMAL_MAYBE_YES-
634        extra.maybeYesCompositions.length()*2;
635
636    // Pad to even length for 4-byte alignment of following data.
637    if(extraData.length()&1) {
638        extraData.append((UChar)0);
639    }
640
641    int32_t minNoNoDelta=getMinNoNoDelta();
642    U_ASSERT((minNoNoDelta&7)==0);
643    if(indexes[Normalizer2Impl::IX_LIMIT_NO_NO]>minNoNoDelta) {
644        fprintf(stderr,
645                "gennorm2 error: "
646                "data structure overflow, too much mapping composition data\n");
647        exit(U_BUFFER_OVERFLOW_ERROR);
648    }
649
650    // writeNorm16() and setHangulData() reduce these as needed.
651    indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=0x110000;
652    indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=0x110000;
653    indexes[Normalizer2Impl::IX_MIN_LCCC_CP]=0x110000;
654
655    // Map each code point to its norm16 value,
656    // including the properties that fit directly,
657    // and the offset to the "extra data" if necessary.
658    Norm16Writer norm16Writer(norms, *this);
659    norms.enumRanges(norm16Writer);
660
661    setHangulData();
662
663    // Look for the "worst" norm16 value of any supplementary code point
664    // corresponding to a lead surrogate, and set it as that surrogate's value.
665    // Enables UTF-16 quick check inner loops to look at only code units.
666    //
667    // We could be more sophisticated:
668    // We could collect a bit set for whether there are values in the different
669    // norm16 ranges (yesNo, maybeYes, yesYesWithCC etc.)
670    // and select the best value that only breaks the composition and/or decomposition
671    // inner loops if necessary.
672    // However, that seems like overkill for an optimization for supplementary characters.
673    for(UChar lead=0xd800; lead<0xdc00; ++lead) {
674        uint32_t surrogateCPNorm16=utrie2_get32(norm16Trie, lead);
675        Norm16Summary summary={ surrogateCPNorm16, surrogateCPNorm16 };
676        utrie2_enumForLeadSurrogate(norm16Trie, lead, NULL, enumRangeMaxValue, &summary);
677        uint32_t norm16=summary.maxNorm16;
678        if(norm16>=(uint32_t)indexes[Normalizer2Impl::IX_LIMIT_NO_NO] &&
679                norm16>(uint32_t)indexes[Normalizer2Impl::IX_MIN_NO_NO]) {
680            // Set noNo ("worst" value) if it got into "less-bad" maybeYes or ccc!=0.
681            // Otherwise it might end up at something like JAMO_VT which stays in
682            // the inner decomposition quick check loop.
683            norm16=(uint32_t)indexes[Normalizer2Impl::IX_LIMIT_NO_NO]-1;
684        }
685        norm16=
686            (norm16&~Normalizer2Impl::HAS_COMP_BOUNDARY_AFTER)|
687            (summary.andedNorm16&Normalizer2Impl::HAS_COMP_BOUNDARY_AFTER);
688        utrie2_set32ForLeadSurrogateCodeUnit(norm16Trie, lead, norm16, errorCode);
689    }
690
691    // Adjust supplementary minimum code points to break quick check loops at their lead surrogates.
692    // For an empty data file, minCP=0x110000 turns into 0xdc00 (first trail surrogate)
693    // which is harmless.
694    // As a result, the minimum code points are always BMP code points.
695    int32_t minCP=indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP];
696    if(minCP>=0x10000) {
697        indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]=U16_LEAD(minCP);
698    }
699    minCP=indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP];
700    if(minCP>=0x10000) {
701        indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]=U16_LEAD(minCP);
702    }
703    minCP=indexes[Normalizer2Impl::IX_MIN_LCCC_CP];
704    if(minCP>=0x10000) {
705        indexes[Normalizer2Impl::IX_MIN_LCCC_CP]=U16_LEAD(minCP);
706    }
707
708    utrie2_freeze(norm16Trie, UTRIE2_16_VALUE_BITS, errorCode);
709    norm16TrieLength=utrie2_serialize(norm16Trie, NULL, 0, errorCode);
710    if(errorCode.get()!=U_BUFFER_OVERFLOW_ERROR) {
711        fprintf(stderr, "gennorm2 error: unable to freeze/serialize the normalization trie - %s\n",
712                errorCode.errorName());
713        exit(errorCode.reset());
714    }
715    errorCode.reset();
716
717    int32_t offset=(int32_t)sizeof(indexes);
718    indexes[Normalizer2Impl::IX_NORM_TRIE_OFFSET]=offset;
719    offset+=norm16TrieLength;
720    indexes[Normalizer2Impl::IX_EXTRA_DATA_OFFSET]=offset;
721    offset+=extraData.length()*2;
722    indexes[Normalizer2Impl::IX_SMALL_FCD_OFFSET]=offset;
723    offset+=sizeof(smallFCD);
724    int32_t totalSize=offset;
725    for(int32_t i=Normalizer2Impl::IX_RESERVED3_OFFSET; i<=Normalizer2Impl::IX_TOTAL_SIZE; ++i) {
726        indexes[i]=totalSize;
727    }
728
729    if(beVerbose) {
730        printf("size of normalization trie:         %5ld bytes\n", (long)norm16TrieLength);
731        printf("size of 16-bit extra data:          %5ld uint16_t\n", (long)extraData.length());
732        printf("size of small-FCD data:             %5ld bytes\n", (long)sizeof(smallFCD));
733        printf("size of binary data file contents:  %5ld bytes\n", (long)totalSize);
734        printf("minDecompNoCodePoint:              U+%04lX\n", (long)indexes[Normalizer2Impl::IX_MIN_DECOMP_NO_CP]);
735        printf("minCompNoMaybeCodePoint:           U+%04lX\n", (long)indexes[Normalizer2Impl::IX_MIN_COMP_NO_MAYBE_CP]);
736        printf("minLcccCodePoint:                  U+%04lX\n", (long)indexes[Normalizer2Impl::IX_MIN_LCCC_CP]);
737        printf("minYesNo: (with compositions)      0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_YES_NO]);
738        printf("minYesNoMappingsOnly:              0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_YES_NO_MAPPINGS_ONLY]);
739        printf("minNoNo: (comp-normalized)         0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_NO_NO]);
740        printf("minNoNoCompBoundaryBefore:         0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_BOUNDARY_BEFORE]);
741        printf("minNoNoCompNoMaybeCC:              0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_NO_NO_COMP_NO_MAYBE_CC]);
742        printf("minNoNoEmpty:                      0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_NO_NO_EMPTY]);
743        printf("limitNoNo:                         0x%04x\n", (int)indexes[Normalizer2Impl::IX_LIMIT_NO_NO]);
744        printf("minNoNoDelta:                      0x%04x\n", (int)minNoNoDelta);
745        printf("minMaybeYes:                       0x%04x\n", (int)indexes[Normalizer2Impl::IX_MIN_MAYBE_YES]);
746    }
747
748    UVersionInfo nullVersion={ 0, 0, 0, 0 };
749    if(0==memcmp(nullVersion, unicodeVersion, 4)) {
750        u_versionFromString(unicodeVersion, U_UNICODE_VERSION);
751    }
752    memcpy(dataInfo.dataVersion, unicodeVersion, 4);
753}
754
755void Normalizer2DataBuilder::writeBinaryFile(const char *filename) {
756    processData();
757
758    IcuToolErrorCode errorCode("gennorm2/writeBinaryFile()");
759    LocalArray<uint8_t> norm16TrieBytes(new uint8_t[norm16TrieLength]);
760    utrie2_serialize(norm16Trie, norm16TrieBytes.getAlias(), norm16TrieLength, errorCode);
761    errorCode.assertSuccess();
762
763    UNewDataMemory *pData=
764        udata_create(NULL, NULL, filename, &dataInfo,
765                     haveCopyright ? U_COPYRIGHT_STRING : NULL, errorCode);
766    if(errorCode.isFailure()) {
767        fprintf(stderr, "gennorm2 error: unable to create the output file %s - %s\n",
768                filename, errorCode.errorName());
769        exit(errorCode.reset());
770    }
771    udata_writeBlock(pData, indexes, sizeof(indexes));
772    udata_writeBlock(pData, norm16TrieBytes.getAlias(), norm16TrieLength);
773    udata_writeUString(pData, toUCharPtr(extraData.getBuffer()), extraData.length());
774    udata_writeBlock(pData, smallFCD, sizeof(smallFCD));
775    int32_t writtenSize=udata_finish(pData, errorCode);
776    if(errorCode.isFailure()) {
777        fprintf(stderr, "gennorm2: error %s writing the output file\n", errorCode.errorName());
778        exit(errorCode.reset());
779    }
780    int32_t totalSize=indexes[Normalizer2Impl::IX_TOTAL_SIZE];
781    if(writtenSize!=totalSize) {
782        fprintf(stderr, "gennorm2 error: written size %ld != calculated size %ld\n",
783            (long)writtenSize, (long)totalSize);
784        exit(U_INTERNAL_PROGRAM_ERROR);
785    }
786}
787
788void
789Normalizer2DataBuilder::writeCSourceFile(const char *filename) {
790    processData();
791
792    IcuToolErrorCode errorCode("gennorm2/writeCSourceFile()");
793    const char *basename=findBasename(filename);
794    CharString path(filename, (int32_t)(basename-filename), errorCode);
795    CharString dataName(basename, errorCode);
796    const char *extension=strrchr(basename, '.');
797    if(extension!=NULL) {
798        dataName.truncate((int32_t)(extension-basename));
799    }
800    errorCode.assertSuccess();
801
802    LocalArray<uint8_t> norm16TrieBytes(new uint8_t[norm16TrieLength]);
803    utrie2_serialize(norm16Trie, norm16TrieBytes.getAlias(), norm16TrieLength, errorCode);
804    errorCode.assertSuccess();
805
806    FILE *f=usrc_create(path.data(), basename, "icu/source/tools/gennorm2/n2builder.cpp");
807    if(f==NULL) {
808        fprintf(stderr, "gennorm2/writeCSourceFile() error: unable to create the output file %s\n",
809                filename);
810        exit(U_FILE_ACCESS_ERROR);
811        return;
812    }
813    fputs("#ifdef INCLUDED_FROM_NORMALIZER2_CPP\n\n", f);
814    char line[100];
815    sprintf(line, "static const UVersionInfo %s_formatVersion={", dataName.data());
816    usrc_writeArray(f, line, dataInfo.formatVersion, 8, 4, "};\n");
817    sprintf(line, "static const UVersionInfo %s_dataVersion={", dataName.data());
818    usrc_writeArray(f, line, dataInfo.dataVersion, 8, 4, "};\n\n");
819    sprintf(line, "static const int32_t %s_indexes[Normalizer2Impl::IX_COUNT]={\n",
820            dataName.data());
821    usrc_writeArray(f,
822        line,
823        indexes, 32, Normalizer2Impl::IX_COUNT,
824        "\n};\n\n");
825    sprintf(line, "static const uint16_t %s_trieIndex[%%ld]={\n", dataName.data());
826    usrc_writeUTrie2Arrays(f,
827        line, NULL,
828        norm16Trie,
829        "\n};\n\n");
830    sprintf(line, "static const uint16_t %s_extraData[%%ld]={\n", dataName.data());
831    usrc_writeArray(f,
832        line,
833        extraData.getBuffer(), 16, extraData.length(),
834        "\n};\n\n");
835    sprintf(line, "static const uint8_t %s_smallFCD[%%ld]={\n", dataName.data());
836    usrc_writeArray(f,
837        line,
838        smallFCD, 8, sizeof(smallFCD),
839        "\n};\n\n");
840    sprintf(line, "static const UTrie2 %s_trie={\n", dataName.data());
841    char line2[100];
842    sprintf(line2, "%s_trieIndex", dataName.data());
843    usrc_writeUTrie2Struct(f,
844        line,
845        norm16Trie, line2, NULL,
846        "};\n");
847    fputs("\n#endif  // INCLUDED_FROM_NORMALIZER2_CPP\n", f);
848    fclose(f);
849}
850
851namespace {
852
853bool equalStrings(const UnicodeString *s1, const UnicodeString *s2) {
854    if(s1 == nullptr) {
855        return s2 == nullptr;
856    } else if(s2 == nullptr) {
857        return false;
858    } else {
859        return *s1 == *s2;
860    }
861}
862
863const char *typeChars = "?-=>";
864
865void writeMapping(FILE *f, const UnicodeString *m) {
866    if(m != nullptr && !m->isEmpty()) {
867        int32_t i = 0;
868        UChar32 c = m->char32At(i);
869        fprintf(f, "%04lX", (long)c);
870        while((i += U16_LENGTH(c)) < m->length()) {
871            c = m->char32At(i);
872            fprintf(f, " %04lX", (long)c);
873        }
874    }
875    fputs("\n", f);
876}
877
878}  // namespace
879
880void
881Normalizer2DataBuilder::writeDataFile(const char *filename, bool writeRemoved) const {
882    // Do not processData() before writing the input-syntax data file.
883    FILE *f = fopen(filename, "w");
884    if(f == nullptr) {
885        fprintf(stderr, "gennorm2/writeDataFile() error: unable to create the output file %s\n",
886                filename);
887        exit(U_FILE_ACCESS_ERROR);
888        return;
889    }
890
891    if(unicodeVersion[0] != 0 || unicodeVersion[1] != 0 ||
892            unicodeVersion[2] != 0 || unicodeVersion[3] != 0) {
893        char uv[U_MAX_VERSION_STRING_LENGTH];
894        u_versionToString(unicodeVersion, uv);
895        fprintf(f, "* Unicode %s\n\n", uv);
896    }
897
898    UnicodeSetIterator ccIter(norms.ccSet);
899    UChar32 start = U_SENTINEL;
900    UChar32 end = U_SENTINEL;
901    uint8_t prevCC = 0;
902    bool done = false;
903    bool didWrite = false;
904    do {
905        UChar32 c;
906        uint8_t cc;
907        if(ccIter.next() && !ccIter.isString()) {
908            c = ccIter.getCodepoint();
909            cc = norms.getCC(c);
910        } else {
911            c = 0x110000;
912            cc = 0;
913            done = true;
914        }
915        if(cc == prevCC && c == (end + 1)) {
916            end = c;
917        } else {
918            if(prevCC != 0) {
919                if(start == end) {
920                    fprintf(f, "%04lX:%d\n", (long)start, (int)prevCC);
921                } else {
922                    fprintf(f, "%04lX..%04lX:%d\n", (long)start, (long)end, (int)prevCC);
923                }
924                didWrite = true;
925            }
926            start = end = c;
927            prevCC = cc;
928        }
929    } while(!done);
930    if(didWrite) {
931        fputs("\n", f);
932    }
933
934    UnicodeSetIterator mIter(norms.mappingSet);
935    start = U_SENTINEL;
936    end = U_SENTINEL;
937    const UnicodeString *prevMapping = nullptr;
938    Norm::MappingType prevType = Norm::NONE;
939    done = false;
940    do {
941        UChar32 c;
942        const Norm *norm;
943        if(mIter.next() && !mIter.isString()) {
944            c = mIter.getCodepoint();
945            norm = norms.getNorm(c);
946        } else {
947            c = 0x110000;
948            norm = nullptr;
949            done = true;
950        }
951        const UnicodeString *mapping;
952        Norm::MappingType type;
953        if(norm == nullptr) {
954            mapping = nullptr;
955            type = Norm::NONE;
956        } else {
957            type = norm->mappingType;
958            if(type == Norm::NONE) {
959                mapping = nullptr;
960            } else {
961                mapping = norm->mapping;
962            }
963        }
964        if(type == prevType && equalStrings(mapping, prevMapping) && c == (end + 1)) {
965            end = c;
966        } else {
967            if(writeRemoved ? prevType != Norm::NONE : prevType > Norm::REMOVED) {
968                if(start == end) {
969                    fprintf(f, "%04lX%c", (long)start, typeChars[prevType]);
970                } else {
971                    fprintf(f, "%04lX..%04lX%c", (long)start, (long)end, typeChars[prevType]);
972                }
973                writeMapping(f, prevMapping);
974            }
975            start = end = c;
976            prevMapping = mapping;
977            prevType = type;
978        }
979    } while(!done);
980
981    fclose(f);
982}
983
984void
985Normalizer2DataBuilder::computeDiff(const Normalizer2DataBuilder &b1,
986                                    const Normalizer2DataBuilder &b2,
987                                    Normalizer2DataBuilder &diff) {
988    // Compute diff = b1 - b2
989    // so that we should be able to get b1 = b2 + diff.
990    if(0 != memcmp(b1.unicodeVersion, b2.unicodeVersion, U_MAX_VERSION_LENGTH)) {
991        memcpy(diff.unicodeVersion, b1.unicodeVersion, U_MAX_VERSION_LENGTH);
992    }
993
994    UnicodeSet ccSet(b1.norms.ccSet);
995    ccSet.addAll(b2.norms.ccSet);
996    UnicodeSetIterator ccIter(ccSet);
997    while(ccIter.next() && !ccIter.isString()) {
998        UChar32 c = ccIter.getCodepoint();
999        uint8_t cc1 = b1.norms.getCC(c);
1000        uint8_t cc2 = b2.norms.getCC(c);
1001        if(cc1 != cc2) {
1002            diff.setCC(c, cc1);
1003        }
1004    }
1005
1006    UnicodeSet mSet(b1.norms.mappingSet);
1007    mSet.addAll(b2.norms.mappingSet);
1008    UnicodeSetIterator mIter(mSet);
1009    while(mIter.next() && !mIter.isString()) {
1010        UChar32 c = mIter.getCodepoint();
1011        const Norm *norm1 = b1.norms.getNorm(c);
1012        const Norm *norm2 = b2.norms.getNorm(c);
1013        const UnicodeString *mapping1;
1014        Norm::MappingType type1;
1015        if(norm1 == nullptr || !norm1->hasMapping()) {
1016            mapping1 = nullptr;
1017            type1 = Norm::NONE;
1018        } else {
1019            mapping1 = norm1->mapping;
1020            type1 = norm1->mappingType;
1021        }
1022        const UnicodeString *mapping2;
1023        Norm::MappingType type2;
1024        if(norm2 == nullptr || !norm2->hasMapping()) {
1025            mapping2 = nullptr;
1026            type2 = Norm::NONE;
1027        } else {
1028            mapping2 = norm2->mapping;
1029            type2 = norm2->mappingType;
1030        }
1031        if(type1 == type2 && equalStrings(mapping1, mapping2)) {
1032            // Nothing to do.
1033        } else if(type1 == Norm::NONE) {
1034            diff.removeMapping(c);
1035        } else if(type1 == Norm::ROUND_TRIP) {
1036            diff.setRoundTripMapping(c, *mapping1);
1037        } else if(type1 == Norm::ONE_WAY) {
1038            diff.setOneWayMapping(c, *mapping1);
1039        }
1040    }
1041}
1042
1043U_NAMESPACE_END
1044
1045#endif /* #if !UCONFIG_NO_NORMALIZATION */
1046
1047/*
1048 * Hey, Emacs, please set the following:
1049 *
1050 * Local Variables:
1051 * indent-tabs-mode: nil
1052 * End:
1053 */
1054