2017-04-28 12:13:10 +00:00
|
|
|
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#ifndef V8_STRING_HASHER_INL_H_
|
|
|
|
#define V8_STRING_HASHER_INL_H_
|
|
|
|
|
2018-07-30 13:48:03 +00:00
|
|
|
#include "src/string-hasher.h"
|
|
|
|
|
2017-06-13 15:01:22 +00:00
|
|
|
#include "src/char-predicates-inl.h"
|
2017-04-28 12:13:10 +00:00
|
|
|
#include "src/objects.h"
|
2018-07-30 13:48:03 +00:00
|
|
|
#include "src/objects/string-inl.h"
|
2018-11-20 08:40:26 +00:00
|
|
|
#include "src/utils-inl.h"
|
2017-04-28 12:13:10 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
2018-07-16 09:52:50 +00:00
|
|
|
StringHasher::StringHasher(int length, uint64_t seed)
|
2017-04-28 12:13:10 +00:00
|
|
|
: length_(length),
|
2018-07-16 09:52:50 +00:00
|
|
|
raw_running_hash_(static_cast<uint32_t>(seed)),
|
2017-04-28 12:13:10 +00:00
|
|
|
array_index_(0),
|
2018-11-20 08:40:26 +00:00
|
|
|
is_array_index_(IsInRange(length, 1, String::kMaxArrayIndexSize)) {
|
2017-04-28 12:13:10 +00:00
|
|
|
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);
|
|
|
|
running_hash ^= (running_hash >> 6);
|
|
|
|
return running_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t StringHasher::GetHashCore(uint32_t running_hash) {
|
|
|
|
running_hash += (running_hash << 3);
|
|
|
|
running_hash ^= (running_hash >> 11);
|
|
|
|
running_hash += (running_hash << 15);
|
2018-11-19 14:07:43 +00:00
|
|
|
int32_t hash = static_cast<int32_t>(running_hash & String::kHashBitMask);
|
|
|
|
int32_t mask = (hash - 1) >> 31;
|
|
|
|
return running_hash | (kZeroHash & mask);
|
2017-04-28 12:13:10 +00:00
|
|
|
}
|
|
|
|
|
2018-11-20 08:40:26 +00:00
|
|
|
template <typename Char>
|
2017-04-28 12:13:10 +00:00
|
|
|
uint32_t StringHasher::ComputeRunningHash(uint32_t running_hash,
|
2018-11-20 08:40:26 +00:00
|
|
|
const Char* chars, int length) {
|
|
|
|
DCHECK_LE(0, length);
|
|
|
|
DCHECK_IMPLIES(0 < length, chars != nullptr);
|
|
|
|
const Char* end = &chars[length];
|
|
|
|
while (chars != end) {
|
2017-04-28 12:13:10 +00:00
|
|
|
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_);
|
2018-11-20 08:40:26 +00:00
|
|
|
if (!TryAddIndexChar(&array_index_, c)) {
|
2017-04-28 12:13:10 +00:00
|
|
|
is_array_index_ = false;
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-20 08:40:26 +00:00
|
|
|
is_array_index_ = array_index_ != 0 || length_ == 1;
|
|
|
|
return is_array_index_;
|
2017-04-28 12:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-20 08:40:26 +00:00
|
|
|
raw_running_hash_ =
|
|
|
|
ComputeRunningHash(raw_running_hash_, &chars[i], length - i);
|
2017-04-28 12:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename schar>
|
|
|
|
uint32_t StringHasher::HashSequentialString(const schar* chars, int length,
|
2018-07-16 09:52:50 +00:00
|
|
|
uint64_t seed) {
|
2018-11-20 08:40:26 +00:00
|
|
|
#ifdef DEBUG
|
2017-04-28 12:13:10 +00:00
|
|
|
StringHasher hasher(length, seed);
|
|
|
|
if (!hasher.has_trivial_hash()) hasher.AddCharacters(chars, length);
|
2018-11-20 08:40:26 +00:00
|
|
|
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.
|
|
|
|
if (IsInRange(length, 1, String::kMaxArrayIndexSize)) {
|
|
|
|
if (IsDecimalDigit(chars[0]) && (length == 1 || chars[0] != '0')) {
|
|
|
|
uint32_t index = chars[0] - '0';
|
|
|
|
int i = 1;
|
|
|
|
do {
|
|
|
|
if (i == length) {
|
|
|
|
uint32_t result = MakeArrayIndexHash(index, length);
|
|
|
|
DCHECK_EQ(expected, result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2017-04-28 12:13:10 +00:00
|
|
|
}
|
|
|
|
|
2018-07-16 09:52:50 +00:00
|
|
|
IteratingStringHasher::IteratingStringHasher(int len, uint64_t seed)
|
2017-04-28 12:13:10 +00:00
|
|
|
: StringHasher(len, seed) {}
|
|
|
|
|
2018-11-27 00:48:42 +00:00
|
|
|
uint32_t IteratingStringHasher::Hash(String string, uint64_t seed) {
|
2017-04-28 12:13:10 +00:00
|
|
|
IteratingStringHasher hasher(string->length(), seed);
|
|
|
|
// Nothing to do.
|
|
|
|
if (hasher.has_trivial_hash()) return hasher.GetHashField();
|
2018-11-27 00:48:42 +00:00
|
|
|
ConsString cons_string = String::VisitFlat(&hasher, string);
|
|
|
|
if (cons_string.is_null()) return hasher.GetHashField();
|
2017-04-28 12:13:10 +00:00
|
|
|
hasher.VisitConsString(cons_string);
|
|
|
|
return hasher.GetHashField();
|
|
|
|
}
|
|
|
|
|
|
|
|
void IteratingStringHasher::VisitOneByteString(const uint8_t* chars,
|
|
|
|
int length) {
|
|
|
|
AddCharacters(chars, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void IteratingStringHasher::VisitTwoByteString(const uint16_t* chars,
|
|
|
|
int length) {
|
|
|
|
AddCharacters(chars, length);
|
|
|
|
}
|
|
|
|
|
2018-05-07 14:39:43 +00:00
|
|
|
std::size_t SeededStringHasher::operator()(const char* name) const {
|
|
|
|
return StringHasher::HashSequentialString(
|
|
|
|
name, static_cast<int>(strlen(name)), hashseed_);
|
|
|
|
}
|
|
|
|
|
2017-04-28 12:13:10 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|
|
|
|
|
|
|
|
#endif // V8_STRING_HASHER_INL_H_
|