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

Implement deepCopy as copying, instead of merge #742

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Changes from 8 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
43 changes: 39 additions & 4 deletions protobuf/lib/src/protobuf/extension_field_set.dart
Original file line number Diff line number Diff line change
@@ -6,11 +6,20 @@ part of protobuf;

class _ExtensionFieldSet {
final _FieldSet _parent;
final Map<int, Extension> _info = <int, Extension>{};
final Map<int, dynamic> _values = <int, dynamic>{};
bool _isReadOnly = false;

_ExtensionFieldSet(this._parent);
final Map<int, Extension> _info;

final Map<int, dynamic> _values;

bool _isReadOnly;

_ExtensionFieldSet(this._parent)
: _info = <int, Extension>{},
_values = <int, dynamic>{},
_isReadOnly = false;

_ExtensionFieldSet._(
this._parent, this._info, this._values, this._isReadOnly);

Extension? _getInfoOrNull(int? tagNumber) => _info[tagNumber];

@@ -203,4 +212,30 @@ class _ExtensionFieldSet {
'use `ExtensionRegistry.reparseMessage`.');
}
}

_ExtensionFieldSet deepCopy(_FieldSet parent, {bool freeze = false}) {
final values = <int, dynamic>{};

for (final entry in _values.entries) {
final key = entry.key;
final value = entry.value;

if (value is PbMap) {
values[key] = value.deepCopy(freeze: freeze) as dynamic;
} else if (value is PbList) {
values[key] = value.deepCopy(freeze: freeze) as dynamic;
} else if (value is GeneratedMessage) {
values[key] = value.deepCopy(freeze: freeze) as dynamic;
} else {
values[key] = value;
}
}

return _ExtensionFieldSet._(
parent,
_info,
values,
freeze,
);
}
}
34 changes: 34 additions & 0 deletions protobuf/lib/src/protobuf/field_set.dart
Original file line number Diff line number Diff line change
@@ -78,6 +78,9 @@ class _FieldSet {
: _values = _makeValueList(meta.byIndex.length),
_oneofCases = meta.oneofs.isEmpty ? null : <int, int>{};

_FieldSet._(this._message, this._eventPlugin, this._values, this._extensions,
this._unknownFields, this._frozenState, this._oneofCases);

static List _makeValueList(int length) {
if (length == 0) return _zeroList;
return List.filled(length, null, growable: false);
@@ -897,4 +900,35 @@ class _FieldSet {

_oneofCases?.addAll(original._oneofCases!);
}

_FieldSet deepCopy(GeneratedMessage message, {bool freeze = false}) {
final values = _makeValueList(_values.length);

for (var valueIdx = 0; valueIdx < _values.length; valueIdx += 1) {
final value = _values[valueIdx];
if (value is PbMap) {
values[valueIdx] = value.deepCopy(freeze: freeze);
} else if (value is PbList) {
values[valueIdx] = value.deepCopy(freeze: freeze);
} else if (value is GeneratedMessage) {
values[valueIdx] = value.deepCopy(freeze: freeze);
} else {
values[valueIdx] = value;
}
}

final copy = _FieldSet._(
message,
null, // TODO: event plugin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave a LDAP or an issue number in todos.

Suggested change
null, // TODO: event plugin
null, // TODO(omersa): event plugin

values,
null,
_unknownFields?.deepCopy(freeze: freeze),
freeze, // TODO: Can we use `_frozenState` here?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

_oneofCases == null ? null : Map.from(_oneofCases!),
);

copy._extensions = _extensions?.deepCopy(copy, freeze: freeze);

return copy;
}
}
12 changes: 6 additions & 6 deletions protobuf/lib/src/protobuf/generated_message.dart
Original file line number Diff line number Diff line change
@@ -43,11 +43,7 @@ abstract class GeneratedMessage {
EventPlugin? get eventPlugin => null;

/// Creates a deep copy of the fields in this message.
/// (The generated code uses [mergeFromMessage].)
@Deprecated('Using this can add significant size overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
GeneratedMessage clone();
GeneratedMessage clone() => deepCopy();

/// Creates an empty instance of the same message type as this.
///
@@ -585,5 +581,9 @@ extension GeneratedMessageGenericExtensions<T extends GeneratedMessage> on T {
}

/// Returns a writable deep copy of this message.
T deepCopy() => info_.createEmptyInstance!() as T..mergeFromMessage(this);
T deepCopy({bool freeze = false}) {
final T message = info_.createEmptyInstance!() as dynamic;
message.__fieldSet = __fieldSet?.deepCopy(message, freeze: freeze);
return message;
}
}
32 changes: 29 additions & 3 deletions protobuf/lib/src/protobuf/pb_list.dart
Original file line number Diff line number Diff line change
@@ -14,13 +14,14 @@ class PbList<E> extends ListBase<E> {
final List<E> _wrappedList;
final CheckFunc<E> _check;

bool _isReadOnly = false;
bool _isReadOnly;

bool get isFrozen => _isReadOnly;

PbList({CheckFunc<E> check = _checkNotNull})
: _wrappedList = <E>[],
_check = check;
_check = check,
_isReadOnly = false;

PbList.unmodifiable()
: _wrappedList = const [],
@@ -29,7 +30,10 @@ class PbList<E> extends ListBase<E> {

PbList.from(List from)
: _wrappedList = List<E>.from(from),
_check = _checkNotNull;
_check = _checkNotNull,
_isReadOnly = false;

PbList._(this._wrappedList, this._check, this._isReadOnly);

@override
void add(E element) {
@@ -200,4 +204,26 @@ class PbList<E> extends ListBase<E> {
static Never _readOnlyError(String methodName) {
throw UnsupportedError("'$methodName' on a read-only list");
}

PbList<E> deepCopy({bool freeze = false}) {
// TODO: We know the capacity, is it possible to allocate a list with the right size?
final wrappedList = <E>[];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could possibly use List.generate to fill in the right values from the start, thereby avoiding the re-allocation.


for (final value in _wrappedList) {
// TODO: I'm not sure why I need to cast to dynamic below. If `value : E`
// and `value : PbMap`, then `value.deepCopy : E`
if (value is PbMap) {
wrappedList.add(value.deepCopy(freeze: freeze) as dynamic);
} else if (value is PbList) {
wrappedList.add(value.deepCopy(freeze: freeze) as dynamic);
} else if (value is GeneratedMessage) {
final GeneratedMessage message = value;
wrappedList.add(message.deepCopy(freeze: freeze) as dynamic);
} else {
wrappedList.add(value);
}
}

return PbList._(wrappedList, _check, freeze);
}
}
25 changes: 25 additions & 0 deletions protobuf/lib/src/protobuf/pb_map.dart
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@ class PbMap<K, V> extends MapBase<K, V> {
_wrappedMap = Map.unmodifiable(other._wrappedMap),
_isReadonly = true;

PbMap._(this.keyFieldType, this.valueFieldType, this._wrappedMap,
this._isReadonly);

@override
V? operator [](Object? key) => _wrappedMap[key];

@@ -119,4 +122,26 @@ class PbMap<K, V> extends MapBase<K, V> {
}
return this;
}

PbMap<K, V> deepCopy({bool freeze = false}) {
final wrappedMap = <K, V>{};

for (final entry in _wrappedMap.entries) {
final key = entry.key;
final value = entry.value;

if (value is PbMap) {
wrappedMap[key] = value.deepCopy(freeze: freeze) as dynamic;
} else if (value is PbList) {
wrappedMap[key] = value.deepCopy(freeze: freeze) as dynamic;
} else if (value is GeneratedMessage) {
final GeneratedMessage message = value;
wrappedMap[key] = message.deepCopy(freeze: freeze) as dynamic;
} else {
wrappedMap[key] = value;
}
}

return PbMap._(keyFieldType, valueFieldType, wrappedMap, freeze);
}
}
52 changes: 44 additions & 8 deletions protobuf/lib/src/protobuf/unknown_field_set.dart
Original file line number Diff line number Diff line change
@@ -8,11 +8,15 @@ part of protobuf;
class UnknownFieldSet {
static final UnknownFieldSet emptyUnknownFieldSet = UnknownFieldSet()
.._markReadOnly();
final Map<int, UnknownFieldSetField> _fields = <int, UnknownFieldSetField>{};

UnknownFieldSet();
final Map<int, UnknownFieldSetField> _fields;

UnknownFieldSet._clone(UnknownFieldSet unknownFieldSet) {
UnknownFieldSet() : _fields = <int, UnknownFieldSetField>{};

UnknownFieldSet._(this._fields, this._isReadOnly);

UnknownFieldSet._clone(UnknownFieldSet unknownFieldSet)
: _fields = <int, UnknownFieldSetField>{} {
mergeFromUnknownFieldSet(unknownFieldSet);
}

@@ -195,15 +199,37 @@ class UnknownFieldSet {
_throwFrozenMessageModificationError('UnknownFieldSet', methodName);
}
}

UnknownFieldSet deepCopy({bool freeze = false}) {
final fields = <int, UnknownFieldSetField>{};

for (final entry in _fields.entries) {
final key = entry.key;
final value = entry.value;
fields[key] = value.deepCopy(freeze: freeze);
}

return UnknownFieldSet._(fields, freeze);
}
}

/// An unknown field in a [UnknownFieldSet].
class UnknownFieldSetField {
List<List<int>> _lengthDelimited = <List<int>>[];
List<Int64> _varints = <Int64>[];
List<int> _fixed32s = <int>[];
List<Int64> _fixed64s = <Int64>[];
List<UnknownFieldSet> _groups = <UnknownFieldSet>[];
List<List<int>> _lengthDelimited;
List<Int64> _varints;
List<int> _fixed32s;
List<Int64> _fixed64s;
List<UnknownFieldSet> _groups;

UnknownFieldSetField()
: _lengthDelimited = <List<int>>[],
_varints = <Int64>[],
_fixed32s = <int>[],
_fixed64s = <Int64>[],
_groups = <UnknownFieldSet>[];

UnknownFieldSetField._(this._lengthDelimited, this._varints, this._fixed32s,
this._fixed64s, this._groups);

List<List<int>> get lengthDelimited => _lengthDelimited;
List<Int64> get varints => _varints;
@@ -309,4 +335,14 @@ class UnknownFieldSetField {
void addVarint(Int64 value) {
varints.add(value);
}

UnknownFieldSetField deepCopy({bool freeze = false}) {
return UnknownFieldSetField._(
_lengthDelimited.map((List<int> e) => List<int>.from(e)).toList(),
List.from(_varints),
List.from(_fixed32s),
List.from(_fixed64s),
List.from(_groups.map((e) => e.deepCopy(freeze: freeze))),
);
}
}
8 changes: 0 additions & 8 deletions protoc_plugin/lib/src/generated/dart_options.pb.dart
Original file line number Diff line number Diff line change
@@ -44,10 +44,6 @@ class DartMixin extends $pb.GeneratedMessage {
factory DartMixin.fromJson($core.String i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(i, r);
@$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DartMixin clone() => DartMixin()..mergeFromMessage(this);
@$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
@@ -128,10 +124,6 @@ class Imports extends $pb.GeneratedMessage {
factory Imports.fromJson($core.String i,
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(i, r);
@$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Imports clone() => Imports()..mergeFromMessage(this);
@$core.Deprecated('Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Loading