Changeset 261257 in webkit


Ignore:
Timestamp:
May 6, 2020, 4:01:06 PM (5 years ago)
Author:
Darin Adler
Message:

Make a helper for the pattern of ICU functions that may need to be called twice to populate a buffer
https://bugs.webkit.org/show_bug.cgi?id=211499

Reviewed by Ross Kirsling.

Source/JavaScriptCore:

  • runtime/IntlDateTimeFormat.cpp:

(JSC::defaultTimeZone): Use callBufferProducingFunction.
(JSC::canonicalizeTimeZoneName): Ditto.
(JSC::IntlDateTimeFormat::initializeDateTimeFormat): Ditto.
(JSC::IntlDateTimeFormat::format const): Ditto.
(JSC::IntlDateTimeFormat::formatToParts const): Ditto.

  • runtime/IntlLocale.cpp:

(JSC::LocaleIDBuilder::toCanonical): Ditto.
(JSC::IntlLocale::language): Ditto.
(JSC::IntlLocale::script): Ditto.
(JSC::IntlLocale::region): Ditto.

  • runtime/IntlNumberFormat.cpp:

(JSC::IntlNumberFormat::format const): Ditto.
(JSC::IntlNumberFormat::formatToParts const): Ditto.

  • runtime/IntlObject.cpp:

(JSC::languageTagForLocaleID): Ditto.

  • runtime/IntlRelativeTimeFormat.cpp:

(JSC::IntlRelativeTimeFormat::formatInternal const): Ditto.
(JSC::IntlRelativeTimeFormat::formatToParts const): Ditto.

  • runtime/StringPrototype.cpp:

(JSC::toLocaleCase): Ditto.

Source/WebCore:

  • editing/TextIterator.cpp:

(WebCore::normalizeCharacters): Use callBufferProducingFunction.

Source/WTF:

This first cut version is ready to be used in most, but not all, of the places we use the
needsToGrowToProduceBuffer function. The places it is not right for yet are ones that have
special considerations because of null character termination or destinations that are
not a Vector. Later we can refine that further, if we like, and possibly use something
similar in call sites that use needsToGrowToProduceCString as well.

  • wtf/unicode/icu/ICUHelpers.h:

(WTF::needsToGrowToProduceBuffer): Changed to constexpr, since we can.
(WTF::needsToGrowToProduceCString): Ditto.
(WTF::CallBufferProducingFunction::findVector): Added. Implementation detail
of callBufferProducingFunction.
(WTF::CallBufferProducingFunction::argumentTuple): Ditto.
(WTF::callBufferProducingFunction): Added.

Location:
trunk/Source
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r261233 r261257  
     12020-05-06  Darin Adler  <darin@apple.com>
     2
     3        Make a helper for the pattern of ICU functions that may need to be called twice to populate a buffer
     4        https://bugs.webkit.org/show_bug.cgi?id=211499
     5
     6        Reviewed by Ross Kirsling.
     7
     8        * runtime/IntlDateTimeFormat.cpp:
     9        (JSC::defaultTimeZone): Use callBufferProducingFunction.
     10        (JSC::canonicalizeTimeZoneName): Ditto.
     11        (JSC::IntlDateTimeFormat::initializeDateTimeFormat): Ditto.
     12        (JSC::IntlDateTimeFormat::format const): Ditto.
     13        (JSC::IntlDateTimeFormat::formatToParts const): Ditto.
     14        * runtime/IntlLocale.cpp:
     15        (JSC::LocaleIDBuilder::toCanonical): Ditto.
     16        (JSC::IntlLocale::language): Ditto.
     17        (JSC::IntlLocale::script): Ditto.
     18        (JSC::IntlLocale::region): Ditto.
     19        * runtime/IntlNumberFormat.cpp:
     20        (JSC::IntlNumberFormat::format const): Ditto.
     21        (JSC::IntlNumberFormat::formatToParts const): Ditto.
     22        * runtime/IntlObject.cpp:
     23        (JSC::languageTagForLocaleID): Ditto.
     24        * runtime/IntlRelativeTimeFormat.cpp:
     25        (JSC::IntlRelativeTimeFormat::formatInternal const): Ditto.
     26        (JSC::IntlRelativeTimeFormat::formatToParts const): Ditto.
     27        * runtime/StringPrototype.cpp:
     28        (JSC::toLocaleCase): Ditto.
     29
    1302020-05-06  Devin Rousso  <drousso@apple.com>
    231
  • trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp

    r261182 r261257  
    106106static String defaultTimeZone()
    107107{
    108     UErrorCode status = U_ZERO_ERROR;
    109108    String canonical;
    110109
    111     Vector<UChar, 32> buffer(32);
    112     auto bufferLength = ucal_getDefaultTimeZone(buffer.data(), buffer.size(), &status);
    113     if (needsToGrowToProduceBuffer(status)) {
    114         status = U_ZERO_ERROR;
    115         buffer.grow(bufferLength);
    116         ucal_getDefaultTimeZone(buffer.data(), bufferLength, &status);
    117     }
     110    Vector<UChar, 32> buffer;
     111    auto status = callBufferProducingFunction(ucal_getDefaultTimeZone, buffer);
    118112    if (U_SUCCESS(status)) {
    119         status = U_ZERO_ERROR;
    120         Vector<UChar, 32> canonicalBuffer(32);
    121         auto canonicalLength = ucal_getCanonicalTimeZoneID(buffer.data(), bufferLength, canonicalBuffer.data(), canonicalBuffer.size(), nullptr, &status);
    122         if (needsToGrowToProduceBuffer(status)) {
    123             status = U_ZERO_ERROR;
    124             canonicalBuffer.grow(canonicalLength);
    125             ucal_getCanonicalTimeZoneID(buffer.data(), bufferLength, canonicalBuffer.data(), canonicalLength, nullptr, &status);
    126         }
     113        Vector<UChar, 32> canonicalBuffer;
     114        status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, buffer.data(), buffer.size(), canonicalBuffer, nullptr);
    127115        if (U_SUCCESS(status))
    128             canonical = String(canonicalBuffer.data(), canonicalLength);
     116            canonical = String(canonicalBuffer);
    129117    }
    130118
     
    147135        status = U_ZERO_ERROR;
    148136        int32_t ianaTimeZoneLength;
    149         // Time zone names are respresented as UChar[] in all related ICU apis.
     137        // Time zone names are represented as UChar[] in all related ICU APIs.
    150138        const UChar* ianaTimeZone = uenum_unext(timeZones, &ianaTimeZoneLength, &status);
    151139        ASSERT(U_SUCCESS(status));
     
    164152        // 2. If ianaTimeZone is a Link name, then let ianaTimeZone be the corresponding Zone name as specified in the “backward” file of the IANA Time Zone Database.
    165153
    166         Vector<UChar, 32> buffer(ianaTimeZoneLength);
    167         status = U_ZERO_ERROR;
    168         auto canonicalLength = ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), ianaTimeZoneLength, nullptr, &status);
    169         if (needsToGrowToProduceBuffer(status)) {
    170             buffer.grow(canonicalLength);
    171             status = U_ZERO_ERROR;
    172             ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), canonicalLength, nullptr, &status);
    173         }
    174         ASSERT(U_SUCCESS(status));
    175         canonical = String(buffer.data(), canonicalLength);
     154        Vector<UChar, 32> buffer;
     155        auto status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, ianaTimeZone, ianaTimeZoneLength, buffer, nullptr);
     156        ASSERT_UNUSED(status, U_SUCCESS(status));
     157        canonical = String(buffer);
    176158    } while (canonical.isNull());
    177159    uenum_close(timeZones);
     
    637619    String skeleton = skeletonBuilder.toString();
    638620    StringView skeletonView(skeleton);
    639     Vector<UChar, 32> patternBuffer(32);
    640     status = U_ZERO_ERROR;
    641     auto patternLength = udatpg_getBestPatternWithOptions(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer.data(), patternBuffer.size(), &status);
    642     if (needsToGrowToProduceBuffer(status)) {
    643         status = U_ZERO_ERROR;
    644         patternBuffer.grow(patternLength);
    645         udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternLength, &status);
    646     }
     621    Vector<UChar, 32> patternBuffer;
     622    status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator, skeletonView.upconvertedCharacters(), skeletonView.length(), UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer);
    647623    udatpg_close(generator);
    648624    if (U_FAILURE(status)) {
     
    663639        bool isEscaped = false;
    664640        bool hasHour = false;
    665         for (auto i = 0; i < patternLength; ++i) {
    666             UChar c = patternBuffer[i];
     641        for (auto& c : patternBuffer) {
    667642            if (c == '\'')
    668643                isEscaped = !isEscaped;
    669644            else if (!isEscaped && (c == 'h' || c == 'H' || c == 'k' || c == 'K')) {
    670                 patternBuffer[i] = hour;
     645                c = hour;
    671646                hasHour = true;
    672647            }
     
    676651    }
    677652
    678     StringView pattern(patternBuffer.data(), patternLength);
     653    StringView pattern(patternBuffer.data(), patternBuffer.size());
    679654    setFormatsFromPattern(pattern);
    680655
     
    895870        return throwRangeError(globalObject, scope, "date value is not finite in DateTimeFormat format()"_s);
    896871
    897     UErrorCode status = U_ZERO_ERROR;
    898     Vector<UChar, 32> result(32);
    899     auto resultLength = udat_format(m_dateFormat.get(), value, result.data(), result.size(), nullptr, &status);
    900     if (needsToGrowToProduceBuffer(status)) {
    901         status = U_ZERO_ERROR;
    902         result.grow(resultLength);
    903         udat_format(m_dateFormat.get(), value, result.data(), resultLength, nullptr, &status);
    904     }
     872    Vector<UChar, 32> result;
     873    auto status = callBufferProducingFunction(udat_format, m_dateFormat.get(), value, result, nullptr);
    905874    if (U_FAILURE(status))
    906875        return throwTypeError(globalObject, scope, "failed to format date value"_s);
    907876
    908     return jsString(vm, String(result.data(), resultLength));
     877    return jsString(vm, String(result));
    909878}
    910879
     
    987956        return throwTypeError(globalObject, scope, "failed to open field position iterator"_s);
    988957
    989     status = U_ZERO_ERROR;
    990     Vector<UChar, 32> result(32);
    991     auto resultLength = udat_formatForFields(m_dateFormat.get(), value, result.data(), result.size(), fields.get(), &status);
    992     if (needsToGrowToProduceBuffer(status)) {
    993         status = U_ZERO_ERROR;
    994         result.grow(resultLength);
    995         udat_formatForFields(m_dateFormat.get(), value, result.data(), resultLength, fields.get(), &status);
    996     }
     958    Vector<UChar, 32> result;
     959    status = callBufferProducingFunction(udat_formatForFields, m_dateFormat.get(), value, result, fields.get());
    997960    if (U_FAILURE(status))
    998961        return throwTypeError(globalObject, scope, "failed to format date value"_s);
     
    1002965        return throwOutOfMemoryError(globalObject, scope);
    1003966
    1004     auto resultString = String(result.data(), resultLength);
     967    auto resultString = String(result);
    1005968    auto literalString = jsNontrivialString(vm, "literal"_s);
    1006969
     970    int32_t resultLength = result.size();
    1007971    int32_t previousEndIndex = 0;
    1008972    int32_t beginIndex = 0;
  • trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp

    r261215 r261257  
    9191    ASSERT(m_buffer.size());
    9292
    93     UErrorCode status = U_ZERO_ERROR;
    94     Vector<char, 32> result(32);
    95     auto resultLength = uloc_canonicalize(m_buffer.data(), result.data(), result.size(), &status);
    96     if (needsToGrowToProduceBuffer(status)) {
    97         result.grow(resultLength);
    98         status = U_ZERO_ERROR;
    99         uloc_canonicalize(m_buffer.data(), result.data(), resultLength, &status);
    100     }
     93    Vector<char, 32> result;
     94    auto status = callBufferProducingFunction(uloc_canonicalize, m_buffer.data(), result);
    10195    if (U_FAILURE(status))
    10296        return CString();
    10397
    104     return CString(result.data(), resultLength);
     98    return CString(result.data(), result.size());
    10599}
    106100
     
    387381{
    388382    if (m_language.isNull()) {
    389         UErrorCode status = U_ZERO_ERROR;
    390         Vector<char, 8> buffer(8);
    391         auto bufferLength = uloc_getLanguage(m_localeID.data(), buffer.data(), buffer.size(), &status);
    392         if (needsToGrowToProduceBuffer(status)) {
    393             buffer.grow(bufferLength);
    394             status = U_ZERO_ERROR;
    395             uloc_getLanguage(m_localeID.data(), buffer.data(), bufferLength, &status);
    396         }
    397         ASSERT(U_SUCCESS(status));
    398 
    399         m_language = String(buffer.data(), bufferLength);
     383        Vector<char, 8> buffer;
     384        auto status = callBufferProducingFunction(uloc_getLanguage, m_localeID.data(), buffer);
     385        ASSERT_UNUSED(status, U_SUCCESS(status));
     386        m_language = String(buffer.data(), buffer.size());
    400387    }
    401388    return m_language;
     
    406393{
    407394    if (m_script.isNull()) {
    408         UErrorCode status = U_ZERO_ERROR;
    409         Vector<char, 4> buffer(4);
    410         auto bufferLength = uloc_getScript(m_localeID.data(), buffer.data(), buffer.size(), &status);
    411         if (needsToGrowToProduceBuffer(status)) {
    412             buffer.grow(bufferLength);
    413             status = U_ZERO_ERROR;
    414             uloc_getScript(m_localeID.data(), buffer.data(), bufferLength, &status);
    415         }
    416         ASSERT(U_SUCCESS(status));
    417 
    418         m_script = String(buffer.data(), bufferLength);
     395        Vector<char, 4> buffer;
     396        auto status = callBufferProducingFunction(uloc_getScript, m_localeID.data(), buffer);
     397        ASSERT_UNUSED(status, U_SUCCESS(status));
     398        m_script = String(buffer.data(), buffer.size());
    419399    }
    420400    return m_script;
     
    425405{
    426406    if (m_region.isNull()) {
    427         UErrorCode status = U_ZERO_ERROR;
    428         Vector<char, 3> buffer(3);
    429         auto bufferLength = uloc_getCountry(m_localeID.data(), buffer.data(), buffer.size(), &status);
    430         if (needsToGrowToProduceBuffer(status)) {
    431             buffer.grow(bufferLength);
    432             status = U_ZERO_ERROR;
    433             uloc_getCountry(m_localeID.data(), buffer.data(), bufferLength, &status);
    434         }
    435         ASSERT(U_SUCCESS(status));
    436 
    437         m_region = String(buffer.data(), bufferLength);
     407        Vector<char, 3> buffer;
     408        auto status = callBufferProducingFunction(uloc_getCountry, m_localeID.data(), buffer);
     409        ASSERT_UNUSED(status, U_SUCCESS(status));
     410        m_region = String(buffer.data(), buffer.size());
    438411    }
    439412    return m_region;
  • trunk/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp

    r261182 r261257  
    348348    auto scope = DECLARE_THROW_SCOPE(vm);
    349349
    350     UErrorCode status = U_ZERO_ERROR;
    351     Vector<UChar, 32> buffer(32);
    352     auto length = unum_formatDouble(m_numberFormat.get(), value, buffer.data(), buffer.size(), nullptr, &status);
    353     if (needsToGrowToProduceBuffer(status)) {
    354         buffer.grow(length);
    355         status = U_ZERO_ERROR;
    356         unum_formatDouble(m_numberFormat.get(), value, buffer.data(), length, nullptr, &status);
    357     }
     350    Vector<UChar, 32> buffer;
     351    auto status = callBufferProducingFunction(unum_formatDouble, m_numberFormat.get(), value, buffer, nullptr);
    358352    if (U_FAILURE(status))
    359353        return throwTypeError(globalObject, scope, "Failed to format a number."_s);
    360354
    361     return jsString(vm, String(buffer.data(), length));
     355    return jsString(vm, String(buffer));
    362356}
    363357
     
    376370    auto* rawString = reinterpret_cast<const char*>(string.characters8());
    377371
    378     UErrorCode status = U_ZERO_ERROR;
    379     Vector<UChar, 32> buffer(32);
    380     auto length = unum_formatDecimal(m_numberFormat.get(), rawString, string.length(), buffer.data(), buffer.size(), nullptr, &status);
    381     if (needsToGrowToProduceBuffer(status)) {
    382         buffer.grow(length);
    383         status = U_ZERO_ERROR;
    384         unum_formatDecimal(m_numberFormat.get(), rawString, string.length(), buffer.data(), length, nullptr, &status);
    385     }
     372    Vector<UChar, 32> buffer;
     373    auto status = callBufferProducingFunction(unum_formatDecimal, m_numberFormat.get(), rawString, string.length(), buffer, nullptr);
    386374    if (U_FAILURE(status))
    387375        return throwTypeError(globalObject, scope, "Failed to format a BigInt."_s);
    388376
    389     return jsString(vm, String(buffer.data(), length));
     377    return jsString(vm, String(buffer));
    390378}
    391379
     
    540528        return throwTypeError(globalObject, scope, "failed to open field position iterator"_s);
    541529
    542     Vector<UChar, 32> result(32);
    543     auto resultLength = unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), result.size(), fieldItr.get(), &status);
    544     if (needsToGrowToProduceBuffer(status)) {
    545         status = U_ZERO_ERROR;
    546         result.grow(resultLength);
    547         unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), resultLength, fieldItr.get(), &status);
    548     }
     530    Vector<UChar, 32> result;
     531    status = callBufferProducingFunction(unum_formatDoubleForFields, m_numberFormat.get(), value, result, fieldItr.get());
    549532    if (U_FAILURE(status))
    550533        return throwTypeError(globalObject, scope, "failed to format a number."_s);
    551534
    552     auto resultString = String(result.data(), resultLength);
     535    auto resultString = String(result);
    553536
    554537    JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
  • trunk/Source/JavaScriptCore/runtime/IntlObject.cpp

    r261215 r261257  
    179179String languageTagForLocaleID(const char* localeID, bool isImmortal)
    180180{
    181     UErrorCode status = U_ZERO_ERROR;
    182     Vector<char, 32> buffer(32);
    183     auto length = uloc_toLanguageTag(localeID, buffer.data(), buffer.size(), false, &status);
    184     if (needsToGrowToProduceBuffer(status)) {
    185         buffer.grow(length);
    186         status = U_ZERO_ERROR;
    187         uloc_toLanguageTag(localeID, buffer.data(), length, false, &status);
    188     }
     181    Vector<char, 32> buffer;
     182    auto status = callBufferProducingFunction(uloc_toLanguageTag, localeID, buffer, false);
    189183    if (U_FAILURE(status))
    190184        return String();
     
    193187    // This must be immortal to make concurrent ref/deref safe.
    194188    if (isImmortal)
    195         return String(StringImpl::createStaticStringImpl(buffer.data(), length));
    196 
    197     return String(buffer.data(), length);
     189        return StringImpl::createStaticStringImpl(buffer.data(), buffer.size());
     190
     191    return String(buffer.data(), buffer.size());
    198192}
    199193
  • trunk/Source/JavaScriptCore/runtime/IntlRelativeTimeFormat.cpp

    r261182 r261257  
    246246    auto formatRelativeTime = m_numeric ? ureldatefmt_formatNumeric : ureldatefmt_format;
    247247
    248     UErrorCode status = U_ZERO_ERROR;
    249     Vector<UChar, 32> result(32);
    250     auto resultLength = formatRelativeTime(m_relativeDateTimeFormatter.get(), value, unitType.value(), result.data(), result.size(), &status);
    251     if (needsToGrowToProduceBuffer(status)) {
    252         status = U_ZERO_ERROR;
    253         result.grow(resultLength);
    254         formatRelativeTime(m_relativeDateTimeFormatter.get(), value, unitType.value(), result.data(), resultLength, &status);
    255     }
     248    Vector<UChar, 32> result;
     249    auto status = callBufferProducingFunction(formatRelativeTime, m_relativeDateTimeFormatter.get(), value, unitType.value(), result);
    256250    if (UNLIKELY(U_FAILURE(status))) {
    257251        throwTypeError(globalObject, scope, "failed to format relative time"_s);
     
    259253    }
    260254
    261     return String(result.data(), resultLength);
     255    return String(result);
    262256}
    263257
     
    289283    double absValue = std::abs(value);
    290284
    291     Vector<UChar, 32> buffer(32);
    292     auto numberLength = unum_formatDoubleForFields(m_numberFormat.get(), absValue, buffer.data(), buffer.size(), iterator.get(), &status);
    293     if (needsToGrowToProduceBuffer(status)) {
    294         status = U_ZERO_ERROR;
    295         buffer.grow(numberLength);
    296         unum_formatDoubleForFields(m_numberFormat.get(), absValue, buffer.data(), numberLength, iterator.get(), &status);
    297     }
     285    Vector<UChar, 32> buffer;
     286    status = callBufferProducingFunction(unum_formatDoubleForFields, m_numberFormat.get(), absValue, buffer, iterator.get());
    298287    if (U_FAILURE(status))
    299288        return throwTypeError(globalObject, scope, "failed to format relative time"_s);
    300289
    301     auto formattedNumber = String(buffer.data(), numberLength);
     290    auto formattedNumber = String(buffer);
    302291
    303292    JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
     
    312301    size_t numberStart = formattedRelativeTime.find(formattedNumber);
    313302    if (numberStart != notFound) {
    314         numberEnd = numberStart + numberLength;
     303        numberEnd = numberStart + buffer.size();
    315304
    316305        // Add initial literal if there is one.
  • trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp

    r260882 r261257  
    15581558    auto scope = DECLARE_THROW_SCOPE(vm);
    15591559
    1560     auto convertCase = [&] (auto&&... args) {
    1561         if (mode == CaseConversionMode::Lower)
    1562             return u_strToLower(std::forward<decltype(args)>(args)...);
    1563         return u_strToUpper(std::forward<decltype(args)>(args)...);
    1564     };
    1565 
    15661560    // 1. Let O be RequireObjectCoercible(this value).
    15671561    JSValue thisValue = callFrame->thisValue();
     
    16091603    if (locale.isNull())
    16101604        locale = "und"_s;
    1611 
    1612     CString utf8LocaleBuffer = locale.utf8();
    1613     const StringView view(s);
    1614     const int32_t viewLength = view.length();
    16151605
    16161606    // Delegate the following steps to icu u_strToLower or u_strToUpper.
     
    16221612
    16231613    // Most strings lower/upper case will be the same size as original, so try that first.
    1624     UErrorCode error = U_ZERO_ERROR;
    1625     Vector<UChar> buffer(viewLength);
    1626     String lower;
    1627     const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
    1628     if (U_SUCCESS(error))
    1629         lower = String(buffer.data(), resultLength);
    1630     else if (needsToGrowToProduceBuffer(error)) {
    1631         // Converted case needs more space than original. Try again.
    1632         UErrorCode error = U_ZERO_ERROR;
    1633         Vector<UChar> buffer(resultLength);
    1634         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
    1635         if (U_FAILURE(error))
    1636             return throwVMTypeError(globalObject, scope, u_errorName(error));
    1637         lower = String(buffer.data(), resultLength);
    1638     } else
    1639         return throwVMTypeError(globalObject, scope, u_errorName(error));
     1614    Vector<UChar> buffer;
     1615    buffer.reserveInitialCapacity(s.length());
     1616    auto convertCase = mode == CaseConversionMode::Lower ? u_strToLower : u_strToUpper;
     1617    auto status = callBufferProducingFunction(convertCase, buffer, StringView { s }.upconvertedCharacters(), s.length(), locale.utf8().data());
     1618    if (U_FAILURE(status))
     1619        return throwVMTypeError(globalObject, scope, u_errorName(status));
    16401620
    16411621    // 18. Return L.
    1642     RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, lower)));
     1622    RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, String(buffer))));
    16431623}
    16441624
  • trunk/Source/WTF/ChangeLog

    r261254 r261257  
     12020-05-06  Darin Adler  <darin@apple.com>
     2
     3        Make a helper for the pattern of ICU functions that may need to be called twice to populate a buffer
     4        https://bugs.webkit.org/show_bug.cgi?id=211499
     5
     6        Reviewed by Ross Kirsling.
     7
     8        This first cut version is ready to be used in most, but not all, of the places we use the
     9        needsToGrowToProduceBuffer function. The places it is not right for yet are ones that have
     10        special considerations because of null character termination or destinations that are
     11        not a Vector. Later we can refine that further, if we like, and possibly use something
     12        similar in call sites that use needsToGrowToProduceCString as well.
     13
     14        * wtf/unicode/icu/ICUHelpers.h:
     15        (WTF::needsToGrowToProduceBuffer): Changed to constexpr, since we can.
     16        (WTF::needsToGrowToProduceCString): Ditto.
     17        (WTF::CallBufferProducingFunction::findVector): Added. Implementation detail
     18        of callBufferProducingFunction.
     19        (WTF::CallBufferProducingFunction::argumentTuple): Ditto.
     20        (WTF::callBufferProducingFunction): Added.
     21
    1222020-05-06  Alex Christensen  <achristensen@webkit.org>
    223
  • trunk/Source/WTF/wtf/unicode/icu/ICUHelpers.h

    r260882 r261257  
    2626#pragma once
    2727
     28#include <tuple>
    2829#include <unicode/utypes.h>
     30#include <wtf/Forward.h>
    2931
    3032namespace WTF {
    3133
    32 inline bool needsToGrowToProduceCString(UErrorCode errorCode)
    33 {
    34     return errorCode == U_BUFFER_OVERFLOW_ERROR || errorCode == U_STRING_NOT_TERMINATED_WARNING;
    35 }
     34constexpr bool needsToGrowToProduceBuffer(UErrorCode);
     35constexpr bool needsToGrowToProduceCString(UErrorCode);
    3636
    37 inline bool needsToGrowToProduceBuffer(UErrorCode errorCode)
     37// Use this to call a function from ICU that has the following properties:
     38// - Takes a buffer pointer and capacity.
     39// - Returns the length of the buffer needed.
     40// - Takes a UErrorCode* as its last argument, returning the status, including U_BUFFER_OVERFLOW_ERROR.
     41// Pass the arguments, but pass a Vector in place of the buffer pointer and capacity, and don't pass a UErrorCode*.
     42// This will call the function, once or twice as needed, resizing the buffer as needed.
     43//
     44// Example:
     45//
     46//    Vector<UChar, 32> buffer;
     47//    auto status = callBufferProducingFunction(ucal_getDefaultTimeZone, buffer);
     48//
     49template<typename FunctionType, typename ...ArgumentTypes> UErrorCode callBufferProducingFunction(const FunctionType&, ArgumentTypes&&...);
     50
     51// Implementations of the functions declared above.
     52
     53constexpr bool needsToGrowToProduceBuffer(UErrorCode errorCode)
    3854{
    3955    return errorCode == U_BUFFER_OVERFLOW_ERROR;
    4056}
    4157
     58constexpr bool needsToGrowToProduceCString(UErrorCode errorCode)
     59{
     60    return needsToGrowToProduceBuffer(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING;
    4261}
    4362
     63namespace CallBufferProducingFunction {
     64
     65template<typename CharacterType, size_t inlineCapacity, typename ...ArgumentTypes> auto& findVector(Vector<CharacterType, inlineCapacity>& buffer, ArgumentTypes&&...)
     66{
     67    return buffer;
     68}
     69
     70template<typename FirstArgumentType, typename ...ArgumentTypes> auto& findVector(FirstArgumentType&&, ArgumentTypes&&... arguments)
     71{
     72    return findVector(std::forward<ArgumentTypes>(arguments)...);
     73}
     74
     75constexpr std::tuple<> argumentTuple() { return { }; }
     76
     77template<typename FirstArgumentType, typename ...OtherArgumentTypes> auto argumentTuple(FirstArgumentType&&, OtherArgumentTypes&&...);
     78
     79template<typename CharacterType, size_t inlineCapacity, typename ...OtherArgumentTypes> auto argumentTuple(Vector<CharacterType, inlineCapacity>& buffer, OtherArgumentTypes&&... otherArguments)
     80{
     81    return tuple_cat(std::make_tuple(buffer.data(), buffer.size()), argumentTuple(std::forward<OtherArgumentTypes>(otherArguments)...));
     82}
     83
     84template<typename FirstArgumentType, typename ...OtherArgumentTypes> auto argumentTuple(FirstArgumentType&& firstArgument, OtherArgumentTypes&&... otherArguments)
     85{
     86    return tuple_cat(std::make_tuple(std::forward<FirstArgumentType>(firstArgument)), argumentTuple(std::forward<OtherArgumentTypes>(otherArguments)...));
     87}
     88
     89}
     90
     91template<typename FunctionType, typename ...ArgumentTypes> UErrorCode callBufferProducingFunction(const FunctionType& function, ArgumentTypes&&... arguments)
     92{
     93    auto& buffer = CallBufferProducingFunction::findVector(std::forward<ArgumentTypes>(arguments)...);
     94    buffer.grow(buffer.capacity());
     95    auto status = U_ZERO_ERROR;
     96    auto resultLength = apply(function, CallBufferProducingFunction::argumentTuple(std::forward<ArgumentTypes>(arguments)..., &status));
     97    if (U_SUCCESS(status))
     98        buffer.shrink(resultLength);
     99    else if (needsToGrowToProduceBuffer(status)) {
     100        status = U_ZERO_ERROR;
     101        buffer.grow(resultLength);
     102        apply(function, CallBufferProducingFunction::argumentTuple(std::forward<ArgumentTypes>(arguments)..., &status));
     103        ASSERT(U_SUCCESS(status));
     104    }
     105    return status;
     106}
     107
     108}
     109
     110using WTF::callBufferProducingFunction;
    44111using WTF::needsToGrowToProduceCString;
    45112using WTF::needsToGrowToProduceBuffer;
  • trunk/Source/WebCore/ChangeLog

    r261256 r261257  
     12020-05-06  Darin Adler  <darin@apple.com>
     2
     3        Make a helper for the pattern of ICU functions that may need to be called twice to populate a buffer
     4        https://bugs.webkit.org/show_bug.cgi?id=211499
     5
     6        Reviewed by Ross Kirsling.
     7
     8        * editing/TextIterator.cpp:
     9        (WebCore::normalizeCharacters): Use callBufferProducingFunction.
     10
    1112020-05-06  Simon Fraser  <simon.fraser@apple.com>
    212
  • trunk/Source/WebCore/editing/TextIterator.cpp

    r260882 r261257  
    18321832{
    18331833    UErrorCode status = U_ZERO_ERROR;
    1834     const UNormalizer2* normalizer = unorm2_getNFCInstance(&status);
     1834    auto* normalizer = unorm2_getNFCInstance(&status);
    18351835    ASSERT(U_SUCCESS(status));
    18361836
    1837     buffer.resize(length);
    1838 
    1839     auto normalizedLength = unorm2_normalize(normalizer, characters, length, buffer.data(), length, &status);
    1840     ASSERT(U_SUCCESS(status) || needsToGrowToProduceBuffer(status));
    1841 
    1842     buffer.resize(normalizedLength);
    1843 
    1844     if (U_SUCCESS(status))
    1845         return;
    1846 
    1847     status = U_ZERO_ERROR;
    1848     unorm2_normalize(normalizer, characters, length, buffer.data(), length, &status);
     1837    buffer.reserveCapacity(length);
     1838
     1839    status = callBufferProducingFunction(unorm2_normalize, normalizer, characters, length, buffer);
    18491840    ASSERT(U_SUCCESS(status));
    18501841}
Note: See TracChangeset for help on using the changeset viewer.