Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 48d64c1

Browse files
authored
update hash code logic on the web (#16624)
1 parent 77c5812 commit 48d64c1

File tree

2 files changed

+191
-83
lines changed

2 files changed

+191
-83
lines changed

lib/web_ui/lib/src/ui/hash_codes.dart

Lines changed: 69 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,102 +4,90 @@
44

55
part of ui;
66

7-
class _HashEnd {
8-
const _HashEnd();
9-
}
10-
7+
class _HashEnd { const _HashEnd(); }
118
const _HashEnd _hashEnd = _HashEnd();
129

13-
/// Combine up to twenty values' hashCodes into one value.
10+
/// Jenkins hash function, optimized for small integers.
11+
//
12+
// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart.
13+
class _Jenkins {
14+
static int combine(int hash, Object o) {
15+
assert(o is! Iterable);
16+
hash = 0x1fffffff & (hash + o.hashCode);
17+
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
18+
hash = hash ^ (hash >> 6);
19+
return hash;
20+
}
21+
22+
static int finish(int hash) {
23+
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
24+
hash = hash ^ (hash >> 11);
25+
hash = 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
26+
return hash;
27+
}
28+
}
29+
30+
/// Combine up to twenty objects' hash codes into one value.
1431
///
15-
/// If you only need to handle one value's hashCode, then just refer to its
16-
/// [hashCode] getter directly.
32+
/// If you only need to handle one object's hash code, then just refer to its
33+
/// [Object.hashCode] getter directly.
1734
///
18-
/// If you need to combine an arbitrary number of values from a List or other
19-
/// Iterable, use [hashList]. The output of hashList can be used as one of the
20-
/// arguments to this function.
35+
/// If you need to combine an arbitrary number of objects from a [List] or other
36+
/// [Iterable], use [hashList]. The output of [hashList] can be used as one of
37+
/// the arguments to this function.
2138
///
2239
/// For example:
2340
///
24-
/// int hashCode => hashValues(foo, bar, hashList(quux), baz);
25-
int hashValues(Object arg01, Object arg02,
26-
[Object arg03 = _hashEnd,
27-
Object arg04 = _hashEnd,
28-
Object arg05 = _hashEnd,
29-
Object arg06 = _hashEnd,
30-
Object arg07 = _hashEnd,
31-
Object arg08 = _hashEnd,
32-
Object arg09 = _hashEnd,
33-
Object arg10 = _hashEnd,
34-
Object arg11 = _hashEnd,
35-
Object arg12 = _hashEnd,
36-
Object arg13 = _hashEnd,
37-
Object arg14 = _hashEnd,
38-
Object arg15 = _hashEnd,
39-
Object arg16 = _hashEnd,
40-
Object arg17 = _hashEnd,
41-
Object arg18 = _hashEnd,
42-
Object arg19 = _hashEnd,
43-
Object arg20 = _hashEnd]) {
44-
int result = 373;
45-
assert(arg01 is! Iterable);
46-
result = 37 * result + arg01.hashCode;
47-
assert(arg02 is! Iterable);
48-
result = 37 * result + arg02.hashCode;
41+
/// ```dart
42+
/// int hashCode => hashValues(foo, bar, hashList(quux), baz);
43+
/// ```
44+
int hashValues(
45+
Object arg01, Object arg02, [ Object arg03 = _hashEnd,
46+
Object arg04 = _hashEnd, Object arg05 = _hashEnd, Object arg06 = _hashEnd,
47+
Object arg07 = _hashEnd, Object arg08 = _hashEnd, Object arg09 = _hashEnd,
48+
Object arg10 = _hashEnd, Object arg11 = _hashEnd, Object arg12 = _hashEnd,
49+
Object arg13 = _hashEnd, Object arg14 = _hashEnd, Object arg15 = _hashEnd,
50+
Object arg16 = _hashEnd, Object arg17 = _hashEnd, Object arg18 = _hashEnd,
51+
Object arg19 = _hashEnd, Object arg20 = _hashEnd ]) {
52+
int result = 0;
53+
result = _Jenkins.combine(result, arg01);
54+
result = _Jenkins.combine(result, arg02);
4955
if (arg03 != _hashEnd) {
50-
assert(arg03 is! Iterable);
51-
result = 37 * result + arg03.hashCode;
56+
result = _Jenkins.combine(result, arg03);
5257
if (arg04 != _hashEnd) {
53-
assert(arg04 is! Iterable);
54-
result = 37 * result + arg04.hashCode;
58+
result = _Jenkins.combine(result, arg04);
5559
if (arg05 != _hashEnd) {
56-
assert(arg05 is! Iterable);
57-
result = 37 * result + arg05.hashCode;
60+
result = _Jenkins.combine(result, arg05);
5861
if (arg06 != _hashEnd) {
59-
assert(arg06 is! Iterable);
60-
result = 37 * result + arg06.hashCode;
62+
result = _Jenkins.combine(result, arg06);
6163
if (arg07 != _hashEnd) {
62-
assert(arg07 is! Iterable);
63-
result = 37 * result + arg07.hashCode;
64+
result = _Jenkins.combine(result, arg07);
6465
if (arg08 != _hashEnd) {
65-
assert(arg08 is! Iterable);
66-
result = 37 * result + arg08.hashCode;
66+
result = _Jenkins.combine(result, arg08);
6767
if (arg09 != _hashEnd) {
68-
assert(arg09 is! Iterable);
69-
result = 37 * result + arg09.hashCode;
68+
result = _Jenkins.combine(result, arg09);
7069
if (arg10 != _hashEnd) {
71-
assert(arg10 is! Iterable);
72-
result = 37 * result + arg10.hashCode;
70+
result = _Jenkins.combine(result, arg10);
7371
if (arg11 != _hashEnd) {
74-
assert(arg11 is! Iterable);
75-
result = 37 * result + arg11.hashCode;
72+
result = _Jenkins.combine(result, arg11);
7673
if (arg12 != _hashEnd) {
77-
assert(arg12 is! Iterable);
78-
result = 37 * result + arg12.hashCode;
74+
result = _Jenkins.combine(result, arg12);
7975
if (arg13 != _hashEnd) {
80-
assert(arg13 is! Iterable);
81-
result = 37 * result + arg13.hashCode;
76+
result = _Jenkins.combine(result, arg13);
8277
if (arg14 != _hashEnd) {
83-
assert(arg14 is! Iterable);
84-
result = 37 * result + arg14.hashCode;
78+
result = _Jenkins.combine(result, arg14);
8579
if (arg15 != _hashEnd) {
86-
assert(arg15 is! Iterable);
87-
result = 37 * result + arg15.hashCode;
80+
result = _Jenkins.combine(result, arg15);
8881
if (arg16 != _hashEnd) {
89-
assert(arg16 is! Iterable);
90-
result = 37 * result + arg16.hashCode;
82+
result = _Jenkins.combine(result, arg16);
9183
if (arg17 != _hashEnd) {
92-
assert(arg17 is! Iterable);
93-
result = 37 * result + arg17.hashCode;
84+
result = _Jenkins.combine(result, arg17);
9485
if (arg18 != _hashEnd) {
95-
assert(arg18 is! Iterable);
96-
result = 37 * result + arg18.hashCode;
86+
result = _Jenkins.combine(result, arg18);
9787
if (arg19 != _hashEnd) {
98-
assert(arg19 is! Iterable);
99-
result = 37 * result + arg19.hashCode;
88+
result = _Jenkins.combine(result, arg19);
10089
if (arg20 != _hashEnd) {
101-
assert(arg20 is! Iterable);
102-
result = 37 * result + arg20.hashCode;
90+
result = _Jenkins.combine(result, arg20);
10391
// I can see my house from here!
10492
}
10593
}
@@ -119,19 +107,17 @@ int hashValues(Object arg01, Object arg02,
119107
}
120108
}
121109
}
122-
return result;
110+
return _Jenkins.finish(result);
123111
}
124112

125-
/// Combine the hashCodes of an arbitrary number of values from an Iterable into
126-
/// one value. This function will return the same value if given "null" as if
127-
/// given an empty list.
128-
int hashList(Iterable<Object> args) {
129-
int result = 373;
130-
if (args != null) {
131-
for (Object arg in args) {
132-
assert(arg is! Iterable);
133-
result = 37 * result + arg.hashCode;
134-
}
113+
/// Combine the [Object.hashCode] values of an arbitrary number of objects from
114+
/// an [Iterable] into one value. This function will return the same value if
115+
/// given null as if given an empty list.
116+
int hashList(Iterable<Object> arguments) {
117+
int result = 0;
118+
if (arguments != null) {
119+
for (Object argument in arguments)
120+
result = _Jenkins.combine(result, argument);
135121
}
136-
return result;
122+
return _Jenkins.finish(result);
137123
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:test/test.dart';
6+
import 'package:ui/ui.dart';
7+
8+
// The biggest integer value that can be represented in JavaScript is 1 << 53.
9+
// However, the 1 << 53 expression cannot be used in JavaScript because that
10+
// would apply the bitwise shift to a "number" (i.e. float64), which is
11+
// meaningless. Instead, a decimal literal is used here.
12+
const int _kBiggestExactJavaScriptInt = 9007199254740992;
13+
14+
void main() {
15+
test('hashValues can hash lots of huge values effectively', () {
16+
expect(
17+
hashValues(
18+
_kBiggestExactJavaScriptInt,
19+
_kBiggestExactJavaScriptInt,
20+
_kBiggestExactJavaScriptInt,
21+
_kBiggestExactJavaScriptInt,
22+
_kBiggestExactJavaScriptInt,
23+
_kBiggestExactJavaScriptInt,
24+
_kBiggestExactJavaScriptInt,
25+
_kBiggestExactJavaScriptInt,
26+
_kBiggestExactJavaScriptInt,
27+
_kBiggestExactJavaScriptInt,
28+
_kBiggestExactJavaScriptInt,
29+
_kBiggestExactJavaScriptInt,
30+
_kBiggestExactJavaScriptInt,
31+
_kBiggestExactJavaScriptInt,
32+
_kBiggestExactJavaScriptInt,
33+
_kBiggestExactJavaScriptInt,
34+
_kBiggestExactJavaScriptInt,
35+
_kBiggestExactJavaScriptInt,
36+
_kBiggestExactJavaScriptInt,
37+
_kBiggestExactJavaScriptInt,
38+
),
39+
496984395,
40+
);
41+
// Hash a slightly smaller number to verify that the hash code is different.
42+
expect(
43+
hashValues(
44+
_kBiggestExactJavaScriptInt,
45+
_kBiggestExactJavaScriptInt,
46+
_kBiggestExactJavaScriptInt,
47+
_kBiggestExactJavaScriptInt,
48+
_kBiggestExactJavaScriptInt,
49+
_kBiggestExactJavaScriptInt,
50+
_kBiggestExactJavaScriptInt,
51+
_kBiggestExactJavaScriptInt,
52+
_kBiggestExactJavaScriptInt,
53+
_kBiggestExactJavaScriptInt,
54+
_kBiggestExactJavaScriptInt,
55+
_kBiggestExactJavaScriptInt,
56+
_kBiggestExactJavaScriptInt,
57+
_kBiggestExactJavaScriptInt,
58+
_kBiggestExactJavaScriptInt,
59+
_kBiggestExactJavaScriptInt,
60+
_kBiggestExactJavaScriptInt,
61+
_kBiggestExactJavaScriptInt,
62+
_kBiggestExactJavaScriptInt,
63+
_kBiggestExactJavaScriptInt - 1,
64+
),
65+
455584273,
66+
);
67+
});
68+
69+
test('hashList can hash lots of huge values effectively', () {
70+
expect(
71+
hashList(<int>[
72+
_kBiggestExactJavaScriptInt,
73+
_kBiggestExactJavaScriptInt,
74+
_kBiggestExactJavaScriptInt,
75+
_kBiggestExactJavaScriptInt,
76+
_kBiggestExactJavaScriptInt,
77+
_kBiggestExactJavaScriptInt,
78+
_kBiggestExactJavaScriptInt,
79+
_kBiggestExactJavaScriptInt,
80+
_kBiggestExactJavaScriptInt,
81+
_kBiggestExactJavaScriptInt,
82+
_kBiggestExactJavaScriptInt,
83+
_kBiggestExactJavaScriptInt,
84+
_kBiggestExactJavaScriptInt,
85+
_kBiggestExactJavaScriptInt,
86+
_kBiggestExactJavaScriptInt,
87+
_kBiggestExactJavaScriptInt,
88+
_kBiggestExactJavaScriptInt,
89+
_kBiggestExactJavaScriptInt,
90+
_kBiggestExactJavaScriptInt,
91+
_kBiggestExactJavaScriptInt,
92+
]),
93+
496984395,
94+
);
95+
// Hash a slightly smaller number to verify that the hash code is different.
96+
expect(
97+
hashList(<int>[
98+
_kBiggestExactJavaScriptInt,
99+
_kBiggestExactJavaScriptInt,
100+
_kBiggestExactJavaScriptInt,
101+
_kBiggestExactJavaScriptInt,
102+
_kBiggestExactJavaScriptInt,
103+
_kBiggestExactJavaScriptInt,
104+
_kBiggestExactJavaScriptInt,
105+
_kBiggestExactJavaScriptInt,
106+
_kBiggestExactJavaScriptInt,
107+
_kBiggestExactJavaScriptInt,
108+
_kBiggestExactJavaScriptInt,
109+
_kBiggestExactJavaScriptInt,
110+
_kBiggestExactJavaScriptInt,
111+
_kBiggestExactJavaScriptInt,
112+
_kBiggestExactJavaScriptInt,
113+
_kBiggestExactJavaScriptInt,
114+
_kBiggestExactJavaScriptInt,
115+
_kBiggestExactJavaScriptInt,
116+
_kBiggestExactJavaScriptInt,
117+
_kBiggestExactJavaScriptInt - 1,
118+
]),
119+
455584273,
120+
);
121+
});
122+
}

0 commit comments

Comments
 (0)