Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Object.hash to hash raw bytes #134

Merged
merged 4 commits into from
Nov 7, 2024
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
4 changes: 2 additions & 2 deletions pkgs/_macro_host/lib/src/macro_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MacroResultsCache {
.skip(1)
.fold(queryResults.first.response,
(model, next) => model.mergeWith(next.response))
.identityHash,
.fingerprint,
response: response
);
}
Expand All @@ -61,7 +61,7 @@ class MacroResultsCache {
.skip(1)
.fold(queryResults.first.model,
(model, next) => model.mergeWith(next.model))
.identityHash;
.fingerprint;
if (newResultsHash != cached.resultsHash) {
_cache.remove(cacheKey);
return null;
Expand Down
9 changes: 6 additions & 3 deletions pkgs/dart_model/lib/src/dart_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// 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.

import 'package:collection/collection.dart';

import 'dart_model.g.dart';
import 'json_buffer/json_buffer_builder.dart';
import 'lazy_merged_map.dart';
Expand Down Expand Up @@ -33,7 +31,12 @@ extension ModelExtension on Model {
/// An identity hash for `this`, used for comparing query results.
///
/// TODO: A faster/better implementation?
int get identityHash => const DeepCollectionEquality().hash(node);
int get fingerprint {
// TODO: Implementation for non-buffer maps?
var node = this.node as MapInBuffer;
return node.buffer.fingerprint(node.pointer,
type: Type.typedMapPointer, alreadyDereferenced: true);
}

/// Looks up [name] in `this`.
///
Expand Down
20 changes: 18 additions & 2 deletions pkgs/dart_model/lib/src/json_buffer/closed_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,19 @@ class _ClosedList with ListMixin<Object?> {
set length(int length) {
throw UnsupportedError('This JsonBufferBuilder list is read-only.');
}

int get fingerprint {
var iterator = _ClosedListHashIterator(_buffer, _pointer, length);
var hash = 0;
while (iterator.moveNext()) {
hash = Object.hash(hash, iterator.current);
}
return hash;
}
}

/// `Iterator` that reads a "closed list" in a [JsonBufferBuilder].
class _ClosedListIterator implements Iterator<Object?> {
class _ClosedListIterator<T extends Object?> implements Iterator<T> {
final JsonBufferBuilder _buffer;
final _Pointer _last;
_Pointer _pointer;
Expand All @@ -91,7 +100,7 @@ class _ClosedListIterator implements Iterator<Object?> {
_pointer = pointer + _lengthSize - ClosedLists._valueSize;

@override
Object? get current => _buffer._readAny(_pointer);
T get current => _buffer._readAny(_pointer) as T;

@override
bool moveNext() {
Expand All @@ -101,3 +110,10 @@ class _ClosedListIterator implements Iterator<Object?> {
return _pointer != _last;
}
}

class _ClosedListHashIterator extends _ClosedListIterator<int> {
_ClosedListHashIterator(super.buffer, super.pointer, super.length);

@override
int get current => _buffer.fingerprint(_pointer);
}
47 changes: 33 additions & 14 deletions pkgs/dart_model/lib/src/json_buffer/closed_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,15 @@ class _ClosedMap
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final _Pointer pointer;
@override
final Map<String, Object?>? parent;
@override
final int length;

_ClosedMap(this.buffer, this._pointer, this.parent)
: length = buffer._readLength(_pointer);
_ClosedMap(this.buffer, this.pointer, this.parent)
: length = buffer._readLength(pointer);

@override
Object? operator [](Object? key) {
Expand All @@ -75,18 +76,18 @@ class _ClosedMap

@override
late final Iterable<String> keys = _IteratorFunctionIterable(
() => _ClosedMapKeyIterator(buffer, this, _pointer, length),
() => _ClosedMapKeyIterator(buffer, this, pointer, length),
length: length);

@override
late final Iterable<Object?> values = _IteratorFunctionIterable(
() => _ClosedMapValueIterator(buffer, this, _pointer, length),
() => _ClosedMapValueIterator(buffer, this, pointer, length),
length: length);

@override
late final Iterable<MapEntry<String, Object?>> entries =
_IteratorFunctionIterable(
() => _ClosedMapEntryIterator(buffer, this, _pointer, length),
() => _ClosedMapEntryIterator(buffer, this, pointer, length),
length: length);

@override
Expand All @@ -109,18 +110,25 @@ class _ClosedMap

@override
bool operator ==(Object other) =>
other is _ClosedMap &&
other.buffer == buffer &&
other._pointer == _pointer;
other is _ClosedMap && other.buffer == buffer && other.pointer == pointer;

@override
int get hashCode => Object.hash(buffer, _pointer);
int get hashCode => Object.hash(buffer, pointer);

int get fingerprint {
var iterator = _ClosedMapHashIterator(buffer, null, pointer, length);
var hash = 0;
while (iterator.moveNext()) {
hash = Object.hash(hash, iterator.current);
}
return hash;
}
}

/// `Iterator` that reads a "closed map" in a [JsonBufferBuilder].
abstract class _ClosedMapIterator<T> implements Iterator<T> {
final JsonBufferBuilder _buffer;
final _ClosedMap _parent;
final _ClosedMap? _parent;
final _Pointer _last;

_Pointer _pointer;
Expand Down Expand Up @@ -148,15 +156,15 @@ abstract class _ClosedMapIterator<T> implements Iterator<T> {

class _ClosedMapKeyIterator extends _ClosedMapIterator<String> {
_ClosedMapKeyIterator(
super._buffer, super._porent, super.pointer, super.length);
super._buffer, super._parent, super.pointer, super.length);

@override
String get current => _currentKey;
}

class _ClosedMapValueIterator extends _ClosedMapIterator<Object?> {
_ClosedMapValueIterator(
super._buffer, super._porent, super.pointer, super.length);
super._buffer, super._parent, super.pointer, super.length);

@override
Object? get current => _currentValue;
Expand All @@ -165,8 +173,19 @@ class _ClosedMapValueIterator extends _ClosedMapIterator<Object?> {
class _ClosedMapEntryIterator
extends _ClosedMapIterator<MapEntry<String, Object?>> {
_ClosedMapEntryIterator(
super._buffer, super._porent, super.pointer, super.length);
super._buffer, super._parent, super.pointer, super.length);

@override
MapEntry<String, Object?> get current => MapEntry(_currentKey, _currentValue);
}

class _ClosedMapHashIterator extends _ClosedMapIterator<int> {
_ClosedMapHashIterator(
super._buffer, super._parent, super.pointer, super.length);

@override
int get current => Object.hash(
_buffer._fingerprint(_pointer, Type.stringPointer),
_buffer.fingerprint(_pointer + ClosedMaps._keySize),
);
}
54 changes: 37 additions & 17 deletions pkgs/dart_model/lib/src/json_buffer/growable_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extension GrowableMaps on JsonBufferBuilder {
/// [createGrowableMap]. Otherwise, [UnsupportedError] is thrown.
_Pointer _pointerToGrowableMap(_GrowableMap<Object?> map) {
_checkGrowableMapOwnership(map);
return map._pointer;
return map.pointer;
}

/// Throws if [map is backed by a different buffer to `this`.
Expand All @@ -75,14 +75,15 @@ class _GrowableMap<V>
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final _Pointer pointer;
@override
final Map<String, Object?>? parent;
int _length;
_Pointer? _lastPointer;

_GrowableMap(this.buffer, this._pointer, this.parent)
: _length = buffer._readLength(_pointer + _pointerSize);
_GrowableMap(this.buffer, this.pointer, this.parent)
: _length = buffer._readLength(pointer + _pointerSize);

@override
int get length => _length;
Expand All @@ -100,17 +101,17 @@ class _GrowableMap<V>

@override
late final Iterable<String> keys = _IteratorFunctionIterable(
() => _GrowableMapKeyIterator(buffer, this, _pointer),
() => _GrowableMapKeyIterator(buffer, this, pointer),
length: length);

@override
late final Iterable<V> values = _IteratorFunctionIterable(
() => _GrowableMapValueIterator<V>(buffer, this, _pointer),
() => _GrowableMapValueIterator<V>(buffer, this, pointer),
length: length);

@override
late final Iterable<MapEntry<String, V>> entries = _IteratorFunctionIterable(
() => _GrowableMapEntryIterator(buffer, this, _pointer),
() => _GrowableMapEntryIterator(buffer, this, pointer),
length: length);

/// Add [value] to the map with key [key].
Expand All @@ -125,27 +126,27 @@ class _GrowableMap<V>

// If `_lastPointer` is not set yet, walk the map to find the end of it.
if (_lastPointer == null) {
final iterator = _GrowableMapEntryIterator<V>(buffer, this, _pointer);
_lastPointer = _pointer;
final iterator = _GrowableMapEntryIterator<V>(buffer, this, pointer);
_lastPointer = pointer;
while (iterator.moveNext()) {
_lastPointer = iterator._pointer;
}
}

// Reserve and write the new node.
final pointer = buffer._reserve(GrowableMaps._entrySize);
final entryPointer = pointer + _pointerSize;
final newPointer = buffer._reserve(GrowableMaps._entrySize);
final entryPointer = newPointer + _pointerSize;
buffer._writePointer(entryPointer, buffer._pointerToString(key));
buffer._writeAny(entryPointer + _pointerSize, value);

// Point to the new node in the previous node.
buffer._writePointer(_lastPointer!, pointer);
buffer._writePointer(_lastPointer!, newPointer);
// Update `_lastPointer` to the new node.
_lastPointer = pointer;
_lastPointer = newPointer;

// Update length.
++_length;
buffer._writeLength(_pointer + _pointerSize, length, allowOverwrite: true);
buffer._writeLength(pointer + _pointerSize, length, allowOverwrite: true);
buffer._explanations?.pop();
}

Expand All @@ -163,16 +164,25 @@ class _GrowableMap<V>
bool operator ==(Object other) =>
other is _GrowableMap &&
other.buffer == buffer &&
other._pointer == _pointer;
other.pointer == pointer;

@override
int get hashCode => Object.hash(buffer, _pointer);
int get hashCode => Object.hash(buffer, pointer);

int get fingerprint {
var iterator = _GrowableMapHashIterator(buffer, null, pointer);
var hash = 0;
while (iterator.moveNext()) {
hash = Object.hash(hash, iterator.current);
}
return hash;
}
}

/// `Iterator` that reads a "growable map" in a [JsonBufferBuilder].
abstract class _GrowableMapIterator<T> implements Iterator<T> {
final JsonBufferBuilder _buffer;
final _GrowableMap _parent;
final _GrowableMap? _parent;
_Pointer _pointer;

_GrowableMapIterator(this._buffer, this._parent, this._pointer);
Expand Down Expand Up @@ -214,3 +224,13 @@ class _GrowableMapEntryIterator<V>
@override
MapEntry<String, V> get current => MapEntry(_currentKey, _currentValue as V);
}

class _GrowableMapHashIterator extends _GrowableMapIterator<int> {
_GrowableMapHashIterator(super._buffer, super._parent, super._pointer);

@override
int get current => Object.hash(
_buffer._fingerprint(_pointer + _pointerSize, Type.stringPointer),
_buffer.fingerprint(_pointer + _pointerSize + GrowableMaps._keySize),
);
}
3 changes: 3 additions & 0 deletions pkgs/dart_model/lib/src/json_buffer/iterables.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ abstract interface class MapInBuffer {
/// The `Map` that contains this value, or `null` if this value has not been
/// added to a `Map` or is itself the root `Map`.
Map<String, Object?>? get parent;

/// A pointer to the start of this object in [buffer].
int get pointer;
}
61 changes: 61 additions & 0 deletions pkgs/dart_model/lib/src/json_buffer/json_buffer_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,67 @@ class JsonBufferBuilder {
map = createGrowableMap<Object?>();
}

/// Computes the identity hash of the object at [pointer] from its raw bytes.
///
/// Any nested pointers use the hash of the values they point to.
///
/// If [type] is provided, [pointer] should point directly at the object.
/// Otherwise a [Type] will be read first, followed by the value.
///
/// If [alreadyDereferenced] is `true`, then for types which are pointers,
/// [pointer] already points at the top of the object, and should not be
/// followed before reading the object.
int fingerprint(int pointer, {Type? type, bool alreadyDereferenced = false}) {
if (type == null) {
type = _readType(pointer);
pointer += _typeSize;
}
return _fingerprint(pointer, type,
alreadyDereferenced: alreadyDereferenced);
}

/// Computes the identity hash of the object at [pointer] with a known [type]
/// from its raw bytes.
///
/// If [alreadyDereferenced] is `true`, then for types which are pointers,
/// [pointer] already points at the top of the object, and should not be
/// followed before reading the object.
int _fingerprint(_Pointer pointer, Type type,
{bool alreadyDereferenced = false}) {
// Dereference [pointer] if it is a pointer type, and hasn't already been
// dereferenced.
if (type.isPointer && !alreadyDereferenced) {
pointer = _readPointer(pointer);
}

switch (type) {
case Type.nil:
return null.hashCode;
case Type.type:
return _buffer[pointer];
case Type.pointer:
return fingerprint(pointer);
case Type.uint32:
return _readUint32(pointer);
case Type.boolean:
return _buffer[pointer];
case Type.anyPointer:
return fingerprint(pointer);
case Type.stringPointer:
final length = _readLength(pointer);
pointer += _lengthSize;
return Object.hashAll(_buffer.sublist(pointer, pointer + length));
case Type.closedListPointer:
return _ClosedList(this, pointer).fingerprint;
case Type.closedMapPointer:
return _ClosedMap(this, pointer, null).fingerprint;
case Type.growableMapPointer:
return _GrowableMap<Object?>(this, pointer, null).fingerprint;
case Type.typedMapPointer:
return _TypedMap(this, pointer, null).fingerprint;
}
}

/// The JSON data.
///
/// The buffer is _not_ copied, unpredictable behavior will result if it is
Expand Down
Loading