Title: [261257] trunk/Source
Revision
261257
Author
[email protected]
Date
2020-05-06 16:01:06 -0700 (Wed, 06 May 2020)

Log 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/_javascript_Core:

* 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.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (261256 => 261257)


--- trunk/Source/_javascript_Core/ChangeLog	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-05-06 23:01:06 UTC (rev 261257)
@@ -1,3 +1,32 @@
+2020-05-06  Darin Adler  <[email protected]>
+
+        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.
+
+        * 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.
+
 2020-05-06  Devin Rousso  <[email protected]>
 
         ASSERT_WITH_MESSAGE(m_isOwnedByMainThread == isMainThread()) when web inspecting

Modified: trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -105,27 +105,15 @@
 // https://tc39.es/ecma402/#sec-defaulttimezone
 static String defaultTimeZone()
 {
-    UErrorCode status = U_ZERO_ERROR;
     String canonical;
 
-    Vector<UChar, 32> buffer(32);
-    auto bufferLength = ucal_getDefaultTimeZone(buffer.data(), buffer.size(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        buffer.grow(bufferLength);
-        ucal_getDefaultTimeZone(buffer.data(), bufferLength, &status);
-    }
+    Vector<UChar, 32> buffer;
+    auto status = callBufferProducingFunction(ucal_getDefaultTimeZone, buffer);
     if (U_SUCCESS(status)) {
-        status = U_ZERO_ERROR;
-        Vector<UChar, 32> canonicalBuffer(32);
-        auto canonicalLength = ucal_getCanonicalTimeZoneID(buffer.data(), bufferLength, canonicalBuffer.data(), canonicalBuffer.size(), nullptr, &status);
-        if (needsToGrowToProduceBuffer(status)) {
-            status = U_ZERO_ERROR;
-            canonicalBuffer.grow(canonicalLength);
-            ucal_getCanonicalTimeZoneID(buffer.data(), bufferLength, canonicalBuffer.data(), canonicalLength, nullptr, &status);
-        }
+        Vector<UChar, 32> canonicalBuffer;
+        status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, buffer.data(), buffer.size(), canonicalBuffer, nullptr);
         if (U_SUCCESS(status))
-            canonical = String(canonicalBuffer.data(), canonicalLength);
+            canonical = String(canonicalBuffer);
     }
 
     if (canonical.isNull() || isUTCEquivalent(canonical))
@@ -146,7 +134,7 @@
     do {
         status = U_ZERO_ERROR;
         int32_t ianaTimeZoneLength;
-        // Time zone names are respresented as UChar[] in all related ICU apis.
+        // Time zone names are represented as UChar[] in all related ICU APIs.
         const UChar* ianaTimeZone = uenum_unext(timeZones, &ianaTimeZoneLength, &status);
         ASSERT(U_SUCCESS(status));
 
@@ -163,16 +151,10 @@
         // 1. Let ianaTimeZone be the Zone or Link name of the IANA Time Zone Database such that timeZone, converted to upper case as described in 6.1, is equal to ianaTimeZone, converted to upper case as described in 6.1.
         // 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.
 
-        Vector<UChar, 32> buffer(ianaTimeZoneLength);
-        status = U_ZERO_ERROR;
-        auto canonicalLength = ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), ianaTimeZoneLength, nullptr, &status);
-        if (needsToGrowToProduceBuffer(status)) {
-            buffer.grow(canonicalLength);
-            status = U_ZERO_ERROR;
-            ucal_getCanonicalTimeZoneID(ianaTimeZone, ianaTimeZoneLength, buffer.data(), canonicalLength, nullptr, &status);
-        }
-        ASSERT(U_SUCCESS(status));
-        canonical = String(buffer.data(), canonicalLength);
+        Vector<UChar, 32> buffer;
+        auto status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, ianaTimeZone, ianaTimeZoneLength, buffer, nullptr);
+        ASSERT_UNUSED(status, U_SUCCESS(status));
+        canonical = String(buffer);
     } while (canonical.isNull());
     uenum_close(timeZones);
 
@@ -636,14 +618,8 @@
 
     String skeleton = skeletonBuilder.toString();
     StringView skeletonView(skeleton);
-    Vector<UChar, 32> patternBuffer(32);
-    status = U_ZERO_ERROR;
-    auto patternLength = udatpg_getBestPatternWithOptions(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer.data(), patternBuffer.size(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        patternBuffer.grow(patternLength);
-        udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternLength, &status);
-    }
+    Vector<UChar, 32> patternBuffer;
+    status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator, skeletonView.upconvertedCharacters(), skeletonView.length(), UDATPG_MATCH_HOUR_FIELD_LENGTH, patternBuffer);
     udatpg_close(generator);
     if (U_FAILURE(status)) {
         throwTypeError(globalObject, scope, "failed to initialize DateTimeFormat"_s);
@@ -662,12 +638,11 @@
 
         bool isEscaped = false;
         bool hasHour = false;
-        for (auto i = 0; i < patternLength; ++i) {
-            UChar c = patternBuffer[i];
+        for (auto& c : patternBuffer) {
             if (c == '\'')
                 isEscaped = !isEscaped;
             else if (!isEscaped && (c == 'h' || c == 'H' || c == 'k' || c == 'K')) {
-                patternBuffer[i] = hour;
+                c = hour;
                 hasHour = true;
             }
         }
@@ -675,7 +650,7 @@
             m_hourCycle = String();
     }
 
-    StringView pattern(patternBuffer.data(), patternLength);
+    StringView pattern(patternBuffer.data(), patternBuffer.size());
     setFormatsFromPattern(pattern);
 
     status = U_ZERO_ERROR;
@@ -894,18 +869,12 @@
     if (!std::isfinite(value))
         return throwRangeError(globalObject, scope, "date value is not finite in DateTimeFormat format()"_s);
 
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<UChar, 32> result(32);
-    auto resultLength = udat_format(m_dateFormat.get(), value, result.data(), result.size(), nullptr, &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        result.grow(resultLength);
-        udat_format(m_dateFormat.get(), value, result.data(), resultLength, nullptr, &status);
-    }
+    Vector<UChar, 32> result;
+    auto status = callBufferProducingFunction(udat_format, m_dateFormat.get(), value, result, nullptr);
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to format date value"_s);
 
-    return jsString(vm, String(result.data(), resultLength));
+    return jsString(vm, String(result));
 }
 
 static ASCIILiteral partTypeString(UDateFormatField field)
@@ -986,14 +955,8 @@
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to open field position iterator"_s);
 
-    status = U_ZERO_ERROR;
-    Vector<UChar, 32> result(32);
-    auto resultLength = udat_formatForFields(m_dateFormat.get(), value, result.data(), result.size(), fields.get(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        result.grow(resultLength);
-        udat_formatForFields(m_dateFormat.get(), value, result.data(), resultLength, fields.get(), &status);
-    }
+    Vector<UChar, 32> result;
+    status = callBufferProducingFunction(udat_formatForFields, m_dateFormat.get(), value, result, fields.get());
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to format date value"_s);
 
@@ -1001,9 +964,10 @@
     if (!parts)
         return throwOutOfMemoryError(globalObject, scope);
 
-    auto resultString = String(result.data(), resultLength);
+    auto resultString = String(result);
     auto literalString = jsNontrivialString(vm, "literal"_s);
 
+    int32_t resultLength = result.size();
     int32_t previousEndIndex = 0;
     int32_t beginIndex = 0;
     int32_t endIndex = 0;

Modified: trunk/Source/_javascript_Core/runtime/IntlLocale.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/IntlLocale.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/IntlLocale.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -90,18 +90,12 @@
 {
     ASSERT(m_buffer.size());
 
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<char, 32> result(32);
-    auto resultLength = uloc_canonicalize(m_buffer.data(), result.data(), result.size(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        result.grow(resultLength);
-        status = U_ZERO_ERROR;
-        uloc_canonicalize(m_buffer.data(), result.data(), resultLength, &status);
-    }
+    Vector<char, 32> result;
+    auto status = callBufferProducingFunction(uloc_canonicalize, m_buffer.data(), result);
     if (U_FAILURE(status))
         return CString();
 
-    return CString(result.data(), resultLength);
+    return CString(result.data(), result.size());
 }
 
 // Because ICU's C API doesn't have set[Language|Script|Region] functions...
@@ -386,17 +380,10 @@
 const String& IntlLocale::language()
 {
     if (m_language.isNull()) {
-        UErrorCode status = U_ZERO_ERROR;
-        Vector<char, 8> buffer(8);
-        auto bufferLength = uloc_getLanguage(m_localeID.data(), buffer.data(), buffer.size(), &status);
-        if (needsToGrowToProduceBuffer(status)) {
-            buffer.grow(bufferLength);
-            status = U_ZERO_ERROR;
-            uloc_getLanguage(m_localeID.data(), buffer.data(), bufferLength, &status);
-        }
-        ASSERT(U_SUCCESS(status));
-
-        m_language = String(buffer.data(), bufferLength);
+        Vector<char, 8> buffer;
+        auto status = callBufferProducingFunction(uloc_getLanguage, m_localeID.data(), buffer);
+        ASSERT_UNUSED(status, U_SUCCESS(status));
+        m_language = String(buffer.data(), buffer.size());
     }
     return m_language;
 }
@@ -405,17 +392,10 @@
 const String& IntlLocale::script()
 {
     if (m_script.isNull()) {
-        UErrorCode status = U_ZERO_ERROR;
-        Vector<char, 4> buffer(4);
-        auto bufferLength = uloc_getScript(m_localeID.data(), buffer.data(), buffer.size(), &status);
-        if (needsToGrowToProduceBuffer(status)) {
-            buffer.grow(bufferLength);
-            status = U_ZERO_ERROR;
-            uloc_getScript(m_localeID.data(), buffer.data(), bufferLength, &status);
-        }
-        ASSERT(U_SUCCESS(status));
-
-        m_script = String(buffer.data(), bufferLength);
+        Vector<char, 4> buffer;
+        auto status = callBufferProducingFunction(uloc_getScript, m_localeID.data(), buffer);
+        ASSERT_UNUSED(status, U_SUCCESS(status));
+        m_script = String(buffer.data(), buffer.size());
     }
     return m_script;
 }
@@ -424,17 +404,10 @@
 const String& IntlLocale::region()
 {
     if (m_region.isNull()) {
-        UErrorCode status = U_ZERO_ERROR;
-        Vector<char, 3> buffer(3);
-        auto bufferLength = uloc_getCountry(m_localeID.data(), buffer.data(), buffer.size(), &status);
-        if (needsToGrowToProduceBuffer(status)) {
-            buffer.grow(bufferLength);
-            status = U_ZERO_ERROR;
-            uloc_getCountry(m_localeID.data(), buffer.data(), bufferLength, &status);
-        }
-        ASSERT(U_SUCCESS(status));
-
-        m_region = String(buffer.data(), bufferLength);
+        Vector<char, 3> buffer;
+        auto status = callBufferProducingFunction(uloc_getCountry, m_localeID.data(), buffer);
+        ASSERT_UNUSED(status, U_SUCCESS(status));
+        m_region = String(buffer.data(), buffer.size());
     }
     return m_region;
 }

Modified: trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/IntlNumberFormat.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -347,18 +347,12 @@
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<UChar, 32> buffer(32);
-    auto length = unum_formatDouble(m_numberFormat.get(), value, buffer.data(), buffer.size(), nullptr, &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        buffer.grow(length);
-        status = U_ZERO_ERROR;
-        unum_formatDouble(m_numberFormat.get(), value, buffer.data(), length, nullptr, &status);
-    }
+    Vector<UChar, 32> buffer;
+    auto status = callBufferProducingFunction(unum_formatDouble, m_numberFormat.get(), value, buffer, nullptr);
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "Failed to format a number."_s);
 
-    return jsString(vm, String(buffer.data(), length));
+    return jsString(vm, String(buffer));
 }
 
 // https://tc39.es/ecma402/#sec-formatnumber
@@ -375,18 +369,12 @@
     ASSERT(string.is8Bit() && string.isAllASCII());
     auto* rawString = reinterpret_cast<const char*>(string.characters8());
 
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<UChar, 32> buffer(32);
-    auto length = unum_formatDecimal(m_numberFormat.get(), rawString, string.length(), buffer.data(), buffer.size(), nullptr, &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        buffer.grow(length);
-        status = U_ZERO_ERROR;
-        unum_formatDecimal(m_numberFormat.get(), rawString, string.length(), buffer.data(), length, nullptr, &status);
-    }
+    Vector<UChar, 32> buffer;
+    auto status = callBufferProducingFunction(unum_formatDecimal, m_numberFormat.get(), rawString, string.length(), buffer, nullptr);
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "Failed to format a BigInt."_s);
 
-    return jsString(vm, String(buffer.data(), length));
+    return jsString(vm, String(buffer));
 }
 
 ASCIILiteral IntlNumberFormat::styleString(Style style)
@@ -539,17 +527,12 @@
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to open field position iterator"_s);
 
-    Vector<UChar, 32> result(32);
-    auto resultLength = unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), result.size(), fieldItr.get(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        result.grow(resultLength);
-        unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), resultLength, fieldItr.get(), &status);
-    }
+    Vector<UChar, 32> result;
+    status = callBufferProducingFunction(unum_formatDoubleForFields, m_numberFormat.get(), value, result, fieldItr.get());
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to format a number."_s);
 
-    auto resultString = String(result.data(), resultLength);
+    auto resultString = String(result);
 
     JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
     if (!parts)

Modified: trunk/Source/_javascript_Core/runtime/IntlObject.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/IntlObject.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/IntlObject.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -178,14 +178,8 @@
 
 String languageTagForLocaleID(const char* localeID, bool isImmortal)
 {
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<char, 32> buffer(32);
-    auto length = uloc_toLanguageTag(localeID, buffer.data(), buffer.size(), false, &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        buffer.grow(length);
-        status = U_ZERO_ERROR;
-        uloc_toLanguageTag(localeID, buffer.data(), length, false, &status);
-    }
+    Vector<char, 32> buffer;
+    auto status = callBufferProducingFunction(uloc_toLanguageTag, localeID, buffer, false);
     if (U_FAILURE(status))
         return String();
 
@@ -192,9 +186,9 @@
     // This is used to store into static variables that may be shared across JSC execution threads.
     // This must be immortal to make concurrent ref/deref safe.
     if (isImmortal)
-        return String(StringImpl::createStaticStringImpl(buffer.data(), length));
+        return StringImpl::createStaticStringImpl(buffer.data(), buffer.size());
 
-    return String(buffer.data(), length);
+    return String(buffer.data(), buffer.size());
 }
 
 const HashSet<String>& intlAvailableLocales()

Modified: trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/IntlRelativeTimeFormat.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -245,20 +245,14 @@
 
     auto formatRelativeTime = m_numeric ? ureldatefmt_formatNumeric : ureldatefmt_format;
 
-    UErrorCode status = U_ZERO_ERROR;
-    Vector<UChar, 32> result(32);
-    auto resultLength = formatRelativeTime(m_relativeDateTimeFormatter.get(), value, unitType.value(), result.data(), result.size(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        result.grow(resultLength);
-        formatRelativeTime(m_relativeDateTimeFormatter.get(), value, unitType.value(), result.data(), resultLength, &status);
-    }
+    Vector<UChar, 32> result;
+    auto status = callBufferProducingFunction(formatRelativeTime, m_relativeDateTimeFormatter.get(), value, unitType.value(), result);
     if (UNLIKELY(U_FAILURE(status))) {
         throwTypeError(globalObject, scope, "failed to format relative time"_s);
         return String();
     }
 
-    return String(result.data(), resultLength);
+    return String(result);
 }
 
 // https://tc39.es/ecma402/#sec-FormatRelativeTime
@@ -288,17 +282,12 @@
 
     double absValue = std::abs(value);
 
-    Vector<UChar, 32> buffer(32);
-    auto numberLength = unum_formatDoubleForFields(m_numberFormat.get(), absValue, buffer.data(), buffer.size(), iterator.get(), &status);
-    if (needsToGrowToProduceBuffer(status)) {
-        status = U_ZERO_ERROR;
-        buffer.grow(numberLength);
-        unum_formatDoubleForFields(m_numberFormat.get(), absValue, buffer.data(), numberLength, iterator.get(), &status);
-    }
+    Vector<UChar, 32> buffer;
+    status = callBufferProducingFunction(unum_formatDoubleForFields, m_numberFormat.get(), absValue, buffer, iterator.get());
     if (U_FAILURE(status))
         return throwTypeError(globalObject, scope, "failed to format relative time"_s);
 
-    auto formattedNumber = String(buffer.data(), numberLength);
+    auto formattedNumber = String(buffer);
 
     JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
     if (!parts)
@@ -311,7 +300,7 @@
     size_t numberEnd = 0;
     size_t numberStart = formattedRelativeTime.find(formattedNumber);
     if (numberStart != notFound) {
-        numberEnd = numberStart + numberLength;
+        numberEnd = numberStart + buffer.size();
 
         // Add initial literal if there is one.
         if (numberStart) {

Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (261256 => 261257)


--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -1557,12 +1557,6 @@
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    auto convertCase = [&] (auto&&... args) {
-        if (mode == CaseConversionMode::Lower)
-            return u_strToLower(std::forward<decltype(args)>(args)...);
-        return u_strToUpper(std::forward<decltype(args)>(args)...);
-    };
-
     // 1. Let O be RequireObjectCoercible(this value).
     JSValue thisValue = callFrame->thisValue();
     if (!checkObjectCoercible(thisValue))
@@ -1609,10 +1603,6 @@
     if (locale.isNull())
         locale = "und"_s;
 
-    CString utf8LocaleBuffer = locale.utf8();
-    const StringView view(s);
-    const int32_t viewLength = view.length();
-
     // Delegate the following steps to icu u_strToLower or u_strToUpper.
     // 13. Let cpList be a List containing in order the code points of S as defined in ES2015, 6.1.4, starting at the first element of S.
     // 14. For each code point c in cpList, if the Unicode Character Database provides a lower(/upper) case equivalent of c that is either language insensitive or for the language locale, then replace c in cpList with that/those equivalent code point(s).
@@ -1621,25 +1611,15 @@
     // 17. Let L be a String whose elements are, in order, the elements of cuList.
 
     // Most strings lower/upper case will be the same size as original, so try that first.
-    UErrorCode error = U_ZERO_ERROR;
-    Vector<UChar> buffer(viewLength);
-    String lower;
-    const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
-    if (U_SUCCESS(error))
-        lower = String(buffer.data(), resultLength);
-    else if (needsToGrowToProduceBuffer(error)) {
-        // Converted case needs more space than original. Try again.
-        UErrorCode error = U_ZERO_ERROR;
-        Vector<UChar> buffer(resultLength);
-        convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
-        if (U_FAILURE(error))
-            return throwVMTypeError(globalObject, scope, u_errorName(error));
-        lower = String(buffer.data(), resultLength);
-    } else
-        return throwVMTypeError(globalObject, scope, u_errorName(error));
+    Vector<UChar> buffer;
+    buffer.reserveInitialCapacity(s.length());
+    auto convertCase = mode == CaseConversionMode::Lower ? u_strToLower : u_strToUpper;
+    auto status = callBufferProducingFunction(convertCase, buffer, StringView { s }.upconvertedCharacters(), s.length(), locale.utf8().data());
+    if (U_FAILURE(status))
+        return throwVMTypeError(globalObject, scope, u_errorName(status));
 
     // 18. Return L.
-    RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, lower)));
+    RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, String(buffer))));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(JSGlobalObject* globalObject, CallFrame* callFrame)

Modified: trunk/Source/WTF/ChangeLog (261256 => 261257)


--- trunk/Source/WTF/ChangeLog	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/WTF/ChangeLog	2020-05-06 23:01:06 UTC (rev 261257)
@@ -1,3 +1,24 @@
+2020-05-06  Darin Adler  <[email protected]>
+
+        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.
+
+        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.
+
 2020-05-06  Alex Christensen  <[email protected]>
 
         Reduce IPC overhead for message receiver name and message name to 2 bytes

Modified: trunk/Source/WTF/wtf/unicode/icu/ICUHelpers.h (261256 => 261257)


--- trunk/Source/WTF/wtf/unicode/icu/ICUHelpers.h	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/WTF/wtf/unicode/icu/ICUHelpers.h	2020-05-06 23:01:06 UTC (rev 261257)
@@ -25,21 +25,88 @@
 
 #pragma once
 
+#include <tuple>
 #include <unicode/utypes.h>
+#include <wtf/Forward.h>
 
 namespace WTF {
 
-inline bool needsToGrowToProduceCString(UErrorCode errorCode)
+constexpr bool needsToGrowToProduceBuffer(UErrorCode);
+constexpr bool needsToGrowToProduceCString(UErrorCode);
+
+// Use this to call a function from ICU that has the following properties:
+// - Takes a buffer pointer and capacity.
+// - Returns the length of the buffer needed.
+// - Takes a UErrorCode* as its last argument, returning the status, including U_BUFFER_OVERFLOW_ERROR.
+// Pass the arguments, but pass a Vector in place of the buffer pointer and capacity, and don't pass a UErrorCode*.
+// This will call the function, once or twice as needed, resizing the buffer as needed.
+//
+// Example:
+//
+//    Vector<UChar, 32> buffer;
+//    auto status = callBufferProducingFunction(ucal_getDefaultTimeZone, buffer);
+//
+template<typename FunctionType, typename ...ArgumentTypes> UErrorCode callBufferProducingFunction(const FunctionType&, ArgumentTypes&&...);
+
+// Implementations of the functions declared above.
+
+constexpr bool needsToGrowToProduceBuffer(UErrorCode errorCode)
 {
-    return errorCode == U_BUFFER_OVERFLOW_ERROR || errorCode == U_STRING_NOT_TERMINATED_WARNING;
+    return errorCode == U_BUFFER_OVERFLOW_ERROR;
 }
 
-inline bool needsToGrowToProduceBuffer(UErrorCode errorCode)
+constexpr bool needsToGrowToProduceCString(UErrorCode errorCode)
 {
-    return errorCode == U_BUFFER_OVERFLOW_ERROR;
+    return needsToGrowToProduceBuffer(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING;
 }
 
+namespace CallBufferProducingFunction {
+
+template<typename CharacterType, size_t inlineCapacity, typename ...ArgumentTypes> auto& findVector(Vector<CharacterType, inlineCapacity>& buffer, ArgumentTypes&&...)
+{
+    return buffer;
 }
 
+template<typename FirstArgumentType, typename ...ArgumentTypes> auto& findVector(FirstArgumentType&&, ArgumentTypes&&... arguments)
+{
+    return findVector(std::forward<ArgumentTypes>(arguments)...);
+}
+
+constexpr std::tuple<> argumentTuple() { return { }; }
+
+template<typename FirstArgumentType, typename ...OtherArgumentTypes> auto argumentTuple(FirstArgumentType&&, OtherArgumentTypes&&...);
+
+template<typename CharacterType, size_t inlineCapacity, typename ...OtherArgumentTypes> auto argumentTuple(Vector<CharacterType, inlineCapacity>& buffer, OtherArgumentTypes&&... otherArguments)
+{
+    return tuple_cat(std::make_tuple(buffer.data(), buffer.size()), argumentTuple(std::forward<OtherArgumentTypes>(otherArguments)...));
+}
+
+template<typename FirstArgumentType, typename ...OtherArgumentTypes> auto argumentTuple(FirstArgumentType&& firstArgument, OtherArgumentTypes&&... otherArguments)
+{
+    return tuple_cat(std::make_tuple(std::forward<FirstArgumentType>(firstArgument)), argumentTuple(std::forward<OtherArgumentTypes>(otherArguments)...));
+}
+
+}
+
+template<typename FunctionType, typename ...ArgumentTypes> UErrorCode callBufferProducingFunction(const FunctionType& function, ArgumentTypes&&... arguments)
+{
+    auto& buffer = CallBufferProducingFunction::findVector(std::forward<ArgumentTypes>(arguments)...);
+    buffer.grow(buffer.capacity());
+    auto status = U_ZERO_ERROR;
+    auto resultLength = apply(function, CallBufferProducingFunction::argumentTuple(std::forward<ArgumentTypes>(arguments)..., &status));
+    if (U_SUCCESS(status))
+        buffer.shrink(resultLength);
+    else if (needsToGrowToProduceBuffer(status)) {
+        status = U_ZERO_ERROR;
+        buffer.grow(resultLength);
+        apply(function, CallBufferProducingFunction::argumentTuple(std::forward<ArgumentTypes>(arguments)..., &status));
+        ASSERT(U_SUCCESS(status));
+    }
+    return status;
+}
+
+}
+
+using WTF::callBufferProducingFunction;
 using WTF::needsToGrowToProduceCString;
 using WTF::needsToGrowToProduceBuffer;

Modified: trunk/Source/WebCore/ChangeLog (261256 => 261257)


--- trunk/Source/WebCore/ChangeLog	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/WebCore/ChangeLog	2020-05-06 23:01:06 UTC (rev 261257)
@@ -1,3 +1,13 @@
+2020-05-06  Darin Adler  <[email protected]>
+
+        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.
+
+        * editing/TextIterator.cpp:
+        (WebCore::normalizeCharacters): Use callBufferProducingFunction.
+
 2020-05-06  Simon Fraser  <[email protected]>
 
         REGRESSION (r261056): [ Mac WK1 ] inspector/console/console-api.html is flaky crashing

Modified: trunk/Source/WebCore/editing/TextIterator.cpp (261256 => 261257)


--- trunk/Source/WebCore/editing/TextIterator.cpp	2020-05-06 22:56:01 UTC (rev 261256)
+++ trunk/Source/WebCore/editing/TextIterator.cpp	2020-05-06 23:01:06 UTC (rev 261257)
@@ -1831,21 +1831,12 @@
 static void normalizeCharacters(const UChar* characters, unsigned length, Vector<UChar>& buffer)
 {
     UErrorCode status = U_ZERO_ERROR;
-    const UNormalizer2* normalizer = unorm2_getNFCInstance(&status);
+    auto* normalizer = unorm2_getNFCInstance(&status);
     ASSERT(U_SUCCESS(status));
 
-    buffer.resize(length);
+    buffer.reserveCapacity(length);
 
-    auto normalizedLength = unorm2_normalize(normalizer, characters, length, buffer.data(), length, &status);
-    ASSERT(U_SUCCESS(status) || needsToGrowToProduceBuffer(status));
-
-    buffer.resize(normalizedLength);
-
-    if (U_SUCCESS(status))
-        return;
-
-    status = U_ZERO_ERROR;
-    unorm2_normalize(normalizer, characters, length, buffer.data(), length, &status);
+    status = callBufferProducingFunction(unorm2_normalize, normalizer, characters, length, buffer);
     ASSERT(U_SUCCESS(status));
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to