diff --git a/src/heap.cc b/src/heap.cc index 1f1599a79b..5421dcc195 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1961,8 +1961,9 @@ Object* Heap::AllocateConsString(String* first, String* second) { return MakeOrFindTwoCharacterString(c1, c2); } - bool is_ascii = first->IsAsciiRepresentation() - && second->IsAsciiRepresentation(); + bool first_is_ascii = first->IsAsciiRepresentation(); + bool second_is_ascii = second->IsAsciiRepresentation(); + bool is_ascii = first_is_ascii && second_is_ascii; // Make sure that an out of memory exception is thrown if the length // of the new cons string is too large. @@ -1997,6 +1998,25 @@ Object* Heap::AllocateConsString(String* first, String* second) { for (int i = 0; i < second_length; i++) *dest++ = src[i]; return result; } else { + // For short external two-byte strings we check whether they can + // be represented using ascii. + if (!first_is_ascii) { + first_is_ascii = first->IsExternalTwoByteStringWithAsciiChars(); + } + if (first_is_ascii && !second_is_ascii) { + second_is_ascii = second->IsExternalTwoByteStringWithAsciiChars(); + } + if (first_is_ascii && second_is_ascii) { + Object* result = AllocateRawAsciiString(length); + if (result->IsFailure()) return result; + // Copy the characters into the new object. + char* dest = SeqAsciiString::cast(result)->GetChars(); + String::WriteToFlat(first, dest, 0, first_length); + String::WriteToFlat(second, dest + first_length, 0, second_length); + Counters::string_add_runtime_ext_to_ascii.Increment(); + return result; + } + Object* result = AllocateRawTwoByteString(length); if (result->IsFailure()) return result; // Copy the characters into the new object. diff --git a/src/objects-inl.h b/src/objects-inl.h index a26da7dd62..6d7bad7512 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -255,6 +255,16 @@ bool String::IsTwoByteRepresentation() { } +bool String::IsExternalTwoByteStringWithAsciiChars() { + if (!IsExternalTwoByteString()) return false; + const uc16* data = ExternalTwoByteString::cast(this)->resource()->data(); + for (int i = 0, len = length(); i < len; i++) { + if (data[i] > kMaxAsciiCharCode) return false; + } + return true; +} + + bool StringShape::IsCons() { return (type_ & kStringRepresentationMask) == kConsStringTag; } diff --git a/src/objects.cc b/src/objects.cc index a1fbc99277..02ea5b0455 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4660,13 +4660,38 @@ bool String::IsEqualTo(Vector str) { } +template +static inline uint32_t HashSequentialString(const schar* chars, int length) { + StringHasher hasher(length); + if (!hasher.has_trivial_hash()) { + int i; + for (i = 0; hasher.is_array_index() && (i < length); i++) { + hasher.AddCharacter(chars[i]); + } + for (; i < length; i++) { + hasher.AddCharacterNoIndex(chars[i]); + } + } + return hasher.GetHashField(); +} + + uint32_t String::ComputeAndSetHash() { // Should only be called if hash code has not yet been computed. ASSERT(!(hash_field() & kHashComputedMask)); + const int len = length(); + // Compute the hash code. - StringInputBuffer buffer(this); - uint32_t field = ComputeHashField(&buffer, length()); + uint32_t field = 0; + if (StringShape(this).IsSequentialAscii()) { + field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len); + } else if (StringShape(this).IsSequentialTwoByte()) { + field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len); + } else { + StringInputBuffer buffer(this); + field = ComputeHashField(&buffer, len); + } // Store the hash code in the object. set_hash_field(field); diff --git a/src/objects.h b/src/objects.h index d299d92a25..9197466e85 100644 --- a/src/objects.h +++ b/src/objects.h @@ -3926,6 +3926,13 @@ class String: public HeapObject { inline bool IsAsciiRepresentation(); inline bool IsTwoByteRepresentation(); + // Check whether this string is an external two-byte string that in + // fact contains only ascii characters. + // + // Such strings may appear when the embedder prefers two-byte + // representations even for ascii data. + inline bool IsExternalTwoByteStringWithAsciiChars(); + // Get and set individual two byte chars in the string. inline void Set(int index, uint16_t value); // Get individual two byte char in the string. Repeated calls diff --git a/src/v8-counters.h b/src/v8-counters.h index a5f3594ca7..bd671a13fe 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -166,6 +166,7 @@ namespace internal { SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \ SC(string_add_runtime, V8.StringAddRuntime) \ SC(string_add_native, V8.StringAddNative) \ + SC(string_add_runtime_ext_to_ascii, V8.StringAddRuntimeExtToAscii) \ SC(sub_string_runtime, V8.SubStringRuntime) \ SC(sub_string_native, V8.SubStringNative) \ SC(string_compare_native, V8.StringCompareNative) \