11package io.ably.lib.objects
22
3+ import com.google.gson.JsonArray
4+ import com.google.gson.JsonObject
5+
36/* *
47 * An enum class representing the different actions that can be performed on an object.
58 * Spec: OOP2
@@ -15,7 +18,7 @@ internal enum class ObjectOperationAction(val code: Int) {
1518
1619/* *
1720 * An enum class representing the conflict-resolution semantics used by a Map object.
18- * Spec: MAP2
21+ * Spec: OMP2
1922 */
2023internal enum class MapSemantics (val code : Int ) {
2124 LWW (0 );
@@ -42,91 +45,117 @@ internal data class ObjectData(
4245 * String, number, boolean or binary - a concrete value of the object
4346 * Spec: OD2c
4447 */
45- val value : Any ? = null ,
48+ val value : ObjectValue ? = null ,
4649)
4750
51+ /* *
52+ * Represents a value that can be a String, Number, Boolean, Binary, JsonObject or JsonArray.
53+ * Performs a type check on initialization.
54+ * Spec: OD2c
55+ */
56+ internal data class ObjectValue (
57+ /* *
58+ * The concrete value of the object. Can be a String, Number, Boolean, Binary, JsonObject or JsonArray.
59+ * Spec: OD2c
60+ */
61+ val value : Any ,
62+ ) {
63+ init {
64+ require(
65+ value is String ||
66+ value is Number ||
67+ value is Boolean ||
68+ value is Binary ||
69+ value is JsonObject ||
70+ value is JsonArray
71+ ) {
72+ " value must be String, Number, Boolean, Binary, JsonObject or JsonArray"
73+ }
74+ }
75+ }
76+
4877/* *
4978 * A MapOp describes an operation to be applied to a Map object.
50- * Spec: MOP1
79+ * Spec: OMO1
5180 */
52- internal data class MapOp (
81+ internal data class ObjectMapOp (
5382 /* *
5483 * The key of the map entry to which the operation should be applied.
55- * Spec: MOP2a
84+ * Spec: OMO2a
5685 */
5786 val key : String ,
5887
5988 /* *
6089 * The data that the map entry should contain if the operation is a MAP_SET operation.
61- * Spec: MOP2b
90+ * Spec: OMO2b
6291 */
6392 val data : ObjectData ? = null
6493)
6594
6695/* *
6796 * A CounterOp describes an operation to be applied to a Counter object.
68- * Spec: COP1
97+ * Spec: OCO1
6998 */
70- internal data class CounterOp (
99+ internal data class ObjectCounterOp (
71100 /* *
72101 * The data value that should be added to the counter
73- * Spec: COP2a
102+ * Spec: OCO2a
74103 */
75- val amount : Double
104+ val amount : Double? = null
76105)
77106
78107/* *
79108 * A MapEntry represents the value at a given key in a Map object.
80109 * Spec: ME1
81110 */
82- internal data class MapEntry (
111+ internal data class ObjectMapEntry (
83112 /* *
84113 * Indicates whether the map entry has been removed.
85- * Spec: ME2a
114+ * Spec: OME2a
86115 */
87116 val tombstone : Boolean? = null ,
88117
89118 /* *
90119 * The serial value of the last operation that was applied to the map entry.
91120 * It is optional in a MAP_CREATE operation and might be missing, in which case the client should use a nullish value for it
92121 * and treat it as the "earliest possible" serial for comparison purposes.
93- * Spec: ME2b
122+ * Spec: OME2b
94123 */
95124 val timeserial : String? = null ,
96125
97126 /* *
98127 * The data that represents the value of the map entry.
99- * Spec: ME2c
128+ * Spec: OME2c
100129 */
101130 val data : ObjectData ? = null
102131)
103132
104133/* *
105134 * An ObjectMap object represents a map of key-value pairs.
106- * Spec: MAP1
135+ * Spec: OMP1
107136 */
108137internal data class ObjectMap (
109138 /* *
110139 * The conflict-resolution semantics used by the map object.
111- * Spec: MAP3a
140+ * Spec: OMP3a
112141 */
113142 val semantics : MapSemantics ? = null ,
114143
115144 /* *
116145 * The map entries, indexed by key.
117- * Spec: MAP3b
146+ * Spec: OMP3b
118147 */
119- val entries : Map <String , MapEntry >? = null
148+ val entries : Map <String , ObjectMapEntry >? = null
120149)
121150
122151/* *
123152 * An ObjectCounter object represents an incrementable and decrementable value
124- * Spec: CNT1
153+ * Spec: OCN1
125154 */
126155internal data class ObjectCounter (
127156 /* *
128157 * The value of the counter
129- * Spec: CNT2a
158+ * Spec: OCN2a
130159 */
131160 val count : Double? = null
132161)
@@ -152,13 +181,13 @@ internal data class ObjectOperation(
152181 * The payload for the operation if it is an operation on a Map object type.
153182 * Spec: OOP3c
154183 */
155- val mapOp : MapOp ? = null ,
184+ val mapOp : ObjectMapOp ? = null ,
156185
157186 /* *
158187 * The payload for the operation if it is an operation on a Counter object type.
159188 * Spec: OOP3d
160189 */
161- val counterOp : CounterOp ? = null ,
190+ val counterOp : ObjectCounterOp ? = null ,
162191
163192 /* *
164193 * The payload for the operation if the operation is MAP_CREATE.
@@ -313,3 +342,114 @@ internal data class ObjectMessage(
313342 */
314343 val siteCode : String? = null
315344)
345+
346+ /* *
347+ * Calculates the size of an ObjectMessage in bytes.
348+ * Spec: OM3
349+ */
350+ internal fun ObjectMessage.size (): Int {
351+ val clientIdSize = clientId?.length ? : 0 // Spec: OM3f
352+ val operationSize = operation?.size() ? : 0 // Spec: OM3b, OOP4
353+ val objectStateSize = objectState?.size() ? : 0 // Spec: OM3c, OST3
354+ val extrasSize = extras?.let { gson.toJson(it).length } ? : 0 // Spec: OM3d
355+
356+ return clientIdSize + operationSize + objectStateSize + extrasSize
357+ }
358+
359+ /* *
360+ * Calculates the size of an ObjectOperation in bytes.
361+ * Spec: OOP4
362+ */
363+ private fun ObjectOperation.size (): Int {
364+ val mapOpSize = mapOp?.size() ? : 0 // Spec: OOP4b, OMO3
365+ val counterOpSize = counterOp?.size() ? : 0 // Spec: OOP4c, OCO3
366+ val mapSize = map?.size() ? : 0 // Spec: OOP4d, OMP4
367+ val counterSize = counter?.size() ? : 0 // Spec: OOP4e, OCN3
368+
369+ return mapOpSize + counterOpSize + mapSize + counterSize
370+ }
371+
372+ /* *
373+ * Calculates the size of an ObjectState in bytes.
374+ * Spec: OST3
375+ */
376+ private fun ObjectState.size (): Int {
377+ val mapSize = map?.size() ? : 0 // Spec: OST3b, OMP4
378+ val counterSize = counter?.size() ? : 0 // Spec: OST3c, OCN3
379+ val createOpSize = createOp?.size() ? : 0 // Spec: OST3d, OOP4
380+
381+ return mapSize + counterSize + createOpSize
382+ }
383+
384+ /* *
385+ * Calculates the size of an ObjectMapOp in bytes.
386+ * Spec: OMO3
387+ */
388+ private fun ObjectMapOp.size (): Int {
389+ val keySize = key.length // Spec: OMO3d - Size of the key
390+ val dataSize = data?.size() ? : 0 // Spec: OMO3b - Size of the data, calculated per "OD3"
391+ return keySize + dataSize
392+ }
393+
394+ /* *
395+ * Calculates the size of a CounterOp in bytes.
396+ * Spec: OCO3
397+ */
398+ private fun ObjectCounterOp.size (): Int {
399+ // Size is 8 if amount is a number, 0 if amount is null or omitted
400+ return if (amount != null ) 8 else 0 // Spec: OCO3a, OCO3b
401+ }
402+
403+ /* *
404+ * Calculates the size of an ObjectMap in bytes.
405+ * Spec: OMP4
406+ */
407+ private fun ObjectMap.size (): Int {
408+ // Calculate the size of all map entries in the map property
409+ val entriesSize = entries?.entries?.sumOf {
410+ it.key.length + it.value.size() // // Spec: OMP4a1, OMP4a2
411+ } ? : 0
412+
413+ return entriesSize
414+ }
415+
416+ /* *
417+ * Calculates the size of an ObjectCounter in bytes.
418+ * Spec: OCN3
419+ */
420+ private fun ObjectCounter.size (): Int {
421+ // Size is 8 if count is a number, 0 if count is null or omitted
422+ return if (count != null ) 8 else 0
423+ }
424+
425+ /* *
426+ * Calculates the size of a MapEntry in bytes.
427+ * Spec: OME3
428+ */
429+ private fun ObjectMapEntry.size (): Int {
430+ // The size is equal to the size of the data property, calculated per "OD3"
431+ return data?.size() ? : 0
432+ }
433+
434+ /* *
435+ * Calculates the size of an ObjectData in bytes.
436+ * Spec: OD3
437+ */
438+ private fun ObjectData.size (): Int {
439+ return value?.size() ? : 0 // Spec: OD3f
440+ }
441+
442+ /* *
443+ * Calculates the size of an ObjectValue in bytes.
444+ * Spec: OD3*
445+ */
446+ private fun ObjectValue.size (): Int {
447+ return when (value) {
448+ is Boolean -> 1 // Spec: OD3b
449+ is Binary -> value.size() // Spec: OD3c
450+ is Number -> 8 // Spec: OD3d
451+ is String -> value.byteSize // Spec: OD3e
452+ is JsonObject , is JsonArray -> value.toString().byteSize // Spec: OD3e
453+ else -> 0 // Spec: OD3f
454+ }
455+ }
0 commit comments