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 1 commit
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
Next Next commit
Implement efficient deep copying
osa1 committed Aug 25, 2022
commit f8d29532f05caad9dbcdd95819b14533b326b629
4 changes: 4 additions & 0 deletions protobuf/lib/src/protobuf/extension_field_set.dart
Original file line number Diff line number Diff line change
@@ -199,4 +199,8 @@ class _ExtensionFieldSet {
'use `ExtensionRegistry.reparseMessage`.');
}
}

_ExtensionFieldSet deepCopy() {
throw 'TODO';
}
}
30 changes: 30 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);
@@ -892,4 +895,31 @@ 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;
}
}

return _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,
_extensions?.deepCopy(),
_unknownFields?.deepCopy(),
_frozenState, // TODO: Not sure about this part
_oneofCases == null ? null : Map.from(_oneofCases!),
);
}
}
9 changes: 8 additions & 1 deletion protobuf/lib/src/protobuf/generated_message.dart
Original file line number Diff line number Diff line change
@@ -595,5 +595,12 @@ 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;
final fieldSet = __fieldSet;
if (fieldSet != null) {
message.__fieldSet = fieldSet.deepCopy(message, freeze: freeze);
}
return message;
}
}
23 changes: 23 additions & 0 deletions protobuf/lib/src/protobuf/pb_list.dart
Original file line number Diff line number Diff line change
@@ -31,6 +31,8 @@ class PbList<E> extends ListBase<E> {
: _wrappedList = List<E>.from(from),
_check = _checkNotNull;

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

@override
void add(E element) {
_checkModifiable('add');
@@ -200,4 +202,25 @@ 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) {
wrappedList.add(value.deepCopy(freeze: freeze) as dynamic);
} else {
wrappedList.add(value);
}
}

return PbList._(wrappedList, _check, freeze);
}
}
24 changes: 24 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 = other._isReadonly;

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

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

@@ -119,4 +122,25 @@ 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) {
wrappedMap[key] = value.deepCopy(freeze: freeze) as dynamic;
} else {
wrappedMap[key] = value;
}
}

return PbMap._(keyFieldType, valueFieldType, wrappedMap, freeze);
}
}
4 changes: 4 additions & 0 deletions protobuf/lib/src/protobuf/unknown_field_set.dart
Original file line number Diff line number Diff line change
@@ -195,6 +195,10 @@ class UnknownFieldSet {
_throwFrozenMessageModificationError('UnknownFieldSet', methodName);
}
}

UnknownFieldSet deepCopy() {
throw 'TODO';
}
}

/// An unknown field in a [UnknownFieldSet].