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
40U_NAMESPACE_USE
41
42U_CDECL_BEGIN
43
44/*
45Static cache for already opened StringPrep profiles
46*/
47static UHashtable *SHARED_DATA_HASHTABLE = nullptr;
48static icu::UInitOnce gSharedDataInitOnce {};
49
50static 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 */
55static UVersionInfo dataVersion={ 0, 0, 0, 0 };
56
57/* Profile names must be aligned to UStringPrepProfileType */
58static 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
75static UBool U_CALLCONV
76isSPrepAcceptable(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
100static int32_t U_CALLCONV
101getSPrepFoldingOffset(uint32_t data) {
102
103 return (int32_t)data;
104
105}
106
107/* hashes an entry */
108static int32_t U_CALLCONV
109hashEntry(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 */
120static UBool U_CALLCONV
121compareEntries(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
133static void
134usprep_unload(UStringPrepProfile* data){
135 udata_close(data->sprepData);
136}
137
138static int32_t
139usprep_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()
189static int32_t
190usprep_flushCache(){
191 return usprep_internal_flushCache(false);
192}
193*/
194
195static 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}
206U_CDECL_END
207
208
209/** Initializes the cache for resources */
210static void U_CALLCONV
211createCache(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
219static void
220initCache(UErrorCode *status) {
221 umtx_initOnce(gSharedDataInitOnce, &createCache, *status);
222}
223
224static UBool U_CALLCONV
225loadData(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
304static UStringPrepProfile*
305usprep_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
391U_CAPI UStringPrepProfile* U_EXPORT2
392usprep_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
404U_CAPI UStringPrepProfile* U_EXPORT2
405usprep_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
418U_CAPI void U_EXPORT2
419usprep_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
433U_CFUNC void
434uprv_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
466static inline UStringPrepType
467getValues(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 *
506static int32_t
507usprep_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*/
638U_CAPI int32_t U_EXPORT2
639usprep_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
773U_CAPI int32_t U_EXPORT2
774usprep_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