Skip to content

Commit 31c81d2

Browse files
authored
CRDT.ORMultiMap and LWWMap serialization (#602)
Motivation: Make all CRDTs serializable. See #507. Modifications: - Add `ProtobufRepresentable` conformance to `CRDT.ORMultiMap`. - Add `ProtobufRepresentable` conformance to `CRDT.LWWMap`. Result: Resolves #507
1 parent b423553 commit 31c81d2

File tree

5 files changed

+391
-0
lines changed

5 files changed

+391
-0
lines changed

Protos/CRDT/CRDT.proto

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,22 @@ message CRDTORMap {
107107
repeated CRDTORMapKeyValue updatedValues = 4;
108108
}
109109

110+
// ***** CRDT.ORMultiMap *****
111+
112+
message CRDTORMultiMap {
113+
VersionReplicaID replicaID = 1;
114+
// Includes delta
115+
CRDTORMap state = 2;
116+
}
117+
118+
// ***** CRDT.LWWMap *****
119+
120+
message CRDTLWWMap {
121+
VersionReplicaID replicaID = 1;
122+
// Includes delta
123+
CRDTORMap state = 2;
124+
}
125+
110126
// ***** CRDT.LWWRegister *****
111127

112128
message CRDTLWWRegister {

Sources/DistributedActors/CRDT/Protobuf/CRDT+Serialization.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,50 @@ extension CRDT.ORMapDelta: ProtobufRepresentable {
354354
}
355355
}
356356

357+
// ==== ----------------------------------------------------------------------------------------------------------------
358+
// MARK: CRDT.ORMultiMap
359+
360+
extension CRDT.ORMultiMap: ProtobufRepresentable {
361+
public typealias ProtobufRepresentation = ProtoCRDTORMultiMap
362+
363+
public func toProto(context: Serialization.Context) throws -> ProtobufRepresentation {
364+
var proto = ProtobufRepresentation()
365+
proto.replicaID = try self.replicaID.toProto(context: context)
366+
proto.state = try self.state.toProto(context: context)
367+
return proto
368+
}
369+
370+
public init(fromProto proto: ProtobufRepresentation, context: Serialization.Context) throws {
371+
guard proto.hasReplicaID else {
372+
throw SerializationError.missingField("replicaID", type: String(describing: CRDT.ORMultiMap<Key, Value>.self))
373+
}
374+
self.replicaID = try ReplicaID(fromProto: proto.replicaID, context: context)
375+
self.state = try CRDT.ORMap<Key, CRDT.ORSet<Value>>(fromProto: proto.state, context: context)
376+
}
377+
}
378+
379+
// ==== ----------------------------------------------------------------------------------------------------------------
380+
// MARK: CRDT.LWWMap
381+
382+
extension CRDT.LWWMap: ProtobufRepresentable {
383+
public typealias ProtobufRepresentation = ProtoCRDTLWWMap
384+
385+
public func toProto(context: Serialization.Context) throws -> ProtobufRepresentation {
386+
var proto = ProtobufRepresentation()
387+
proto.replicaID = try self.replicaID.toProto(context: context)
388+
proto.state = try self.state.toProto(context: context)
389+
return proto
390+
}
391+
392+
public init(fromProto proto: ProtobufRepresentation, context: Serialization.Context) throws {
393+
guard proto.hasReplicaID else {
394+
throw SerializationError.missingField("replicaID", type: String(describing: CRDT.LWWMap<Key, Value>.self))
395+
}
396+
self.replicaID = try ReplicaID(fromProto: proto.replicaID, context: context)
397+
self.state = try CRDT.ORMap<Key, CRDT.LWWRegister<Value>>(fromProto: proto.state, context: context)
398+
}
399+
}
400+
357401
// ==== ----------------------------------------------------------------------------------------------------------------
358402
// MARK: CRDT.LWWRegister
359403

Sources/DistributedActors/CRDT/Protobuf/CRDT.pb.swift

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,68 @@ public struct ProtoCRDTORMap {
395395
fileprivate var _storage = _StorageClass.defaultInstance
396396
}
397397

398+
public struct ProtoCRDTORMultiMap {
399+
// SwiftProtobuf.Message conformance is added in an extension below. See the
400+
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
401+
// methods supported on all messages.
402+
403+
public var replicaID: ProtoVersionReplicaID {
404+
get {return _storage._replicaID ?? ProtoVersionReplicaID()}
405+
set {_uniqueStorage()._replicaID = newValue}
406+
}
407+
/// Returns true if `replicaID` has been explicitly set.
408+
public var hasReplicaID: Bool {return _storage._replicaID != nil}
409+
/// Clears the value of `replicaID`. Subsequent reads from it will return its default value.
410+
public mutating func clearReplicaID() {_uniqueStorage()._replicaID = nil}
411+
412+
/// Includes delta
413+
public var state: ProtoCRDTORMap {
414+
get {return _storage._state ?? ProtoCRDTORMap()}
415+
set {_uniqueStorage()._state = newValue}
416+
}
417+
/// Returns true if `state` has been explicitly set.
418+
public var hasState: Bool {return _storage._state != nil}
419+
/// Clears the value of `state`. Subsequent reads from it will return its default value.
420+
public mutating func clearState() {_uniqueStorage()._state = nil}
421+
422+
public var unknownFields = SwiftProtobuf.UnknownStorage()
423+
424+
public init() {}
425+
426+
fileprivate var _storage = _StorageClass.defaultInstance
427+
}
428+
429+
public struct ProtoCRDTLWWMap {
430+
// SwiftProtobuf.Message conformance is added in an extension below. See the
431+
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
432+
// methods supported on all messages.
433+
434+
public var replicaID: ProtoVersionReplicaID {
435+
get {return _storage._replicaID ?? ProtoVersionReplicaID()}
436+
set {_uniqueStorage()._replicaID = newValue}
437+
}
438+
/// Returns true if `replicaID` has been explicitly set.
439+
public var hasReplicaID: Bool {return _storage._replicaID != nil}
440+
/// Clears the value of `replicaID`. Subsequent reads from it will return its default value.
441+
public mutating func clearReplicaID() {_uniqueStorage()._replicaID = nil}
442+
443+
/// Includes delta
444+
public var state: ProtoCRDTORMap {
445+
get {return _storage._state ?? ProtoCRDTORMap()}
446+
set {_uniqueStorage()._state = newValue}
447+
}
448+
/// Returns true if `state` has been explicitly set.
449+
public var hasState: Bool {return _storage._state != nil}
450+
/// Clears the value of `state`. Subsequent reads from it will return its default value.
451+
public mutating func clearState() {_uniqueStorage()._state = nil}
452+
453+
public var unknownFields = SwiftProtobuf.UnknownStorage()
454+
455+
public init() {}
456+
457+
fileprivate var _storage = _StorageClass.defaultInstance
458+
}
459+
398460
public struct ProtoCRDTLWWRegister {
399461
// SwiftProtobuf.Message conformance is added in an extension below. See the
400462
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -1363,6 +1425,144 @@ extension ProtoCRDTORMap.Delta: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
13631425
}
13641426
}
13651427

1428+
extension ProtoCRDTORMultiMap: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
1429+
public static let protoMessageName: String = "CRDTORMultiMap"
1430+
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1431+
1: .same(proto: "replicaID"),
1432+
2: .same(proto: "state"),
1433+
]
1434+
1435+
fileprivate class _StorageClass {
1436+
var _replicaID: ProtoVersionReplicaID? = nil
1437+
var _state: ProtoCRDTORMap? = nil
1438+
1439+
static let defaultInstance = _StorageClass()
1440+
1441+
private init() {}
1442+
1443+
init(copying source: _StorageClass) {
1444+
_replicaID = source._replicaID
1445+
_state = source._state
1446+
}
1447+
}
1448+
1449+
fileprivate mutating func _uniqueStorage() -> _StorageClass {
1450+
if !isKnownUniquelyReferenced(&_storage) {
1451+
_storage = _StorageClass(copying: _storage)
1452+
}
1453+
return _storage
1454+
}
1455+
1456+
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
1457+
_ = _uniqueStorage()
1458+
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
1459+
while let fieldNumber = try decoder.nextFieldNumber() {
1460+
switch fieldNumber {
1461+
case 1: try decoder.decodeSingularMessageField(value: &_storage._replicaID)
1462+
case 2: try decoder.decodeSingularMessageField(value: &_storage._state)
1463+
default: break
1464+
}
1465+
}
1466+
}
1467+
}
1468+
1469+
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
1470+
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
1471+
if let v = _storage._replicaID {
1472+
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
1473+
}
1474+
if let v = _storage._state {
1475+
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
1476+
}
1477+
}
1478+
try unknownFields.traverse(visitor: &visitor)
1479+
}
1480+
1481+
public static func ==(lhs: ProtoCRDTORMultiMap, rhs: ProtoCRDTORMultiMap) -> Bool {
1482+
if lhs._storage !== rhs._storage {
1483+
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
1484+
let _storage = _args.0
1485+
let rhs_storage = _args.1
1486+
if _storage._replicaID != rhs_storage._replicaID {return false}
1487+
if _storage._state != rhs_storage._state {return false}
1488+
return true
1489+
}
1490+
if !storagesAreEqual {return false}
1491+
}
1492+
if lhs.unknownFields != rhs.unknownFields {return false}
1493+
return true
1494+
}
1495+
}
1496+
1497+
extension ProtoCRDTLWWMap: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
1498+
public static let protoMessageName: String = "CRDTLWWMap"
1499+
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1500+
1: .same(proto: "replicaID"),
1501+
2: .same(proto: "state"),
1502+
]
1503+
1504+
fileprivate class _StorageClass {
1505+
var _replicaID: ProtoVersionReplicaID? = nil
1506+
var _state: ProtoCRDTORMap? = nil
1507+
1508+
static let defaultInstance = _StorageClass()
1509+
1510+
private init() {}
1511+
1512+
init(copying source: _StorageClass) {
1513+
_replicaID = source._replicaID
1514+
_state = source._state
1515+
}
1516+
}
1517+
1518+
fileprivate mutating func _uniqueStorage() -> _StorageClass {
1519+
if !isKnownUniquelyReferenced(&_storage) {
1520+
_storage = _StorageClass(copying: _storage)
1521+
}
1522+
return _storage
1523+
}
1524+
1525+
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
1526+
_ = _uniqueStorage()
1527+
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
1528+
while let fieldNumber = try decoder.nextFieldNumber() {
1529+
switch fieldNumber {
1530+
case 1: try decoder.decodeSingularMessageField(value: &_storage._replicaID)
1531+
case 2: try decoder.decodeSingularMessageField(value: &_storage._state)
1532+
default: break
1533+
}
1534+
}
1535+
}
1536+
}
1537+
1538+
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
1539+
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
1540+
if let v = _storage._replicaID {
1541+
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
1542+
}
1543+
if let v = _storage._state {
1544+
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
1545+
}
1546+
}
1547+
try unknownFields.traverse(visitor: &visitor)
1548+
}
1549+
1550+
public static func ==(lhs: ProtoCRDTLWWMap, rhs: ProtoCRDTLWWMap) -> Bool {
1551+
if lhs._storage !== rhs._storage {
1552+
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
1553+
let _storage = _args.0
1554+
let rhs_storage = _args.1
1555+
if _storage._replicaID != rhs_storage._replicaID {return false}
1556+
if _storage._state != rhs_storage._state {return false}
1557+
return true
1558+
}
1559+
if !storagesAreEqual {return false}
1560+
}
1561+
if lhs.unknownFields != rhs.unknownFields {return false}
1562+
return true
1563+
}
1564+
}
1565+
13661566
extension ProtoCRDTLWWRegister: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
13671567
public static let protoMessageName: String = "CRDTLWWRegister"
13681568
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [

Sources/DistributedActors/Serialization/Serialization+SerializerID.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ extension Serialization {
116116
internal static let CRDTORSetDelta: SerializerID = .protobufRepresentable
117117
internal static let CRDTORMap: SerializerID = .protobufRepresentable
118118
internal static let CRDTORMapDelta: SerializerID = .protobufRepresentable
119+
internal static let CRDTORMultiMap: SerializerID = .protobufRepresentable
120+
internal static let CRDTORMultiMapDelta: SerializerID = .protobufRepresentable
121+
internal static let CRDTLWWMap: SerializerID = .protobufRepresentable
122+
internal static let CRDTLWWMapDelta: SerializerID = .protobufRepresentable
119123
internal static let CRDTLWWRegister: SerializerID = .protobufRepresentable
120124

121125
internal static let ConvergentGossipMembership: SerializerID = .foundationJSON

0 commit comments

Comments
 (0)