1 /*
2 *******************************************************************************
3 *
4 * Copyright (C) 2009-2012, International Business Machines
5 * Corporation and others. All Rights Reserved.
6 *
7 *******************************************************************************
8 * file name: normalizer2.cpp
9 * encoding: US-ASCII
10 * tab size: 8 (not used)
11 * indentation:4
12 *
13 * created on: 2009nov22
14 * created by: Markus W. Scherer
15 */
16
17 #include "unicode/utypes.h"
18
19 #if !UCONFIG_NO_NORMALIZATION
20
21 #include "unicode/localpointer.h"
22 #include "unicode/normalizer2.h"
23 #include "unicode/unistr.h"
24 #include "unicode/unorm.h"
25 #include "cpputils.h"
26 #include "cstring.h"
27 #include "mutex.h"
28 #include "normalizer2impl.h"
29 #include "ucln_cmn.h"
30 #include "uhash.h"
31
32 U_NAMESPACE_BEGIN
33
34 // Public API dispatch via Normalizer2 subclasses -------------------------- ***
35
~Normalizer2()36 Normalizer2::~Normalizer2() {}
37
38 UBool
getRawDecomposition(UChar32,UnicodeString &) const39 Normalizer2::getRawDecomposition(UChar32, UnicodeString &) const {
40 return FALSE;
41 }
42
43 UChar32
composePair(UChar32,UChar32) const44 Normalizer2::composePair(UChar32, UChar32) const {
45 return U_SENTINEL;
46 }
47
48 uint8_t
getCombiningClass(UChar32) const49 Normalizer2::getCombiningClass(UChar32 /*c*/) const {
50 return 0;
51 }
52
53 UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(Normalizer2)
54
55 // Normalizer2 implementation for the old UNORM_NONE.
56 class NoopNormalizer2 : public Normalizer2 {
57 virtual ~NoopNormalizer2();
58
59 virtual UnicodeString &
normalize(const UnicodeString & src,UnicodeString & dest,UErrorCode & errorCode) const60 normalize(const UnicodeString &src,
61 UnicodeString &dest,
62 UErrorCode &errorCode) const {
63 if(U_SUCCESS(errorCode)) {
64 if(&dest!=&src) {
65 dest=src;
66 } else {
67 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
68 }
69 }
70 return dest;
71 }
72 virtual UnicodeString &
normalizeSecondAndAppend(UnicodeString & first,const UnicodeString & second,UErrorCode & errorCode) const73 normalizeSecondAndAppend(UnicodeString &first,
74 const UnicodeString &second,
75 UErrorCode &errorCode) const {
76 if(U_SUCCESS(errorCode)) {
77 if(&first!=&second) {
78 first.append(second);
79 } else {
80 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
81 }
82 }
83 return first;
84 }
85 virtual UnicodeString &
append(UnicodeString & first,const UnicodeString & second,UErrorCode & errorCode) const86 append(UnicodeString &first,
87 const UnicodeString &second,
88 UErrorCode &errorCode) const {
89 if(U_SUCCESS(errorCode)) {
90 if(&first!=&second) {
91 first.append(second);
92 } else {
93 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
94 }
95 }
96 return first;
97 }
98 virtual UBool
getDecomposition(UChar32,UnicodeString &) const99 getDecomposition(UChar32, UnicodeString &) const {
100 return FALSE;
101 }
102 // No need to override the default getRawDecomposition().
103 virtual UBool
isNormalized(const UnicodeString &,UErrorCode &) const104 isNormalized(const UnicodeString &, UErrorCode &) const {
105 return TRUE;
106 }
107 virtual UNormalizationCheckResult
quickCheck(const UnicodeString &,UErrorCode &) const108 quickCheck(const UnicodeString &, UErrorCode &) const {
109 return UNORM_YES;
110 }
111 virtual int32_t
spanQuickCheckYes(const UnicodeString & s,UErrorCode &) const112 spanQuickCheckYes(const UnicodeString &s, UErrorCode &) const {
113 return s.length();
114 }
hasBoundaryBefore(UChar32) const115 virtual UBool hasBoundaryBefore(UChar32) const { return TRUE; }
hasBoundaryAfter(UChar32) const116 virtual UBool hasBoundaryAfter(UChar32) const { return TRUE; }
isInert(UChar32) const117 virtual UBool isInert(UChar32) const { return TRUE; }
118 };
119
~NoopNormalizer2()120 NoopNormalizer2::~NoopNormalizer2() {}
121
122 // Intermediate class:
123 // Has Normalizer2Impl and does boilerplate argument checking and setup.
124 class Normalizer2WithImpl : public Normalizer2 {
125 public:
Normalizer2WithImpl(const Normalizer2Impl & ni)126 Normalizer2WithImpl(const Normalizer2Impl &ni) : impl(ni) {}
127 virtual ~Normalizer2WithImpl();
128
129 // normalize
130 virtual UnicodeString &
normalize(const UnicodeString & src,UnicodeString & dest,UErrorCode & errorCode) const131 normalize(const UnicodeString &src,
132 UnicodeString &dest,
133 UErrorCode &errorCode) const {
134 if(U_FAILURE(errorCode)) {
135 dest.setToBogus();
136 return dest;
137 }
138 const UChar *sArray=src.getBuffer();
139 if(&dest==&src || sArray==NULL) {
140 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
141 dest.setToBogus();
142 return dest;
143 }
144 dest.remove();
145 ReorderingBuffer buffer(impl, dest);
146 if(buffer.init(src.length(), errorCode)) {
147 normalize(sArray, sArray+src.length(), buffer, errorCode);
148 }
149 return dest;
150 }
151 virtual void
152 normalize(const UChar *src, const UChar *limit,
153 ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0;
154
155 // normalize and append
156 virtual UnicodeString &
normalizeSecondAndAppend(UnicodeString & first,const UnicodeString & second,UErrorCode & errorCode) const157 normalizeSecondAndAppend(UnicodeString &first,
158 const UnicodeString &second,
159 UErrorCode &errorCode) const {
160 return normalizeSecondAndAppend(first, second, TRUE, errorCode);
161 }
162 virtual UnicodeString &
append(UnicodeString & first,const UnicodeString & second,UErrorCode & errorCode) const163 append(UnicodeString &first,
164 const UnicodeString &second,
165 UErrorCode &errorCode) const {
166 return normalizeSecondAndAppend(first, second, FALSE, errorCode);
167 }
168 UnicodeString &
normalizeSecondAndAppend(UnicodeString & first,const UnicodeString & second,UBool doNormalize,UErrorCode & errorCode) const169 normalizeSecondAndAppend(UnicodeString &first,
170 const UnicodeString &second,
171 UBool doNormalize,
172 UErrorCode &errorCode) const {
173 uprv_checkCanGetBuffer(first, errorCode);
174 if(U_FAILURE(errorCode)) {
175 return first;
176 }
177 const UChar *secondArray=second.getBuffer();
178 if(&first==&second || secondArray==NULL) {
179 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
180 return first;
181 }
182 int32_t firstLength=first.length();
183 UnicodeString safeMiddle;
184 {
185 ReorderingBuffer buffer(impl, first);
186 if(buffer.init(firstLength+second.length(), errorCode)) {
187 normalizeAndAppend(secondArray, secondArray+second.length(), doNormalize,
188 safeMiddle, buffer, errorCode);
189 }
190 } // The ReorderingBuffer destructor finalizes the first string.
191 if(U_FAILURE(errorCode)) {
192 // Restore the modified suffix of the first string.
193 first.replace(firstLength-safeMiddle.length(), 0x7fffffff, safeMiddle);
194 }
195 return first;
196 }
197 virtual void
198 normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize,
199 UnicodeString &safeMiddle,
200 ReorderingBuffer &buffer, UErrorCode &errorCode) const = 0;
201 virtual UBool
getDecomposition(UChar32 c,UnicodeString & decomposition) const202 getDecomposition(UChar32 c, UnicodeString &decomposition) const {
203 UChar buffer[4];
204 int32_t length;
205 const UChar *d=impl.getDecomposition(c, buffer, length);
206 if(d==NULL) {
207 return FALSE;
208 }
209 if(d==buffer) {
210 decomposition.setTo(buffer, length); // copy the string (Jamos from Hangul syllable c)
211 } else {
212 decomposition.setTo(FALSE, d, length); // read-only alias
213 }
214 return TRUE;
215 }
216 virtual UBool
getRawDecomposition(UChar32 c,UnicodeString & decomposition) const217 getRawDecomposition(UChar32 c, UnicodeString &decomposition) const {
218 UChar buffer[30];
219 int32_t length;
220 const UChar *d=impl.getRawDecomposition(c, buffer, length);
221 if(d==NULL) {
222 return FALSE;
223 }
224 if(d==buffer) {
225 decomposition.setTo(buffer, length); // copy the string (algorithmic decomposition)
226 } else {
227 decomposition.setTo(FALSE, d, length); // read-only alias
228 }
229 return TRUE;
230 }
231 virtual UChar32
composePair(UChar32 a,UChar32 b) const232 composePair(UChar32 a, UChar32 b) const {
233 return impl.composePair(a, b);
234 }
235
236 virtual uint8_t
getCombiningClass(UChar32 c) const237 getCombiningClass(UChar32 c) const {
238 return impl.getCC(impl.getNorm16(c));
239 }
240
241 // quick checks
242 virtual UBool
isNormalized(const UnicodeString & s,UErrorCode & errorCode) const243 isNormalized(const UnicodeString &s, UErrorCode &errorCode) const {
244 if(U_FAILURE(errorCode)) {
245 return FALSE;
246 }
247 const UChar *sArray=s.getBuffer();
248 if(sArray==NULL) {
249 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
250 return FALSE;
251 }
252 const UChar *sLimit=sArray+s.length();
253 return sLimit==spanQuickCheckYes(sArray, sLimit, errorCode);
254 }
255 virtual UNormalizationCheckResult
quickCheck(const UnicodeString & s,UErrorCode & errorCode) const256 quickCheck(const UnicodeString &s, UErrorCode &errorCode) const {
257 return Normalizer2WithImpl::isNormalized(s, errorCode) ? UNORM_YES : UNORM_NO;
258 }
259 virtual int32_t
spanQuickCheckYes(const UnicodeString & s,UErrorCode & errorCode) const260 spanQuickCheckYes(const UnicodeString &s, UErrorCode &errorCode) const {
261 if(U_FAILURE(errorCode)) {
262 return 0;
263 }
264 const UChar *sArray=s.getBuffer();
265 if(sArray==NULL) {
266 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
267 return 0;
268 }
269 return (int32_t)(spanQuickCheckYes(sArray, sArray+s.length(), errorCode)-sArray);
270 }
271 virtual const UChar *
272 spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const = 0;
273
getQuickCheck(UChar32) const274 virtual UNormalizationCheckResult getQuickCheck(UChar32) const {
275 return UNORM_YES;
276 }
277
278 const Normalizer2Impl &impl;
279 };
280
~Normalizer2WithImpl()281 Normalizer2WithImpl::~Normalizer2WithImpl() {}
282
283 class DecomposeNormalizer2 : public Normalizer2WithImpl {
284 public:
DecomposeNormalizer2(const Normalizer2Impl & ni)285 DecomposeNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {}
286 virtual ~DecomposeNormalizer2();
287
288 private:
289 virtual void
normalize(const UChar * src,const UChar * limit,ReorderingBuffer & buffer,UErrorCode & errorCode) const290 normalize(const UChar *src, const UChar *limit,
291 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
292 impl.decompose(src, limit, &buffer, errorCode);
293 }
294 using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function.
295 virtual void
normalizeAndAppend(const UChar * src,const UChar * limit,UBool doNormalize,UnicodeString & safeMiddle,ReorderingBuffer & buffer,UErrorCode & errorCode) const296 normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize,
297 UnicodeString &safeMiddle,
298 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
299 impl.decomposeAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode);
300 }
301 virtual const UChar *
spanQuickCheckYes(const UChar * src,const UChar * limit,UErrorCode & errorCode) const302 spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const {
303 return impl.decompose(src, limit, NULL, errorCode);
304 }
305 using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function.
getQuickCheck(UChar32 c) const306 virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const {
307 return impl.isDecompYes(impl.getNorm16(c)) ? UNORM_YES : UNORM_NO;
308 }
hasBoundaryBefore(UChar32 c) const309 virtual UBool hasBoundaryBefore(UChar32 c) const { return impl.hasDecompBoundary(c, TRUE); }
hasBoundaryAfter(UChar32 c) const310 virtual UBool hasBoundaryAfter(UChar32 c) const { return impl.hasDecompBoundary(c, FALSE); }
isInert(UChar32 c) const311 virtual UBool isInert(UChar32 c) const { return impl.isDecompInert(c); }
312 };
313
~DecomposeNormalizer2()314 DecomposeNormalizer2::~DecomposeNormalizer2() {}
315
316 class ComposeNormalizer2 : public Normalizer2WithImpl {
317 public:
ComposeNormalizer2(const Normalizer2Impl & ni,UBool fcc)318 ComposeNormalizer2(const Normalizer2Impl &ni, UBool fcc) :
319 Normalizer2WithImpl(ni), onlyContiguous(fcc) {}
320 virtual ~ComposeNormalizer2();
321
322 private:
323 virtual void
normalize(const UChar * src,const UChar * limit,ReorderingBuffer & buffer,UErrorCode & errorCode) const324 normalize(const UChar *src, const UChar *limit,
325 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
326 impl.compose(src, limit, onlyContiguous, TRUE, buffer, errorCode);
327 }
328 using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function.
329 virtual void
normalizeAndAppend(const UChar * src,const UChar * limit,UBool doNormalize,UnicodeString & safeMiddle,ReorderingBuffer & buffer,UErrorCode & errorCode) const330 normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize,
331 UnicodeString &safeMiddle,
332 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
333 impl.composeAndAppend(src, limit, doNormalize, onlyContiguous, safeMiddle, buffer, errorCode);
334 }
335
336 virtual UBool
isNormalized(const UnicodeString & s,UErrorCode & errorCode) const337 isNormalized(const UnicodeString &s, UErrorCode &errorCode) const {
338 if(U_FAILURE(errorCode)) {
339 return FALSE;
340 }
341 const UChar *sArray=s.getBuffer();
342 if(sArray==NULL) {
343 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
344 return FALSE;
345 }
346 UnicodeString temp;
347 ReorderingBuffer buffer(impl, temp);
348 if(!buffer.init(5, errorCode)) { // small destCapacity for substring normalization
349 return FALSE;
350 }
351 return impl.compose(sArray, sArray+s.length(), onlyContiguous, FALSE, buffer, errorCode);
352 }
353 virtual UNormalizationCheckResult
quickCheck(const UnicodeString & s,UErrorCode & errorCode) const354 quickCheck(const UnicodeString &s, UErrorCode &errorCode) const {
355 if(U_FAILURE(errorCode)) {
356 return UNORM_MAYBE;
357 }
358 const UChar *sArray=s.getBuffer();
359 if(sArray==NULL) {
360 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
361 return UNORM_MAYBE;
362 }
363 UNormalizationCheckResult qcResult=UNORM_YES;
364 impl.composeQuickCheck(sArray, sArray+s.length(), onlyContiguous, &qcResult);
365 return qcResult;
366 }
367 virtual const UChar *
spanQuickCheckYes(const UChar * src,const UChar * limit,UErrorCode &) const368 spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &) const {
369 return impl.composeQuickCheck(src, limit, onlyContiguous, NULL);
370 }
371 using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function.
getQuickCheck(UChar32 c) const372 virtual UNormalizationCheckResult getQuickCheck(UChar32 c) const {
373 return impl.getCompQuickCheck(impl.getNorm16(c));
374 }
hasBoundaryBefore(UChar32 c) const375 virtual UBool hasBoundaryBefore(UChar32 c) const {
376 return impl.hasCompBoundaryBefore(c);
377 }
hasBoundaryAfter(UChar32 c) const378 virtual UBool hasBoundaryAfter(UChar32 c) const {
379 return impl.hasCompBoundaryAfter(c, onlyContiguous, FALSE);
380 }
isInert(UChar32 c) const381 virtual UBool isInert(UChar32 c) const {
382 return impl.hasCompBoundaryAfter(c, onlyContiguous, TRUE);
383 }
384
385 const UBool onlyContiguous;
386 };
387
~ComposeNormalizer2()388 ComposeNormalizer2::~ComposeNormalizer2() {}
389
390 class FCDNormalizer2 : public Normalizer2WithImpl {
391 public:
FCDNormalizer2(const Normalizer2Impl & ni)392 FCDNormalizer2(const Normalizer2Impl &ni) : Normalizer2WithImpl(ni) {}
393 virtual ~FCDNormalizer2();
394
395 private:
396 virtual void
normalize(const UChar * src,const UChar * limit,ReorderingBuffer & buffer,UErrorCode & errorCode) const397 normalize(const UChar *src, const UChar *limit,
398 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
399 impl.makeFCD(src, limit, &buffer, errorCode);
400 }
401 using Normalizer2WithImpl::normalize; // Avoid warning about hiding base class function.
402 virtual void
normalizeAndAppend(const UChar * src,const UChar * limit,UBool doNormalize,UnicodeString & safeMiddle,ReorderingBuffer & buffer,UErrorCode & errorCode) const403 normalizeAndAppend(const UChar *src, const UChar *limit, UBool doNormalize,
404 UnicodeString &safeMiddle,
405 ReorderingBuffer &buffer, UErrorCode &errorCode) const {
406 impl.makeFCDAndAppend(src, limit, doNormalize, safeMiddle, buffer, errorCode);
407 }
408 virtual const UChar *
spanQuickCheckYes(const UChar * src,const UChar * limit,UErrorCode & errorCode) const409 spanQuickCheckYes(const UChar *src, const UChar *limit, UErrorCode &errorCode) const {
410 return impl.makeFCD(src, limit, NULL, errorCode);
411 }
412 using Normalizer2WithImpl::spanQuickCheckYes; // Avoid warning about hiding base class function.
hasBoundaryBefore(UChar32 c) const413 virtual UBool hasBoundaryBefore(UChar32 c) const { return impl.hasFCDBoundaryBefore(c); }
hasBoundaryAfter(UChar32 c) const414 virtual UBool hasBoundaryAfter(UChar32 c) const { return impl.hasFCDBoundaryAfter(c); }
isInert(UChar32 c) const415 virtual UBool isInert(UChar32 c) const { return impl.isFCDInert(c); }
416 };
417
~FCDNormalizer2()418 FCDNormalizer2::~FCDNormalizer2() {}
419
420 // instance cache ---------------------------------------------------------- ***
421
422 struct Norm2AllModes : public UMemory {
423 static Norm2AllModes *createInstance(const char *packageName,
424 const char *name,
425 UErrorCode &errorCode);
Norm2AllModesNorm2AllModes426 Norm2AllModes() : comp(impl, FALSE), decomp(impl), fcd(impl), fcc(impl, TRUE) {}
427
428 Normalizer2Impl impl;
429 ComposeNormalizer2 comp;
430 DecomposeNormalizer2 decomp;
431 FCDNormalizer2 fcd;
432 ComposeNormalizer2 fcc;
433 };
434
435 Norm2AllModes *
createInstance(const char * packageName,const char * name,UErrorCode & errorCode)436 Norm2AllModes::createInstance(const char *packageName,
437 const char *name,
438 UErrorCode &errorCode) {
439 if(U_FAILURE(errorCode)) {
440 return NULL;
441 }
442 LocalPointer<Norm2AllModes> allModes(new Norm2AllModes);
443 if(allModes.isNull()) {
444 errorCode=U_MEMORY_ALLOCATION_ERROR;
445 return NULL;
446 }
447 allModes->impl.load(packageName, name, errorCode);
448 return U_SUCCESS(errorCode) ? allModes.orphan() : NULL;
449 }
450
451 U_CDECL_BEGIN
452 static UBool U_CALLCONV uprv_normalizer2_cleanup();
453 U_CDECL_END
454
455 class Norm2AllModesSingleton : public TriStateSingletonWrapper<Norm2AllModes> {
456 public:
Norm2AllModesSingleton(TriStateSingleton & s,const char * n)457 Norm2AllModesSingleton(TriStateSingleton &s, const char *n) :
458 TriStateSingletonWrapper<Norm2AllModes>(s), name(n) {}
getInstance(UErrorCode & errorCode)459 Norm2AllModes *getInstance(UErrorCode &errorCode) {
460 return TriStateSingletonWrapper<Norm2AllModes>::getInstance(createInstance, name, errorCode);
461 }
462 private:
createInstance(const void * context,UErrorCode & errorCode)463 static void *createInstance(const void *context, UErrorCode &errorCode) {
464 ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup);
465 return Norm2AllModes::createInstance(NULL, (const char *)context, errorCode);
466 }
467
468 const char *name;
469 };
470
471 STATIC_TRI_STATE_SINGLETON(nfcSingleton);
472 STATIC_TRI_STATE_SINGLETON(nfkcSingleton);
473 STATIC_TRI_STATE_SINGLETON(nfkc_cfSingleton);
474
475 class Norm2Singleton : public SimpleSingletonWrapper<Normalizer2> {
476 public:
Norm2Singleton(SimpleSingleton & s)477 Norm2Singleton(SimpleSingleton &s) : SimpleSingletonWrapper<Normalizer2>(s) {}
getInstance(UErrorCode & errorCode)478 Normalizer2 *getInstance(UErrorCode &errorCode) {
479 return SimpleSingletonWrapper<Normalizer2>::getInstance(createInstance, NULL, errorCode);
480 }
481 private:
createInstance(const void *,UErrorCode & errorCode)482 static void *createInstance(const void *, UErrorCode &errorCode) {
483 Normalizer2 *noop=new NoopNormalizer2;
484 if(noop==NULL) {
485 errorCode=U_MEMORY_ALLOCATION_ERROR;
486 }
487 ucln_common_registerCleanup(UCLN_COMMON_NORMALIZER2, uprv_normalizer2_cleanup);
488 return noop;
489 }
490 };
491
492 STATIC_SIMPLE_SINGLETON(noopSingleton);
493
494 static UHashtable *cache=NULL;
495
496 U_CDECL_BEGIN
497
deleteNorm2AllModes(void * allModes)498 static void U_CALLCONV deleteNorm2AllModes(void *allModes) {
499 delete (Norm2AllModes *)allModes;
500 }
501
uprv_normalizer2_cleanup()502 static UBool U_CALLCONV uprv_normalizer2_cleanup() {
503 Norm2AllModesSingleton(nfcSingleton, NULL).deleteInstance();
504 Norm2AllModesSingleton(nfkcSingleton, NULL).deleteInstance();
505 Norm2AllModesSingleton(nfkc_cfSingleton, NULL).deleteInstance();
506 Norm2Singleton(noopSingleton).deleteInstance();
507 uhash_close(cache);
508 cache=NULL;
509 return TRUE;
510 }
511
512 U_CDECL_END
513
getNFCInstance(UErrorCode & errorCode)514 const Normalizer2 *Normalizer2Factory::getNFCInstance(UErrorCode &errorCode) {
515 Norm2AllModes *allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
516 return allModes!=NULL ? &allModes->comp : NULL;
517 }
518
getNFDInstance(UErrorCode & errorCode)519 const Normalizer2 *Normalizer2Factory::getNFDInstance(UErrorCode &errorCode) {
520 Norm2AllModes *allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
521 return allModes!=NULL ? &allModes->decomp : NULL;
522 }
523
getFCDInstance(UErrorCode & errorCode)524 const Normalizer2 *Normalizer2Factory::getFCDInstance(UErrorCode &errorCode) {
525 Norm2AllModes *allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
526 return allModes!=NULL ? &allModes->fcd : NULL;
527 }
528
getFCCInstance(UErrorCode & errorCode)529 const Normalizer2 *Normalizer2Factory::getFCCInstance(UErrorCode &errorCode) {
530 Norm2AllModes *allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
531 return allModes!=NULL ? &allModes->fcc : NULL;
532 }
533
getNFKCInstance(UErrorCode & errorCode)534 const Normalizer2 *Normalizer2Factory::getNFKCInstance(UErrorCode &errorCode) {
535 Norm2AllModes *allModes=
536 Norm2AllModesSingleton(nfkcSingleton, "nfkc").getInstance(errorCode);
537 return allModes!=NULL ? &allModes->comp : NULL;
538 }
539
getNFKDInstance(UErrorCode & errorCode)540 const Normalizer2 *Normalizer2Factory::getNFKDInstance(UErrorCode &errorCode) {
541 Norm2AllModes *allModes=
542 Norm2AllModesSingleton(nfkcSingleton, "nfkc").getInstance(errorCode);
543 return allModes!=NULL ? &allModes->decomp : NULL;
544 }
545
getNFKC_CFInstance(UErrorCode & errorCode)546 const Normalizer2 *Normalizer2Factory::getNFKC_CFInstance(UErrorCode &errorCode) {
547 Norm2AllModes *allModes=
548 Norm2AllModesSingleton(nfkc_cfSingleton, "nfkc_cf").getInstance(errorCode);
549 return allModes!=NULL ? &allModes->comp : NULL;
550 }
551
getNoopInstance(UErrorCode & errorCode)552 const Normalizer2 *Normalizer2Factory::getNoopInstance(UErrorCode &errorCode) {
553 return Norm2Singleton(noopSingleton).getInstance(errorCode);
554 }
555
556 const Normalizer2 *
getInstance(UNormalizationMode mode,UErrorCode & errorCode)557 Normalizer2Factory::getInstance(UNormalizationMode mode, UErrorCode &errorCode) {
558 if(U_FAILURE(errorCode)) {
559 return NULL;
560 }
561 switch(mode) {
562 case UNORM_NFD:
563 return getNFDInstance(errorCode);
564 case UNORM_NFKD:
565 return getNFKDInstance(errorCode);
566 case UNORM_NFC:
567 return getNFCInstance(errorCode);
568 case UNORM_NFKC:
569 return getNFKCInstance(errorCode);
570 case UNORM_FCD:
571 return getFCDInstance(errorCode);
572 default: // UNORM_NONE
573 return getNoopInstance(errorCode);
574 }
575 }
576
577 const Normalizer2Impl *
getNFCImpl(UErrorCode & errorCode)578 Normalizer2Factory::getNFCImpl(UErrorCode &errorCode) {
579 Norm2AllModes *allModes=
580 Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
581 return allModes!=NULL ? &allModes->impl : NULL;
582 }
583
584 const Normalizer2Impl *
getNFKCImpl(UErrorCode & errorCode)585 Normalizer2Factory::getNFKCImpl(UErrorCode &errorCode) {
586 Norm2AllModes *allModes=
587 Norm2AllModesSingleton(nfkcSingleton, "nfkc").getInstance(errorCode);
588 return allModes!=NULL ? &allModes->impl : NULL;
589 }
590
591 const Normalizer2Impl *
getNFKC_CFImpl(UErrorCode & errorCode)592 Normalizer2Factory::getNFKC_CFImpl(UErrorCode &errorCode) {
593 Norm2AllModes *allModes=
594 Norm2AllModesSingleton(nfkc_cfSingleton, "nfkc_cf").getInstance(errorCode);
595 return allModes!=NULL ? &allModes->impl : NULL;
596 }
597
598 const Normalizer2Impl *
getImpl(const Normalizer2 * norm2)599 Normalizer2Factory::getImpl(const Normalizer2 *norm2) {
600 return &((Normalizer2WithImpl *)norm2)->impl;
601 }
602
603 const Normalizer2 *
getNFCInstance(UErrorCode & errorCode)604 Normalizer2::getNFCInstance(UErrorCode &errorCode) {
605 return Normalizer2Factory::getNFCInstance(errorCode);
606 }
607
608 const Normalizer2 *
getNFDInstance(UErrorCode & errorCode)609 Normalizer2::getNFDInstance(UErrorCode &errorCode) {
610 return Normalizer2Factory::getNFDInstance(errorCode);
611 }
612
613 const Normalizer2 *
getNFKCInstance(UErrorCode & errorCode)614 Normalizer2::getNFKCInstance(UErrorCode &errorCode) {
615 return Normalizer2Factory::getNFKCInstance(errorCode);
616 }
617
618 const Normalizer2 *
getNFKDInstance(UErrorCode & errorCode)619 Normalizer2::getNFKDInstance(UErrorCode &errorCode) {
620 return Normalizer2Factory::getNFKDInstance(errorCode);
621 }
622
623 const Normalizer2 *
getNFKCCasefoldInstance(UErrorCode & errorCode)624 Normalizer2::getNFKCCasefoldInstance(UErrorCode &errorCode) {
625 return Normalizer2Factory::getNFKC_CFInstance(errorCode);
626 }
627
628 const Normalizer2 *
getInstance(const char * packageName,const char * name,UNormalization2Mode mode,UErrorCode & errorCode)629 Normalizer2::getInstance(const char *packageName,
630 const char *name,
631 UNormalization2Mode mode,
632 UErrorCode &errorCode) {
633 if(U_FAILURE(errorCode)) {
634 return NULL;
635 }
636 if(name==NULL || *name==0) {
637 errorCode=U_ILLEGAL_ARGUMENT_ERROR;
638 return NULL;
639 }
640 Norm2AllModes *allModes=NULL;
641 if(packageName==NULL) {
642 if(0==uprv_strcmp(name, "nfc")) {
643 allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
644 } else if(0==uprv_strcmp(name, "nfkc")) {
645 allModes=Norm2AllModesSingleton(nfkcSingleton, "nfkc").getInstance(errorCode);
646 } else if(0==uprv_strcmp(name, "nfkc_cf")) {
647 allModes=Norm2AllModesSingleton(nfkc_cfSingleton, "nfkc_cf").getInstance(errorCode);
648 }
649 }
650 if(allModes==NULL && U_SUCCESS(errorCode)) {
651 {
652 Mutex lock;
653 if(cache!=NULL) {
654 allModes=(Norm2AllModes *)uhash_get(cache, name);
655 }
656 }
657 if(allModes==NULL) {
658 LocalPointer<Norm2AllModes> localAllModes(
659 Norm2AllModes::createInstance(packageName, name, errorCode));
660 if(U_SUCCESS(errorCode)) {
661 Mutex lock;
662 if(cache==NULL) {
663 cache=uhash_open(uhash_hashChars, uhash_compareChars, NULL, &errorCode);
664 if(U_FAILURE(errorCode)) {
665 return NULL;
666 }
667 uhash_setKeyDeleter(cache, uprv_free);
668 uhash_setValueDeleter(cache, deleteNorm2AllModes);
669 }
670 void *temp=uhash_get(cache, name);
671 if(temp==NULL) {
672 int32_t keyLength=uprv_strlen(name)+1;
673 char *nameCopy=(char *)uprv_malloc(keyLength);
674 if(nameCopy==NULL) {
675 errorCode=U_MEMORY_ALLOCATION_ERROR;
676 return NULL;
677 }
678 uprv_memcpy(nameCopy, name, keyLength);
679 uhash_put(cache, nameCopy, allModes=localAllModes.orphan(), &errorCode);
680 } else {
681 // race condition
682 allModes=(Norm2AllModes *)temp;
683 }
684 }
685 }
686 }
687 if(allModes!=NULL && U_SUCCESS(errorCode)) {
688 switch(mode) {
689 case UNORM2_COMPOSE:
690 return &allModes->comp;
691 case UNORM2_DECOMPOSE:
692 return &allModes->decomp;
693 case UNORM2_FCD:
694 return &allModes->fcd;
695 case UNORM2_COMPOSE_CONTIGUOUS:
696 return &allModes->fcc;
697 default:
698 break; // do nothing
699 }
700 }
701 return NULL;
702 }
703
704 U_NAMESPACE_END
705
706 // C API ------------------------------------------------------------------- ***
707
708 U_NAMESPACE_USE
709
710 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getNFCInstance(UErrorCode * pErrorCode)711 unorm2_getNFCInstance(UErrorCode *pErrorCode) {
712 return (const UNormalizer2 *)Normalizer2::getNFCInstance(*pErrorCode);
713 }
714
715 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getNFDInstance(UErrorCode * pErrorCode)716 unorm2_getNFDInstance(UErrorCode *pErrorCode) {
717 return (const UNormalizer2 *)Normalizer2::getNFDInstance(*pErrorCode);
718 }
719
720 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getNFKCInstance(UErrorCode * pErrorCode)721 unorm2_getNFKCInstance(UErrorCode *pErrorCode) {
722 return (const UNormalizer2 *)Normalizer2::getNFKCInstance(*pErrorCode);
723 }
724
725 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getNFKDInstance(UErrorCode * pErrorCode)726 unorm2_getNFKDInstance(UErrorCode *pErrorCode) {
727 return (const UNormalizer2 *)Normalizer2::getNFKDInstance(*pErrorCode);
728 }
729
730 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getNFKCCasefoldInstance(UErrorCode * pErrorCode)731 unorm2_getNFKCCasefoldInstance(UErrorCode *pErrorCode) {
732 return (const UNormalizer2 *)Normalizer2::getNFKCCasefoldInstance(*pErrorCode);
733 }
734
735 U_CAPI const UNormalizer2 * U_EXPORT2
unorm2_getInstance(const char * packageName,const char * name,UNormalization2Mode mode,UErrorCode * pErrorCode)736 unorm2_getInstance(const char *packageName,
737 const char *name,
738 UNormalization2Mode mode,
739 UErrorCode *pErrorCode) {
740 return (const UNormalizer2 *)Normalizer2::getInstance(packageName, name, mode, *pErrorCode);
741 }
742
743 U_CAPI void U_EXPORT2
unorm2_close(UNormalizer2 * norm2)744 unorm2_close(UNormalizer2 *norm2) {
745 delete (Normalizer2 *)norm2;
746 }
747
748 U_CAPI int32_t U_EXPORT2
unorm2_normalize(const UNormalizer2 * norm2,const UChar * src,int32_t length,UChar * dest,int32_t capacity,UErrorCode * pErrorCode)749 unorm2_normalize(const UNormalizer2 *norm2,
750 const UChar *src, int32_t length,
751 UChar *dest, int32_t capacity,
752 UErrorCode *pErrorCode) {
753 if(U_FAILURE(*pErrorCode)) {
754 return 0;
755 }
756 if( (src==NULL ? length!=0 : length<-1) ||
757 (dest==NULL ? capacity!=0 : capacity<0) ||
758 (src==dest && src!=NULL)
759 ) {
760 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
761 return 0;
762 }
763 UnicodeString destString(dest, 0, capacity);
764 // length==0: Nothing to do, and n2wi->normalize(NULL, NULL, buffer, ...) would crash.
765 if(length!=0) {
766 const Normalizer2 *n2=(const Normalizer2 *)norm2;
767 const Normalizer2WithImpl *n2wi=dynamic_cast<const Normalizer2WithImpl *>(n2);
768 if(n2wi!=NULL) {
769 // Avoid duplicate argument checking and support NUL-terminated src.
770 ReorderingBuffer buffer(n2wi->impl, destString);
771 if(buffer.init(length, *pErrorCode)) {
772 n2wi->normalize(src, length>=0 ? src+length : NULL, buffer, *pErrorCode);
773 }
774 } else {
775 UnicodeString srcString(length<0, src, length);
776 n2->normalize(srcString, destString, *pErrorCode);
777 }
778 }
779 return destString.extract(dest, capacity, *pErrorCode);
780 }
781
782 static int32_t
normalizeSecondAndAppend(const UNormalizer2 * norm2,UChar * first,int32_t firstLength,int32_t firstCapacity,const UChar * second,int32_t secondLength,UBool doNormalize,UErrorCode * pErrorCode)783 normalizeSecondAndAppend(const UNormalizer2 *norm2,
784 UChar *first, int32_t firstLength, int32_t firstCapacity,
785 const UChar *second, int32_t secondLength,
786 UBool doNormalize,
787 UErrorCode *pErrorCode) {
788 if(U_FAILURE(*pErrorCode)) {
789 return 0;
790 }
791 if( (second==NULL ? secondLength!=0 : secondLength<-1) ||
792 (first==NULL ? (firstCapacity!=0 || firstLength!=0) :
793 (firstCapacity<0 || firstLength<-1)) ||
794 (first==second && first!=NULL)
795 ) {
796 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
797 return 0;
798 }
799 UnicodeString firstString(first, firstLength, firstCapacity);
800 firstLength=firstString.length(); // In case it was -1.
801 // secondLength==0: Nothing to do, and n2wi->normalizeAndAppend(NULL, NULL, buffer, ...) would crash.
802 if(secondLength!=0) {
803 const Normalizer2 *n2=(const Normalizer2 *)norm2;
804 const Normalizer2WithImpl *n2wi=dynamic_cast<const Normalizer2WithImpl *>(n2);
805 if(n2wi!=NULL) {
806 // Avoid duplicate argument checking and support NUL-terminated src.
807 UnicodeString safeMiddle;
808 {
809 ReorderingBuffer buffer(n2wi->impl, firstString);
810 if(buffer.init(firstLength+secondLength+1, *pErrorCode)) { // destCapacity>=-1
811 n2wi->normalizeAndAppend(second, secondLength>=0 ? second+secondLength : NULL,
812 doNormalize, safeMiddle, buffer, *pErrorCode);
813 }
814 } // The ReorderingBuffer destructor finalizes firstString.
815 if(U_FAILURE(*pErrorCode) || firstString.length()>firstCapacity) {
816 // Restore the modified suffix of the first string.
817 // This does not restore first[] array contents between firstLength and firstCapacity.
818 // (That might be uninitialized memory, as far as we know.)
819 if(first!=NULL) { /* don't dereference NULL */
820 safeMiddle.extract(0, 0x7fffffff, first+firstLength-safeMiddle.length());
821 if(firstLength<firstCapacity) {
822 first[firstLength]=0; // NUL-terminate in case it was originally.
823 }
824 }
825 }
826 } else {
827 UnicodeString secondString(secondLength<0, second, secondLength);
828 if(doNormalize) {
829 n2->normalizeSecondAndAppend(firstString, secondString, *pErrorCode);
830 } else {
831 n2->append(firstString, secondString, *pErrorCode);
832 }
833 }
834 }
835 return firstString.extract(first, firstCapacity, *pErrorCode);
836 }
837
838 U_CAPI int32_t U_EXPORT2
unorm2_normalizeSecondAndAppend(const UNormalizer2 * norm2,UChar * first,int32_t firstLength,int32_t firstCapacity,const UChar * second,int32_t secondLength,UErrorCode * pErrorCode)839 unorm2_normalizeSecondAndAppend(const UNormalizer2 *norm2,
840 UChar *first, int32_t firstLength, int32_t firstCapacity,
841 const UChar *second, int32_t secondLength,
842 UErrorCode *pErrorCode) {
843 return normalizeSecondAndAppend(norm2,
844 first, firstLength, firstCapacity,
845 second, secondLength,
846 TRUE, pErrorCode);
847 }
848
849 U_CAPI int32_t U_EXPORT2
unorm2_append(const UNormalizer2 * norm2,UChar * first,int32_t firstLength,int32_t firstCapacity,const UChar * second,int32_t secondLength,UErrorCode * pErrorCode)850 unorm2_append(const UNormalizer2 *norm2,
851 UChar *first, int32_t firstLength, int32_t firstCapacity,
852 const UChar *second, int32_t secondLength,
853 UErrorCode *pErrorCode) {
854 return normalizeSecondAndAppend(norm2,
855 first, firstLength, firstCapacity,
856 second, secondLength,
857 FALSE, pErrorCode);
858 }
859
860 U_CAPI int32_t U_EXPORT2
unorm2_getDecomposition(const UNormalizer2 * norm2,UChar32 c,UChar * decomposition,int32_t capacity,UErrorCode * pErrorCode)861 unorm2_getDecomposition(const UNormalizer2 *norm2,
862 UChar32 c, UChar *decomposition, int32_t capacity,
863 UErrorCode *pErrorCode) {
864 if(U_FAILURE(*pErrorCode)) {
865 return 0;
866 }
867 if(decomposition==NULL ? capacity!=0 : capacity<0) {
868 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
869 return 0;
870 }
871 UnicodeString destString(decomposition, 0, capacity);
872 if(reinterpret_cast<const Normalizer2 *>(norm2)->getDecomposition(c, destString)) {
873 return destString.extract(decomposition, capacity, *pErrorCode);
874 } else {
875 return -1;
876 }
877 }
878
879 U_CAPI int32_t U_EXPORT2
unorm2_getRawDecomposition(const UNormalizer2 * norm2,UChar32 c,UChar * decomposition,int32_t capacity,UErrorCode * pErrorCode)880 unorm2_getRawDecomposition(const UNormalizer2 *norm2,
881 UChar32 c, UChar *decomposition, int32_t capacity,
882 UErrorCode *pErrorCode) {
883 if(U_FAILURE(*pErrorCode)) {
884 return 0;
885 }
886 if(decomposition==NULL ? capacity!=0 : capacity<0) {
887 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
888 return 0;
889 }
890 UnicodeString destString(decomposition, 0, capacity);
891 if(reinterpret_cast<const Normalizer2 *>(norm2)->getRawDecomposition(c, destString)) {
892 return destString.extract(decomposition, capacity, *pErrorCode);
893 } else {
894 return -1;
895 }
896 }
897
898 U_CAPI UChar32 U_EXPORT2
unorm2_composePair(const UNormalizer2 * norm2,UChar32 a,UChar32 b)899 unorm2_composePair(const UNormalizer2 *norm2, UChar32 a, UChar32 b) {
900 return reinterpret_cast<const Normalizer2 *>(norm2)->composePair(a, b);
901 }
902
903 U_CAPI uint8_t U_EXPORT2
unorm2_getCombiningClass(const UNormalizer2 * norm2,UChar32 c)904 unorm2_getCombiningClass(const UNormalizer2 *norm2, UChar32 c) {
905 return reinterpret_cast<const Normalizer2 *>(norm2)->getCombiningClass(c);
906 }
907
908 U_CAPI UBool U_EXPORT2
unorm2_isNormalized(const UNormalizer2 * norm2,const UChar * s,int32_t length,UErrorCode * pErrorCode)909 unorm2_isNormalized(const UNormalizer2 *norm2,
910 const UChar *s, int32_t length,
911 UErrorCode *pErrorCode) {
912 if(U_FAILURE(*pErrorCode)) {
913 return 0;
914 }
915 if((s==NULL && length!=0) || length<-1) {
916 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
917 return 0;
918 }
919 UnicodeString sString(length<0, s, length);
920 return ((const Normalizer2 *)norm2)->isNormalized(sString, *pErrorCode);
921 }
922
923 U_CAPI UNormalizationCheckResult U_EXPORT2
unorm2_quickCheck(const UNormalizer2 * norm2,const UChar * s,int32_t length,UErrorCode * pErrorCode)924 unorm2_quickCheck(const UNormalizer2 *norm2,
925 const UChar *s, int32_t length,
926 UErrorCode *pErrorCode) {
927 if(U_FAILURE(*pErrorCode)) {
928 return UNORM_NO;
929 }
930 if((s==NULL && length!=0) || length<-1) {
931 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
932 return UNORM_NO;
933 }
934 UnicodeString sString(length<0, s, length);
935 return ((const Normalizer2 *)norm2)->quickCheck(sString, *pErrorCode);
936 }
937
938 U_CAPI int32_t U_EXPORT2
unorm2_spanQuickCheckYes(const UNormalizer2 * norm2,const UChar * s,int32_t length,UErrorCode * pErrorCode)939 unorm2_spanQuickCheckYes(const UNormalizer2 *norm2,
940 const UChar *s, int32_t length,
941 UErrorCode *pErrorCode) {
942 if(U_FAILURE(*pErrorCode)) {
943 return 0;
944 }
945 if((s==NULL && length!=0) || length<-1) {
946 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
947 return 0;
948 }
949 UnicodeString sString(length<0, s, length);
950 return ((const Normalizer2 *)norm2)->spanQuickCheckYes(sString, *pErrorCode);
951 }
952
953 U_CAPI UBool U_EXPORT2
unorm2_hasBoundaryBefore(const UNormalizer2 * norm2,UChar32 c)954 unorm2_hasBoundaryBefore(const UNormalizer2 *norm2, UChar32 c) {
955 return ((const Normalizer2 *)norm2)->hasBoundaryBefore(c);
956 }
957
958 U_CAPI UBool U_EXPORT2
unorm2_hasBoundaryAfter(const UNormalizer2 * norm2,UChar32 c)959 unorm2_hasBoundaryAfter(const UNormalizer2 *norm2, UChar32 c) {
960 return ((const Normalizer2 *)norm2)->hasBoundaryAfter(c);
961 }
962
963 U_CAPI UBool U_EXPORT2
unorm2_isInert(const UNormalizer2 * norm2,UChar32 c)964 unorm2_isInert(const UNormalizer2 *norm2, UChar32 c) {
965 return ((const Normalizer2 *)norm2)->isInert(c);
966 }
967
968 // Some properties APIs ---------------------------------------------------- ***
969
970 U_CAPI uint8_t U_EXPORT2
u_getCombiningClass(UChar32 c)971 u_getCombiningClass(UChar32 c) {
972 UErrorCode errorCode=U_ZERO_ERROR;
973 const Normalizer2 *nfd=Normalizer2Factory::getNFDInstance(errorCode);
974 if(U_SUCCESS(errorCode)) {
975 return nfd->getCombiningClass(c);
976 } else {
977 return 0;
978 }
979 }
980
981 U_CFUNC UNormalizationCheckResult
unorm_getQuickCheck(UChar32 c,UNormalizationMode mode)982 unorm_getQuickCheck(UChar32 c, UNormalizationMode mode) {
983 if(mode<=UNORM_NONE || UNORM_FCD<=mode) {
984 return UNORM_YES;
985 }
986 UErrorCode errorCode=U_ZERO_ERROR;
987 const Normalizer2 *norm2=Normalizer2Factory::getInstance(mode, errorCode);
988 if(U_SUCCESS(errorCode)) {
989 return ((const Normalizer2WithImpl *)norm2)->getQuickCheck(c);
990 } else {
991 return UNORM_MAYBE;
992 }
993 }
994
995 U_CFUNC uint16_t
unorm_getFCD16(UChar32 c)996 unorm_getFCD16(UChar32 c) {
997 UErrorCode errorCode=U_ZERO_ERROR;
998 const Normalizer2Impl *impl=Normalizer2Factory::getNFCImpl(errorCode);
999 if(U_SUCCESS(errorCode)) {
1000 return impl->getFCD16(c);
1001 } else {
1002 return 0;
1003 }
1004 }
1005
1006 #endif // !UCONFIG_NO_NORMALIZATION
1007