@@ -6,8 +6,7 @@ import io.ably.lib.util.Log
66 * Implementation of LiveObject for LiveMap.
77 * Similar to JavaScript LiveMap class.
88 *
9- * @spec RTLM1 - LiveMap implementation
10- * @spec RTLM2 - LiveMap extends LiveObject
9+ * @spec RTLM1/RTLM2 - LiveMap implementation extends LiveObject
1110 */
1211internal class LiveMap (
1312 objectId : String ,
@@ -20,19 +19,19 @@ internal class LiveMap(
2019 /* *
2120 * @spec RTLM3 - Map data structure storing entries
2221 */
23- private data class MapEntry (
22+ private data class LiveMapEntry (
2423 var tombstone : Boolean = false ,
2524 var tombstonedAt : Long? = null ,
2625 var timeserial : String? = null ,
2726 var data : ObjectData ? = null
2827 )
2928
30- private val data = mutableMapOf<String , MapEntry >()
29+ private val data = mutableMapOf<String , LiveMapEntry >()
3130
3231 /* *
3332 * @spec RTLM6 - Overrides object data with state from sync
3433 */
35- override fun overrideWithObjectState (objectState : ObjectState ): Any {
34+ override fun overrideWithObjectState (objectState : ObjectState ): Map < String , String > {
3635 if (objectState.objectId != objectId) {
3736 throw objectError(" Invalid object state: object state objectId=${objectState.objectId} ; LiveMap objectId=$objectId " )
3837 }
@@ -62,8 +61,8 @@ internal class LiveMap(
6261 createOperationIsMerged = false // RTLM6b
6362 data.clear()
6463
65- objectState.map? .entries?.forEach { (key, entry) ->
66- data[key] = MapEntry (
64+ objectState.map.entries?.forEach { (key, entry) ->
65+ data[key] = LiveMapEntry (
6766 tombstone = entry.tombstone ? : false ,
6867 tombstonedAt = if (entry.tombstone == true ) System .currentTimeMillis() else null ,
6968 timeserial = entry.timeserial,
@@ -119,7 +118,7 @@ internal class LiveMap(
119118 notifyUpdated(update)
120119 }
121120
122- override fun clearData (): Any {
121+ override fun clearData (): Map < String , String > {
123122 val previousData = data.toMap()
124123 data.clear()
125124 return calculateUpdateFromDataDiff(previousData, emptyMap())
@@ -128,13 +127,13 @@ internal class LiveMap(
128127 /* *
129128 * @spec RTLM6d - Merges initial data from create operation
130129 */
131- private fun applyMapCreate (operation : ObjectOperation ): Any {
130+ private fun applyMapCreate (operation : ObjectOperation ): Map < String , String > {
132131 if (createOperationIsMerged) {
133132 Log .v(
134133 tag,
135134 " Skipping applying MAP_CREATE op on a map instance as it was already applied before; objectId=$objectId "
136135 )
137- return mapOf< String , String > ()
136+ return mapOf ()
138137 }
139138
140139 if (semantics != operation.map?.semantics) {
@@ -149,7 +148,7 @@ internal class LiveMap(
149148 /* *
150149 * @spec RTLM7 - Applies MAP_SET operation to LiveMap
151150 */
152- private fun applyMapSet (mapOp : ObjectMapOp , opSerial : String? ): Any {
151+ private fun applyMapSet (mapOp : ObjectMapOp , opSerial : String? ): Map < String , String > {
153152 val existingEntry = data[mapOp.key]
154153
155154 // RTLM7a
@@ -159,7 +158,7 @@ internal class LiveMap(
159158 tag,
160159 " Skipping update for key=\" ${mapOp.key} \" : op serial $opSerial <= entry serial ${existingEntry.timeserial} ; objectId=$objectId "
161160 )
162- return mapOf< String , String > ()
161+ return mapOf ()
163162 }
164163
165164 if (existingEntry != null ) {
@@ -170,7 +169,7 @@ internal class LiveMap(
170169 existingEntry.data = mapOp.data // RTLM7a2a
171170 } else {
172171 // RTLM7b, RTLM7b1
173- data[mapOp.key] = MapEntry (
172+ data[mapOp.key] = LiveMapEntry (
174173 tombstone = false , // RTLM7b2
175174 timeserial = opSerial,
176175 data = mapOp.data
@@ -183,7 +182,7 @@ internal class LiveMap(
183182 /* *
184183 * @spec RTLM8 - Applies MAP_REMOVE operation to LiveMap
185184 */
186- private fun applyMapRemove (mapOp : ObjectMapOp , opSerial : String? ): Any {
185+ private fun applyMapRemove (mapOp : ObjectMapOp , opSerial : String? ): Map < String , String > {
187186 val existingEntry = data[mapOp.key]
188187
189188 // RTLM8a
@@ -193,7 +192,7 @@ internal class LiveMap(
193192 tag,
194193 " Skipping remove for key=\" ${mapOp.key} \" : op serial $opSerial <= entry serial ${existingEntry.timeserial} ; objectId=$objectId "
195194 )
196- return mapOf< String , String > ()
195+ return mapOf ()
197196 }
198197
199198 if (existingEntry != null ) {
@@ -204,7 +203,7 @@ internal class LiveMap(
204203 existingEntry.data = null // RTLM8a2a
205204 } else {
206205 // RTLM8b, RTLM8b1
207- data[mapOp.key] = MapEntry (
206+ data[mapOp.key] = LiveMapEntry (
208207 tombstone = true , // RTLM8b2
209208 tombstonedAt = System .currentTimeMillis(),
210209 timeserial = opSerial
@@ -215,38 +214,29 @@ internal class LiveMap(
215214 }
216215
217216 /* *
217+ * For Lww CRDT semantics (the only supported LiveMap semantic) an operation
218+ * Should only be applied if incoming serial is strictly greater than existing entry's serial.
218219 * @spec RTLM9 - Serial comparison logic for map operations
219220 */
220- private fun canApplyMapOperation (mapEntrySerial : String? , opSerial : String? ): Boolean {
221- // for Lww CRDT semantics (the only supported LiveMap semantic) an operation
222- // should only be applied if its serial is strictly greater ("after") than an entry's serial.
223-
224- if (mapEntrySerial.isNullOrEmpty() && opSerial.isNullOrEmpty()) {
225- // RTLM9b - if both serials are nullish or empty strings, we treat them as the "earliest possible" serials,
226- // in which case they are "equal", so the operation should not be applied
221+ private fun canApplyMapOperation (existingMapEntrySerial : String? , opSerial : String? ): Boolean {
222+ if (existingMapEntrySerial.isNullOrEmpty() && opSerial.isNullOrEmpty()) { // RTLM9b
227223 return false
228224 }
229-
230- if (mapEntrySerial.isNullOrEmpty()) {
231- // RTLM9d - any operation serial is greater than non-existing entry serial
225+ if (existingMapEntrySerial.isNullOrEmpty()) { // RTLM9d - If true, means opSerial is not empty based on previous checks
232226 return true
233227 }
234-
235- if (opSerial.isNullOrEmpty()) {
236- // RTLM9c - non-existing operation serial is lower than any entry serial
228+ if (opSerial.isNullOrEmpty()) { // RTLM9c - Check reached here means existingMapEntrySerial is not empty
237229 return false
238230 }
239-
240- // RTLM9e - if both serials exist, compare them lexicographically
241- return opSerial > mapEntrySerial
231+ return opSerial > existingMapEntrySerial // RTLM9e - both are not empty
242232 }
243233
244234 /* *
245235 * @spec RTLM6d - Merges initial data from create operation
246236 */
247- private fun mergeInitialDataFromCreateOperation (operation : ObjectOperation ): Any {
237+ private fun mergeInitialDataFromCreateOperation (operation : ObjectOperation ): Map < String , String > {
248238 if (operation.map?.entries.isNullOrEmpty()) { // no map entries in MAP_CREATE op
249- return mapOf< String , String > ()
239+ return mapOf ()
250240 }
251241
252242 val aggregatedUpdate = mutableMapOf<String , String >()
@@ -275,7 +265,7 @@ internal class LiveMap(
275265 return aggregatedUpdate
276266 }
277267
278- private fun calculateUpdateFromDataDiff (prevData : Map <String , MapEntry >, newData : Map <String , MapEntry >): Map <String , String > {
268+ private fun calculateUpdateFromDataDiff (prevData : Map <String , LiveMapEntry >, newData : Map <String , LiveMapEntry >): Map <String , String > {
279269 val update = mutableMapOf<String , String >()
280270
281271 // Check for removed entries
0 commit comments