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) 2003-2016, International Business Machines |
7 | * Corporation and others. All Rights Reserved. |
8 | * |
9 | ******************************************************************************* |
10 | * file name: usprep.cpp |
11 | * encoding: UTF-8 |
12 | * tab size: 8 (not used) |
13 | * indentation:4 |
14 | * |
15 | * created on: 2003jul2 |
16 | * created by: Ram Viswanadha |
17 | */ |
18 | |
19 | #include "unicode/utypes.h" |
20 | |
21 | #if !UCONFIG_NO_IDNA |
22 | |
23 | #include "unicode/usprep.h" |
24 | |
25 | #include "unicode/normalizer2.h" |
26 | #include "unicode/ustring.h" |
27 | #include "unicode/uchar.h" |
28 | #include "unicode/uversion.h" |
29 | #include "umutex.h" |
30 | #include "cmemory.h" |
31 | #include "sprpimpl.h" |
32 | #include "ustr_imp.h" |
33 | #include "uhash.h" |
34 | #include "cstring.h" |
35 | #include "udataswp.h" |
36 | #include "ucln_cmn.h" |
37 | #include "ubidi_props.h" |
38 | #include "uprops.h" |
39 | |
40 | U_NAMESPACE_USE |
41 | |
42 | U_CDECL_BEGIN |
43 | |
44 | /* |
45 | Static cache for already opened StringPrep profiles |
46 | */ |
47 | static UHashtable *SHARED_DATA_HASHTABLE = nullptr; |
48 | static icu::UInitOnce gSharedDataInitOnce {}; |
49 | |
50 | static UMutex usprepMutex; |
51 | /* format version of spp file */ |
52 | //static uint8_t formatVersion[4]={ 0, 0, 0, 0 }; |
53 | |
54 | /* the Unicode version of the sprep data */ |
55 | static UVersionInfo dataVersion={ 0, 0, 0, 0 }; |
56 | |
57 | /* Profile names must be aligned to UStringPrepProfileType */ |
58 | static const char * const PROFILE_NAMES[] = { |
59 | "rfc3491" , /* USPREP_RFC3491_NAMEPREP */ |
60 | "rfc3530cs" , /* USPREP_RFC3530_NFS4_CS_PREP */ |
61 | "rfc3530csci" , /* USPREP_RFC3530_NFS4_CS_PREP_CI */ |
62 | "rfc3491" , /* USPREP_RFC3530_NSF4_CIS_PREP */ |
63 | "rfc3530mixp" , /* USPREP_RFC3530_NSF4_MIXED_PREP_PREFIX */ |
64 | "rfc3491" , /* USPREP_RFC3530_NSF4_MIXED_PREP_SUFFIX */ |
65 | "rfc3722" , /* USPREP_RFC3722_ISCSI */ |
66 | "rfc3920node" , /* USPREP_RFC3920_NODEPREP */ |
67 | "rfc3920res" , /* USPREP_RFC3920_RESOURCEPREP */ |
68 | "rfc4011" , /* USPREP_RFC4011_MIB */ |
69 | "rfc4013" , /* USPREP_RFC4013_SASLPREP */ |
70 | "rfc4505" , /* USPREP_RFC4505_TRACE */ |
71 | "rfc4518" , /* USPREP_RFC4518_LDAP */ |
72 | "rfc4518ci" , /* USPREP_RFC4518_LDAP_CI */ |
73 | }; |
74 | |
75 | static UBool U_CALLCONV |
76 | isSPrepAcceptable(void * /* context */, |
77 | const char * /* type */, |
78 | const char * /* name */, |
79 | const UDataInfo *pInfo) { |
80 | if( |
81 | pInfo->size>=20 && |
82 | pInfo->isBigEndian==U_IS_BIG_ENDIAN && |
83 | pInfo->charsetFamily==U_CHARSET_FAMILY && |
84 | pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ |
85 | pInfo->dataFormat[1]==0x50 && |
86 | pInfo->dataFormat[2]==0x52 && |
87 | pInfo->dataFormat[3]==0x50 && |
88 | pInfo->formatVersion[0]==3 && |
89 | pInfo->formatVersion[2]==UTRIE_SHIFT && |
90 | pInfo->formatVersion[3]==UTRIE_INDEX_SHIFT |
91 | ) { |
92 | //uprv_memcpy(formatVersion, pInfo->formatVersion, 4); |
93 | uprv_memcpy(dataVersion, pInfo->dataVersion, 4); |
94 | return true; |
95 | } else { |
96 | return false; |
97 | } |
98 | } |
99 | |
100 | static int32_t U_CALLCONV |
101 | getSPrepFoldingOffset(uint32_t data) { |
102 | |
103 | return (int32_t)data; |
104 | |
105 | } |
106 | |
107 | /* hashes an entry */ |
108 | static int32_t U_CALLCONV |
109 | hashEntry(const UHashTok parm) { |
110 | UStringPrepKey *b = (UStringPrepKey *)parm.pointer; |
111 | UHashTok namekey, pathkey; |
112 | namekey.pointer = b->name; |
113 | pathkey.pointer = b->path; |
114 | uint32_t unsignedHash = static_cast<uint32_t>(uhash_hashChars(namekey)) + |
115 | 37u * static_cast<uint32_t>(uhash_hashChars(pathkey)); |
116 | return static_cast<int32_t>(unsignedHash); |
117 | } |
118 | |
119 | /* compares two entries */ |
120 | static UBool U_CALLCONV |
121 | compareEntries(const UHashTok p1, const UHashTok p2) { |
122 | UStringPrepKey *b1 = (UStringPrepKey *)p1.pointer; |
123 | UStringPrepKey *b2 = (UStringPrepKey *)p2.pointer; |
124 | UHashTok name1, name2, path1, path2; |
125 | name1.pointer = b1->name; |
126 | name2.pointer = b2->name; |
127 | path1.pointer = b1->path; |
128 | path2.pointer = b2->path; |
129 | return ((UBool)(uhash_compareChars(name1, name2) & |
130 | uhash_compareChars(path1, path2))); |
131 | } |
132 | |
133 | static void |
134 | usprep_unload(UStringPrepProfile* data){ |
135 | udata_close(data->sprepData); |
136 | } |
137 | |
138 | static int32_t |
139 | usprep_internal_flushCache(UBool noRefCount){ |
140 | UStringPrepProfile *profile = nullptr; |
141 | UStringPrepKey *key = nullptr; |
142 | int32_t pos = UHASH_FIRST; |
143 | int32_t deletedNum = 0; |
144 | const UHashElement *e; |
145 | |
146 | /* |
147 | * if shared data hasn't even been lazy evaluated yet |
148 | * return 0 |
149 | */ |
150 | umtx_lock(&usprepMutex); |
151 | if (SHARED_DATA_HASHTABLE == nullptr) { |
152 | umtx_unlock(&usprepMutex); |
153 | return 0; |
154 | } |
155 | |
156 | /*creates an enumeration to iterate through every element in the table */ |
157 | while ((e = uhash_nextElement(SHARED_DATA_HASHTABLE, &pos)) != nullptr) |
158 | { |
159 | profile = (UStringPrepProfile *) e->value.pointer; |
160 | key = (UStringPrepKey *) e->key.pointer; |
161 | |
162 | if ((noRefCount== false && profile->refCount == 0) || |
163 | noRefCount) { |
164 | deletedNum++; |
165 | uhash_removeElement(SHARED_DATA_HASHTABLE, e); |
166 | |
167 | /* unload the data */ |
168 | usprep_unload(profile); |
169 | |
170 | if(key->name != nullptr) { |
171 | uprv_free(key->name); |
172 | key->name=nullptr; |
173 | } |
174 | if(key->path != nullptr) { |
175 | uprv_free(key->path); |
176 | key->path=nullptr; |
177 | } |
178 | uprv_free(profile); |
179 | uprv_free(key); |
180 | } |
181 | |
182 | } |
183 | umtx_unlock(&usprepMutex); |
184 | |
185 | return deletedNum; |
186 | } |
187 | |
188 | /* Works just like ucnv_flushCache() |
189 | static int32_t |
190 | usprep_flushCache(){ |
191 | return usprep_internal_flushCache(false); |
192 | } |
193 | */ |
194 | |
195 | static UBool U_CALLCONV usprep_cleanup(){ |
196 | if (SHARED_DATA_HASHTABLE != nullptr) { |
197 | usprep_internal_flushCache(true); |
198 | if (SHARED_DATA_HASHTABLE != nullptr && uhash_count(SHARED_DATA_HASHTABLE) == 0) { |
199 | uhash_close(SHARED_DATA_HASHTABLE); |
200 | SHARED_DATA_HASHTABLE = nullptr; |
201 | } |
202 | } |
203 | gSharedDataInitOnce.reset(); |
204 | return (SHARED_DATA_HASHTABLE == nullptr); |
205 | } |
206 | U_CDECL_END |
207 | |
208 | |
209 | /** Initializes the cache for resources */ |
210 | static void U_CALLCONV |
211 | createCache(UErrorCode &status) { |
212 | SHARED_DATA_HASHTABLE = uhash_open(hashEntry, compareEntries, nullptr, &status); |
213 | if (U_FAILURE(status)) { |
214 | SHARED_DATA_HASHTABLE = nullptr; |
215 | } |
216 | ucln_common_registerCleanup(UCLN_COMMON_USPREP, usprep_cleanup); |
217 | } |
218 | |
219 | static void |
220 | initCache(UErrorCode *status) { |
221 | umtx_initOnce(gSharedDataInitOnce, &createCache, *status); |
222 | } |
223 | |
224 | static UBool U_CALLCONV |
225 | loadData(UStringPrepProfile* profile, |
226 | const char* path, |
227 | const char* name, |
228 | const char* type, |
229 | UErrorCode* errorCode) { |
230 | /* load Unicode SPREP data from file */ |
231 | UTrie _sprepTrie={ 0,0,0,0,0,0,0 }; |
232 | UDataMemory *dataMemory; |
233 | const int32_t *p=nullptr; |
234 | const uint8_t *pb; |
235 | UVersionInfo normUnicodeVersion; |
236 | int32_t normUniVer, sprepUniVer, normCorrVer; |
237 | |
238 | if(errorCode==nullptr || U_FAILURE(*errorCode)) { |
239 | return 0; |
240 | } |
241 | |
242 | /* open the data outside the mutex block */ |
243 | //TODO: change the path |
244 | dataMemory=udata_openChoice(path, type, name, isSPrepAcceptable, nullptr, errorCode); |
245 | if(U_FAILURE(*errorCode)) { |
246 | return false; |
247 | } |
248 | |
249 | p=(const int32_t *)udata_getMemory(dataMemory); |
250 | pb=(const uint8_t *)(p+_SPREP_INDEX_TOP); |
251 | utrie_unserialize(&_sprepTrie, pb, p[_SPREP_INDEX_TRIE_SIZE], errorCode); |
252 | _sprepTrie.getFoldingOffset=getSPrepFoldingOffset; |
253 | |
254 | |
255 | if(U_FAILURE(*errorCode)) { |
256 | udata_close(dataMemory); |
257 | return false; |
258 | } |
259 | |
260 | /* in the mutex block, set the data for this process */ |
261 | umtx_lock(&usprepMutex); |
262 | if(profile->sprepData==nullptr) { |
263 | profile->sprepData=dataMemory; |
264 | dataMemory=nullptr; |
265 | uprv_memcpy(&profile->indexes, p, sizeof(profile->indexes)); |
266 | uprv_memcpy(&profile->sprepTrie, &_sprepTrie, sizeof(UTrie)); |
267 | } else { |
268 | p=(const int32_t *)udata_getMemory(profile->sprepData); |
269 | } |
270 | umtx_unlock(&usprepMutex); |
271 | /* initialize some variables */ |
272 | profile->mappingData=(uint16_t *)((uint8_t *)(p+_SPREP_INDEX_TOP)+profile->indexes[_SPREP_INDEX_TRIE_SIZE]); |
273 | |
274 | u_getUnicodeVersion(normUnicodeVersion); |
275 | normUniVer = (normUnicodeVersion[0] << 24) + (normUnicodeVersion[1] << 16) + |
276 | (normUnicodeVersion[2] << 8 ) + (normUnicodeVersion[3]); |
277 | sprepUniVer = (dataVersion[0] << 24) + (dataVersion[1] << 16) + |
278 | (dataVersion[2] << 8 ) + (dataVersion[3]); |
279 | normCorrVer = profile->indexes[_SPREP_NORM_CORRECTNS_LAST_UNI_VERSION]; |
280 | |
281 | if(U_FAILURE(*errorCode)){ |
282 | udata_close(dataMemory); |
283 | return false; |
284 | } |
285 | if( normUniVer < sprepUniVer && /* the Unicode version of SPREP file must be less than the Unicode Version of the normalization data */ |
286 | normUniVer < normCorrVer && /* the Unicode version of the NormalizationCorrections.txt file should be less than the Unicode Version of the normalization data */ |
287 | ((profile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0) /* normalization turned on*/ |
288 | ){ |
289 | *errorCode = U_INVALID_FORMAT_ERROR; |
290 | udata_close(dataMemory); |
291 | return false; |
292 | } |
293 | profile->isDataLoaded = true; |
294 | |
295 | /* if a different thread set it first, then close the extra data */ |
296 | if(dataMemory!=nullptr) { |
297 | udata_close(dataMemory); /* nullptr if it was set correctly */ |
298 | } |
299 | |
300 | |
301 | return profile->isDataLoaded; |
302 | } |
303 | |
304 | static UStringPrepProfile* |
305 | usprep_getProfile(const char* path, |
306 | const char* name, |
307 | UErrorCode *status){ |
308 | |
309 | UStringPrepProfile* profile = nullptr; |
310 | |
311 | initCache(status); |
312 | |
313 | if(U_FAILURE(*status)){ |
314 | return nullptr; |
315 | } |
316 | |
317 | UStringPrepKey stackKey; |
318 | /* |
319 | * const is cast way to save malloc, strcpy and free calls |
320 | * we use the passed in pointers for fetching the data from the |
321 | * hash table which is safe |
322 | */ |
323 | stackKey.name = (char*) name; |
324 | stackKey.path = (char*) path; |
325 | |
326 | /* fetch the data from the cache */ |
327 | umtx_lock(&usprepMutex); |
328 | profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); |
329 | if(profile != nullptr) { |
330 | profile->refCount++; |
331 | } |
332 | umtx_unlock(&usprepMutex); |
333 | |
334 | if(profile == nullptr) { |
335 | /* else load the data and put the data in the cache */ |
336 | LocalMemory<UStringPrepProfile> newProfile; |
337 | if(newProfile.allocateInsteadAndReset() == nullptr) { |
338 | *status = U_MEMORY_ALLOCATION_ERROR; |
339 | return nullptr; |
340 | } |
341 | |
342 | /* load the data */ |
343 | if(!loadData(newProfile.getAlias(), path, name, _SPREP_DATA_TYPE, status) || U_FAILURE(*status) ){ |
344 | return nullptr; |
345 | } |
346 | |
347 | /* get the options */ |
348 | newProfile->doNFKC = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_NORMALIZATION_ON) > 0); |
349 | newProfile->checkBiDi = (UBool)((newProfile->indexes[_SPREP_OPTIONS] & _SPREP_CHECK_BIDI_ON) > 0); |
350 | |
351 | LocalMemory<UStringPrepKey> key; |
352 | LocalMemory<char> keyName; |
353 | LocalMemory<char> keyPath; |
354 | if( key.allocateInsteadAndReset() == nullptr || |
355 | keyName.allocateInsteadAndCopy(static_cast<int32_t>(uprv_strlen(name)+1)) == nullptr || |
356 | (path != nullptr && |
357 | keyPath.allocateInsteadAndCopy(static_cast<int32_t>(uprv_strlen(path)+1)) == nullptr) |
358 | ) { |
359 | *status = U_MEMORY_ALLOCATION_ERROR; |
360 | usprep_unload(newProfile.getAlias()); |
361 | return nullptr; |
362 | } |
363 | |
364 | umtx_lock(&usprepMutex); |
365 | // If another thread already inserted the same key/value, refcount and cleanup our thread data |
366 | profile = (UStringPrepProfile*) (uhash_get(SHARED_DATA_HASHTABLE,&stackKey)); |
367 | if(profile != nullptr) { |
368 | profile->refCount++; |
369 | usprep_unload(newProfile.getAlias()); |
370 | } |
371 | else { |
372 | /* initialize the key members */ |
373 | key->name = keyName.orphan(); |
374 | uprv_strcpy(key->name, name); |
375 | if(path != nullptr){ |
376 | key->path = keyPath.orphan(); |
377 | uprv_strcpy(key->path, path); |
378 | } |
379 | profile = newProfile.orphan(); |
380 | |
381 | /* add the data object to the cache */ |
382 | profile->refCount = 1; |
383 | uhash_put(SHARED_DATA_HASHTABLE, key.orphan(), profile, status); |
384 | } |
385 | umtx_unlock(&usprepMutex); |
386 | } |
387 | |
388 | return profile; |
389 | } |
390 | |
391 | U_CAPI UStringPrepProfile* U_EXPORT2 |
392 | usprep_open(const char* path, |
393 | const char* name, |
394 | UErrorCode* status){ |
395 | |
396 | if(status == nullptr || U_FAILURE(*status)){ |
397 | return nullptr; |
398 | } |
399 | |
400 | /* initialize the profile struct members */ |
401 | return usprep_getProfile(path,name,status); |
402 | } |
403 | |
404 | U_CAPI UStringPrepProfile* U_EXPORT2 |
405 | usprep_openByType(UStringPrepProfileType type, |
406 | UErrorCode* status) { |
407 | if(status == nullptr || U_FAILURE(*status)){ |
408 | return nullptr; |
409 | } |
410 | int32_t index = (int32_t)type; |
411 | if (index < 0 || index >= UPRV_LENGTHOF(PROFILE_NAMES)) { |
412 | *status = U_ILLEGAL_ARGUMENT_ERROR; |
413 | return nullptr; |
414 | } |
415 | return usprep_open(nullptr, PROFILE_NAMES[index], status); |
416 | } |
417 | |
418 | U_CAPI void U_EXPORT2 |
419 | usprep_close(UStringPrepProfile* profile){ |
420 | if(profile==nullptr){ |
421 | return; |
422 | } |
423 | |
424 | umtx_lock(&usprepMutex); |
425 | /* decrement the ref count*/ |
426 | if(profile->refCount > 0){ |
427 | profile->refCount--; |
428 | } |
429 | umtx_unlock(&usprepMutex); |
430 | |
431 | } |
432 | |
433 | U_CFUNC void |
434 | uprv_syntaxError(const char16_t* rules, |
435 | int32_t pos, |
436 | int32_t rulesLen, |
437 | UParseError* parseError){ |
438 | if(parseError == nullptr){ |
439 | return; |
440 | } |
441 | parseError->offset = pos; |
442 | parseError->line = 0 ; // we are not using line numbers |
443 | |
444 | // for pre-context |
445 | int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1)); |
446 | int32_t limit = pos; |
447 | |
448 | u_memcpy(parseError->preContext,rules+start,limit-start); |
449 | //null terminate the buffer |
450 | parseError->preContext[limit-start] = 0; |
451 | |
452 | // for post-context; include error rules[pos] |
453 | start = pos; |
454 | limit = start + (U_PARSE_CONTEXT_LEN-1); |
455 | if (limit > rulesLen) { |
456 | limit = rulesLen; |
457 | } |
458 | if (start < rulesLen) { |
459 | u_memcpy(parseError->postContext,rules+start,limit-start); |
460 | } |
461 | //null terminate the buffer |
462 | parseError->postContext[limit-start]= 0; |
463 | } |
464 | |
465 | |
466 | static inline UStringPrepType |
467 | getValues(uint16_t trieWord, int16_t& value, UBool& isIndex){ |
468 | |
469 | UStringPrepType type; |
470 | if(trieWord == 0){ |
471 | /* |
472 | * Initial value stored in the mapping table |
473 | * just return USPREP_TYPE_LIMIT .. so that |
474 | * the source codepoint is copied to the destination |
475 | */ |
476 | type = USPREP_TYPE_LIMIT; |
477 | isIndex =false; |
478 | value = 0; |
479 | }else if(trieWord >= _SPREP_TYPE_THRESHOLD){ |
480 | type = (UStringPrepType) (trieWord - _SPREP_TYPE_THRESHOLD); |
481 | isIndex =false; |
482 | value = 0; |
483 | }else{ |
484 | /* get the type */ |
485 | type = USPREP_MAP; |
486 | /* ascertain if the value is index or delta */ |
487 | if(trieWord & 0x02){ |
488 | isIndex = true; |
489 | value = trieWord >> 2; //mask off the lower 2 bits and shift |
490 | }else{ |
491 | isIndex = false; |
492 | value = (int16_t)trieWord; |
493 | value = (value >> 2); |
494 | } |
495 | |
496 | if((trieWord>>2) == _SPREP_MAX_INDEX_VALUE){ |
497 | type = USPREP_DELETE; |
498 | isIndex =false; |
499 | value = 0; |
500 | } |
501 | } |
502 | return type; |
503 | } |
504 | |
505 | // TODO: change to writing to UnicodeString not char16_t * |
506 | static int32_t |
507 | usprep_map( const UStringPrepProfile* profile, |
508 | const char16_t* src, int32_t srcLength, |
509 | char16_t* dest, int32_t destCapacity, |
510 | int32_t options, |
511 | UParseError* parseError, |
512 | UErrorCode* status ){ |
513 | |
514 | uint16_t result; |
515 | int32_t destIndex=0; |
516 | int32_t srcIndex; |
517 | UBool allowUnassigned = (UBool) ((options & USPREP_ALLOW_UNASSIGNED)>0); |
518 | UStringPrepType type; |
519 | int16_t value; |
520 | UBool isIndex; |
521 | const int32_t* indexes = profile->indexes; |
522 | |
523 | // no error checking the caller check for error and arguments |
524 | // no string length check the caller finds out the string length |
525 | |
526 | for(srcIndex=0;srcIndex<srcLength;){ |
527 | UChar32 ch; |
528 | |
529 | U16_NEXT(src,srcIndex,srcLength,ch); |
530 | |
531 | result=0; |
532 | |
533 | UTRIE_GET16(&profile->sprepTrie,ch,result); |
534 | |
535 | type = getValues(result, value, isIndex); |
536 | |
537 | // check if the source codepoint is unassigned |
538 | if(type == USPREP_UNASSIGNED && allowUnassigned == false){ |
539 | |
540 | uprv_syntaxError(src,srcIndex-U16_LENGTH(ch), srcLength,parseError); |
541 | *status = U_STRINGPREP_UNASSIGNED_ERROR; |
542 | return 0; |
543 | |
544 | }else if(type == USPREP_MAP){ |
545 | |
546 | int32_t index, length; |
547 | |
548 | if(isIndex){ |
549 | index = value; |
550 | if(index >= indexes[_SPREP_ONE_UCHAR_MAPPING_INDEX_START] && |
551 | index < indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START]){ |
552 | length = 1; |
553 | }else if(index >= indexes[_SPREP_TWO_UCHARS_MAPPING_INDEX_START] && |
554 | index < indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START]){ |
555 | length = 2; |
556 | }else if(index >= indexes[_SPREP_THREE_UCHARS_MAPPING_INDEX_START] && |
557 | index < indexes[_SPREP_FOUR_UCHARS_MAPPING_INDEX_START]){ |
558 | length = 3; |
559 | }else{ |
560 | length = profile->mappingData[index++]; |
561 | |
562 | } |
563 | |
564 | /* copy mapping to destination */ |
565 | for(int32_t i=0; i< length; i++){ |
566 | if(destIndex < destCapacity ){ |
567 | dest[destIndex] = profile->mappingData[index+i]; |
568 | } |
569 | destIndex++; /* for pre-flighting */ |
570 | } |
571 | continue; |
572 | }else{ |
573 | // subtract the delta to arrive at the code point |
574 | ch -= value; |
575 | } |
576 | |
577 | }else if(type==USPREP_DELETE){ |
578 | // just consume the codepoint and continue |
579 | continue; |
580 | } |
581 | //copy the code point into destination |
582 | if(ch <= 0xFFFF){ |
583 | if(destIndex < destCapacity ){ |
584 | dest[destIndex] = (char16_t)ch; |
585 | } |
586 | destIndex++; |
587 | }else{ |
588 | if(destIndex+1 < destCapacity ){ |
589 | dest[destIndex] = U16_LEAD(ch); |
590 | dest[destIndex+1] = U16_TRAIL(ch); |
591 | } |
592 | destIndex +=2; |
593 | } |
594 | |
595 | } |
596 | |
597 | return u_terminateUChars(dest, destCapacity, destIndex, status); |
598 | } |
599 | |
600 | /* |
601 | 1) Map -- For each character in the input, check if it has a mapping |
602 | and, if so, replace it with its mapping. |
603 | |
604 | 2) Normalize -- Possibly normalize the result of step 1 using Unicode |
605 | normalization. |
606 | |
607 | 3) Prohibit -- Check for any characters that are not allowed in the |
608 | output. If any are found, return an error. |
609 | |
610 | 4) Check bidi -- Possibly check for right-to-left characters, and if |
611 | any are found, make sure that the whole string satisfies the |
612 | requirements for bidirectional strings. If the string does not |
613 | satisfy the requirements for bidirectional strings, return an |
614 | error. |
615 | [Unicode3.2] defines several bidirectional categories; each character |
616 | has one bidirectional category assigned to it. For the purposes of |
617 | the requirements below, an "RandALCat character" is a character that |
618 | has Unicode bidirectional categories "R" or "AL"; an "LCat character" |
619 | is a character that has Unicode bidirectional category "L". Note |
620 | |
621 | |
622 | that there are many characters which fall in neither of the above |
623 | definitions; Latin digits (<U+0030> through <U+0039>) are examples of |
624 | this because they have bidirectional category "EN". |
625 | |
626 | In any profile that specifies bidirectional character handling, all |
627 | three of the following requirements MUST be met: |
628 | |
629 | 1) The characters in section 5.8 MUST be prohibited. |
630 | |
631 | 2) If a string contains any RandALCat character, the string MUST NOT |
632 | contain any LCat character. |
633 | |
634 | 3) If a string contains any RandALCat character, a RandALCat |
635 | character MUST be the first character of the string, and a |
636 | RandALCat character MUST be the last character of the string. |
637 | */ |
638 | U_CAPI int32_t U_EXPORT2 |
639 | usprep_prepare( const UStringPrepProfile* profile, |
640 | const char16_t* src, int32_t srcLength, |
641 | char16_t* dest, int32_t destCapacity, |
642 | int32_t options, |
643 | UParseError* parseError, |
644 | UErrorCode* status ){ |
645 | |
646 | // check error status |
647 | if(U_FAILURE(*status)){ |
648 | return 0; |
649 | } |
650 | |
651 | //check arguments |
652 | if(profile==nullptr || |
653 | (src==nullptr ? srcLength!=0 : srcLength<-1) || |
654 | (dest==nullptr ? destCapacity!=0 : destCapacity<0)) { |
655 | *status=U_ILLEGAL_ARGUMENT_ERROR; |
656 | return 0; |
657 | } |
658 | |
659 | //get the string length |
660 | if(srcLength < 0){ |
661 | srcLength = u_strlen(src); |
662 | } |
663 | // map |
664 | UnicodeString s1; |
665 | char16_t *b1 = s1.getBuffer(srcLength); |
666 | if(b1==nullptr){ |
667 | *status = U_MEMORY_ALLOCATION_ERROR; |
668 | return 0; |
669 | } |
670 | int32_t b1Len = usprep_map(profile, src, srcLength, |
671 | b1, s1.getCapacity(), options, parseError, status); |
672 | s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); |
673 | |
674 | if(*status == U_BUFFER_OVERFLOW_ERROR){ |
675 | // redo processing of string |
676 | /* we do not have enough room so grow the buffer*/ |
677 | b1 = s1.getBuffer(b1Len); |
678 | if(b1==nullptr){ |
679 | *status = U_MEMORY_ALLOCATION_ERROR; |
680 | return 0; |
681 | } |
682 | |
683 | *status = U_ZERO_ERROR; // reset error |
684 | b1Len = usprep_map(profile, src, srcLength, |
685 | b1, s1.getCapacity(), options, parseError, status); |
686 | s1.releaseBuffer(U_SUCCESS(*status) ? b1Len : 0); |
687 | } |
688 | if(U_FAILURE(*status)){ |
689 | return 0; |
690 | } |
691 | |
692 | // normalize |
693 | UnicodeString s2; |
694 | if(profile->doNFKC){ |
695 | const Normalizer2 *n2 = Normalizer2::getNFKCInstance(*status); |
696 | FilteredNormalizer2 fn2(*n2, *uniset_getUnicode32Instance(*status)); |
697 | if(U_FAILURE(*status)){ |
698 | return 0; |
699 | } |
700 | fn2.normalize(s1, s2, *status); |
701 | }else{ |
702 | s2.fastCopyFrom(s1); |
703 | } |
704 | if(U_FAILURE(*status)){ |
705 | return 0; |
706 | } |
707 | |
708 | // Prohibit and checkBiDi in one pass |
709 | const char16_t *b2 = s2.getBuffer(); |
710 | int32_t b2Len = s2.length(); |
711 | UCharDirection direction=U_CHAR_DIRECTION_COUNT, firstCharDir=U_CHAR_DIRECTION_COUNT; |
712 | UBool leftToRight=false, rightToLeft=false; |
713 | int32_t rtlPos =-1, ltrPos =-1; |
714 | |
715 | for(int32_t b2Index=0; b2Index<b2Len;){ |
716 | UChar32 ch = 0; |
717 | U16_NEXT(b2, b2Index, b2Len, ch); |
718 | |
719 | uint16_t result; |
720 | UTRIE_GET16(&profile->sprepTrie,ch,result); |
721 | |
722 | int16_t value; |
723 | UBool isIndex; |
724 | UStringPrepType type = getValues(result, value, isIndex); |
725 | |
726 | if( type == USPREP_PROHIBITED || |
727 | ((result < _SPREP_TYPE_THRESHOLD) && (result & 0x01) /* first bit says it the code point is prohibited*/) |
728 | ){ |
729 | *status = U_STRINGPREP_PROHIBITED_ERROR; |
730 | uprv_syntaxError(b2, b2Index-U16_LENGTH(ch), b2Len, parseError); |
731 | return 0; |
732 | } |
733 | |
734 | if(profile->checkBiDi) { |
735 | direction = ubidi_getClass(ch); |
736 | if(firstCharDir == U_CHAR_DIRECTION_COUNT){ |
737 | firstCharDir = direction; |
738 | } |
739 | if(direction == U_LEFT_TO_RIGHT){ |
740 | leftToRight = true; |
741 | ltrPos = b2Index-1; |
742 | } |
743 | if(direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC){ |
744 | rightToLeft = true; |
745 | rtlPos = b2Index-1; |
746 | } |
747 | } |
748 | } |
749 | if(profile->checkBiDi){ |
750 | // satisfy 2 |
751 | if( leftToRight && rightToLeft){ |
752 | *status = U_STRINGPREP_CHECK_BIDI_ERROR; |
753 | uprv_syntaxError(b2,(rtlPos>ltrPos) ? rtlPos : ltrPos, b2Len, parseError); |
754 | return 0; |
755 | } |
756 | |
757 | //satisfy 3 |
758 | if( rightToLeft && |
759 | !((firstCharDir == U_RIGHT_TO_LEFT || firstCharDir == U_RIGHT_TO_LEFT_ARABIC) && |
760 | (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC)) |
761 | ){ |
762 | *status = U_STRINGPREP_CHECK_BIDI_ERROR; |
763 | uprv_syntaxError(b2, rtlPos, b2Len, parseError); |
764 | return false; |
765 | } |
766 | } |
767 | return s2.extract(dest, destCapacity, *status); |
768 | } |
769 | |
770 | |
771 | /* data swapping ------------------------------------------------------------ */ |
772 | |
773 | U_CAPI int32_t U_EXPORT2 |
774 | usprep_swap(const UDataSwapper *ds, |
775 | const void *inData, int32_t length, void *outData, |
776 | UErrorCode *pErrorCode) { |
777 | const UDataInfo *pInfo; |
778 | int32_t headerSize; |
779 | |
780 | const uint8_t *inBytes; |
781 | uint8_t *outBytes; |
782 | |
783 | const int32_t *inIndexes; |
784 | int32_t indexes[16]; |
785 | |
786 | int32_t i, offset, count, size; |
787 | |
788 | /* udata_swapDataHeader checks the arguments */ |
789 | headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); |
790 | if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { |
791 | return 0; |
792 | } |
793 | |
794 | /* check data format and format version */ |
795 | pInfo=(const UDataInfo *)((const char *)inData+4); |
796 | if(!( |
797 | pInfo->dataFormat[0]==0x53 && /* dataFormat="SPRP" */ |
798 | pInfo->dataFormat[1]==0x50 && |
799 | pInfo->dataFormat[2]==0x52 && |
800 | pInfo->dataFormat[3]==0x50 && |
801 | pInfo->formatVersion[0]==3 |
802 | )) { |
803 | udata_printError(ds, "usprep_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as StringPrep .spp data\n" , |
804 | pInfo->dataFormat[0], pInfo->dataFormat[1], |
805 | pInfo->dataFormat[2], pInfo->dataFormat[3], |
806 | pInfo->formatVersion[0]); |
807 | *pErrorCode=U_UNSUPPORTED_ERROR; |
808 | return 0; |
809 | } |
810 | |
811 | inBytes=(const uint8_t *)inData+headerSize; |
812 | outBytes= (outData == nullptr ) ? nullptr : (uint8_t *)outData+headerSize; |
813 | |
814 | inIndexes=(const int32_t *)inBytes; |
815 | |
816 | if(length>=0) { |
817 | length-=headerSize; |
818 | if(length<16*4) { |
819 | udata_printError(ds, "usprep_swap(): too few bytes (%d after header) for StringPrep .spp data\n" , |
820 | length); |
821 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
822 | return 0; |
823 | } |
824 | } |
825 | |
826 | /* read the first 16 indexes (ICU 2.8/format version 3: _SPREP_INDEX_TOP==16, might grow) */ |
827 | for(i=0; i<16; ++i) { |
828 | indexes[i]=udata_readInt32(ds, inIndexes[i]); |
829 | } |
830 | |
831 | /* calculate the total length of the data */ |
832 | size= |
833 | 16*4+ /* size of indexes[] */ |
834 | indexes[_SPREP_INDEX_TRIE_SIZE]+ |
835 | indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; |
836 | |
837 | if(length>=0) { |
838 | if(length<size) { |
839 | udata_printError(ds, "usprep_swap(): too few bytes (%d after header) for all of StringPrep .spp data\n" , |
840 | length); |
841 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
842 | return 0; |
843 | } |
844 | |
845 | /* copy the data for inaccessible bytes */ |
846 | if(inBytes!=outBytes) { |
847 | uprv_memcpy(outBytes, inBytes, size); |
848 | } |
849 | |
850 | offset=0; |
851 | |
852 | /* swap the int32_t indexes[] */ |
853 | count=16*4; |
854 | ds->swapArray32(ds, inBytes, count, outBytes, pErrorCode); |
855 | offset+=count; |
856 | |
857 | /* swap the UTrie */ |
858 | count=indexes[_SPREP_INDEX_TRIE_SIZE]; |
859 | utrie_swap(ds, inBytes+offset, count, outBytes+offset, pErrorCode); |
860 | offset+=count; |
861 | |
862 | /* swap the uint16_t mappingTable[] */ |
863 | count=indexes[_SPREP_INDEX_MAPPING_DATA_SIZE]; |
864 | ds->swapArray16(ds, inBytes+offset, count, outBytes+offset, pErrorCode); |
865 | //offset+=count; |
866 | } |
867 | |
868 | return headerSize+size; |
869 | } |
870 | |
871 | #endif /* #if !UCONFIG_NO_IDNA */ |
872 | |