Commit ed833ff5 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[runtime] Remove iterating string hasher

Flatten cons strings into a buffer instead, as we already did for strings
larger than 64 anyway.

Change-Id: Ida7afb9f7ceb38505d67bedfdfbc43c8b4d8c303
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1599428
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61333}
parent f820041a
......@@ -4547,46 +4547,6 @@ uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
}
uint32_t StringHasher::GetHashField() {
if (length_ <= String::kMaxHashCalcLength) {
if (is_array_index_) {
return MakeArrayIndexHash(array_index_, length_);
}
return (GetHashCore(raw_running_hash_) << String::kHashShift) |
String::kIsNotArrayIndexMask;
} else {
return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
}
}
void IteratingStringHasher::VisitConsString(ConsString cons_string) {
// Run small ConsStrings through ConsStringIterator.
if (cons_string->length() < 64) {
ConsStringIterator iter(cons_string);
int offset;
for (String string = iter.Next(&offset); !string.is_null();
string = iter.Next(&offset)) {
DCHECK_EQ(0, offset);
String::VisitFlat(this, string, 0);
}
return;
}
// Slow case.
const int max_length = String::kMaxHashCalcLength;
int length = std::min(cons_string->length(), max_length);
if (cons_string->IsOneByteRepresentation()) {
uint8_t* buffer = new uint8_t[length];
String::WriteToFlat(cons_string, buffer, 0, length);
AddCharacters(buffer, length);
delete[] buffer;
} else {
uint16_t* buffer = new uint16_t[length];
String::WriteToFlat(cons_string, buffer, 0, length);
AddCharacters(buffer, length);
delete[] buffer;
}
}
Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context,
Handle<Map> initial_map) {
// Replace all of the cached initial array maps in the native context with
......
......@@ -1287,14 +1287,61 @@ bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
return CompareChars(content.ToUC16Vector().begin(), str.begin(), slen) == 0;
}
namespace {
template <typename Char>
uint32_t HashString(String string, size_t start, int length, uint64_t seed) {
DisallowHeapAllocation no_gc;
if (length > String::kMaxHashCalcLength) {
return StringHasher::GetTrivialHash(length);
}
std::unique_ptr<Char[]> buffer;
const Char* chars;
if (string.IsConsString()) {
DCHECK_EQ(0, start);
DCHECK(!string.IsFlat());
buffer.reset(new Char[length]);
String::WriteToFlat(string, buffer.get(), 0, length);
chars = buffer.get();
} else {
chars = string.GetChars<Char>(no_gc) + start;
}
return StringHasher::HashSequentialString<Char>(chars, length, seed);
}
} // namespace
uint32_t String::ComputeAndSetHash() {
DisallowHeapAllocation no_gc;
// Should only be called if hash code has not yet been computed.
DCHECK(!HasHashCode());
// Store the hash code in the object.
uint32_t field =
IteratingStringHasher::Hash(*this, HashSeed(GetReadOnlyRoots()));
uint64_t seed = HashSeed(GetReadOnlyRoots());
size_t start = 0;
String string = *this;
if (string.IsSlicedString()) {
SlicedString sliced = SlicedString::cast(string);
start = sliced.offset();
string = sliced.parent();
}
if (string.IsConsString() && string.IsFlat()) {
string = ConsString::cast(string).first();
}
if (string.IsThinString()) {
string = ThinString::cast(string).actual();
if (start == 0) {
set_hash_field(string.hash_field());
return hash_field() >> kHashShift;
}
}
uint32_t field = string.IsOneByteRepresentation()
? HashString<uint8_t>(string, start, length(), seed)
: HashString<uint16_t>(string, start, length(), seed);
set_hash_field(field);
// Check the hash code is there.
......
......@@ -15,18 +15,6 @@
namespace v8 {
namespace internal {
StringHasher::StringHasher(int length, uint64_t seed)
: length_(length),
raw_running_hash_(static_cast<uint32_t>(seed)),
array_index_(0),
is_array_index_(IsInRange(length, 1, String::kMaxArrayIndexSize)) {
DCHECK(FLAG_randomize_hashes || raw_running_hash_ == 0);
}
bool StringHasher::has_trivial_hash() {
return length_ > String::kMaxHashCalcLength;
}
uint32_t StringHasher::AddCharacterCore(uint32_t running_hash, uint16_t c) {
running_hash += c;
running_hash += (running_hash << 10);
......@@ -43,60 +31,15 @@ uint32_t StringHasher::GetHashCore(uint32_t running_hash) {
return running_hash | (kZeroHash & mask);
}
template <typename Char>
uint32_t StringHasher::ComputeRunningHash(uint32_t running_hash,
const Char* chars, int length) {
DCHECK_LE(0, length);
DCHECK_IMPLIES(0 < length, chars != nullptr);
const Char* end = &chars[length];
while (chars != end) {
running_hash = AddCharacterCore(running_hash, *chars++);
}
return running_hash;
}
void StringHasher::AddCharacter(uint16_t c) {
// Use the Jenkins one-at-a-time hash function to update the hash
// for the given character.
raw_running_hash_ = AddCharacterCore(raw_running_hash_, c);
}
bool StringHasher::UpdateIndex(uint16_t c) {
DCHECK(is_array_index_);
if (!TryAddIndexChar(&array_index_, c)) {
is_array_index_ = false;
return false;
}
is_array_index_ = array_index_ != 0 || length_ == 1;
return is_array_index_;
}
template <typename Char>
inline void StringHasher::AddCharacters(const Char* chars, int length) {
DCHECK(sizeof(Char) == 1 || sizeof(Char) == 2);
int i = 0;
if (is_array_index_) {
for (; i < length; i++) {
AddCharacter(chars[i]);
if (!UpdateIndex(chars[i])) {
i++;
break;
}
}
}
raw_running_hash_ =
ComputeRunningHash(raw_running_hash_, &chars[i], length - i);
uint32_t StringHasher::GetTrivialHash(int length) {
DCHECK_GT(length, String::kMaxHashCalcLength);
// String hash of a large string is simply the length.
return (length << String::kHashShift) | String::kIsNotArrayIndexMask;
}
template <typename schar>
uint32_t StringHasher::HashSequentialString(const schar* chars, int length,
uint64_t seed) {
#ifdef DEBUG
StringHasher hasher(length, seed);
if (!hasher.has_trivial_hash()) hasher.AddCharacters(chars, length);
uint32_t expected = hasher.GetHashField();
#endif
// Check whether the string is a valid array index. In that case, compute the
// array index hash. It'll fall through to compute a regular string hash from
// the start if it turns out that the string isn't a valid array index.
......@@ -106,51 +49,25 @@ uint32_t StringHasher::HashSequentialString(const schar* chars, int length,
int i = 1;
do {
if (i == length) {
uint32_t result = MakeArrayIndexHash(index, length);
DCHECK_EQ(expected, result);
return result;
return MakeArrayIndexHash(index, length);
}
} while (TryAddIndexChar(&index, chars[i++]));
}
} else if (length > String::kMaxHashCalcLength) {
// String hash of a large string is simply the length.
uint32_t result =
(length << String::kHashShift) | String::kIsNotArrayIndexMask;
DCHECK_EQ(result, expected);
return result;
return GetTrivialHash(length);
}
// Non-array-index hash.
uint32_t hash =
ComputeRunningHash(static_cast<uint32_t>(seed), chars, length);
uint32_t result =
(GetHashCore(hash) << String::kHashShift) | String::kIsNotArrayIndexMask;
DCHECK_EQ(result, expected);
return result;
}
IteratingStringHasher::IteratingStringHasher(int len, uint64_t seed)
: StringHasher(len, seed) {}
uint32_t IteratingStringHasher::Hash(String string, uint64_t seed) {
IteratingStringHasher hasher(string->length(), seed);
// Nothing to do.
if (hasher.has_trivial_hash()) return hasher.GetHashField();
ConsString cons_string = String::VisitFlat(&hasher, string);
if (cons_string.is_null()) return hasher.GetHashField();
hasher.VisitConsString(cons_string);
return hasher.GetHashField();
}
void IteratingStringHasher::VisitOneByteString(const uint8_t* chars,
int length) {
AddCharacters(chars, length);
}
DCHECK_LE(0, length);
DCHECK_IMPLIES(0 < length, chars != nullptr);
uint32_t running_hash = static_cast<uint32_t>(seed);
const schar* end = &chars[length];
while (chars != end) {
running_hash = AddCharacterCore(running_hash, *chars++);
}
void IteratingStringHasher::VisitTwoByteString(const uint16_t* chars,
int length) {
AddCharacters(chars, length);
return (GetHashCore(running_hash) << String::kHashShift) |
String::kIsNotArrayIndexMask;
}
std::size_t SeededStringHasher::operator()(const char* name) const {
......
......@@ -10,16 +10,12 @@
namespace v8 {
namespace internal {
class ConsString;
class String;
template <typename T>
class Vector;
class V8_EXPORT_PRIVATE StringHasher {
class V8_EXPORT_PRIVATE StringHasher final {
public:
explicit inline StringHasher(int length, uint64_t seed);
StringHasher() = delete;
template <typename schar>
static inline uint32_t HashSequentialString(const schar* chars, int length,
uint64_t seed);
......@@ -37,44 +33,8 @@ class V8_EXPORT_PRIVATE StringHasher {
// Reusable parts of the hashing algorithm.
V8_INLINE static uint32_t AddCharacterCore(uint32_t running_hash, uint16_t c);
V8_INLINE static uint32_t GetHashCore(uint32_t running_hash);
template <typename Char>
V8_INLINE static uint32_t ComputeRunningHash(uint32_t running_hash,
const Char* chars, int length);
protected:
// Returns the value to store in the hash field of a string with
// the given length and contents.
uint32_t GetHashField();
// Returns true if the hash of this string can be computed without
// looking at the contents.
inline bool has_trivial_hash();
// Adds a block of characters to the hash.
template <typename Char>
inline void AddCharacters(const Char* chars, int len);
private:
// Add a character to the hash.
inline void AddCharacter(uint16_t c);
// Update index. Returns true if string is still an index.
inline bool UpdateIndex(uint16_t c);
int length_;
uint32_t raw_running_hash_;
uint32_t array_index_;
bool is_array_index_;
DISALLOW_COPY_AND_ASSIGN(StringHasher);
};
class IteratingStringHasher : public StringHasher {
public:
static inline uint32_t Hash(String string, uint64_t seed);
inline void VisitOneByteString(const uint8_t* chars, int length);
inline void VisitTwoByteString(const uint16_t* chars, int length);
private:
inline IteratingStringHasher(int len, uint64_t seed);
void VisitConsString(ConsString cons_string);
DISALLOW_COPY_AND_ASSIGN(IteratingStringHasher);
static inline uint32_t GetTrivialHash(int length);
};
// Useful for std containers that require something ()'able.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment