Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 1d95a5c15271ab3d955e2cbd3f2b6ad1b270cc25..864a4e99a928899e6cd07b51ef8e26adcfb31cd5 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -7021,24 +7021,36 @@ void StringInputBuffer::Seek(unsigned pos) { |
} |
-String* ConsStringIteratorOp::Operate(ConsString* cons_string, |
- unsigned* offset_out, int32_t* type_out, unsigned* length_out) { |
- ASSERT(*length_out == (unsigned)cons_string->length()); |
- ASSERT(depth_ == 0); |
- // Push the root string. |
- PushLeft(cons_string); |
+String* ConsStringIteratorOp::Operate(String* string, |
+ unsigned* offset_out, |
+ int32_t* type_out, |
+ unsigned* length_out) { |
+ ASSERT(string->IsConsString()); |
+ ConsString* cons_string = ConsString::cast(string); |
+ // Set up search data. |
root_ = cons_string; |
- root_type_ = *type_out; |
- root_length_ = *length_out; |
consumed_ = *offset_out; |
- unsigned targetOffset = *offset_out; |
+ // Now search. |
+ return Search(offset_out, type_out, length_out); |
+} |
+ |
+ |
+String* ConsStringIteratorOp::Search(unsigned* offset_out, |
+ int32_t* type_out, |
+ unsigned* length_out) { |
+ ConsString* cons_string = root_; |
+ // Reset the stack, pushing the root string. |
+ depth_ = 1; |
+ maximum_depth_ = 1; |
+ frames_[0] = cons_string; |
+ const unsigned consumed = consumed_; |
unsigned offset = 0; |
while (true) { |
// Loop until the string is found which contains the target offset. |
String* string = cons_string->first(); |
unsigned length = string->length(); |
int32_t type; |
- if (targetOffset < offset + length) { |
+ if (consumed < offset + length) { |
// Target offset is in the left branch. |
// Keep going if we're still in a ConString. |
type = string->map()->instance_type(); |
@@ -7067,6 +7079,7 @@ String* ConsStringIteratorOp::Operate(ConsString* cons_string, |
// Account for the possibility of an empty right leaf. |
// This happens only if we have asked for an offset outside the string. |
if (length == 0) { |
+ // Reset depth so future operations will return null immediately. |
Reset(); |
return NULL; |
} |
@@ -7077,9 +7090,8 @@ String* ConsStringIteratorOp::Operate(ConsString* cons_string, |
} |
ASSERT(length != 0); |
// Adjust return values and exit. |
- unsigned innerOffset = targetOffset - offset; |
- consumed_ += length - innerOffset; |
- *offset_out = innerOffset; |
+ consumed_ = offset + length; |
+ *offset_out = consumed - offset; |
*type_out = type; |
*length_out = length; |
return string; |
@@ -7089,8 +7101,9 @@ String* ConsStringIteratorOp::Operate(ConsString* cons_string, |
} |
-String* ConsStringIteratorOp::NextLeaf( |
- bool* blew_stack, int32_t* type_out, unsigned* length_out) { |
+String* ConsStringIteratorOp::NextLeaf(bool* blew_stack, |
+ int32_t* type_out, |
+ unsigned* length_out) { |
while (true) { |
// Tree traversal complete. |
if (depth_ == 0) { |
@@ -7114,6 +7127,7 @@ String* ConsStringIteratorOp::NextLeaf( |
if (length == 0) continue; |
*length_out = length; |
*type_out = type; |
+ consumed_ += length; |
return string; |
} |
cons_string = ConsString::cast(string); |
@@ -7126,8 +7140,11 @@ String* ConsStringIteratorOp::NextLeaf( |
type = string->map()->instance_type(); |
if ((type & kStringRepresentationMask) != kConsStringTag) { |
AdjustMaximumDepth(); |
+ unsigned length = (unsigned) string->length(); |
+ ASSERT(length != 0); |
+ *length_out = length; |
*type_out = type; |
- *length_out = string->length(); |
+ consumed_ += length; |
return string; |
} |
cons_string = ConsString::cast(string); |
@@ -7666,28 +7683,62 @@ bool String::IsTwoByteEqualTo(Vector<const uc16> str) { |
} |
+class IteratingStringHasher: public StringHasher { |
+ public: |
+ static inline uint32_t Hash(String* string, uint32_t seed) { |
+ const unsigned len = static_cast<unsigned>(string->length()); |
+ IteratingStringHasher hasher(len, seed); |
+ if (hasher.has_trivial_hash()) { |
+ return hasher.GetHashField(); |
+ } |
+ int32_t type = string->map()->instance_type(); |
+ ConsStringNullOp null_op; |
+ String::Visit(string, 0, hasher, null_op, type, len); |
+ // Flat strings terminate immediately. |
+ if (hasher.consumed_ == len) { |
+ ASSERT(!string->IsConsString()); |
+ return hasher.GetHashField(); |
+ } |
+ ASSERT(string->IsConsString()); |
+ // This is a ConsString, iterate across it. |
+ ConsStringIteratorOp op; |
+ unsigned offset = 0; |
+ unsigned leaf_length = len; |
+ string = op.Operate(string, &offset, &type, &leaf_length); |
+ while (true) { |
+ ASSERT(hasher.consumed_ < len); |
+ String::Visit(string, 0, hasher, null_op, type, leaf_length); |
+ if (hasher.consumed_ == len) break; |
+ string = op.ContinueOperation(&type, &leaf_length); |
+ // This should be taken care of by the length check. |
+ ASSERT(string != NULL); |
+ } |
+ return hasher.GetHashField(); |
+ } |
+ inline void VisitOneByteString(const uint8_t* chars, unsigned length) { |
+ AddCharacters(chars, static_cast<int>(length)); |
+ consumed_ += length; |
+ } |
+ inline void VisitTwoByteString(const uint16_t* chars, unsigned length) { |
+ AddCharacters(chars, static_cast<int>(length)); |
+ consumed_ += length; |
+ } |
+ |
+ private: |
+ inline IteratingStringHasher(int len, uint32_t seed) |
+ : StringHasher(len, seed), |
+ consumed_(0) {} |
+ unsigned consumed_; |
+ DISALLOW_COPY_AND_ASSIGN(IteratingStringHasher); |
+}; |
+ |
+ |
uint32_t String::ComputeAndSetHash() { |
// Should only be called if hash code has not yet been computed. |
ASSERT(!HasHashCode()); |
- const int len = length(); |
- |
- // Compute the hash code. |
- uint32_t field = 0; |
- if (StringShape(this).IsSequentialAscii()) { |
- field = HashSequentialString(SeqOneByteString::cast(this)->GetChars(), |
- len, |
- GetHeap()->HashSeed()); |
- } else if (StringShape(this).IsSequentialTwoByte()) { |
- field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), |
- len, |
- GetHeap()->HashSeed()); |
- } else { |
- StringInputBuffer buffer(this); |
- field = ComputeHashField(&buffer, len, GetHeap()->HashSeed()); |
- } |
- |
// Store the hash code in the object. |
+ uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed()); |
set_hash_field(field); |
// Check the hash code is there. |
@@ -7791,57 +7842,59 @@ uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { |
} |
-void StringHasher::AddSurrogatePair(uc32 c) { |
- uint16_t lead = unibrow::Utf16::LeadSurrogate(c); |
- AddCharacter(lead); |
- uint16_t trail = unibrow::Utf16::TrailSurrogate(c); |
- AddCharacter(trail); |
-} |
- |
- |
-void StringHasher::AddSurrogatePairNoIndex(uc32 c) { |
- uint16_t lead = unibrow::Utf16::LeadSurrogate(c); |
- AddCharacterNoIndex(lead); |
- uint16_t trail = unibrow::Utf16::TrailSurrogate(c); |
- AddCharacterNoIndex(trail); |
-} |
- |
- |
uint32_t StringHasher::GetHashField() { |
if (length_ <= String::kMaxHashCalcLength) { |
- if (is_array_index()) { |
- return MakeArrayIndexHash(array_index(), length_); |
+ if (is_array_index_) { |
+ return MakeArrayIndexHash(array_index_, length_); |
} |
- return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask; |
+ return (GetHashCore(raw_running_hash_) << String::kHashShift) | |
+ String::kIsNotArrayIndexMask; |
} else { |
return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; |
} |
} |
-uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer, |
- int length, |
- uint32_t seed) { |
+uint32_t StringHasher::ComputeHashField(unibrow::CharacterStream* buffer, |
+ int length, |
+ uint32_t seed) { |
+ typedef unibrow::Utf16 u; |
StringHasher hasher(length, seed); |
- |
// Very long strings have a trivial hash that doesn't inspect the |
// string contents. |
if (hasher.has_trivial_hash()) { |
return hasher.GetHashField(); |
} |
- |
// Do the iterative array index computation as long as there is a |
// chance this is an array index. |
- while (buffer->has_more() && hasher.is_array_index()) { |
- hasher.AddCharacter(buffer->GetNext()); |
+ if (hasher.is_array_index_) { |
+ while (buffer->has_more()) { |
+ uint32_t c = buffer->GetNext(); |
+ if (c > u::kMaxNonSurrogateCharCode) { |
+ uint16_t c1 = u::LeadSurrogate(c); |
+ uint16_t c2 = u::TrailSurrogate(c); |
+ hasher.AddCharacter(c1); |
+ hasher.AddCharacter(c2); |
+ if (!hasher.UpdateIndex(c1)) break; |
+ if (!hasher.UpdateIndex(c2)) break; |
+ } else { |
+ hasher.AddCharacter(c); |
+ if (!hasher.UpdateIndex(c)) break; |
+ } |
+ } |
} |
- |
// Process the remaining characters without updating the array |
// index. |
while (buffer->has_more()) { |
- hasher.AddCharacterNoIndex(buffer->GetNext()); |
+ ASSERT(!hasher.is_array_index_); |
+ uint32_t c = buffer->GetNext(); |
+ if (c > u::kMaxNonSurrogateCharCode) { |
+ hasher.AddCharacter(u::LeadSurrogate(c)); |
+ hasher.AddCharacter(u::TrailSurrogate(c)); |
+ } else { |
+ hasher.AddCharacter(c); |
+ } |
} |
- |
return hasher.GetHashField(); |
} |
@@ -11652,7 +11705,7 @@ class Utf8SymbolKey : public HashTableKey { |
unibrow::Utf8InputBuffer<> buffer(string_.start(), |
static_cast<unsigned>(string_.length())); |
chars_ = buffer.Utf16Length(); |
- hash_field_ = String::ComputeHashField(&buffer, chars_, seed_); |
+ hash_field_ = StringHasher::ComputeHashField(&buffer, chars_, seed_); |
uint32_t result = hash_field_ >> String::kHashShift; |
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. |
return result; |
@@ -11682,29 +11735,9 @@ class SequentialSymbolKey : public HashTableKey { |
: string_(string), hash_field_(0), seed_(seed) { } |
uint32_t Hash() { |
- StringHasher hasher(string_.length(), seed_); |
- |
- // Very long strings have a trivial hash that doesn't inspect the |
- // string contents. |
- if (hasher.has_trivial_hash()) { |
- hash_field_ = hasher.GetHashField(); |
- } else { |
- int i = 0; |
- // Do the iterative array index computation as long as there is a |
- // chance this is an array index. |
- while (i < string_.length() && hasher.is_array_index()) { |
- hasher.AddCharacter(static_cast<uc32>(string_[i])); |
- i++; |
- } |
- |
- // Process the remaining characters without updating the array |
- // index. |
- while (i < string_.length()) { |
- hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i])); |
- i++; |
- } |
- hash_field_ = hasher.GetHashField(); |
- } |
+ hash_field_ = StringHasher::HashSequentialString<Char>(string_.start(), |
+ string_.length(), |
+ seed_); |
uint32_t result = hash_field_ >> String::kHashShift; |
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. |
@@ -11749,32 +11782,9 @@ class SubStringAsciiSymbolKey : public HashTableKey { |
uint32_t Hash() { |
ASSERT(length_ >= 0); |
ASSERT(from_ + length_ <= string_->length()); |
- StringHasher hasher(length_, string_->GetHeap()->HashSeed()); |
- |
- // Very long strings have a trivial hash that doesn't inspect the |
- // string contents. |
- if (hasher.has_trivial_hash()) { |
- hash_field_ = hasher.GetHashField(); |
- } else { |
- int i = 0; |
- // Do the iterative array index computation as long as there is a |
- // chance this is an array index. |
- while (i < length_ && hasher.is_array_index()) { |
- hasher.AddCharacter(static_cast<uc32>( |
- string_->SeqOneByteStringGet(i + from_))); |
- i++; |
- } |
- |
- // Process the remaining characters without updating the array |
- // index. |
- while (i < length_) { |
- hasher.AddCharacterNoIndex(static_cast<uc32>( |
- string_->SeqOneByteStringGet(i + from_))); |
- i++; |
- } |
- hash_field_ = hasher.GetHashField(); |
- } |
- |
+ char* chars = string_->GetChars() + from_; |
+ hash_field_ = StringHasher::HashSequentialString( |
+ chars, length_, string_->GetHeap()->HashSeed()); |
uint32_t result = hash_field_ >> String::kHashShift; |
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. |
return result; |
@@ -11849,8 +11859,7 @@ class SymbolKey : public HashTableKey { |
return string_; |
} |
// Otherwise allocate a new symbol. |
- StringInputBuffer buffer(string_); |
- return heap->AllocateInternalSymbol(&buffer, |
+ return heap->AllocateInternalSymbol(string_, |
string_->length(), |
string_->hash_field()); |
} |
@@ -12620,7 +12629,7 @@ MaybeObject* SymbolTable::LookupString(String* string, Object** s) { |
// algorithm. |
class TwoCharHashTableKey : public HashTableKey { |
public: |
- TwoCharHashTableKey(uint32_t c1, uint32_t c2, uint32_t seed) |
+ TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint32_t seed) |
: c1_(c1), c2_(c2) { |
// Char 1. |
uint32_t hash = seed; |
@@ -12636,17 +12645,17 @@ class TwoCharHashTableKey : public HashTableKey { |
hash ^= hash >> 11; |
hash += hash << 15; |
if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash; |
+ hash_ = hash; |
#ifdef DEBUG |
- StringHasher hasher(2, seed); |
- hasher.AddCharacter(c1); |
- hasher.AddCharacter(c2); |
// If this assert fails then we failed to reproduce the two-character |
// version of the string hashing algorithm above. One reason could be |
// that we were passed two digits as characters, since the hash |
// algorithm is different in that case. |
- ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash)); |
+ uint16_t chars[2] = {c1, c2}; |
+ uint32_t check_hash = StringHasher::HashSequentialString(chars, 2, seed); |
+ hash = (hash << String::kHashShift) | String::kIsNotArrayIndexMask; |
+ ASSERT_EQ(static_cast<int32_t>(hash), static_cast<int32_t>(check_hash)); |
#endif |
- hash_ = hash; |
} |
bool IsMatch(Object* o) { |
@@ -12671,8 +12680,8 @@ class TwoCharHashTableKey : public HashTableKey { |
} |
private: |
- uint32_t c1_; |
- uint32_t c2_; |
+ uint16_t c1_; |
+ uint16_t c2_; |
uint32_t hash_; |
}; |
@@ -12691,8 +12700,8 @@ bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { |
} |
-bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, |
- uint32_t c2, |
+bool SymbolTable::LookupTwoCharsSymbolIfExists(uint16_t c1, |
+ uint16_t c2, |
String** symbol) { |
TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed()); |
int entry = FindEntry(&key); |