11import Ably
2+ internal import AblyPlugin
23import Foundation
34
45/// Our default implementation of ``LiveCounter``.
@@ -8,57 +9,63 @@ internal final class DefaultLiveCounter: LiveCounter {
89
910 private nonisolated ( unsafe) var mutableState: MutableState
1011
11- internal var testsOnly_siteTimeserials : [ String : String ] ? {
12+ internal var testsOnly_siteTimeserials : [ String : String ] {
1213 mutex. withLock {
13- mutableState. siteTimeserials
14+ mutableState. liveObject . siteTimeserials
1415 }
1516 }
1617
17- internal var testsOnly_createOperationIsMerged : Bool ? {
18+ internal var testsOnly_createOperationIsMerged : Bool {
1819 mutex. withLock {
19- mutableState. createOperationIsMerged
20+ mutableState. liveObject . createOperationIsMerged
2021 }
2122 }
2223
23- internal var testsOnly_objectID : String ? {
24+ internal var testsOnly_objectID : String {
2425 mutex. withLock {
25- mutableState. objectID
26+ mutableState. liveObject . objectID
2627 }
2728 }
2829
2930 private let coreSDK : CoreSDK
31+ private let logger : AblyPlugin . Logger
3032
3133 // MARK: - Initialization
3234
3335 internal convenience init (
3436 testsOnly_data data: Double ,
35- objectID: String ? ,
36- coreSDK: CoreSDK
37+ objectID: String ,
38+ coreSDK: CoreSDK ,
39+ logger: AblyPlugin . Logger
3740 ) {
38- self . init ( data: data, objectID: objectID, coreSDK: coreSDK)
41+ self . init ( data: data, objectID: objectID, coreSDK: coreSDK, logger : logger )
3942 }
4043
4144 private init (
4245 data: Double ,
43- objectID: String ? ,
44- coreSDK: CoreSDK
46+ objectID: String ,
47+ coreSDK: CoreSDK ,
48+ logger: AblyPlugin . Logger
4549 ) {
46- mutableState = . init( data : data , objectID: objectID)
50+ mutableState = . init( liveObject : . init ( objectID: objectID) , data : data )
4751 self . coreSDK = coreSDK
52+ self . logger = logger
4853 }
4954
5055 /// Creates a "zero-value LiveCounter", per RTLC4.
5156 ///
5257 /// - Parameters:
5358 /// - objectID: The value for the "private objectId field" of RTO5c1b1a.
5459 internal static func createZeroValued(
55- objectID: String ? = nil ,
60+ objectID: String ,
5661 coreSDK: CoreSDK ,
62+ logger: AblyPlugin . Logger ,
5763 ) -> Self {
5864 . init(
5965 data: 0 ,
6066 objectID: objectID,
6167 coreSDK: coreSDK,
68+ logger: logger,
6269 )
6370 }
6471
@@ -116,41 +123,137 @@ internal final class DefaultLiveCounter: LiveCounter {
116123 }
117124 }
118125
126+ /// Test-only method to merge initial value from an ObjectOperation, per RTLC10.
127+ internal func testsOnly_mergeInitialValue( from operation: ObjectOperation ) {
128+ mutex. withLock {
129+ mutableState. mergeInitialValue ( from: operation)
130+ }
131+ }
132+
133+ /// Test-only method to apply a COUNTER_CREATE operation, per RTLC8.
134+ internal func testsOnly_applyCounterCreateOperation( _ operation: ObjectOperation ) {
135+ mutex. withLock {
136+ mutableState. applyCounterCreateOperation ( operation, logger: logger)
137+ }
138+ }
139+
140+ /// Test-only method to apply a COUNTER_INC operation, per RTLC9.
141+ internal func testsOnly_applyCounterIncOperation( _ operation: WireObjectsCounterOp ? ) {
142+ mutex. withLock {
143+ mutableState. applyCounterIncOperation ( operation)
144+ }
145+ }
146+
147+ /// Attempts to apply an operation from an inbound `ObjectMessage`, per RTLC7.
148+ internal func apply(
149+ _ operation: ObjectOperation ,
150+ objectMessageSerial: String ? ,
151+ objectMessageSiteCode: String ? ,
152+ objectsPool: inout ObjectsPool ,
153+ ) {
154+ mutex. withLock {
155+ mutableState. apply (
156+ operation,
157+ objectMessageSerial: objectMessageSerial,
158+ objectMessageSiteCode: objectMessageSiteCode,
159+ objectsPool: & objectsPool,
160+ logger: logger,
161+ )
162+ }
163+ }
164+
119165 // MARK: - Mutable state and the operations that affect it
120166
121167 private struct MutableState {
168+ /// The mutable state common to all LiveObjects.
169+ internal var liveObject : LiveObjectMutableState
170+
122171 /// The internal data that this map holds, per RTLC3.
123172 internal var data : Double
124173
125- /// The site timeserials for this counter, per RTLC6a.
126- internal var siteTimeserials : [ String : String ] ?
127-
128- /// Whether the create operation has been merged, per RTLC6b and RTLC6d2.
129- internal var createOperationIsMerged : Bool ?
130-
131- /// The "private `objectId` field" of RTO5c1b1a.
132- internal var objectID : String ?
133-
134174 /// Replaces the internal data of this counter with the provided ObjectState, per RTLC6.
135175 internal mutating func replaceData( using state: ObjectState ) {
136176 // RTLC6a: Replace the private siteTimeserials with the value from ObjectState.siteTimeserials
137- siteTimeserials = state. siteTimeserials
177+ liveObject . siteTimeserials = state. siteTimeserials
138178
139179 // RTLC6b: Set the private flag createOperationIsMerged to false
140- createOperationIsMerged = false
180+ liveObject . createOperationIsMerged = false
141181
142182 // RTLC6c: Set data to the value of ObjectState.counter.count, or to 0 if it does not exist
143183 data = state. counter? . count? . doubleValue ?? 0
144184
145- // RTLC6d: If ObjectState.createOp is present
185+ // RTLC6d: If ObjectState.createOp is present, merge the initial value into the LiveCounter as described in RTLC10
146186 if let createOp = state. createOp {
147- // RTLC6d1: Add ObjectState.createOp.counter.count to data, if it exists
148- if let createOpCount = createOp. counter? . count? . doubleValue {
149- data += createOpCount
150- }
151- // RTLC6d2: Set the private flag createOperationIsMerged to true
152- createOperationIsMerged = true
187+ mergeInitialValue ( from: createOp)
188+ }
189+ }
190+
191+ /// Merges the initial value from an ObjectOperation into this LiveCounter, per RTLC10.
192+ internal mutating func mergeInitialValue( from operation: ObjectOperation ) {
193+ // RTLC10a: Add ObjectOperation.counter.count to data, if it exists
194+ if let operationCount = operation. counter? . count? . doubleValue {
195+ data += operationCount
196+ }
197+ // RTLC10b: Set the private flag createOperationIsMerged to true
198+ liveObject. createOperationIsMerged = true
199+ }
200+
201+ /// Attempts to apply an operation from an inbound `ObjectMessage`, per RTLC7.
202+ internal mutating func apply(
203+ _ operation: ObjectOperation ,
204+ objectMessageSerial: String ? ,
205+ objectMessageSiteCode: String ? ,
206+ objectsPool: inout ObjectsPool ,
207+ logger: Logger ,
208+ ) {
209+ guard let applicableOperation = liveObject. canApplyOperation ( objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
210+ // RTLC7b
211+ logger. log ( " Operation \( operation) (serial: \( String ( describing: objectMessageSerial) ) , siteCode: \( String ( describing: objectMessageSiteCode) ) ) should not be applied; discarding " , level: . debug)
212+ return
213+ }
214+
215+ // RTLC7c
216+ liveObject. siteTimeserials [ applicableOperation. objectMessageSiteCode] = applicableOperation. objectMessageSerial
217+
218+ switch operation. action {
219+ case . known( . counterCreate) :
220+ // RTLC7d1
221+ applyCounterCreateOperation (
222+ operation,
223+ logger: logger,
224+ )
225+ case . known( . counterInc) :
226+ // RTLC7d2
227+ applyCounterIncOperation ( operation. counterOp)
228+ default :
229+ // RTLC7d3
230+ logger. log ( " Operation \( operation) has unsupported action for LiveCounter; discarding " , level: . warn)
231+ }
232+ }
233+
234+ /// Applies a `COUNTER_CREATE` operation, per RTLC8.
235+ internal mutating func applyCounterCreateOperation(
236+ _ operation: ObjectOperation ,
237+ logger: Logger ,
238+ ) {
239+ if liveObject. createOperationIsMerged {
240+ // RTLC8b
241+ logger. log ( " Not applying COUNTER_CREATE because a COUNTER_CREATE has already been applied " , level: . warn)
242+ return
153243 }
244+
245+ // RTLC8c
246+ mergeInitialValue ( from: operation)
247+ }
248+
249+ /// Applies a `COUNTER_INC` operation, per RTLC9.
250+ internal mutating func applyCounterIncOperation( _ operation: WireObjectsCounterOp ? ) {
251+ guard let operation else {
252+ return
253+ }
254+
255+ // RTLC9b
256+ data += operation. amount. doubleValue
154257 }
155258 }
156259}
0 commit comments