// 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_ #include "src/string-hasher.h" #include "src/char-predicates-inl.h" #include "src/objects.h" #include "src/objects/string-inl.h" namespace v8 { namespace internal { StringHasher::StringHasher(int length, uint64_t seed) : length_(length), raw_running_hash_(static_cast(seed)), array_index_(0), is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize), is_first_char_(true) { 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); if ((running_hash & String::kHashBitMask) == 0) { return kZeroHash; } return running_hash; } uint32_t StringHasher::ComputeRunningHash(uint32_t running_hash, const uc16* chars, int length) { DCHECK_NOT_NULL(chars); DCHECK_GE(length, 0); for (int i = 0; i < length; ++i) { running_hash = AddCharacterCore(running_hash, *chars++); } return running_hash; } uint32_t StringHasher::ComputeRunningHashOneByte(uint32_t running_hash, const char* chars, int length) { DCHECK_NOT_NULL(chars); DCHECK_GE(length, 0); for (int i = 0; i < length; ++i) { uint16_t c = static_cast(*chars++); running_hash = AddCharacterCore(running_hash, c); } 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 (!IsDecimalDigit(c)) { is_array_index_ = false; return false; } int d = c - '0'; if (is_first_char_) { is_first_char_ = false; if (d == 0 && length_ > 1) { is_array_index_ = false; return false; } } if (array_index_ > 429496729U - ((d + 3) >> 3)) { is_array_index_ = false; return false; } array_index_ = array_index_ * 10 + d; return true; } template 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; } } } for (; i < length; i++) { DCHECK(!is_array_index_); AddCharacter(chars[i]); } } template uint32_t StringHasher::HashSequentialString(const schar* chars, int length, uint64_t seed) { StringHasher hasher(length, seed); if (!hasher.has_trivial_hash()) hasher.AddCharacters(chars, length); return hasher.GetHashField(); } 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 == nullptr) return hasher.GetHashField(); 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); } std::size_t SeededStringHasher::operator()(const char* name) const { return StringHasher::HashSequentialString( name, static_cast(strlen(name)), hashseed_); } } // namespace internal } // namespace v8 #endif // V8_STRING_HASHER_INL_H_