diff --git a/common.gypi b/common.gypi index d71df903c12..707f83b707e 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.35', + 'v8_embedder_string': '-node.36', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/.gitignore b/deps/v8/.gitignore index bfc93c7d444..82dbccdd81f 100644 --- a/deps/v8/.gitignore +++ b/deps/v8/.gitignore @@ -80,6 +80,7 @@ !/third_party/inspector_protocol !/third_party/jsoncpp /third_party/jsoncpp/source +!/third_party/rapidhash-v8 !/third_party/re2 /third_party/re2/src !/third_party/test262-harness diff --git a/deps/v8/BUILD.bazel b/deps/v8/BUILD.bazel index 05b7472165a..b9c9dbef8e9 100644 --- a/deps/v8/BUILD.bazel +++ b/deps/v8/BUILD.bazel @@ -1849,6 +1849,7 @@ filegroup( "src/numbers/conversions.cc", "src/numbers/conversions.h", "src/numbers/conversions-inl.h", + "src/numbers/hash-seed.h", "src/numbers/hash-seed-inl.h", "src/numbers/integer-literal.h", "src/numbers/integer-literal-inl.h", @@ -2444,6 +2445,8 @@ filegroup( "src/zone/zone-segment.h", "src/zone/zone-type-traits.h", "src/zone/zone-utils.h", + "third_party/rapidhash-v8/rapidhash.h", + "third_party/rapidhash-v8/secret.h", ":cppgc_base_files", ":generated_bytecode_builtins_list", ":v8_bigint", diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index e2e0c16d970..647ece44682 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -428,6 +428,9 @@ declare_args() { # Whether custom embedder snapshots may extend (= allocate new objects in) # ReadOnlySpace. v8_enable_extensible_ro_snapshot = true + + # Use a hard-coded secret value when hashing. + v8_use_default_hasher_secret = true } # Derived defaults. @@ -863,6 +866,7 @@ config("external_startup_data") { # Make sure the |v8_generate_features_json| action is also updated when adding # or removing defines below. external_v8_defines = [ + "V8_USE_DEFAULT_HASHER_SECRET=${v8_use_default_hasher_secret}", "V8_ENABLE_CHECKS", "V8_COMPRESS_POINTERS", "V8_COMPRESS_POINTERS_IN_SHARED_CAGE", @@ -880,7 +884,9 @@ external_v8_defines = [ "V8_MINORMS_STRING_SHORTCUTTING", ] -enabled_external_v8_defines = [] +enabled_external_v8_defines = [ + "V8_USE_DEFAULT_HASHER_SECRET=${v8_use_default_hasher_secret}", +] if (v8_enable_v8_checks) { enabled_external_v8_defines += [ "V8_ENABLE_CHECKS" ] @@ -2768,6 +2774,7 @@ generated_file("v8_generate_features_json") { v8_random_seed = v8_random_seed v8_use_perfetto = v8_use_perfetto v8_use_siphash = v8_use_siphash + v8_use_default_hasher_secret = v8_use_default_hasher_secret } } @@ -3763,6 +3770,7 @@ v8_header_set("v8_internal_headers") { "src/numbers/conversions-inl.h", "src/numbers/conversions.h", "src/numbers/hash-seed-inl.h", + "src/numbers/hash-seed.h", "src/numbers/math-random.h", "src/objects/all-objects-inl.h", "src/objects/allocation-site-inl.h", @@ -4116,6 +4124,8 @@ v8_header_set("v8_internal_headers") { "src/temporal/temporal-parser.h", "src/third_party/siphash/halfsiphash.h", "src/third_party/utf8-decoder/utf8-decoder.h", + "third_party/rapidhash-v8/rapidhash.h", + "third_party/rapidhash-v8/secret.h", "src/torque/runtime-macro-shims.h", "src/tracing/trace-event.h", "src/tracing/traced-value.h", diff --git a/deps/v8/src/DEPS b/deps/v8/src/DEPS index d9c58d01236..c42bc1d59da 100644 --- a/deps/v8/src/DEPS +++ b/deps/v8/src/DEPS @@ -123,4 +123,13 @@ specific_include_rules = { "snapshot\.cc": [ "+src/heap/read-only-promotion.h", ], + "string-hasher-inl\.h": [ + "+third_party/rapidhash-v8/rapidhash.h", + ], + "heap\.cc": [ + "+third_party/rapidhash-v8/secret.h", + ], + "hash-seed-inl\.h": [ + "+third_party/rapidhash-v8/secret.h", + ], } diff --git a/deps/v8/src/ast/ast-value-factory.cc b/deps/v8/src/ast/ast-value-factory.cc index f7470b5cff6..1233adf53ce 100644 --- a/deps/v8/src/ast/ast-value-factory.cc +++ b/deps/v8/src/ast/ast-value-factory.cc @@ -291,7 +291,8 @@ std::forward_list AstConsString::ToRawStrings() const { return result; } -AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed) +AstStringConstants::AstStringConstants(Isolate* isolate, + const HashSeed hash_seed) : zone_(isolate->allocator(), ZONE_NAME), string_table_(), hash_seed_(hash_seed) { diff --git a/deps/v8/src/ast/ast-value-factory.h b/deps/v8/src/ast/ast-value-factory.h index b7bdc33b160..7b644a7fbbe 100644 --- a/deps/v8/src/ast/ast-value-factory.h +++ b/deps/v8/src/ast/ast-value-factory.h @@ -35,6 +35,7 @@ #include "src/common/globals.h" #include "src/handles/handles.h" #include "src/numbers/conversions.h" +#include "src/numbers/hash-seed.h" #include "src/objects/name.h" // Ast(Raw|Cons)String and AstValueFactory are for storing strings and @@ -290,7 +291,7 @@ using AstRawStringMap = class AstStringConstants final { public: - AstStringConstants(Isolate* isolate, uint64_t hash_seed); + AstStringConstants(Isolate* isolate, const HashSeed hash_seed); AstStringConstants(const AstStringConstants&) = delete; AstStringConstants& operator=(const AstStringConstants&) = delete; @@ -299,13 +300,13 @@ class AstStringConstants final { AST_STRING_CONSTANTS(F) #undef F - uint64_t hash_seed() const { return hash_seed_; } + const HashSeed hash_seed() const { return hash_seed_; } const AstRawStringMap* string_table() const { return &string_table_; } private: Zone zone_; AstRawStringMap string_table_; - uint64_t hash_seed_; + const HashSeed hash_seed_; #define F(name, str) AstRawString* name##_string_; AST_STRING_CONSTANTS(F) @@ -315,12 +316,12 @@ class AstStringConstants final { class AstValueFactory { public: AstValueFactory(Zone* zone, const AstStringConstants* string_constants, - uint64_t hash_seed) + const HashSeed hash_seed) : AstValueFactory(zone, zone, string_constants, hash_seed) {} AstValueFactory(Zone* ast_raw_string_zone, Zone* single_parse_zone, const AstStringConstants* string_constants, - uint64_t hash_seed) + const HashSeed hash_seed) : string_table_(string_constants->string_table()), strings_(nullptr), strings_end_(&strings_), @@ -418,7 +419,7 @@ class AstValueFactory { Zone* ast_raw_string_zone_; Zone* single_parse_zone_; - uint64_t hash_seed_; + const HashSeed hash_seed_; }; extern template EXPORT_TEMPLATE_DECLARE( diff --git a/deps/v8/src/codegen/external-reference.cc b/deps/v8/src/codegen/external-reference.cc index bbbaadb12f7..ef05e9f4318 100644 --- a/deps/v8/src/codegen/external-reference.cc +++ b/deps/v8/src/codegen/external-reference.cc @@ -1176,7 +1176,8 @@ FUNCTION_REFERENCE(jsreceiver_create_identity_hash, static uint32_t ComputeSeededIntegerHash(Isolate* isolate, int32_t key) { DisallowGarbageCollection no_gc; - return ComputeSeededHash(static_cast(key), HashSeed(isolate)); + return ComputeSeededHash(static_cast(key), + HashSeed(isolate).seed()); } FUNCTION_REFERENCE(compute_integer_hash, ComputeSeededIntegerHash) diff --git a/deps/v8/src/debug/debug-interface.cc b/deps/v8/src/debug/debug-interface.cc index 49b769a63b1..d52a4ae5052 100644 --- a/deps/v8/src/debug/debug-interface.cc +++ b/deps/v8/src/debug/debug-interface.cc @@ -910,7 +910,8 @@ uint32_t WasmScript::GetFunctionHash(int function_index) { wire_bytes.GetFunctionBytes(&func); // TODO(herhut): Maybe also take module, name and signature into account. return i::StringHasher::HashSequentialString(function_bytes.begin(), - function_bytes.length(), 0); + function_bytes.length(), + internal::HashSeed::Default()); } int WasmScript::CodeOffset() const { diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index eb338f4b52b..c1dd952c88f 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -119,6 +119,7 @@ #include "src/tracing/trace-event.h" #include "src/utils/utils-inl.h" #include "src/utils/utils.h" +#include "third_party/rapidhash-v8/secret.h" #ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING #include "src/heap/conservative-stack-visitor.h" @@ -5825,9 +5826,20 @@ void Heap::InitializeHashSeed() { } else { new_hash_seed = static_cast(v8_flags.hash_seed); } + Tagged hash_seed = ReadOnlyRoots(this).hash_seed(); + MemCopy(hash_seed->begin(), reinterpret_cast(&new_hash_seed), kInt64Size); + +#if V8_USE_DEFAULT_HASHER_SECRET + MemCopy(hash_seed->begin() + kInt64Size, + reinterpret_cast(RAPIDHASH_DEFAULT_SECRET), + kInt64Size * 3); +#else + rapidhash_make_secret(new_hash_seed, reinterpret_cast( + hash_seed->begin() + kInt64Size)); +#endif // V8_USE_DEFAULT_HASHER_SECRET } std::shared_ptr Heap::GetForegroundTaskRunner() const { diff --git a/deps/v8/src/heap/setup-heap-internal.cc b/deps/v8/src/heap/setup-heap-internal.cc index a3bcae17482..35215e9da0b 100644 --- a/deps/v8/src/heap/setup-heap-internal.cc +++ b/deps/v8/src/heap/setup-heap-internal.cc @@ -853,7 +853,8 @@ bool Heap::CreateImportantReadOnlyObjects() { // Hash seed for strings Factory* factory = isolate()->factory(); - set_hash_seed(*factory->NewByteArray(kInt64Size, AllocationType::kReadOnly)); + set_hash_seed( + *factory->NewByteArray(kInt64Size * 4, AllocationType::kReadOnly)); InitializeHashSeed(); // Important strings and symbols diff --git a/deps/v8/src/json/json-parser.cc b/deps/v8/src/json/json-parser.cc index 28e6a56ef11..7bd445f94b2 100644 --- a/deps/v8/src/json/json-parser.cc +++ b/deps/v8/src/json/json-parser.cc @@ -314,7 +314,6 @@ bool JsonParseInternalizer::RecurseAndApply(Handle holder, template JsonParser::JsonParser(Isolate* isolate, Handle source) : isolate_(isolate), - hash_seed_(HashSeed(isolate)), object_constructor_(isolate_->object_function()), original_source_(source) { size_t start = 0; diff --git a/deps/v8/src/json/json-parser.h b/deps/v8/src/json/json-parser.h index d27c1de905e..58fc5c96658 100644 --- a/deps/v8/src/json/json-parser.h +++ b/deps/v8/src/json/json-parser.h @@ -385,7 +385,6 @@ class JsonParser final { int position() const { return static_cast(cursor_ - chars_); } Isolate* isolate_; - const uint64_t hash_seed_; JsonToken next_; // Indicates whether the bytes underneath source_ can relocate during GC. bool chars_may_relocate_; diff --git a/deps/v8/src/numbers/hash-seed-inl.h b/deps/v8/src/numbers/hash-seed-inl.h index aabe6c32e60..fb539869226 100644 --- a/deps/v8/src/numbers/hash-seed-inl.h +++ b/deps/v8/src/numbers/hash-seed-inl.h @@ -5,47 +5,36 @@ #ifndef V8_NUMBERS_HASH_SEED_INL_H_ #define V8_NUMBERS_HASH_SEED_INL_H_ -#include - -// The #includes below currently lead to cyclic transitive includes, so -// HashSeed() ends up being required before it is defined, so we have to -// declare it here. This is a workaround; if we needed this permanently then -// we should put that line into a "hash-seed.h" header; but we won't need -// it for long. -// TODO(jkummerow): Get rid of this by breaking circular include dependencies. -namespace v8 { -namespace internal { - -class Isolate; -class LocalIsolate; -class ReadOnlyRoots; - -inline uint64_t HashSeed(Isolate* isolate); -inline uint64_t HashSeed(LocalIsolate* isolate); -inline uint64_t HashSeed(ReadOnlyRoots roots); - -} // namespace internal -} // namespace v8 - -// See comment above for why this isn't at the top of the file. +#include "src/numbers/hash-seed.h" #include "src/objects/fixed-array-inl.h" #include "src/roots/roots-inl.h" +#include "third_party/rapidhash-v8/secret.h" namespace v8 { namespace internal { -inline uint64_t HashSeed(Isolate* isolate) { - return HashSeed(ReadOnlyRoots(isolate)); +inline HashSeed::HashSeed(Isolate* isolate) + : HashSeed(ReadOnlyRoots(isolate)) {} + +inline HashSeed::HashSeed(LocalIsolate* isolate) + : HashSeed(ReadOnlyRoots(isolate)) {} + +inline HashSeed::HashSeed(ReadOnlyRoots roots) { + // roots.hash_seed is not aligned + MemCopy(&seed_, roots.hash_seed()->begin(), sizeof(seed_)); + MemCopy(secret_, roots.hash_seed()->begin() + sizeof(seed_), sizeof(secret_)); } -inline uint64_t HashSeed(LocalIsolate* isolate) { - return HashSeed(ReadOnlyRoots(isolate)); -} +inline HashSeed::HashSeed(uint64_t seed, const uint64_t secret[3]) + : seed_(seed), + secret_{ + secret[0], + secret[1], + secret[2], + } {} -inline uint64_t HashSeed(ReadOnlyRoots roots) { - uint64_t seed; - MemCopy(&seed, roots.hash_seed()->begin(), sizeof(seed)); - return seed; +inline HashSeed HashSeed::Default() { + return HashSeed(0, RAPIDHASH_DEFAULT_SECRET); } } // namespace internal diff --git a/deps/v8/src/numbers/hash-seed.h b/deps/v8/src/numbers/hash-seed.h new file mode 100644 index 00000000000..48368a25120 --- /dev/null +++ b/deps/v8/src/numbers/hash-seed.h @@ -0,0 +1,42 @@ +// Copyright 2019 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_NUMBERS_HASH_SEED_H_ +#define V8_NUMBERS_HASH_SEED_H_ + +#include + +namespace v8 { +namespace internal { + +class Isolate; +class LocalIsolate; +class ReadOnlyRoots; + +class HashSeed { + public: + inline explicit HashSeed(Isolate* isolate); + inline explicit HashSeed(LocalIsolate* isolate); + inline explicit HashSeed(ReadOnlyRoots roots); + inline explicit HashSeed(uint64_t seed, const uint64_t secret[3]); + + static inline HashSeed Default(); + + uint64_t seed() const { return seed_; } + const uint64_t* secret() const { return secret_; } + + constexpr bool operator==(const HashSeed& b) const { + return seed_ == b.seed_ && secret_[0] == b.secret_[0] && + secret_[1] == b.secret_[1] && secret_[2] == b.secret_[2]; + } + + private: + uint64_t seed_; + uint64_t secret_[3]; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_NUMBERS_HASH_SEED_H_ diff --git a/deps/v8/src/objects/dictionary-inl.h b/deps/v8/src/objects/dictionary-inl.h index 149de8ee58f..dde61fe775e 100644 --- a/deps/v8/src/objects/dictionary-inl.h +++ b/deps/v8/src/objects/dictionary-inl.h @@ -320,14 +320,14 @@ bool NumberDictionaryBaseShape::IsMatch(uint32_t key, Tagged other) { } uint32_t NumberDictionaryBaseShape::Hash(ReadOnlyRoots roots, uint32_t key) { - return ComputeSeededHash(key, HashSeed(roots)); + return ComputeSeededHash(key, HashSeed(roots).seed()); } uint32_t NumberDictionaryBaseShape::HashForObject(ReadOnlyRoots roots, Tagged other) { DCHECK(IsNumber(other)); return ComputeSeededHash(static_cast(Object::Number(other)), - HashSeed(roots)); + HashSeed(roots).seed()); } template diff --git a/deps/v8/src/objects/string-inl.h b/deps/v8/src/objects/string-inl.h index fd14ceb2e1e..82cb6ec87a2 100644 --- a/deps/v8/src/objects/string-inl.h +++ b/deps/v8/src/objects/string-inl.h @@ -399,7 +399,7 @@ Char FlatStringReader::Get(int index) const { template class SequentialStringKey final : public StringTableKey { public: - SequentialStringKey(base::Vector chars, uint64_t seed, + SequentialStringKey(base::Vector chars, const HashSeed seed, bool convert = false) : SequentialStringKey(StringHasher::HashSequentialString( chars.begin(), chars.length(), seed), @@ -798,13 +798,14 @@ String::FlatContent::~FlatContent() { #ifdef ENABLE_SLOW_DCHECKS uint32_t String::FlatContent::ComputeChecksum() const { - constexpr uint64_t hashseed = 1; uint32_t hash; if (state_ == ONE_BYTE) { - hash = StringHasher::HashSequentialString(onebyte_start, length_, hashseed); + hash = StringHasher::HashSequentialString(onebyte_start, length_, + HashSeed::Default()); } else { DCHECK_EQ(TWO_BYTE, state_); - hash = StringHasher::HashSequentialString(twobyte_start, length_, hashseed); + hash = StringHasher::HashSequentialString(twobyte_start, length_, + HashSeed::Default()); } DCHECK_NE(kChecksumVerificationDisabled, hash); return hash; diff --git a/deps/v8/src/objects/string-table.cc b/deps/v8/src/objects/string-table.cc index 19523a6f927..71ff3a70105 100644 --- a/deps/v8/src/objects/string-table.cc +++ b/deps/v8/src/objects/string-table.cc @@ -567,7 +567,7 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting( return internalized.ptr(); } - uint64_t seed = HashSeed(isolate); + const HashSeed seed = HashSeed(isolate); CharBuffer buffer; const Char* chars; @@ -588,7 +588,8 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting( } // TODO(verwaest): Internalize to one-byte when possible. SequentialStringKey key(raw_hash_field, - base::Vector(chars, length), seed); + base::Vector(chars, length), + seed.seed()); // String could be an array index. if (Name::ContainsCachedArrayIndex(raw_hash_field)) { diff --git a/deps/v8/src/objects/string.cc b/deps/v8/src/objects/string.cc index 3bfb418520a..9cae245b642 100644 --- a/deps/v8/src/objects/string.cc +++ b/deps/v8/src/objects/string.cc @@ -1660,7 +1660,7 @@ namespace { template uint32_t HashString(Tagged string, size_t start, int length, - uint64_t seed, + const HashSeed seed, const SharedStringAccessGuardIfNeeded& access_guard) { DisallowGarbageCollection no_gc; @@ -1703,7 +1703,7 @@ uint32_t String::ComputeAndSetRawHash( DCHECK_IMPLIES(!v8_flags.shared_string_table, !HasHashCode()); // Store the hash code in the object. - uint64_t seed = HashSeed(EarlyGetReadOnlyRoots()); + const HashSeed seed = HashSeed(EarlyGetReadOnlyRoots()); size_t start = 0; Tagged string = this; StringShape shape(string); diff --git a/deps/v8/src/parsing/parse-info.h b/deps/v8/src/parsing/parse-info.h index 59de7059190..0fda95e1132 100644 --- a/deps/v8/src/parsing/parse-info.h +++ b/deps/v8/src/parsing/parse-info.h @@ -13,6 +13,7 @@ #include "src/base/logging.h" #include "src/common/globals.h" #include "src/handles/handles.h" +#include "src/numbers/hash-seed.h" #include "src/objects/function-kind.h" #include "src/objects/function-syntax-kind.h" #include "src/objects/script.h" @@ -202,7 +203,7 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState { AstValueFactory* ast_value_factory() const { return ast_value_factory_.get(); } - uint64_t hash_seed() const { return hash_seed_; } + HashSeed hash_seed() const { return hash_seed_; } AccountingAllocator* allocator() const { return allocator_; } const AstStringConstants* ast_string_constants() const { return ast_string_constants_; @@ -212,7 +213,7 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState { LazyCompileDispatcher* dispatcher() const { return dispatcher_; } private: - uint64_t hash_seed_; + const HashSeed hash_seed_; AccountingAllocator* allocator_; V8FileLogger* v8_file_logger_; LazyCompileDispatcher* dispatcher_; @@ -247,7 +248,7 @@ class V8_EXPORT_PRIVATE ParseInfo { const UnoptimizedCompileFlags& flags() const { return flags_; } // Getters for reusable state. - uint64_t hash_seed() const { return reusable_state_->hash_seed(); } + HashSeed hash_seed() const { return reusable_state_->hash_seed(); } AccountingAllocator* allocator() const { return reusable_state_->allocator(); } diff --git a/deps/v8/src/profiler/heap-snapshot-generator-inl.h b/deps/v8/src/profiler/heap-snapshot-generator-inl.h index c1fdd65e5a4..3057a5e1d41 100644 --- a/deps/v8/src/profiler/heap-snapshot-generator-inl.h +++ b/deps/v8/src/profiler/heap-snapshot-generator-inl.h @@ -55,8 +55,7 @@ Isolate* HeapEntry::isolate() const { return snapshot_->profiler()->isolate(); } uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) { const char* s = reinterpret_cast(string); int len = static_cast(strlen(s)); - return StringHasher::HashSequentialString(s, len, - v8::internal::kZeroHashSeed); + return StringHasher::HashSequentialString(s, len, HashSeed::Default()); } int HeapSnapshotJSONSerializer::to_node_index(const HeapEntry* e) { diff --git a/deps/v8/src/profiler/strings-storage.cc b/deps/v8/src/profiler/strings-storage.cc index c9b4e44874c..69f8d7b0c32 100644 --- a/deps/v8/src/profiler/strings-storage.cc +++ b/deps/v8/src/profiler/strings-storage.cc @@ -138,7 +138,7 @@ namespace { inline uint32_t ComputeStringHash(const char* str, int len) { uint32_t raw_hash_field = base::bits::RotateLeft32( - StringHasher::HashSequentialString(str, len, kZeroHashSeed), 2); + StringHasher::HashSequentialString(str, len, HashSeed::Default()), 2); return Name::HashBits::decode(raw_hash_field); } diff --git a/deps/v8/src/runtime/runtime.cc b/deps/v8/src/runtime/runtime.cc index af669bbc926..11d042ed320 100644 --- a/deps/v8/src/runtime/runtime.cc +++ b/deps/v8/src/runtime/runtime.cc @@ -65,8 +65,8 @@ struct IntrinsicFunctionIdentifier { } uint32_t Hash() { - return StringHasher::HashSequentialString( - data_, length_, v8::internal::kZeroHashSeed); + return StringHasher::HashSequentialString(data_, length_, + HashSeed::Default()); } const unsigned char* data_; diff --git a/deps/v8/src/strings/string-hasher-inl.h b/deps/v8/src/strings/string-hasher-inl.h index 4a6c7ada4b3..0f362c93b67 100644 --- a/deps/v8/src/strings/string-hasher-inl.h +++ b/deps/v8/src/strings/string-hasher-inl.h @@ -48,7 +48,7 @@ uint32_t StringHasher::GetTrivialHash(int length) { template uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length, - uint64_t seed) { + const HashSeed seed) { static_assert(std::is_integral::value); static_assert(sizeof(char_t) <= 2); using uchar = typename std::make_unsigned::type; @@ -80,7 +80,7 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length, // Perform a regular hash computation, and additionally check // if there are non-digit characters. String::HashFieldType type = String::HashFieldType::kIntegerIndex; - uint32_t running_hash = static_cast(seed); + uint32_t running_hash = static_cast(seed.seed()); uint64_t index_big = 0; const uchar* end = &chars[length]; while (chars != end) { @@ -112,7 +112,7 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length, } // Non-index hash. - uint32_t running_hash = static_cast(seed); + uint32_t running_hash = static_cast(seed.seed()); const uchar* end = &chars[length]; while (chars != end) { running_hash = AddCharacterCore(running_hash, *chars++); diff --git a/deps/v8/src/strings/string-hasher.h b/deps/v8/src/strings/string-hasher.h index c26fe794267..adba58268cc 100644 --- a/deps/v8/src/strings/string-hasher.h +++ b/deps/v8/src/strings/string-hasher.h @@ -6,6 +6,7 @@ #define V8_STRINGS_STRING_HASHER_H_ #include "src/common/globals.h" +#include "src/numbers/hash-seed.h" namespace v8 { @@ -23,7 +24,7 @@ class V8_EXPORT_PRIVATE StringHasher final { StringHasher() = delete; template static inline uint32_t HashSequentialString(const char_t* chars, int length, - uint64_t seed); + const HashSeed seed); // Calculated hash value for a string consisting of 1 to // String::kMaxArrayIndexSize digits with no leading zeros (except "0"). @@ -44,10 +45,10 @@ class V8_EXPORT_PRIVATE StringHasher final { // Useful for std containers that require something ()'able. struct SeededStringHasher { - explicit SeededStringHasher(uint64_t hashseed) : hashseed_(hashseed) {} + explicit SeededStringHasher(const HashSeed hashseed) : hashseed_(hashseed) {} inline std::size_t operator()(const char* name) const; - uint64_t hashseed_; + const HashSeed hashseed_; }; // Useful for std containers that require something ()'able. diff --git a/deps/v8/src/utils/utils.h b/deps/v8/src/utils/utils.h index 38ec292c346..37c9b6e3b87 100644 --- a/deps/v8/src/utils/utils.h +++ b/deps/v8/src/utils/utils.h @@ -251,8 +251,6 @@ inline T RoundingAverageUnsigned(T a, T b) { // ---------------------------------------------------------------------------- // Hash function. -static const uint64_t kZeroHashSeed = 0; - // Thomas Wang, Integer Hash Functions. // http://www.concentric.net/~Ttwang/tech/inthash.htm` inline uint32_t ComputeUnseededHash(uint32_t key) { diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc index 565755c688c..3e58d80ee75 100644 --- a/deps/v8/src/wasm/wasm-module.cc +++ b/deps/v8/src/wasm/wasm-module.cc @@ -762,7 +762,7 @@ int JumpTableOffset(const WasmModule* module, int func_index) { size_t GetWireBytesHash(base::Vector wire_bytes) { return StringHasher::HashSequentialString( reinterpret_cast(wire_bytes.begin()), wire_bytes.length(), - kZeroHashSeed); + HashSeed::Default()); } int NumFeedbackSlots(const WasmModule* module, int func_index) { diff --git a/deps/v8/test/cctest/heap/test-heap.cc b/deps/v8/test/cctest/heap/test-heap.cc index 4b8fe9bf073..aad1fd64f1a 100644 --- a/deps/v8/test/cctest/heap/test-heap.cc +++ b/deps/v8/test/cctest/heap/test-heap.cc @@ -6644,7 +6644,7 @@ UNINITIALIZED_TEST(ReinitializeStringHashSeed) { { v8::Isolate::Scope isolate_scope(isolate); CHECK_EQ(static_cast(1337 * i), - HashSeed(reinterpret_cast(isolate))); + HashSeed(reinterpret_cast(isolate)).seed()); v8::HandleScope handle_scope(isolate); v8::Local context = v8::Context::New(isolate); CHECK(!context.IsEmpty()); diff --git a/deps/v8/test/cctest/test-code-stub-assembler.cc b/deps/v8/test/cctest/test-code-stub-assembler.cc index 199e445f4b2..1577ba27f44 100644 --- a/deps/v8/test/cctest/test-code-stub-assembler.cc +++ b/deps/v8/test/cctest/test-code-stub-assembler.cc @@ -501,7 +501,7 @@ TEST(ComputeIntegerHash) { Handle key(Smi::FromInt(k), isolate); Handle result = ft.Call(key).ToHandleChecked(); - uint32_t hash = ComputeSeededHash(k, HashSeed(isolate)); + uint32_t hash = ComputeSeededHash(k, HashSeed(isolate).seed()); Tagged expected = Smi::FromInt(hash); CHECK_EQ(expected, Smi::cast(*result)); } diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 017569cef37..38533a4a7e6 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -4878,7 +4878,7 @@ UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) { v8::Isolate::Scope isolate_scope(isolate); // Check that rehashing has been performed. CHECK_EQ(static_cast(1337), - HashSeed(reinterpret_cast(isolate))); + HashSeed(reinterpret_cast(isolate)).seed()); v8::HandleScope handle_scope(isolate); v8::Local context = v8::Context::New(isolate); CHECK(!context.IsEmpty()); @@ -4947,7 +4947,7 @@ UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) { v8::Isolate::Scope isolate_scope(isolate); // Check that rehashing has been performed. CHECK_EQ(static_cast(1337), - HashSeed(reinterpret_cast(isolate))); + HashSeed(reinterpret_cast(isolate)).seed()); v8::HandleScope handle_scope(isolate); v8::Local context = v8::Context::New(isolate); CHECK(!context.IsEmpty()); diff --git a/deps/v8/test/cctest/test-shared-strings.cc b/deps/v8/test/cctest/test-shared-strings.cc index 4464aa694f4..31ac214175c 100644 --- a/deps/v8/test/cctest/test-shared-strings.cc +++ b/deps/v8/test/cctest/test-shared-strings.cc @@ -128,7 +128,7 @@ UNINITIALIZED_TEST(InPlaceInternalizableStringsAreShared) { CHECK(!InAnySharedSpace(*young_two_byte_seq)); // Internalized strings are shared. - uint64_t seed = HashSeed(i_isolate1); + HashSeed seed = HashSeed(i_isolate1); Handle one_byte_intern = factory1->NewOneByteInternalizedString( base::OneByteVector(raw_one_byte), StringHasher::HashSequentialString(raw_one_byte, 3, seed)); diff --git a/deps/v8/third_party/rapidhash-v8/LICENSE b/deps/v8/third_party/rapidhash-v8/LICENSE new file mode 100644 index 00000000000..e70352870fb --- /dev/null +++ b/deps/v8/third_party/rapidhash-v8/LICENSE @@ -0,0 +1,34 @@ +/* + * rapidhash - Very fast, high quality, platform independant hashing algorithm. + * Copyright (C) 2024 Nicolas De Carli + * + * Based on 'wyhash', by Wang Yi + * + * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at: + * - rapidhash source repository: https://github.com/Nicoshev/rapidhash + */ diff --git a/deps/v8/third_party/rapidhash-v8/OWNERS b/deps/v8/third_party/rapidhash-v8/OWNERS new file mode 100644 index 00000000000..6a5523a74ae --- /dev/null +++ b/deps/v8/third_party/rapidhash-v8/OWNERS @@ -0,0 +1,2 @@ +leszeks@chromium.org +mlippautz@chromium.org diff --git a/deps/v8/third_party/rapidhash-v8/README.chromium b/deps/v8/third_party/rapidhash-v8/README.chromium new file mode 100644 index 00000000000..be5b85de6fb --- /dev/null +++ b/deps/v8/third_party/rapidhash-v8/README.chromium @@ -0,0 +1,20 @@ +Name: rapidhash +URL: https://github.com/Nicoshev/rapidhash/blob/master/rapidhash.h +Version: N/A +Date: 2024-07-08 +Revision: 588978411df8683777429f729be5213eb1bfd5f3 +License: BSD 2-clause +License File: LICENSE +Shipped: Yes +Security Critical: yes + +Description: +A fast hash function. + +This is a fork of upstream, with the parts that we don't need or want +removed. We do not intend to sync regularly with upstream git. This +particular version is copied over from Chromium. + +Local Modifications: +- Copied over from Chromium's third_party/rapidhash with all its changes. +- Removed base/ includes and replaced with V8 versions. diff --git a/deps/v8/third_party/rapidhash-v8/rapidhash.h b/deps/v8/third_party/rapidhash-v8/rapidhash.h new file mode 100644 index 00000000000..dc0c20dde57 --- /dev/null +++ b/deps/v8/third_party/rapidhash-v8/rapidhash.h @@ -0,0 +1,290 @@ +/* + * rapidhash - Very fast, high quality, platform-independent hashing algorithm. + * Copyright (C) 2024 Nicolas De Carli + * + * Based on 'wyhash', by Wang Yi + * + * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at: + * - rapidhash source repository: https://github.com/Nicoshev/rapidhash + */ + +#ifndef _THIRD_PARTY_RAPIDHASH_H +#define _THIRD_PARTY_RAPIDHASH_H 1 + +#include +#include +#include + +#include +#include + +#include "include/v8config.h" +#include "src/base/logging.h" + +// Chromium has some local modifications to upstream rapidhash, +// mostly around the concept of HashReaders (including slightly +// more comments for ease of understanding). Generally, rapidhash +// hashes bytes without really caring what these bytes are, +// but often in Chromium, we want to hash _strings_, and strings +// can have multiple representations. In particular, the WTF +// StringImpl class (and by extension, String and AtomicString) +// can have three different states: +// +// 1. Latin1 (or ASCII) code points, in 8 bits per character (LChar). +// 2. The same code points, in 16 bits per character (UChar). +// 3. Strings actually including non-Latin1 code points, in 16 bits +// per character (UChar, UTF-16 encoding). +// +// The problem is that we'd like to hash case #1 and #2 to actually +// return the same hash. There are several ways we could deal with this: +// +// a) Just ignore the problem and hash everything as raw bytes; +// case #2 is fairly rare, and some algorithms (e.g. opportunistic +// deduplication) could live with some false negatives. +// b) Expand all strings to UTF-16 before hashing. This was the +// strategy for the old StringHasher (using SuperFastHash), +// as it naturally worked in 16-bit increments and it is probably +// the simplest. However, this both halves the throughput of the +// hasher and incurs conversion costs. +// c) Detect #2 and convert those cases to #1 (UTF-16 to Latin1 +// conversion), and apart from that, juts hash bytes. +// +// b) is used in e.g. CaseFoldingHash, but c) is the one we use the most +// often. Most strings that we want to hash are 8-bit (e.g. HTML tags), so +// that makes the common case fast. And for UChar strings, it is fairly fast +// to make a pre-pass over the string to check for the existence of any +// non-Latin1 characters. Of course, #1 and #3 can just be hashed as raw +// bytes, as strings from those groups can never be equal anyway. +// +// To support these 8-to-16 and 16-to-8 conversions, and also things like +// lowercasing on the fly, we have modified rapidhash to be templatized +// on a HashReader, supplying bytes to the hash function. For the default +// case of just hashing raw bytes, this is simply reading. But for other +// conversions, the reader is doing slightly more work, such as packing +// or unpacking. See e.g. ConvertTo8BitHashReader in string_hasher.h. +// +// Note that this reader could be made constexpr if we needed to evaluate +// hashes at compile-time. +struct PlainHashReader { + // If this is different from 1 (only 1, 2 and 4 are really reasonable + // options), it means the reader consumes its input at a faster rate than + // would be normally expected. For instance, if it is 2, it means that when + // the reader produces 64 bits, it needs to move its input 128 bits + // ahead. This is used when e.g. a reader converts from UTF-16 to ASCII, + // by removing zeros. The input length must divide the compression factor. + static constexpr unsigned kCompressionFactor = 1; + + // This is the opposite of kExpansionFactor. It does not make sense to have + // both kCompressionFactor and kExpansionFactor different from 1. + // The output length must divide the expansion factor. + static constexpr unsigned kExpansionFactor = 1; + + // Read 64 little-endian bits from the input, taking into account + // the expansion/compression if relevant. Unaligned reads must be + // supported. + static inline uint64_t Read64(const uint8_t* p) { + uint64_t v; + memcpy(&v, p, 8); + return v; + } + + // Similarly, read 32 little-endian (unaligned) bits. Note that + // this must return uint64_t, _not_ uint32_t, as the hasher assumes + // it can freely shift such numbers up and down. + static inline uint64_t Read32(const uint8_t* p) { + uint32_t v; + memcpy(&v, p, 4); + return v; + } + + // Read 1, 2 or 3 bytes from the input, and distribute them somewhat + // reasonably across the resulting 64-bit number. Note that this is + // done in a branch-free fashion, so that it always reads three bytes + // but never reads past the end. + // + // This is only used in the case where we hash a string of exactly + // 1, 2 or 3 bytes; if the hash is e.g. 7 bytes, two overlapping 32-bit + // reads will be done instead. Note that if you have kCompressionFactor == 2, + // this means that this will only ever be called with k = 2. + static inline uint64_t ReadSmall(const uint8_t* p, size_t k) { + return (uint64_t{p[0]} << 56) | (uint64_t{p[k >> 1]} << 32) | p[k - 1]; + } +}; + +/* + * Likely and unlikely macros. + */ +#ifdef _MSC_VER +#define _likely_(x) (x) +#define _unlikely_(x) (x) +#else +#define _likely_(x) __builtin_expect(x, 1) +#define _unlikely_(x) __builtin_expect(x, 0) +#endif + +/* + * 64*64 -> 128bit multiply function. + * + * @param A Address of 64-bit number. + * @param B Address of 64-bit number. + * + * Calculates 128-bit C = *A * *B. + */ +inline std::pair rapid_mul128(uint64_t A, uint64_t B) { +#if defined(__SIZEOF_INT128__) + __uint128_t r = A; + r *= B; + return {static_cast(r), static_cast(r >> 64)}; +#else + // High and low 32 bits of A and B. + uint64_t a_high = A >> 32, b_high = B >> 32, a_low = (uint32_t)A, + b_low = (uint32_t)B; + + // Intermediate products. + uint64_t result_high = a_high * b_high; + uint64_t result_m0 = a_high * b_low; + uint64_t result_m1 = b_high * a_low; + uint64_t result_low = a_low * b_low; + + // Final sum. The lower 64-bit addition can overflow twice, + // so accumulate carry as we go. + uint64_t high = result_high + (result_m0 >> 32) + (result_m1 >> 32); + uint64_t t = result_low + (result_m0 << 32); + high += (t < result_low); // Carry. + uint64_t low = t + (result_m1 << 32); + high += (low < t); // Carry. + + return {low, high}; +#endif +} + +/* + * Multiply and xor mix function. + * + * @param A 64-bit number. + * @param B 64-bit number. + * + * Calculates 128-bit C = A * B. + * Returns 64-bit xor between high and low 64 bits of C. + */ +inline uint64_t rapid_mix(uint64_t A, uint64_t B) { + std::tie(A, B) = rapid_mul128(A, B); + return A ^ B; +} + +/* + * rapidhash main function. + * + * @param key Buffer to be hashed. + * @param len Number of input bytes coming from the reader. + * @param seed 64-bit seed used to alter the hash result predictably. + * @param secret Triplet of 64-bit secrets used to alter hash result + * predictably. + * + * Returns a 64-bit hash. + * + * The data flow is separated so that we never mix input data with pointers; + * + * a, b, seed, secret[0], secret[1], secret[2], see1 and see2 are affected + * by the input data. + * + * p is only ever indexed by len, delta (comes from len only), i (comes from + * len only) or integral constants. len is const, so no data can flow into it. + * + * No other reads from memory take place. No writes to memory take place. + */ +template +V8_INLINE uint64_t rapidhash(const uint8_t* p, const size_t len, uint64_t seed, + const uint64_t secret[3]) { + // For brevity. + constexpr unsigned x = Reader::kCompressionFactor; + constexpr unsigned y = Reader::kExpansionFactor; + DCHECK_EQ(len % y, 0u); + + seed ^= rapid_mix(seed ^ secret[0], secret[1]) ^ len; + uint64_t a, b; + if (_likely_(len <= 16)) { + if (_likely_(len >= 4)) { + // Read the first and last 32 bits (they may overlap). + const uint8_t* plast = p + (len - 4) * x / y; + a = (Reader::Read32(p) << 32) | Reader::Read32(plast); + + // This is equivalent to: delta = (len >= 8) ? 4 : 0; + const uint64_t delta = ((len & 24) >> (len >> 3)) * x / y; + b = ((Reader::Read32(p + delta) << 32) | Reader::Read32(plast - delta)); + } else if (_likely_(len > 0)) { + // 1, 2 or 3 bytes. + a = Reader::ReadSmall(p, len); + b = 0; + } else { + a = b = 0; + } + } else { + size_t i = len; + if (_unlikely_(i > 48)) { + uint64_t see1 = seed, see2 = seed; + do { + seed = rapid_mix(Reader::Read64(p) ^ secret[0], + Reader::Read64(p + 8 * x / y) ^ seed); + see1 = rapid_mix(Reader::Read64(p + 16 * x / y) ^ secret[1], + Reader::Read64(p + 24 * x / y) ^ see1); + see2 = rapid_mix(Reader::Read64(p + 32 * x / y) ^ secret[2], + Reader::Read64(p + 40 * x / y) ^ see2); + p += 48 * x / y; + i -= 48; + } while (_likely_(i >= 48)); + seed ^= see1 ^ see2; + } + if (i > 16) { + seed = rapid_mix(Reader::Read64(p) ^ secret[2], + Reader::Read64(p + 8 * x / y) ^ seed ^ secret[1]); + if (i > 32) { + seed = rapid_mix(Reader::Read64(p + 16 * x / y) ^ secret[2], + Reader::Read64(p + 24 * x / y) ^ seed); + } + } + + // Read the last 2x64 bits. Note that due to the division by y, + // this must be a signed quantity (or the division would become + // unsigned, possibly moving the sign bit down into the address). + // Similarly for x. + a = Reader::Read64(p + (static_cast(i) - 16) * + static_cast(x) / static_cast(y)); + b = Reader::Read64(p + (static_cast(i) - 8) * + static_cast(x) / static_cast(y)); + } + a ^= secret[1]; + b ^= seed; + std::tie(a, b) = rapid_mul128(a, b); + return rapid_mix(a ^ secret[0] ^ len, b ^ secret[1]); +} + +#undef _likely_ +#undef _unlikely_ + +#endif // _THIRD_PARTY_RAPIDHASH_H diff --git a/deps/v8/third_party/rapidhash-v8/secret.h b/deps/v8/third_party/rapidhash-v8/secret.h new file mode 100644 index 00000000000..021125bc4ce --- /dev/null +++ b/deps/v8/third_party/rapidhash-v8/secret.h @@ -0,0 +1,198 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to + * + * author: 王一 Wang Yi + * contributors: Reini Urban, Dietrich Epp, Joshua Haberman, Tommy Ettinger, + * Daniel Lemire, Otmar Ertl, cocowalla, leo-yuriev, Diego Barrios Romero, + * paulie-g, dumblob, Yann Collet, ivte-ms, hyb, James Z.M. Gao, easyaspi314 + * (Devin), TheOneric + */ + +#ifndef _THIRD_PARTY_RAPIDHASH_SECRET_H +#define _THIRD_PARTY_RAPIDHASH_SECRET_H 1 + +#include "rapidhash.h" +#include "src/base/bits.h" + +// Default secret parameters. If we wanted to, we could generate our own +// versions of these at renderer startup in order to perturb the hash +// and make it more DoS-resistant (similar to what base/hash.h does), +// but generating new ones takes a little bit of time (~200 µs on a desktop +// machine as of 2024), and good-quality random numbers may not be copious +// from within the sandbox. The secret concept is inherited from wyhash, +// described by the wyhash author here: +// +// https://github.com/wangyi-fudan/wyhash/issues/139 +// +// The rules are: +// +// 1. Each byte must be “balanced”, i.e., have exactly 4 bits set. +// (This is trivially done by just precompting a LUT with the +// possible bytes and picking from those.) +// +// 2. Each 64-bit group should have a Hamming distance of 32 to +// all the others (i.e., popcount(secret[i] ^ secret[j]) == 32). +// This is just done by rejection sampling. +// +// 3. Each 64-bit group should be prime. It's not obvious that this +// is really needed for the hash, as opposed to wyrand which also +// uses the same secret, but according to the author, it is +// “a feeling to be perfect”. This naturally means the last byte +// cannot be divisible by 2, but apart from that, is easiest +// checked by testing a few small factors and then the Miller-Rabin +// test, which rejects nearly all bad candidates in the first iteration +// and is fast as long as we have 64x64 -> 128 bit muls and modulos. + +static constexpr uint64_t RAPIDHASH_DEFAULT_SECRET[3] = { + 0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull}; + +#if !V8_USE_DEFAULT_HASHER_SECRET + +namespace detail { + +static inline uint64_t wyrand(uint64_t* seed) { + *seed += 0x2d358dccaa6c78a5ull; + return rapid_mix(*seed, *seed ^ 0x8bb84b93962eacc9ull); +} + +static inline unsigned long long mul_mod(unsigned long long a, + unsigned long long b, + unsigned long long m) { + unsigned long long r = 0; + while (b) { + if (b & 1) { + unsigned long long r2 = r + a; + if (r2 < r) r2 -= m; + r = r2 % m; + } + b >>= 1; + if (b) { + unsigned long long a2 = a + a; + if (a2 < a) a2 -= m; + a = a2 % m; + } + } + return r; +} + +static inline unsigned long long pow_mod(unsigned long long a, + unsigned long long b, + unsigned long long m) { + unsigned long long r = 1; + while (b) { + if (b & 1) r = mul_mod(r, a, m); + b >>= 1; + if (b) a = mul_mod(a, a, m); + } + return r; +} + +static unsigned sprp(unsigned long long n, unsigned long long a) { + unsigned long long d = n - 1; + unsigned char s = 0; + while (!(d & 0xff)) { + d >>= 8; + s += 8; + } + if (!(d & 0xf)) { + d >>= 4; + s += 4; + } + if (!(d & 0x3)) { + d >>= 2; + s += 2; + } + if (!(d & 0x1)) { + d >>= 1; + s += 1; + } + unsigned long long b = pow_mod(a, d, n); + if ((b == 1) || (b == (n - 1))) return 1; + unsigned char r; + for (r = 1; r < s; r++) { + b = mul_mod(b, b, n); + if (b <= 1) return 0; + if (b == (n - 1)) return 1; + } + return 0; +} + +static unsigned is_prime(unsigned long long n) { + if (n < 2 || !(n & 1)) return 0; + if (n < 4) return 1; + if (!sprp(n, 2)) return 0; + if (n < 2047) return 1; + if (!sprp(n, 3)) return 0; + if (!sprp(n, 5)) return 0; + if (!sprp(n, 7)) return 0; + if (!sprp(n, 11)) return 0; + if (!sprp(n, 13)) return 0; + if (!sprp(n, 17)) return 0; + if (!sprp(n, 19)) return 0; + if (!sprp(n, 23)) return 0; + if (!sprp(n, 29)) return 0; + if (!sprp(n, 31)) return 0; + if (!sprp(n, 37)) return 0; + return 1; +} + +} // namespace detail + +static inline void rapidhash_make_secret(uint64_t seed, uint64_t* secret) { + uint8_t c[] = {15, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54, + 57, 58, 60, 71, 75, 77, 78, 83, 85, 86, 89, 90, + 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135, + 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, + 169, 170, 172, 177, 178, 180, 184, 195, 197, 198, 201, 202, + 204, 209, 210, 212, 216, 225, 226, 228, 232, 240}; + for (size_t i = 0; i < 3; i++) { + uint8_t ok; + do { + ok = 1; + secret[i] = 0; + for (size_t j = 0; j < 64; j += 8) + secret[i] |= static_cast(c[detail::wyrand(&seed) % sizeof(c)]) + << j; + if (secret[i] % 2 == 0) { + ok = 0; + continue; + } + for (size_t j = 0; j < i; j++) { + if (v8::base::bits::CountPopulation(secret[j] ^ secret[i]) != 32) { + ok = 0; + break; + } + } + if (ok && !detail::is_prime(secret[i])) { + ok = 0; + } + } while (!ok); + } +} + +#endif // !V8_USE_DEFAULT_HASHER_SECRET + +#endif // _THIRD_PARTY_RAPIDHASH_SECRET_H