mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
deps: V8: backport 0a8b1cdcc8b2
Original commit message:
implement rapidhash secret generation
Bug: 409717082
Change-Id: I471f33d66de32002f744aeba534c1d34f71e27d2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6733490
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: snek <snek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#101499}
Refs: https://github.com/v8/v8/commit/0a8b1cdcc8b243c62cf045fa8beb50600e11758a
Co-authored-by: Joyee Cheung <joyeec9h3@gmail.com>
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/833
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
PR-URL: https://github.com/nodejs-private/node-private/pull/809
CVE-ID: CVE-2026-21717
This commit is contained in:
1 parent
06fc3436f6
commit
076acd052d
36 files changed
+692
-78
No files matched your search
+1
-1
@@ -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 #####
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Vendored
+3
@@ -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",
|
||||
|
||||
Vendored
+11
-1
@@ -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",
|
||||
|
||||
Vendored
+9
@@ -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",
|
||||
],
|
||||
}
|
||||
Vendored
+2
-1
@@ -291,7 +291,8 @@ std::forward_list<const AstRawString*> 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) {
|
||||
|
||||
Vendored
+7
-6
@@ -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(
|
||||
|
||||
+2
-1
@@ -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<uint32_t>(key), HashSeed(isolate));
|
||||
return ComputeSeededHash(static_cast<uint32_t>(key),
|
||||
HashSeed(isolate).seed());
|
||||
}
|
||||
|
||||
FUNCTION_REFERENCE(compute_integer_hash, ComputeSeededIntegerHash)
|
||||
|
||||
Vendored
+2
-1
@@ -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 {
|
||||
|
||||
Vendored
+12
@@ -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<uint64_t>(v8_flags.hash_seed);
|
||||
}
|
||||
|
||||
Tagged<ByteArray> hash_seed = ReadOnlyRoots(this).hash_seed();
|
||||
|
||||
MemCopy(hash_seed->begin(), reinterpret_cast<uint8_t*>(&new_hash_seed),
|
||||
kInt64Size);
|
||||
|
||||
#if V8_USE_DEFAULT_HASHER_SECRET
|
||||
MemCopy(hash_seed->begin() + kInt64Size,
|
||||
reinterpret_cast<const uint8_t*>(RAPIDHASH_DEFAULT_SECRET),
|
||||
kInt64Size * 3);
|
||||
#else
|
||||
rapidhash_make_secret(new_hash_seed, reinterpret_cast<uint64_t*>(
|
||||
hash_seed->begin() + kInt64Size));
|
||||
#endif // V8_USE_DEFAULT_HASHER_SECRET
|
||||
}
|
||||
|
||||
std::shared_ptr<v8::TaskRunner> Heap::GetForegroundTaskRunner() const {
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
Vendored
-1
@@ -314,7 +314,6 @@ bool JsonParseInternalizer::RecurseAndApply(Handle<JSReceiver> holder,
|
||||
template <typename Char>
|
||||
JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
|
||||
: isolate_(isolate),
|
||||
hash_seed_(HashSeed(isolate)),
|
||||
object_constructor_(isolate_->object_function()),
|
||||
original_source_(source) {
|
||||
size_t start = 0;
|
||||
|
||||
Vendored
-1
@@ -385,7 +385,6 @@ class JsonParser final {
|
||||
int position() const { return static_cast<int>(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_;
|
||||
|
||||
Vendored
+21
-32
@@ -5,47 +5,36 @@
|
||||
#ifndef V8_NUMBERS_HASH_SEED_INL_H_
|
||||
#define V8_NUMBERS_HASH_SEED_INL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
|
||||
Vendored
+42
@@ -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 <stdint.h>
|
||||
|
||||
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_
|
||||
Vendored
+2
-2
@@ -320,14 +320,14 @@ bool NumberDictionaryBaseShape::IsMatch(uint32_t key, Tagged<Object> 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<Object> other) {
|
||||
DCHECK(IsNumber(other));
|
||||
return ComputeSeededHash(static_cast<uint32_t>(Object::Number(other)),
|
||||
HashSeed(roots));
|
||||
HashSeed(roots).seed());
|
||||
}
|
||||
|
||||
template <AllocationType allocation>
|
||||
|
||||
Vendored
+5
-4
@@ -399,7 +399,7 @@ Char FlatStringReader::Get(int index) const {
|
||||
template <typename Char>
|
||||
class SequentialStringKey final : public StringTableKey {
|
||||
public:
|
||||
SequentialStringKey(base::Vector<const Char> chars, uint64_t seed,
|
||||
SequentialStringKey(base::Vector<const Char> chars, const HashSeed seed,
|
||||
bool convert = false)
|
||||
: SequentialStringKey(StringHasher::HashSequentialString<Char>(
|
||||
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;
|
||||
|
||||
Vendored
+3
-2
@@ -567,7 +567,7 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting(
|
||||
return internalized.ptr();
|
||||
}
|
||||
|
||||
uint64_t seed = HashSeed(isolate);
|
||||
const HashSeed seed = HashSeed(isolate);
|
||||
|
||||
CharBuffer<Char> buffer;
|
||||
const Char* chars;
|
||||
@@ -588,7 +588,8 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting(
|
||||
}
|
||||
// TODO(verwaest): Internalize to one-byte when possible.
|
||||
SequentialStringKey<Char> key(raw_hash_field,
|
||||
base::Vector<const Char>(chars, length), seed);
|
||||
base::Vector<const Char>(chars, length),
|
||||
seed.seed());
|
||||
|
||||
// String could be an array index.
|
||||
if (Name::ContainsCachedArrayIndex(raw_hash_field)) {
|
||||
|
||||
Vendored
+2
-2
@@ -1660,7 +1660,7 @@ namespace {
|
||||
|
||||
template <typename Char>
|
||||
uint32_t HashString(Tagged<String> 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> string = this;
|
||||
StringShape shape(string);
|
||||
|
||||
Vendored
+4
-3
@@ -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();
|
||||
}
|
||||
|
||||
+1
-2
@@ -55,8 +55,7 @@ Isolate* HeapEntry::isolate() const { return snapshot_->profiler()->isolate(); }
|
||||
uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) {
|
||||
const char* s = reinterpret_cast<const char*>(string);
|
||||
int len = static_cast<int>(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) {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -65,8 +65,8 @@ struct IntrinsicFunctionIdentifier {
|
||||
}
|
||||
|
||||
uint32_t Hash() {
|
||||
return StringHasher::HashSequentialString<uint8_t>(
|
||||
data_, length_, v8::internal::kZeroHashSeed);
|
||||
return StringHasher::HashSequentialString<uint8_t>(data_, length_,
|
||||
HashSeed::Default());
|
||||
}
|
||||
|
||||
const unsigned char* data_;
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ uint32_t StringHasher::GetTrivialHash(int length) {
|
||||
|
||||
template <typename char_t>
|
||||
uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
|
||||
uint64_t seed) {
|
||||
const HashSeed seed) {
|
||||
static_assert(std::is_integral<char_t>::value);
|
||||
static_assert(sizeof(char_t) <= 2);
|
||||
using uchar = typename std::make_unsigned<char_t>::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<uint32_t>(seed);
|
||||
uint32_t running_hash = static_cast<uint32_t>(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<uint32_t>(seed);
|
||||
uint32_t running_hash = static_cast<uint32_t>(seed.seed());
|
||||
const uchar* end = &chars[length];
|
||||
while (chars != end) {
|
||||
running_hash = AddCharacterCore(running_hash, *chars++);
|
||||
|
||||
Vendored
+4
-3
@@ -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 <typename char_t>
|
||||
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.
|
||||
|
||||
Vendored
-2
@@ -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) {
|
||||
|
||||
Vendored
+1
-1
@@ -762,7 +762,7 @@ int JumpTableOffset(const WasmModule* module, int func_index) {
|
||||
size_t GetWireBytesHash(base::Vector<const uint8_t> wire_bytes) {
|
||||
return StringHasher::HashSequentialString(
|
||||
reinterpret_cast<const char*>(wire_bytes.begin()), wire_bytes.length(),
|
||||
kZeroHashSeed);
|
||||
HashSeed::Default());
|
||||
}
|
||||
|
||||
int NumFeedbackSlots(const WasmModule* module, int func_index) {
|
||||
|
||||
+1
-1
@@ -6644,7 +6644,7 @@ UNINITIALIZED_TEST(ReinitializeStringHashSeed) {
|
||||
{
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
CHECK_EQ(static_cast<uint64_t>(1337 * i),
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
CHECK(!context.IsEmpty());
|
||||
|
||||
+1
-1
@@ -501,7 +501,7 @@ TEST(ComputeIntegerHash) {
|
||||
Handle<Smi> key(Smi::FromInt(k), isolate);
|
||||
Handle<Object> result = ft.Call(key).ToHandleChecked();
|
||||
|
||||
uint32_t hash = ComputeSeededHash(k, HashSeed(isolate));
|
||||
uint32_t hash = ComputeSeededHash(k, HashSeed(isolate).seed());
|
||||
Tagged<Smi> expected = Smi::FromInt(hash);
|
||||
CHECK_EQ(expected, Smi::cast(*result));
|
||||
}
|
||||
|
||||
+2
-2
@@ -4878,7 +4878,7 @@ UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
// Check that rehashing has been performed.
|
||||
CHECK_EQ(static_cast<uint64_t>(1337),
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> 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<uint64_t>(1337),
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
CHECK(!context.IsEmpty());
|
||||
|
||||
+1
-1
@@ -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<String> one_byte_intern = factory1->NewOneByteInternalizedString(
|
||||
base::OneByteVector(raw_one_byte),
|
||||
StringHasher::HashSequentialString<char>(raw_one_byte, 3, seed));
|
||||
|
||||
+34
@@ -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 <godspeed_china@yeah.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
leszeks@chromium.org
|
||||
mlippautz@chromium.org
|
||||
@@ -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.
|
||||
+290
@@ -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 <godspeed_china@yeah.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#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<uint64_t, uint64_t> rapid_mul128(uint64_t A, uint64_t B) {
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
__uint128_t r = A;
|
||||
r *= B;
|
||||
return {static_cast<uint64_t>(r), static_cast<uint64_t>(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 <class Reader = PlainHashReader>
|
||||
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<ptrdiff_t>(i) - 16) *
|
||||
static_cast<int>(x) / static_cast<int>(y));
|
||||
b = Reader::Read64(p + (static_cast<ptrdiff_t>(i) - 8) *
|
||||
static_cast<int>(x) / static_cast<int>(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
|
||||
+198
@@ -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 <https://unlicense.org/>
|
||||
*
|
||||
* author: 王一 Wang Yi <godspeed_china@yeah.net>
|
||||
* 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<uint64_t>(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
|
||||
Reference in new issue
Block a user