1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_STRINGS_STRING_HASHER_INL_H_
6 #define V8_STRINGS_STRING_HASHER_INL_H_
7
8 #include "src/strings/string-hasher.h"
9
10 // Comment inserted to prevent header reordering.
11 #include <type_traits>
12
13 #include "src/objects/name-inl.h"
14 #include "src/objects/objects.h"
15 #include "src/objects/string.h"
16 #include "src/strings/char-predicates-inl.h"
17 #include "src/utils/utils-inl.h"
18
19 namespace v8 {
20 namespace internal {
21
AddCharacterCore(uint32_t running_hash,uint16_t c)22 uint32_t StringHasher::AddCharacterCore(uint32_t running_hash, uint16_t c) {
23 running_hash += c;
24 running_hash += (running_hash << 10);
25 running_hash ^= (running_hash >> 6);
26 return running_hash;
27 }
28
GetHashCore(uint32_t running_hash)29 uint32_t StringHasher::GetHashCore(uint32_t running_hash) {
30 running_hash += (running_hash << 3);
31 running_hash ^= (running_hash >> 11);
32 running_hash += (running_hash << 15);
33 int32_t hash = static_cast<int32_t>(running_hash & String::kHashBitMask);
34 int32_t mask = (hash - 1) >> 31;
35 return running_hash | (kZeroHash & mask);
36 }
37
GetTrivialHash(int length)38 uint32_t StringHasher::GetTrivialHash(int length) {
39 DCHECK_GT(length, String::kMaxHashCalcLength);
40 // The hash of a large string is simply computed from the length.
41 // Ensure that the max length is small enough to be shifted without losing
42 // information.
43 STATIC_ASSERT(base::bits::CountLeadingZeros32(String::kMaxLength) >=
44 String::kHashShift);
45 uint32_t hash = static_cast<uint32_t>(length);
46 return (hash << String::kHashShift) | String::kIsNotIntegerIndexMask;
47 }
48
49 template <typename char_t>
HashSequentialString(const char_t * chars_raw,int length,uint64_t seed)50 uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
51 uint64_t seed) {
52 STATIC_ASSERT(std::is_integral<char_t>::value);
53 STATIC_ASSERT(sizeof(char_t) <= 2);
54 using uchar = typename std::make_unsigned<char_t>::type;
55 const uchar* chars = reinterpret_cast<const uchar*>(chars_raw);
56 DCHECK_LE(0, length);
57 DCHECK_IMPLIES(0 < length, chars != nullptr);
58 if (length >= 1) {
59 if (IsDecimalDigit(chars[0]) && (length == 1 || chars[0] != '0')) {
60 if (length <= String::kMaxArrayIndexSize) {
61 // Possible array index; try to compute the array index hash.
62 uint32_t index = chars[0] - '0';
63 int i = 1;
64 do {
65 if (i == length) {
66 return MakeArrayIndexHash(index, length);
67 }
68 } while (TryAddArrayIndexChar(&index, chars[i++]));
69 }
70 // The following block wouldn't do anything on 32-bit platforms,
71 // because kMaxArrayIndexSize == kMaxIntegerIndexSize there, and
72 // if we wanted to compile it everywhere, then {index_big} would
73 // have to be a {size_t}, which the Mac compiler doesn't like to
74 // implicitly cast to uint64_t for the {TryAddIndexChar} call.
75 #if V8_HOST_ARCH_64_BIT
76 // No "else" here: if the block above was entered and fell through,
77 // we'll have to take this branch.
78 if (length <= String::kMaxIntegerIndexSize) {
79 // Not an array index, but it could still be an integer index.
80 // Perform a regular hash computation, and additionally check
81 // if there are non-digit characters.
82 uint32_t is_integer_index = 0;
83 uint32_t running_hash = static_cast<uint32_t>(seed);
84 uint64_t index_big = 0;
85 const uchar* end = &chars[length];
86 while (chars != end) {
87 if (is_integer_index == 0 &&
88 !TryAddIntegerIndexChar(&index_big, *chars)) {
89 is_integer_index = String::kIsNotIntegerIndexMask;
90 }
91 running_hash = AddCharacterCore(running_hash, *chars++);
92 }
93 uint32_t hash = (GetHashCore(running_hash) << String::kHashShift) |
94 is_integer_index;
95 if (Name::ContainsCachedArrayIndex(hash)) {
96 // The hash accidentally looks like a cached index. Fix that by
97 // setting a bit that looks like a longer-than-cacheable string
98 // length.
99 hash |= (String::kMaxCachedArrayIndexLength + 1)
100 << String::ArrayIndexLengthBits::kShift;
101 }
102 DCHECK(!Name::ContainsCachedArrayIndex(hash));
103 return hash;
104 }
105 #endif
106 }
107 // No "else" here: if the first character was a decimal digit, we might
108 // still have to take this branch.
109 if (length > String::kMaxHashCalcLength) {
110 return GetTrivialHash(length);
111 }
112 }
113
114 // Non-index hash.
115 uint32_t running_hash = static_cast<uint32_t>(seed);
116 const uchar* end = &chars[length];
117 while (chars != end) {
118 running_hash = AddCharacterCore(running_hash, *chars++);
119 }
120
121 return (GetHashCore(running_hash) << String::kHashShift) |
122 String::kIsNotIntegerIndexMask;
123 }
124
operator()125 std::size_t SeededStringHasher::operator()(const char* name) const {
126 return StringHasher::HashSequentialString(
127 name, static_cast<int>(strlen(name)), hashseed_);
128 }
129
130 } // namespace internal
131 } // namespace v8
132
133 #endif // V8_STRINGS_STRING_HASHER_INL_H_
134