Skip to content

Commit

Permalink
Add Model.qualifiedNameOfMember, add fast way to find the path to Jso…
Browse files Browse the repository at this point in the history
…nBufferBuilder (#87)

* Add Model.pathToMember, add fast way to find the path to JsonBufferBuilder.

* Address review comments.

* Address review comments.
  • Loading branch information
davidmorgan authored Oct 10, 2024
1 parent 8c4b89e commit d306c9b
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 62 deletions.
88 changes: 88 additions & 0 deletions pkgs/dart_model/lib/src/dart_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart_model.g.dart';
import 'json_buffer/json_buffer_builder.dart';

export 'dart_model.g.dart';

Expand All @@ -11,3 +12,90 @@ extension QualifiedNameExtension on QualifiedName {

bool equals(QualifiedName other) => other.uri == uri && other.name == name;
}

extension ModelExtension on Model {
/// Returns the path in the model to [member], or `null` if [member] is not
/// in this [Model].
///
/// Comparison is by identity, not by value, so the exact instance must be in
/// this [Model].
///
/// TODO(davidmorgan): this works for any node, but it's not clear yet which
/// types of node we want this functionality exposed for.
/// TODO(davidmorgan): a list of path segments is probably more useful than
/// `String`.
QualifiedName? qualifiedNameOfMember(Member member) =>
_qualifiedNameOf(member.node);

/// Returns the [QualifiedName] in the model to [node], or `null` if [node] is not in this [Model].
QualifiedName? _qualifiedNameOf(Map<String, Object?> node) {
final members = _getParent(node);
if (members == null) return null;
final interface = _getParent(members);
if (interface == null) return null;
final scopes = _getParent(interface);
if (scopes == null) return null;
final library = _getParent(scopes);
if (library == null) return null;
final libraries = _getParent(library);
if (libraries == null) return null;

final uri = _keyOf(library, libraries);
final name = _keyOf(interface, scopes);

return QualifiedName(uri: uri, name: name);
}

/// Returns the key of [value] in [map].
///
/// Throws if [value] is not in [map].
String _keyOf(Object value, Map<String, Object?> map) {
for (final entry in map.entries) {
if (entry.value == value) return entry.key;
}
throw ArgumentError('Value not in map: $value, $map');
}

/// Gets the `Map` that contains [node], or `null` if there isn't one.
Map<String, Object?>? _getParent(Map<String, Object?> node) {
// If both maps are in the same `JsonBufferBuilder` then the parent is
// immediately available.
if (this case MapInBuffer thisMapInBuffer) {
if (node case MapInBuffer thatMapInBuffer) {
if (thisMapInBuffer.buffer == thatMapInBuffer.buffer) {
return thatMapInBuffer.parent;
}
}
}
// Otherwise, build a `Map` of references to parents and use that.
return _lazyParentsMap[node];
}

/// Gets a `Map` from values to parent `Map`s.
Map<Map<String, Object?>, Map<String, Object?>> get _lazyParentsMap {
var result = _parentsMaps[this];
if (result == null) {
result =
_parentsMaps[this] = <Map<String, Object?>, Map<String, Object?>>{};
_buildParentsMap(node, result);
}
return result;
}

/// Builds a `Map` from values to parent `Map`s.
static void _buildParentsMap(Map<String, Object?> parent,
Map<Map<String, Object?>, Map<String, Object?>> result) {
for (final child in parent.values.whereType<Map<String, Object?>>()) {
if (result.containsKey(child)) {
throw StateError(
'Same node found twice.\n\nChild:\n$child\n\nParent:\n$parent');
} else {
result[child] = parent;
_buildParentsMap(child, result);
}
}
}
}

/// Expando storing a `Map` from values to parent `Map`s.
final _parentsMaps = Expando<Map<Map<String, Object?>, Map<String, Object?>>>();
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 @@ -44,20 +44,25 @@ extension ClosedMaps on JsonBufferBuilder {
}

/// Returns the [_ClosedMap] at [pointer].
Map<String, Object?> _readClosedMap(_Pointer pointer) {
return _ClosedMap(this, pointer);
Map<String, Object?> _readClosedMap(
_Pointer pointer, Map<String, Object?>? parent) {
return _ClosedMap(this, pointer, parent);
}
}

class _ClosedMap
with MapMixin<String, Object?>, _EntryMapMixin<String, Object?> {
final JsonBufferBuilder _buffer;
with MapMixin<String, Object?>, _EntryMapMixin<String, Object?>
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final Map<String, Object?>? parent;
@override
final int length;

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

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

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

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

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

@override
Expand All @@ -101,16 +106,26 @@ class _ClosedMap
throw UnsupportedError(
'This JsonBufferBuilder map is read-only, see "createGrowableMap".');
}

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

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

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

_Pointer _pointer;

_ClosedMapIterator(this._buffer, _Pointer pointer, int length)
_ClosedMapIterator(this._buffer, this._parent, _Pointer pointer, int length)
: _last = pointer + _lengthSize + length * ClosedMaps._entrySize,
// Subtract because `moveNext` is called before reading.
_pointer = pointer + _lengthSize - ClosedMaps._entrySize;
Expand All @@ -119,7 +134,8 @@ abstract class _ClosedMapIterator<T> implements Iterator<T> {
T get current;

String get _currentKey => _buffer._readString(_buffer._readPointer(_pointer));
Object? get _currentValue => _buffer._readAny(_pointer + _pointerSize);
Object? get _currentValue =>
_buffer._readAny(_pointer + _pointerSize, parent: _parent);

@override
bool moveNext() {
Expand All @@ -131,22 +147,25 @@ abstract class _ClosedMapIterator<T> implements Iterator<T> {
}

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

@override
String get current => _currentKey;
}

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

@override
Object? get current => _currentValue;
}

class _ClosedMapEntryIterator
extends _ClosedMapIterator<MapEntry<String, Object?>> {
_ClosedMapEntryIterator(super._buffer, super.pointer, super.length);
_ClosedMapEntryIterator(
super._buffer, super._porent, super.pointer, super.length);

@override
MapEntry<String, Object?> get current => MapEntry(_currentKey, _currentValue);
Expand Down
65 changes: 41 additions & 24 deletions pkgs/dart_model/lib/src/json_buffer/growable_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension GrowableMaps on JsonBufferBuilder {
// Initially a "growable map" is just a null pointer and zero size, so
// there is nothing to write.
_explanations?.pop();
return _readGrowableMap<V>(pointer);
return _readGrowableMap<V>(pointer, null);
}

/// Returns the [_Pointer] to [map].
Expand All @@ -57,26 +57,32 @@ extension GrowableMaps on JsonBufferBuilder {

/// Throws if [map is backed by a different buffer to `this`.
void _checkGrowableMapOwnership(_GrowableMap map) {
if (map._buffer != this) {
if (map.buffer != this) {
throw UnsupportedError('Maps created with `createGrowableMap` can only '
'be added to the JsonBufferBuilder instance that created them.');
}
}

/// Returns the [_GrowableMap] at [pointer].
Map<String, V> _readGrowableMap<V>(_Pointer pointer) {
return _GrowableMap<V>(this, pointer);
Map<String, V> _readGrowableMap<V>(
_Pointer pointer, Map<String, Object?>? parent) {
return _GrowableMap<V>(this, pointer, parent);
}
}

class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
final JsonBufferBuilder _buffer;
class _GrowableMap<V>
with MapMixin<String, V>, _EntryMapMixin<String, V>
implements MapInBuffer {
@override
final JsonBufferBuilder buffer;
final _Pointer _pointer;
@override
final Map<String, Object?>? parent;
int _length;
_Pointer? _lastPointer;

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

@override
int get length => _length;
Expand All @@ -94,17 +100,17 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {

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

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

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

/// Add [value] to the map with key [key].
Expand All @@ -115,32 +121,32 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
/// iterable.
@override
void operator []=(String key, V value) {
_buffer._explanations?.push('GrowableMap[]= $key $value');
buffer._explanations?.push('GrowableMap[]= $key $value');

// If `_lastPointer` is not set yet, walk the map to find the end of it.
if (_lastPointer == null) {
final iterator = _GrowableMapEntryIterator<V>(_buffer, _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 pointer = buffer._reserve(GrowableMaps._entrySize);
final entryPointer = pointer + _pointerSize;
_buffer._writePointer(entryPointer, _buffer._pointerToString(key));
_buffer._writeAny(entryPointer + _pointerSize, value);
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!, pointer);
// Update `_lastPointer` to the new node.
_lastPointer = pointer;

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

@override
Expand All @@ -152,22 +158,33 @@ class _GrowableMap<V> with MapMixin<String, V>, _EntryMapMixin<String, V> {
void clear() {
throw UnsupportedError('JsonBufferBuilder growable maps are append only.');
}

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

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

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

_GrowableMapIterator(this._buffer, this._pointer);
_GrowableMapIterator(this._buffer, this._parent, this._pointer);

@override
T get current;

String get _currentKey =>
_buffer._readString(_buffer._readPointer(_pointer + _pointerSize));
Object? get _currentValue =>
_buffer._readAny(_pointer + _pointerSize + GrowableMaps._keySize);
_buffer._readAny(_pointer + _pointerSize + GrowableMaps._keySize,
parent: _parent);

@override
bool moveNext() {
Expand All @@ -177,22 +194,22 @@ abstract class _GrowableMapIterator<T> implements Iterator<T> {
}

class _GrowableMapKeyIterator extends _GrowableMapIterator<String> {
_GrowableMapKeyIterator(super._buffer, super._pointer);
_GrowableMapKeyIterator(super._buffer, super._parent, super._pointer);

@override
String get current => _currentKey;
}

class _GrowableMapValueIterator<V> extends _GrowableMapIterator<V> {
_GrowableMapValueIterator(super._buffer, super._pointer);
_GrowableMapValueIterator(super._buffer, super._parent, super._pointer);

@override
V get current => _currentValue as V;
}

class _GrowableMapEntryIterator<V>
extends _GrowableMapIterator<MapEntry<String, V>> {
_GrowableMapEntryIterator(super._buffer, super._pointer);
_GrowableMapEntryIterator(super._buffer, super._parent, super._pointer);

@override
MapEntry<String, V> get current => MapEntry(_currentKey, _currentValue as V);
Expand Down
Loading

0 comments on commit d306c9b

Please sign in to comment.