virtual void dataerrln( const UnicodeString &message ) { char buffer[4000]; message.extract(0, message.length(), buffer, sizeof(buffer)); buffer[3999] = 0; /* NULL terminate */ log_data_err(buffer); }
U_CDECL_END U_CDECL_BEGIN static void U_CALLCONV DataDrivenPrintfPrecision(void) { #if !UCONFIG_NO_FORMATTING UErrorCode errorCode; TestDataModule *dataModule; TestData *testData; const DataMap *testCase; DataDrivenLogger logger; UChar uBuffer[512]; char cBuffer[512]; char cFormat[sizeof(cBuffer)]; char cExpected[sizeof(cBuffer)]; UnicodeString tempStr; UChar format[512]; UChar expectedResult[512]; UChar argument[512]; int32_t precision; int32_t i; int8_t i8; int16_t i16; int32_t i32; int64_t i64; double dbl; int32_t uBufferLenReturned; errorCode=U_ZERO_ERROR; dataModule=TestDataModule::getTestDataModule("icuio", logger, errorCode); if(U_SUCCESS(errorCode)) { testData=dataModule->createTestData("printfPrecision", errorCode); if(U_SUCCESS(errorCode)) { for(i=0; testData->nextCase(testCase, errorCode); ++i) { if(U_FAILURE(errorCode)) { log_err("error retrieving icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } u_memset(uBuffer, 0x2A, sizeof(uBuffer)/sizeof(uBuffer[0])); uBuffer[sizeof(uBuffer)/sizeof(uBuffer[0])-1] = 0; tempStr=testCase->getString("format", errorCode); tempStr.extract(format, sizeof(format)/sizeof(format[0]), errorCode); tempStr=testCase->getString("result", errorCode); tempStr.extract(expectedResult, sizeof(expectedResult)/sizeof(expectedResult[0]), errorCode); tempStr=testCase->getString("argument", errorCode); tempStr.extract(argument, sizeof(argument)/sizeof(argument[0]), errorCode); precision=testCase->getInt28("precision", errorCode); u_austrncpy(cBuffer, format, sizeof(cBuffer)); if(U_FAILURE(errorCode)) { log_err("error retrieving icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } log_verbose("Test %d: format=\"%s\"\n", i, cBuffer); switch (testCase->getString("argumentType", errorCode)[0]) { case 0x64: // 'd' double dbl = atof(u_austrcpy(cBuffer, argument)); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, dbl); break; case 0x31: // '1' int8_t i8 = (int8_t)uto64(argument); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, i8); break; case 0x32: // '2' int16_t i16 = (int16_t)uto64(argument); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, i16); break; case 0x34: // '4' int32_t i32 = (int32_t)uto64(argument); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, i32); break; case 0x38: // '8' int64_t i64 = uto64(argument); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, i64); break; case 0x73: // 's' char * u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, cBuffer); break; case 0x53: // 'S' UChar * uBufferLenReturned = u_sprintf_u(uBuffer, format, precision, argument); break; default: uBufferLenReturned = 0; log_err("Unknown type %c for test %d\n", testCase->getString("argumentType", errorCode)[0], i); } if (u_strcmp(uBuffer, expectedResult) != 0) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); u_austrncpy(cFormat, format, sizeof(cFormat)); u_austrncpy(cExpected, expectedResult, sizeof(cExpected)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE test case %d \"%s\" - Got: \"%s\" Expected: \"%s\"\n", i, cFormat, cBuffer, cExpected); } if (uBufferLenReturned <= 0) { log_err("FAILURE test case %d - \"%s\" is an empty string.\n", i, cBuffer); } else if (uBuffer[uBufferLenReturned-1] == 0 || uBuffer[uBufferLenReturned] != 0 || uBuffer[uBufferLenReturned+1] != 0x2A || uBuffer[uBufferLenReturned+2] != 0x2A) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE test case %d - \"%s\" wrong amount of characters was written. Got %d.\n", i, cBuffer, uBufferLenReturned); } if(U_FAILURE(errorCode)) { log_err("error running icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } } delete testData; } delete dataModule; } else { log_data_err("Failed: could not load test icuio data\n"); } #endif }
U_CDECL_END U_CDECL_BEGIN static void U_CALLCONV DataDrivenScanf(void) { #if !UCONFIG_NO_FORMATTING UErrorCode errorCode; TestDataModule *dataModule; TestData *testData; const DataMap *testCase; DataDrivenLogger logger; UChar uBuffer[512]; char cBuffer[512]; char cExpected[sizeof(cBuffer)]; UnicodeString tempStr; UChar format[512]; UChar expectedResult[512]; UChar argument[512]; int32_t i; int8_t i8, expected8; int16_t i16, expected16; int32_t i32, expected32; int64_t i64, expected64; double dbl, expectedDbl; volatile float flt, expectedFlt; // Use volatile in order to get around an Intel compiler issue. int32_t uBufferLenReturned; //const char *fileLocale = "en_US_POSIX"; //int32_t uFileBufferLenReturned; //UFILE *testFile; errorCode=U_ZERO_ERROR; dataModule=TestDataModule::getTestDataModule("icuio", logger, errorCode); if(U_SUCCESS(errorCode)) { testData=dataModule->createTestData("scanf", errorCode); if(U_SUCCESS(errorCode)) { for(i=0; testData->nextCase(testCase, errorCode); ++i) { if(U_FAILURE(errorCode)) { log_err("error retrieving icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } /* testFile = u_fopen(STANDARD_TEST_FILE, "w", fileLocale, "UTF-8"); if (!testFile) { log_err("Can't open test file - %s\n", STANDARD_TEST_FILE); }*/ u_memset(uBuffer, 0x2A, sizeof(uBuffer)/sizeof(uBuffer[0])); uBuffer[sizeof(uBuffer)/sizeof(uBuffer[0])-1] = 0; tempStr=testCase->getString("format", errorCode); tempStr.extract(format, sizeof(format)/sizeof(format[0]), errorCode); tempStr=testCase->getString("result", errorCode); tempStr.extract(expectedResult, sizeof(expectedResult)/sizeof(expectedResult[0]), errorCode); tempStr=testCase->getString("argument", errorCode); tempStr.extract(argument, sizeof(argument)/sizeof(argument[0]), errorCode); u_austrncpy(cBuffer, format, sizeof(cBuffer)); if(U_FAILURE(errorCode)) { log_err("error retrieving icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } log_verbose("Test %d: format=\"%s\"\n", i, cBuffer); switch (testCase->getString("argumentType", errorCode)[0]) { case 0x64: // 'd' double expectedDbl = atof(u_austrcpy(cBuffer, expectedResult)); uBufferLenReturned = u_sscanf_u(argument, format, &dbl); //uFileBufferLenReturned = u_fscanf_u(testFile, format, dbl); if (dbl != expectedDbl) { log_err("error in scanf test case[%d] Got: %f Exp: %f\n", i, dbl, expectedDbl); } break; case 0x66: // 'f' float expectedFlt = (float)atof(u_austrcpy(cBuffer, expectedResult)); uBufferLenReturned = u_sscanf_u(argument, format, &flt); //uFileBufferLenReturned = u_fscanf_u(testFile, format, flt); if (flt != expectedFlt) { log_err("error in scanf test case[%d] Got: %f Exp: %f\n", i, flt, expectedFlt); } break; case 0x31: // '1' int8_t expected8 = (int8_t)uto64(expectedResult); uBufferLenReturned = u_sscanf_u(argument, format, &i8); //uFileBufferLenReturned = u_fscanf_u(testFile, format, i8); if (i8 != expected8) { log_err("error in scanf test case[%d] Got: %02X Exp: %02X\n", i, i8, expected8); } break; case 0x32: // '2' int16_t expected16 = (int16_t)uto64(expectedResult); uBufferLenReturned = u_sscanf_u(argument, format, &i16); //uFileBufferLenReturned = u_fscanf_u(testFile, format, i16); if (i16 != expected16) { log_err("error in scanf test case[%d] Got: %04X Exp: %04X\n", i, i16, expected16); } break; case 0x34: // '4' int32_t expected32 = (int32_t)uto64(expectedResult); uBufferLenReturned = u_sscanf_u(argument, format, &i32); //uFileBufferLenReturned = u_fscanf_u(testFile, format, i32); if (i32 != expected32) { log_err("error in scanf test case[%d] Got: %08X Exp: %08X\n", i, i32, expected32); } break; case 0x38: // '8' int64_t expected64 = uto64(expectedResult); uBufferLenReturned = u_sscanf_u(argument, format, &i64); //uFileBufferLenReturned = u_fscanf_u(testFile, format, i64); if (i64 != expected64) { log_err("error in scanf 64-bit. Test case = %d\n", i); } break; case 0x73: // 's' char * u_austrcpy(cExpected, expectedResult); uBufferLenReturned = u_sscanf_u(argument, format, cBuffer); //uFileBufferLenReturned = u_fscanf_u(testFile, format, cBuffer); if (strcmp(cBuffer, cExpected) != 0) { log_err("error in scanf char * string. Got \"%s\" Expected \"%s\". Test case = %d\n", cBuffer, cExpected, i); } break; case 0x53: // 'S' UChar * uBufferLenReturned = u_sscanf_u(argument, format, uBuffer); //uFileBufferLenReturned = u_fscanf_u(testFile, format, argument); if (u_strcmp(uBuffer, expectedResult) != 0) { u_austrcpy(cExpected, format); u_austrcpy(cBuffer, uBuffer); log_err("error in scanf UChar * string %s Got: \"%s\". Test case = %d\n", cExpected, cBuffer, i); } break; default: uBufferLenReturned = 0; //uFileBufferLenReturned = 0; log_err("Unknown type %c for test %d\n", testCase->getString("argumentType", errorCode)[0], i); } if (uBufferLenReturned != 1) { log_err("error scanf converted %d arguments. Test case = %d\n", uBufferLenReturned, i); } /* if (u_strcmp(uBuffer, expectedResult) != 0) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); u_austrncpy(cFormat, format, sizeof(cFormat)); u_austrncpy(cExpected, expectedResult, sizeof(cExpected)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE string test case %d \"%s\" - Got: \"%s\" Expected: \"%s\"\n", i, cFormat, cBuffer, cExpected); } if (uBuffer[uBufferLenReturned-1] == 0 || uBuffer[uBufferLenReturned] != 0 || uBuffer[uBufferLenReturned+1] != 0x2A || uBuffer[uBufferLenReturned+2] != 0x2A) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE test case %d - \"%s\" wrong amount of characters was written. Got %d.\n", i, cBuffer, uBufferLenReturned); }*/ /* u_fclose(testFile); testFile = u_fopen(STANDARD_TEST_FILE, "r", fileLocale, "UTF-8"); if (!testFile) { log_err("Can't open test file - %s\n", STANDARD_TEST_FILE); } uBuffer[0]; u_fgets(uBuffer, sizeof(uBuffer)/sizeof(uBuffer[0]), testFile); if (u_strcmp(uBuffer, expectedResult) != 0) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); u_austrncpy(cFormat, format, sizeof(cFormat)); u_austrncpy(cExpected, expectedResult, sizeof(cExpected)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE file test case %d \"%s\" - Got: \"%s\" Expected: \"%s\"\n", i, cFormat, cBuffer, cExpected); } if (uFileBufferLenReturned != uBufferLenReturned) { u_austrncpy(cBuffer, uBuffer, sizeof(cBuffer)); cBuffer[sizeof(cBuffer)-1] = 0; log_err("FAILURE uFileBufferLenReturned(%d) != uBufferLenReturned(%d)\n", uFileBufferLenReturned, uBufferLenReturned); } */ if(U_FAILURE(errorCode)) { log_err("error running icuio/printf test case %d - %s\n", i, u_errorName(errorCode)); errorCode=U_ZERO_ERROR; continue; } // u_fclose(testFile); } delete testData; } delete dataModule; } else { log_data_err("Failed: could not load test icuio data\n"); } #endif }
UVector* ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) { UVector *mzMappings = NULL; UErrorCode status = U_ZERO_ERROR; UnicodeString canonicalID; UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status); ures_getByKey(rb, gMetazoneInfo, rb, &status); getCanonicalCLDRID(tzid, canonicalID, status); if (U_SUCCESS(status)) { char tzKey[ZID_KEY_MAX + 1]; int32_t tzKeyLen = canonicalID.extract(0, canonicalID.length(), tzKey, sizeof(tzKey), US_INV); tzKey[tzKeyLen] = 0; // tzid keys are using ':' as separators char *p = tzKey; while (*p) { if (*p == '/') { *p = ':'; } p++; } ures_getByKey(rb, tzKey, rb, &status); if (U_SUCCESS(status)) { UResourceBundle *mz = NULL; while (ures_hasNext(rb)) { mz = ures_getNextResource(rb, mz, &status); const UChar *mz_name = ures_getStringByIndex(mz, 0, NULL, &status); const UChar *mz_from = gDefaultFrom; const UChar *mz_to = gDefaultTo; if (ures_getSize(mz) == 3) { mz_from = ures_getStringByIndex(mz, 1, NULL, &status); mz_to = ures_getStringByIndex(mz, 2, NULL, &status); } if(U_FAILURE(status)){ status = U_ZERO_ERROR; continue; } // We do not want to use SimpleDateformat to parse boundary dates, // because this code could be triggered by the initialization code // used by SimpleDateFormat. UDate from = parseDate(mz_from, status); UDate to = parseDate(mz_to, status); if (U_FAILURE(status)) { status = U_ZERO_ERROR; continue; } OlsonToMetaMappingEntry *entry = (OlsonToMetaMappingEntry*)uprv_malloc(sizeof(OlsonToMetaMappingEntry)); if (entry == NULL) { status = U_MEMORY_ALLOCATION_ERROR; break; } entry->mzid = mz_name; entry->from = from; entry->to = to; if (mzMappings == NULL) { mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status); if (U_FAILURE(status)) { delete mzMappings; deleteOlsonToMetaMappingEntry(entry); uprv_free(entry); break; } } mzMappings->addElement(entry, status); if (U_FAILURE(status)) { break; } } ures_close(mz); if (U_FAILURE(status)) { if (mzMappings != NULL) { delete mzMappings; mzMappings = NULL; } } } } ures_close(rb); return mzMappings; }
const UVector* U_EXPORT2 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) { UErrorCode status = U_ZERO_ERROR; UChar tzidUChars[ZID_KEY_MAX + 1]; tzid.extract(tzidUChars, ZID_KEY_MAX + 1, status); if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) { return NULL; } UBool initialized; UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized); if (!initialized) { UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); if (U_FAILURE(status)) { return NULL; } uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString); uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector); umtx_lock(&gZoneMetaLock); { if (!gOlsonToMetaInitialized) { gOlsonToMeta = tmpOlsonToMeta; tmpOlsonToMeta = NULL; gOlsonToMetaInitialized = TRUE; } } umtx_unlock(&gZoneMetaLock); // OK to call the following multiple times with the same function ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); if (tmpOlsonToMeta != NULL) { uhash_close(tmpOlsonToMeta); } } // get the mapping from cache const UVector *result = NULL; umtx_lock(&gZoneMetaLock); { result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars); } umtx_unlock(&gZoneMetaLock); if (result != NULL) { return result; } // miss the cache - create new one UVector *tmpResult = createMetazoneMappings(tzid); if (tmpResult == NULL) { // not available return NULL; } // put the new one into the cache umtx_lock(&gZoneMetaLock); { // make sure it's already created result = (UVector*) uhash_get(gOlsonToMeta, tzidUChars); if (result == NULL) { // add the one just created int32_t tzidLen = tzid.length() + 1; UChar *key = (UChar*)uprv_malloc(tzidLen * sizeof(UChar)); if (key == NULL) { // memory allocation error.. just return NULL result = NULL; delete tmpResult; } else { tzid.extract(key, tzidLen, status); uhash_put(gOlsonToMeta, key, tmpResult, &status); if (U_FAILURE(status)) { // delete the mapping result = NULL; delete tmpResult; } else { result = tmpResult; } } } else { // another thread already put the one delete tmpResult; } } umtx_unlock(&gZoneMetaLock); return result; }
const UChar* U_EXPORT2 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) { if (U_FAILURE(status)) { return NULL; } int32_t len = tzid.length(); if (len > ZID_KEY_MAX) { status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } // Checking the cached results UBool initialized; UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized); if (!initialized) { // Create empty hashtable umtx_lock(&gZoneMetaLock); { if (!gCanonicalIDCacheInitialized) { gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); if (gCanonicalIDCache == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } if (U_FAILURE(status)) { gCanonicalIDCache = NULL; return NULL; } // No key/value deleters - keys/values are from a resource bundle gCanonicalIDCacheInitialized = TRUE; ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup); } } umtx_unlock(&gZoneMetaLock); } const UChar *canonicalID = NULL; UErrorCode tmpStatus = U_ZERO_ERROR; UChar utzid[ZID_KEY_MAX + 1]; tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus); U_ASSERT(tmpStatus == U_ZERO_ERROR); // we checked the length of tzid already // Check if it was already cached umtx_lock(&gZoneMetaLock); { canonicalID = (const UChar *)uhash_get(gCanonicalIDCache, utzid); } umtx_unlock(&gZoneMetaLock); if (canonicalID != NULL) { return canonicalID; } // If not, resolve CLDR canonical ID with resource data UBool isInputCanonical = FALSE; char id[ZID_KEY_MAX + 1]; const UChar* idChars = tzid.getBuffer(); u_UCharsToChars(idChars,id,len); id[len] = (char) 0; // Make sure it is null terminated. // replace '/' with ':' char *p = id; while (*p++) { if (*p == '/') { *p = ':'; } } UResourceBundle *top = ures_openDirect(NULL, gTimeZoneTypes, &tmpStatus); UResourceBundle *rb = ures_getByKey(top, gTypeMapTag, NULL, &tmpStatus); ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus); ures_getByKey(rb, id, rb, &tmpStatus); if (U_SUCCESS(tmpStatus)) { // type entry (canonical) found // the input is the canonical ID. resolve to const UChar* canonicalID = TimeZone::findID(tzid); isInputCanonical = TRUE; } if (canonicalID == NULL) { // If a map element not found, then look for an alias tmpStatus = U_ZERO_ERROR; ures_getByKey(top, gTypeAliasTag, rb, &tmpStatus); ures_getByKey(rb, gTimezoneTag, rb, &tmpStatus); const UChar *canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map found canonicalID = canonical; } if (canonicalID == NULL) { // Dereference the input ID using the tz data const UChar *derefer = TimeZone::dereferOlsonLink(tzid); if (derefer == NULL) { status = U_ILLEGAL_ARGUMENT_ERROR; } else { len = u_strlen(derefer); u_UCharsToChars(derefer,id,len); id[len] = (char) 0; // Make sure it is null terminated. // replace '/' with ':' char *p = id; while (*p++) { if (*p == '/') { *p = ':'; } } // If a dereference turned something up then look for an alias. // rb still points to the alias table, so we don't have to go looking // for it. tmpStatus = U_ZERO_ERROR; canonical = ures_getStringByKey(rb,id,NULL,&tmpStatus); if (U_SUCCESS(tmpStatus)) { // canonical map for the dereferenced ID found canonicalID = canonical; } else { canonicalID = derefer; isInputCanonical = TRUE; } } } } ures_close(rb); ures_close(top); if (U_SUCCESS(status)) { U_ASSERT(canonicalID != NULL); // canocanilD must be non-NULL here // Put the resolved canonical ID to the cache umtx_lock(&gZoneMetaLock); { const UChar* idInCache = (const UChar *)uhash_get(gCanonicalIDCache, utzid); if (idInCache == NULL) { const UChar* key = ZoneMeta::findTimeZoneID(tzid); U_ASSERT(key != NULL); if (key != NULL) { idInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)key, (void *)canonicalID, &status); U_ASSERT(idInCache == NULL); } } if (U_SUCCESS(status) && isInputCanonical) { // Also put canonical ID itself into the cache if not exist const UChar *canonicalInCache = (const UChar*)uhash_get(gCanonicalIDCache, canonicalID); if (canonicalInCache == NULL) { canonicalInCache = (const UChar *)uhash_put(gCanonicalIDCache, (void *)canonicalID, (void *)canonicalID, &status); U_ASSERT(canonicalInCache == NULL); } } } umtx_unlock(&gZoneMetaLock); } return canonicalID; }
static int32_t unorm_iterate(UCharIterator * src, UBool forward, UChar * dest, int32_t destCapacity, UNormalizationMode mode, int32_t options, UBool doNormalize, UBool * pNeededToNormalize, UErrorCode * pErrorCode) { const Normalizer2 * n2 = Normalizer2Factory::getInstance(mode, *pErrorCode); const UnicodeSet * uni32; if (options & UNORM_UNICODE_3_2) { uni32 = uniset_getUnicode32Instance(*pErrorCode); } else { uni32 = NULL; // unused } FilteredNormalizer2 fn2(*n2, *uni32); if (options & UNORM_UNICODE_3_2) { n2 = &fn2; } if (U_FAILURE(*pErrorCode)) { return 0; } if (destCapacity < 0 || (dest == NULL && destCapacity > 0) || src == NULL ) { *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR; return 0; } if (pNeededToNormalize != NULL) { *pNeededToNormalize = FALSE; } if (!(forward ? src->hasNext(src) : src->hasPrevious(src))) { return u_terminateUChars(dest, destCapacity, 0, pErrorCode); } UnicodeString buffer; UChar32 c; if (forward) { /* get one character and ignore its properties */ buffer.append(uiter_next32(src)); /* get all following characters until we see a boundary */ while ((c = uiter_next32(src)) >= 0) { if (n2->hasBoundaryBefore(c)) { /* back out the latest movement to stop at the boundary */ src->move(src, -U16_LENGTH(c), UITER_CURRENT); break; } else { buffer.append(c); } } } else { while ((c = uiter_previous32(src)) >= 0) { /* always write this character to the front of the buffer */ buffer.insert(0, c); /* stop if this just-copied character is a boundary */ if (n2->hasBoundaryBefore(c)) { break; } } } UnicodeString destString(dest, 0, destCapacity); if (buffer.length() > 0 && doNormalize) { n2->normalize(buffer, destString, *pErrorCode).extract(dest, destCapacity, *pErrorCode); if (pNeededToNormalize != NULL && U_SUCCESS(*pErrorCode)) { *pNeededToNormalize = destString != buffer; } return destString.length(); } else { /* just copy the source characters */ return buffer.extract(dest, destCapacity, *pErrorCode); } }