Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 69 additions & 83 deletions lib/web_ui/lib/src/ui/hash_codes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,102 +4,90 @@

part of ui;

class _HashEnd {
const _HashEnd();
}

class _HashEnd { const _HashEnd(); }
const _HashEnd _hashEnd = _HashEnd();

/// Combine up to twenty values' hashCodes into one value.
/// Jenkins hash function, optimized for small integers.
//
// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart.
class _Jenkins {
static int combine(int hash, Object o) {
assert(o is! Iterable);
hash = 0x1fffffff & (hash + o.hashCode);
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
hash = hash ^ (hash >> 6);
return hash;
}

static int finish(int hash) {
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
hash = hash ^ (hash >> 11);
hash = 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
return hash;
}
}

/// Combine up to twenty objects' hash codes into one value.
///
/// If you only need to handle one value's hashCode, then just refer to its
/// [hashCode] getter directly.
/// If you only need to handle one object's hash code, then just refer to its
/// [Object.hashCode] getter directly.
///
/// If you need to combine an arbitrary number of values from a List or other
/// Iterable, use [hashList]. The output of hashList can be used as one of the
/// arguments to this function.
/// If you need to combine an arbitrary number of objects from a [List] or other
/// [Iterable], use [hashList]. The output of [hashList] can be used as one of
/// the arguments to this function.
///
/// For example:
///
/// int hashCode => hashValues(foo, bar, hashList(quux), baz);
int hashValues(Object arg01, Object arg02,
[Object arg03 = _hashEnd,
Object arg04 = _hashEnd,
Object arg05 = _hashEnd,
Object arg06 = _hashEnd,
Object arg07 = _hashEnd,
Object arg08 = _hashEnd,
Object arg09 = _hashEnd,
Object arg10 = _hashEnd,
Object arg11 = _hashEnd,
Object arg12 = _hashEnd,
Object arg13 = _hashEnd,
Object arg14 = _hashEnd,
Object arg15 = _hashEnd,
Object arg16 = _hashEnd,
Object arg17 = _hashEnd,
Object arg18 = _hashEnd,
Object arg19 = _hashEnd,
Object arg20 = _hashEnd]) {
int result = 373;
assert(arg01 is! Iterable);
result = 37 * result + arg01.hashCode;
assert(arg02 is! Iterable);
result = 37 * result + arg02.hashCode;
/// ```dart
/// int hashCode => hashValues(foo, bar, hashList(quux), baz);
/// ```
int hashValues(
Object arg01, Object arg02, [ Object arg03 = _hashEnd,
Object arg04 = _hashEnd, Object arg05 = _hashEnd, Object arg06 = _hashEnd,
Object arg07 = _hashEnd, Object arg08 = _hashEnd, Object arg09 = _hashEnd,
Object arg10 = _hashEnd, Object arg11 = _hashEnd, Object arg12 = _hashEnd,
Object arg13 = _hashEnd, Object arg14 = _hashEnd, Object arg15 = _hashEnd,
Object arg16 = _hashEnd, Object arg17 = _hashEnd, Object arg18 = _hashEnd,
Object arg19 = _hashEnd, Object arg20 = _hashEnd ]) {
int result = 0;
result = _Jenkins.combine(result, arg01);
result = _Jenkins.combine(result, arg02);
if (arg03 != _hashEnd) {
assert(arg03 is! Iterable);
result = 37 * result + arg03.hashCode;
result = _Jenkins.combine(result, arg03);
if (arg04 != _hashEnd) {
assert(arg04 is! Iterable);
result = 37 * result + arg04.hashCode;
result = _Jenkins.combine(result, arg04);
if (arg05 != _hashEnd) {
assert(arg05 is! Iterable);
result = 37 * result + arg05.hashCode;
result = _Jenkins.combine(result, arg05);
if (arg06 != _hashEnd) {
assert(arg06 is! Iterable);
result = 37 * result + arg06.hashCode;
result = _Jenkins.combine(result, arg06);
if (arg07 != _hashEnd) {
assert(arg07 is! Iterable);
result = 37 * result + arg07.hashCode;
result = _Jenkins.combine(result, arg07);
if (arg08 != _hashEnd) {
assert(arg08 is! Iterable);
result = 37 * result + arg08.hashCode;
result = _Jenkins.combine(result, arg08);
if (arg09 != _hashEnd) {
assert(arg09 is! Iterable);
result = 37 * result + arg09.hashCode;
result = _Jenkins.combine(result, arg09);
if (arg10 != _hashEnd) {
assert(arg10 is! Iterable);
result = 37 * result + arg10.hashCode;
result = _Jenkins.combine(result, arg10);
if (arg11 != _hashEnd) {
assert(arg11 is! Iterable);
result = 37 * result + arg11.hashCode;
result = _Jenkins.combine(result, arg11);
if (arg12 != _hashEnd) {
assert(arg12 is! Iterable);
result = 37 * result + arg12.hashCode;
result = _Jenkins.combine(result, arg12);
if (arg13 != _hashEnd) {
assert(arg13 is! Iterable);
result = 37 * result + arg13.hashCode;
result = _Jenkins.combine(result, arg13);
if (arg14 != _hashEnd) {
assert(arg14 is! Iterable);
result = 37 * result + arg14.hashCode;
result = _Jenkins.combine(result, arg14);
if (arg15 != _hashEnd) {
assert(arg15 is! Iterable);
result = 37 * result + arg15.hashCode;
result = _Jenkins.combine(result, arg15);
if (arg16 != _hashEnd) {
assert(arg16 is! Iterable);
result = 37 * result + arg16.hashCode;
result = _Jenkins.combine(result, arg16);
if (arg17 != _hashEnd) {
assert(arg17 is! Iterable);
result = 37 * result + arg17.hashCode;
result = _Jenkins.combine(result, arg17);
if (arg18 != _hashEnd) {
assert(arg18 is! Iterable);
result = 37 * result + arg18.hashCode;
result = _Jenkins.combine(result, arg18);
if (arg19 != _hashEnd) {
assert(arg19 is! Iterable);
result = 37 * result + arg19.hashCode;
result = _Jenkins.combine(result, arg19);
if (arg20 != _hashEnd) {
assert(arg20 is! Iterable);
result = 37 * result + arg20.hashCode;
result = _Jenkins.combine(result, arg20);
// I can see my house from here!
}
}
Expand All @@ -119,19 +107,17 @@ int hashValues(Object arg01, Object arg02,
}
}
}
return result;
return _Jenkins.finish(result);
}

/// Combine the hashCodes of an arbitrary number of values from an Iterable into
/// one value. This function will return the same value if given "null" as if
/// given an empty list.
int hashList(Iterable<Object> args) {
int result = 373;
if (args != null) {
for (Object arg in args) {
assert(arg is! Iterable);
result = 37 * result + arg.hashCode;
}
/// Combine the [Object.hashCode] values of an arbitrary number of objects from
/// an [Iterable] into one value. This function will return the same value if
/// given null as if given an empty list.
int hashList(Iterable<Object> arguments) {
int result = 0;
if (arguments != null) {
for (Object argument in arguments)
result = _Jenkins.combine(result, argument);
}
return result;
return _Jenkins.finish(result);
}
122 changes: 122 additions & 0 deletions lib/web_ui/test/hash_codes_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:test/test.dart';
import 'package:ui/ui.dart';

// The biggest integer value that can be represented in JavaScript is 1 << 53.
// However, the 1 << 53 expression cannot be used in JavaScript because that
// would apply the bitwise shift to a "number" (i.e. float64), which is
// meaningless. Instead, a decimal literal is used here.
const int _kBiggestExactJavaScriptInt = 9007199254740992;

void main() {
test('hashValues can hash lots of huge values effectively', () {
expect(
hashValues(
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
),
496984395,
);
// Hash a slightly smaller number to verify that the hash code is different.
expect(
hashValues(
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt - 1,
),
455584273,
);
});

test('hashList can hash lots of huge values effectively', () {
expect(
hashList(<int>[
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
]),
496984395,
);
// Hash a slightly smaller number to verify that the hash code is different.
expect(
hashList(<int>[
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt,
_kBiggestExactJavaScriptInt - 1,
]),
455584273,
);
});
}