Chromium Code Reviews| Index: src/runtime/runtime-strings.cc |
| diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc |
| index 6786fa99fba0f92e1c41e5e228d9f6852349dd74..38be231f27e5ad0aaee57e0257d23b4835f6f0bd 100644 |
| --- a/src/runtime/runtime-strings.cc |
| +++ b/src/runtime/runtime-strings.cc |
| @@ -12,6 +12,12 @@ |
| #include "src/string-builder.h" |
| #include "src/string-search.h" |
| +#ifdef V8_I18N_SUPPORT |
| +#include "unicode/locid.h" |
| +#include "unicode/uchar.h" |
| +#include "unicode/unistr.h" |
| +#endif |
| + |
| namespace v8 { |
| namespace internal { |
| @@ -1077,20 +1083,210 @@ MUST_USE_RESULT static Object* ConvertCase( |
| return ConvertCaseHelper(isolate, *s, *result, length, mapping); |
| } |
| +#ifdef V8_I18N_SUPPORT |
| +namespace { |
| + |
| +MUST_USE_RESULT static Handle<String> ConvertCaseICU(Handle<String> s, |
| + Isolate* isolate, |
| + bool is_to_upper) { |
| + DCHECK(s->IsFlat()); |
| + // Handle<String> flattened = String::Flatten(s); |
| + String::FlatContent flat = s->GetFlatContent(); |
|
adamk
2016/04/07 20:50:55
You'll want to add a DisallowHeapAllocation object
|
| + |
| + const UChar* src; |
| + if (flat.IsOneByte()) { |
| + base::SmartArrayPointer<uc16> sap = s->ToWideCString(); |
| + src = reinterpret_cast<const UChar*>(sap.get()); |
| + } else { |
| + src = reinterpret_cast<const UChar*>(flat.ToUC16Vector().start()); |
| + } |
| + |
| + int32_t length = s->length(); |
| + |
| + // This UnicodeString ctor has copy-on-write semantics. It starts as a |
|
adamk
2016/04/07 20:50:55
...this comment worries me. Does this mean that co
|
| + // read-only alias but the buffer is copied when it's written to. |
| + icu::UnicodeString converted(0, src, length); |
| + const icu::Locale& root_locale = icu::Locale::getRoot(); |
| + if (is_to_upper) |
| + converted.toUpper(root_locale); |
| + else |
| + converted.toLower(root_locale); |
| + |
| + 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(); |
| + s = String::Flatten(s); |
| + // 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(); |
| + if (s->IsSeqOneByteString()) { |
| + SeqOneByteString* source = SeqOneByteString::cast(*s); |
| + CopyChars(result->GetChars(), source->GetChars(), first_index_to_lower); |
| + } else { |
| + // Do we have to worry about External{One,Two}ByteString? |
| + DCHECK(s->IsSeqTwoByteString()); |
| + SeqTwoByteString* source = SeqTwoByteString::cast(*s); |
| + CopyChars(result->GetChars(), source->GetChars(), first_index_to_lower); |
| + } |
| + |
| + for (int index = first_index_to_lower; 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, false); |
| +} |
| + |
| +const uint16_t sharp_s = 0x00DFu; |
| + |
| +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(); |
| + s = String::Flatten(s); |
| + |
| + 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; |
| + continue; |
| + } |
| + 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. |
| + return ConvertCaseICU(s, isolate, true); |
| + } |
| + 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); |
| + } |
| + |
| + return ConvertCaseICU(s, isolate, true); |
| +} |
| + |
| +} // 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()); |
| } |