| // Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | // ignore_for_file: hash_and_equals | 
 |  | 
 | // Benchmark for `Object.hash` and `Object.hashAll`. | 
 |  | 
 | import 'dart:math'; | 
 |  | 
 | import 'package:benchmark_harness/benchmark_harness.dart'; | 
 |  | 
 | int get nextHash => Random().nextInt(0x20000000); | 
 |  | 
 | // An object with a fast hashCode. | 
 | class Leaf { | 
 |   @override | 
 |   final int hashCode = nextHash; | 
 | } | 
 |  | 
 | abstract class Node5 { | 
 |   final item1 = Leaf(); | 
 |   final item2 = Leaf(); | 
 |   final item3 = Leaf(); | 
 |   final item4 = Random().nextBool(); | 
 |   final item5 = nextHash; | 
 | } | 
 |  | 
 | class Node5Hash extends Node5 { | 
 |   // This is the main subject of the benchmark - a typical use of `Object.hash`. | 
 |   @override | 
 |   int get hashCode => Object.hash(item1, item2, item3, item4, item5); | 
 | } | 
 |  | 
 | class Node5Manual extends Node5 { | 
 |   // This is a similar quality hashCode but with statically resolvable | 
 |   // `hashCode` calls and a 0 seed (instead of loading a unique random seed from | 
 |   // global late-final variable). | 
 |   @override | 
 |   int get hashCode => _SystemHash.hash5(item1.hashCode, item2.hashCode, | 
 |       item3.hashCode, item4.hashCode, item5.hashCode, 0); | 
 | } | 
 |  | 
 | class Node5List extends Node5 { | 
 |   // This is a pattern that is sometimes used, especially for large numbers of | 
 |   // items. | 
 |   @override | 
 |   int get hashCode => Object.hashAll([item1, item2, item3, item4, item5]); | 
 | } | 
 |  | 
 | /// Returns a list with most values created by [makeValue], and a few objects of | 
 | /// different types so that the `hashCode` calls are polymorphic, like the ones | 
 | /// in the hashed collections. | 
 | List generateData(Object Function(int) makeValue) { | 
 |   final List data = List.generate(1000, makeValue); | 
 |   final exceptions = [ | 
 |     Leaf(), | 
 |     Node5Hash(), | 
 |     Node5Manual(), | 
 |     Node5List(), | 
 |     '', | 
 |     true, | 
 |     false, | 
 |     123, | 
 |     Object() | 
 |   ]; | 
 |   data.setRange(1, 1 + exceptions.length, exceptions); | 
 |   return data; | 
 | } | 
 |  | 
 | class BenchmarkNode5Hash extends BenchmarkBase { | 
 |   final List data = generateData((_) => Node5Hash()); | 
 |  | 
 |   BenchmarkNode5Hash() : super('ObjectHash.hash.5'); | 
 |  | 
 |   @override | 
 |   void run() { | 
 |     for (final e in data) { | 
 |       sink = e.hashCode; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | class BenchmarkNode5Manual extends BenchmarkBase { | 
 |   final List data = generateData((_) => Node5Manual()); | 
 |  | 
 |   BenchmarkNode5Manual() : super('ObjectHash.manual.5'); | 
 |  | 
 |   @override | 
 |   void run() { | 
 |     for (final e in data) { | 
 |       sink = e.hashCode; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | class BenchmarkNode5List extends BenchmarkBase { | 
 |   final List data = generateData((_) => Node5List()); | 
 |  | 
 |   BenchmarkNode5List() : super('ObjectHash.list.5'); | 
 |  | 
 |   @override | 
 |   void run() { | 
 |     for (final e in data) { | 
 |       sink = e.hashCode; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | class BenchmarkNode5HashHashAll extends BenchmarkBase { | 
 |   final List data = generateData((_) => Node5Hash()); | 
 |  | 
 |   BenchmarkNode5HashHashAll() : super('ObjectHash.hash.5.hashAll'); | 
 |  | 
 |   @override | 
 |   void run() { | 
 |     sink = Object.hashAll(data); | 
 |   } | 
 | } | 
 |  | 
 | class BenchmarkNode5ManualHashAll extends BenchmarkBase { | 
 |   final List data = generateData((_) => Node5Manual()); | 
 |  | 
 |   BenchmarkNode5ManualHashAll() : super('ObjectHash.manual.5.hashAll'); | 
 |  | 
 |   @override | 
 |   void run() { | 
 |     sink = Object.hashAll(data); | 
 |   } | 
 | } | 
 |  | 
 | Object? sink; | 
 |  | 
 | void main() { | 
 |   generalUses(); | 
 |  | 
 |   final benchmarks = [ | 
 |     BenchmarkNode5Hash.new, | 
 |     BenchmarkNode5Manual.new, | 
 |     BenchmarkNode5List.new, | 
 |     BenchmarkNode5HashHashAll.new, | 
 |     BenchmarkNode5ManualHashAll.new, | 
 |   ]; | 
 |  | 
 |   // Warmup all benchmarks so that JIT compilers see full polymorphism before | 
 |   // measuring. | 
 |   for (var benchmark in benchmarks) { | 
 |     benchmark().warmup(); | 
 |   } | 
 |  | 
 |   if (sink == null) throw StateError('sink unassigned'); | 
 |  | 
 |   generalUses(); | 
 |  | 
 |   for (var benchmark in benchmarks) { | 
 |     benchmark().report(); | 
 |   } | 
 | } | 
 |  | 
 | /// Does a variety of calls to `Object.hash` to ensure the compiler does not | 
 | /// over-specialize the code on a few benchmark inputs. | 
 | void generalUses() { | 
 |   void check(int a, int b) { | 
 |     if (a != b) throw StateError('inconsistent'); | 
 |   } | 
 |  | 
 |   // Exercise arity dispatch. | 
 |   check(Object.hash(1, 2), Object.hash(1, 2)); | 
 |   check(Object.hash(1, 2, 3), Object.hash(1, 2, 3)); | 
 |   check(Object.hash(1, 2, 3, 4), Object.hash(1, 2, 3, 4)); | 
 |   check(Object.hash(1, 2, 3, 4, 5), Object.hash(1, 2, 3, 4, 5)); | 
 |   check(Object.hash(1, 2, 3, 4, 5, 6), Object.hash(1, 2, 3, 4, 5, 6)); | 
 |   check(Object.hash(1, 2, 3, 4, 5, 6, 7), Object.hash(1, 2, 3, 4, 5, 6, 7)); | 
 |  | 
 |   final xs = Iterable.generate(20).toList(); | 
 |   check(Function.apply(Object.hash, xs), Function.apply(Object.hash, xs)); | 
 |  | 
 |   // Exercise internal hashCode dispatch. | 
 |   final a1 = 123; | 
 |   final a2 = 'hello'; | 
 |   final a3 = true; | 
 |   final a4 = Object(); | 
 |   final a5 = StringBuffer(); | 
 |   const a6 = Point<int>(1, 2); | 
 |   const a7 = Rectangle<int>(100, 200, 1, 1); | 
 |  | 
 |   check(Object.hash(a1, a2, a3, a4, a5), Object.hash(a1, a2, a3, a4, a5)); | 
 |   check(Object.hash(a2, a3, a4, a5, a6), Object.hash(a2, a3, a4, a5, a6)); | 
 |   check(Object.hash(a3, a4, a5, a6, a7), Object.hash(a3, a4, a5, a6, a7)); | 
 |   check(Object.hash(a4, a5, a6, a7, a1), Object.hash(a4, a5, a6, a7, a1)); | 
 |   check(Object.hash(a5, a6, a7, a1, a2), Object.hash(a5, a6, a7, a1, a2)); | 
 |   check(Object.hash(a6, a7, a1, a2, a3), Object.hash(a6, a7, a1, a2, a3)); | 
 |   check(Object.hash(a7, a1, a2, a3, a4), Object.hash(a7, a1, a2, a3, a4)); | 
 |  | 
 |   check(_SystemHash.hash2(1, 2, 0), _SystemHash.hash2(1, 2, 0)); | 
 |   check(_SystemHash.hash3(1, 2, 3, 0), _SystemHash.hash3(1, 2, 3, 0)); | 
 |   check(_SystemHash.hash4(1, 2, 3, 4, 0), _SystemHash.hash4(1, 2, 3, 4, 0)); | 
 |   check( | 
 |       _SystemHash.hash5(1, 2, 3, 4, 5, 0), _SystemHash.hash5(1, 2, 3, 4, 5, 0)); | 
 |  | 
 |   // Pollute hashAll argument type. | 
 |   check(Object.hashAll({}), Object.hashAll([])); | 
 |   check(Object.hashAll({}.values), Object.hashAll({}.keys)); | 
 |   check(Object.hashAll(''.codeUnits), Object.hashAll(const Iterable.empty())); | 
 |   check(Object.hashAll(const [0]), Object.hashAll(Iterable.generate(1))); | 
 | } | 
 |  | 
 | // Partial copy of dart:internal `SystemHash` that is used by `Object.hash` so | 
 | // that we can create comparable manual hashCode methods. | 
 | class _SystemHash { | 
 |   static int combine(int hash, int value) { | 
 |     hash = 0x1fffffff & (hash + value); | 
 |     hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | 
 |     return hash ^ (hash >> 6); | 
 |   } | 
 |  | 
 |   static int finish(int hash) { | 
 |     hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 
 |     hash = hash ^ (hash >> 11); | 
 |     return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 
 |   } | 
 |  | 
 |   static int hash2(int v1, int v2, int seed) { | 
 |     int hash = seed; | 
 |     hash = combine(hash, v1); | 
 |     hash = combine(hash, v2); | 
 |     return finish(hash); | 
 |   } | 
 |  | 
 |   static int hash3(int v1, int v2, int v3, int seed) { | 
 |     int hash = seed; | 
 |     hash = combine(hash, v1); | 
 |     hash = combine(hash, v2); | 
 |     hash = combine(hash, v3); | 
 |     return finish(hash); | 
 |   } | 
 |  | 
 |   static int hash4(int v1, int v2, int v3, int v4, int seed) { | 
 |     int hash = seed; | 
 |     hash = combine(hash, v1); | 
 |     hash = combine(hash, v2); | 
 |     hash = combine(hash, v3); | 
 |     hash = combine(hash, v4); | 
 |     return finish(hash); | 
 |   } | 
 |  | 
 |   static int hash5(int v1, int v2, int v3, int v4, int v5, int seed) { | 
 |     int hash = seed; | 
 |     hash = combine(hash, v1); | 
 |     hash = combine(hash, v2); | 
 |     hash = combine(hash, v3); | 
 |     hash = combine(hash, v4); | 
 |     hash = combine(hash, v5); | 
 |     return finish(hash); | 
 |   } | 
 | } |