| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /// Jenkins hash function, optimized for small integers. |
| // |
| // Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart. |
| class _Jenkins { |
| static int combine(final int h, Object o) { |
| assert(o is! Iterable); |
| int hash = h; |
| hash = 0x1fffffff & (hash + o.hashCode); |
| hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
| return hash ^ (hash >> 6); |
| } |
| |
| static int finish(final int h) { |
| int hash = h; |
| hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| hash = hash ^ (hash >> 11); |
| return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| } |
| } |
| |
| /// 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 deepHash(Iterable<Object> arguments) { |
| int result = 0; |
| if (arguments != null) { |
| for (Object argument in arguments) { |
| if (argument is Iterable) { |
| argument = deepHash(argument); |
| } |
| result = _Jenkins.combine(result, argument); |
| } |
| } |
| return _Jenkins.finish(result); |
| } |
| |
| /// Deep equality helper function. |
| bool deepEquals(dynamic a, dynamic b) { |
| // iterable |
| if (a is Iterable) { |
| // ignore: avoid_bool_literals_in_conditional_expressions |
| return (b is Iterable) ? |
| _deepEqualsOfIterable(a, b) : |
| false; |
| } |
| |
| // map |
| if (a is Map) { |
| // ignore: avoid_bool_literals_in_conditional_expressions |
| return (b is Map) ? |
| _deepEqualsOfMap(a, b) : |
| false; |
| } |
| |
| // default |
| return a == b; |
| } |
| |
| /// Deep equality helper function for Iterables. |
| bool _deepEqualsOfIterable(Iterable<Object> a, Iterable<Object> b) { |
| if (a.length != b.length) { |
| return false; |
| } |
| final ai = a.iterator; |
| final bi = b.iterator; |
| while (ai.moveNext() && bi.moveNext()) { |
| if (!deepEquals(ai.current, bi.current)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /// Deep equality helper function for Maps. |
| bool _deepEqualsOfMap(Map a, Map b) { |
| if (a.length != b.length) { |
| return false; |
| } |
| for (var key in a.keys) { |
| if (!b.containsKey(key)) { |
| return false; |
| } |
| final aCurrent = a[key]; |
| final bCurrent = b[key]; |
| if (!deepEquals(aCurrent, bCurrent)) { |
| return false; |
| } |
| } |
| return true; |
| } |