Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(917)

Unified Diff: src/runtime/runtime-strings.cc

Issue 1812673005: Use ICU case conversion/transliterator for case conversion behind a flag (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: working now with HandleChecked Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/runtime/runtime-strings.cc
diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc
index e3b589643b474ce291a0d668f669316d8e40f2cb..33f89f42245579276edb762579d65f94470c2697 100644
--- a/src/runtime/runtime-strings.cc
+++ b/src/runtime/runtime-strings.cc
@@ -12,6 +12,11 @@
#include "src/string-builder.h"
#include "src/string-search.h"
+#ifdef V8_I18N_SUPPORT
+#include "unicode/uchar.h"
+#include "unicode/unistr.h"
+#endif
+
namespace v8 {
namespace internal {
@@ -1079,19 +1084,231 @@ MUST_USE_RESULT static Object* ConvertCase(
}
+#ifdef V8_I18N_SUPPORT
+namespace {
+
+MUST_USE_RESULT static Handle<String> ConvertCaseICU(Handle<String> s,
+ Isolate* isolate,
+ bool is_to_upper) {
+ int32_t length = s->length();
+
+ // If we already have a UTF-16 string, use that, otherwise build it
+ base::SmartArrayPointer<uc16> sap;
+ const UChar* src;
+ if (StringShape(*s).IsSequentialTwoByte()) {
+ src =
+ reinterpret_cast<const UChar*>(SeqTwoByteString::cast(*s)->GetChars());
+ } else {
+ sap = s->ToWideCString(ROBUST_STRING_TRAVERSAL);
+ src = reinterpret_cast<const UChar*>(sap.get());
+ }
+
+ // This UnicodeString ctor has copy-on-write semantics. It starts as a
+ // read-only alias but the buffer is copied when it's written to.
+ icu::UnicodeString converted(0, src, length);
+ if (is_to_upper)
+ converted.toUpper();
+ else
+ converted.toLower();
+
+#if 0
+ Handle<String> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(converted.getBuffer()),
+ converted.length())));
+#endif
+ return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(converted.getBuffer()),
+ converted.length())).ToHandleChecked();
+}
+
+
+inline bool IsASCIIUpper(uint16_t ch) { return ch >= 'A' && ch <= 'Z'; }
+
+
+inline uint16_t ToASCIILower(uint16_t ch) {
+ return ch | ((ch >= 'A' && ch <= 'Z') << 5);
+}
+
+
+inline uint16_t ToASCIIUpper(uint16_t ch) {
+ return ch & ~((ch >= 'a' && ch <= 'z') << 5);
+}
+
+
+MUST_USE_RESULT Handle<String> StringToLowerCase(Handle<String> s,
+ Isolate* isolate) {
+ // Note: This is a hot function in the Dromaeo benchmark, specifically the
+ // no-op code path up through the first 'return' statement.
+
+ int length = s->length();
+ // First scan the string for uppercase and non-ASCII characters:
+ if (s->HasOnlyOneByteChars()) {
+ unsigned first_index_to_lower = length;
+ for (int index = 0; index < length; ++index) {
+ // Blink specializes this path for one-byte strings, so it
+ // does not need to do a generic get, but can do the equivalent
+ // of SeqOneByteStringGet.
+ uint16_t ch = s->Get(index);
+ if (V8_UNLIKELY(IsASCIIUpper(ch) || ch & ~0x7F)) {
+ first_index_to_lower = index;
+ break;
+ }
+ }
+
+ // Nothing to do if the string is all ASCII with no uppercase.
+ if (first_index_to_lower == length) return s;
+
+ // We depend here on the invariant that the length of a Latin1
+ // string is invariant under ToLowerCase, and the result always
+ // fits in the Latin1 range (untrue for ToUpperCase, and might
+ // be untrue in some locales, but this is the root locale)
+ Handle<SeqOneByteString> result =
+ isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
+ // In Blink, this is a simple memcpy, but in V8, the path applies in
+ // more cases. The optimization here is conditional on whether the
+ // source is actually a simple 8-bit string (always true in Blink).
+ // The broader condition lets us eliminate a bunch of duplicate code
+ // which Blink had in a separate section below.
+ if (StringShape(*s).IsSequentialOneByte()) {
+ // In this path, we can use the one-byte-specific Get, and
+ // memcpy until the first_index_to_lower.
+ SeqOneByteString* source = SeqOneByteString::cast(*s);
+ memcpy(result->GetChars(), source->GetChars(), first_index_to_lower);
+ for (int index = first_index_to_lower; index < length; ++index) {
+ uint16_t ch = source->SeqOneByteStringGet(index);
+ result->SeqOneByteStringSet(index,
+ V8_UNLIKELY(ch & ~0x7F)
+ ? static_cast<uint16_t>(u_tolower(ch))
+ : ToASCIILower(ch));
+ }
+ } else {
+ // In this path, we start from the beginning of the string,
+ // since there is nothing to memcpy from, and we have to
+ // use the generic Get. Another option here would be to create
+ // a two-byte string as output, and do a memcpy from that,
+ // as Blink does, but there's also the ConsString case.
+ for (int index = 0; index < length; ++index) {
+ uint16_t ch = s->Get(index);
+ result->SeqOneByteStringSet(index,
+ V8_UNLIKELY(ch & ~0x7F)
+ ? static_cast<uint16_t>(u_tolower(ch))
+ : ToASCIILower(ch));
+ }
+ }
+
+ return Handle<String>(*result);
+ }
+
+ // Blink had an additional case here for ASCII 2-byte strings, but
+ // that is subsumed by the above code (assuming there isn't a false
+ // negative for HasOnlyOneByteChars).
+
+ // Do a slower implementation for cases that include non-ASCII characters.
+ return ConvertCaseICU(s, isolate, u_strToLower);
+}
+
+
+const uint16_t sharp_s = L'\u00DF';
+
+MUST_USE_RESULT Handle<String> StringToUpperCase(Handle<String> s,
+ Isolate* isolate) {
+ // This function could be optimized for no-op cases the way lower() is,
+ // but in empirical testing, few actual calls to upper() are no-ops, so
+ // it wouldn't be worth the extra time for pre-scanning.
+
+ int32_t length = s->length();
+
+ if (s->HasOnlyOneByteChars()) {
+ Handle<SeqOneByteString> result =
+ isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
+
+ // Do a faster loop for the case where all the characters are ASCII.
+ uint16_t ored = 0;
+ for (int index = 0; index < length; ++index) {
+ uint16_t ch = s->Get(index);
+ ored |= ch;
+ result->SeqOneByteStringSet(index, ToASCIIUpper(ch));
+ }
+ if (!(ored & ~0x7F)) return Handle<String>(*result);
+
+ // Do a slower implementation for cases that include non-ASCII Latin-1
+ // characters.
+ int sharp_s_count = 0;
+
+ // There are two special cases.
+ // 1. latin-1 characters when converted to upper case are 16 bit
+ // characters.
+ // 2. Lower case sharp-S converts to "SS" (two characters)
+ for (int32_t index = 0; index < length; ++index) {
+ uint16_t ch = s->Get(index);
+ if (V8_UNLIKELY(ch == sharp_s)) ++sharp_s_count;
+ uint16_t upper = static_cast<uint16_t>(u_toupper(static_cast<UChar>(ch)));
+ if (V8_UNLIKELY(upper > 0xff)) {
+ // Since this upper-cased character does not fit in an 8-bit string, we
+ // need to take the 16-bit path.
+ goto upconvert;
+ }
+ result->SeqOneByteStringSet(index, upper);
+ }
+
+ if (sharp_s_count == 0) return Handle<String>(*result);
+
+ // We have sharp_s_count sharp-s characters, but none of the other special
+ // characters.
+ result = isolate->factory()
+ ->NewRawOneByteString(length + sharp_s_count)
+ .ToHandleChecked();
+ for (int32_t index = 0, dest_index = 0; index < length; ++index) {
+ uint16_t ch = s->Get(index);
+ if (ch == sharp_s) {
+ result->SeqOneByteStringSet(dest_index++, 'S');
+ result->SeqOneByteStringSet(dest_index++, 'S');
+ } else {
+ uint16_t upper =
+ static_cast<uint16_t>(u_toupper(static_cast<UChar>(ch)));
+ result->SeqOneByteStringSet(dest_index++, upper);
+ }
+ }
+
+ return Handle<String>(*result);
+ }
+
+upconvert:
+ return ConvertCaseICU(s, isolate, u_strToUpper);
+}
+
+} // namespace
+#endif
+
+
RUNTIME_FUNCTION(Runtime_StringToLowerCase) {
HandleScope scope(isolate);
- DCHECK(args.length() == 1);
+ DCHECK_EQ(args.length(), 1);
CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
- return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping());
+#ifdef V8_I18N_SUPPORT
+ if (FLAG_icu_case_mapping)
+ return *StringToLowerCase(s, isolate);
+ else
+#endif
+ return ConvertCase(s, isolate,
+ isolate->runtime_state()->to_lower_mapping());
}
RUNTIME_FUNCTION(Runtime_StringToUpperCase) {
HandleScope scope(isolate);
- DCHECK(args.length() == 1);
+ DCHECK_EQ(args.length(), 1);
CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
- return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping());
+#ifdef V8_I18N_SUPPORT
+ if (FLAG_icu_case_mapping)
+ return *StringToUpperCase(s, isolate);
+ else
+#endif
+ return ConvertCase(s, isolate,
+ isolate->runtime_state()->to_upper_mapping());
}
« no previous file with comments | « src/flag-definitions.h ('k') | test/intl/general/case-mapping.js » ('j') | test/intl/testcfg.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698