3
3
*/
4
4
5
5
@file:Suppress(" unused" )
6
+ @file:OptIn(ExperimentalUnsignedTypes ::class )
6
7
7
8
package kotlinx.serialization.cbor
8
9
@@ -20,132 +21,144 @@ import kotlinx.serialization.cbor.internal.*
20
21
* The whole hierarchy is [serializable][Serializable] only by [Cbor] format.
21
22
*/
22
23
@Serializable(with = CborElementSerializer ::class )
23
- public sealed class CborElement
24
+ public sealed class CborElement (
25
+ /* *
26
+ * CBOR tags associated with this element.
27
+ * Tags are optional semantic tagging of other major types (major type 6).
28
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
29
+ */
30
+ @OptIn(ExperimentalUnsignedTypes ::class )
31
+ public val tags : ULongArray = ulongArrayOf()
32
+ )
24
33
25
34
/* *
26
35
* Class representing CBOR primitive value.
27
36
* CBOR primitives include numbers, strings, booleans, byte arrays and special null value [CborNull].
28
37
*/
29
38
@Serializable(with = CborPrimitiveSerializer ::class )
30
- public sealed class CborPrimitive : CborElement () {
31
-
32
- }
39
+ public sealed class CborPrimitive (
40
+ tags : ULongArray = ulongArrayOf()
41
+ ) : CborElement(tags)
33
42
34
43
/* *
35
44
* Class representing signed CBOR integer (major type 1).
36
45
*/
37
46
@Serializable(with = CborIntSerializer ::class )
38
- public class CborNegativeInt (public val value : Long ) : CborPrimitive() {
47
+ public class CborNegativeInt (
48
+ public val value : Long ,
49
+ tags : ULongArray = ulongArrayOf()
50
+ ) : CborPrimitive(tags) {
39
51
init {
40
52
require(value < 0 ) { " Number must be negative: $value " }
41
53
}
42
54
43
- override fun equals (other : Any? ): Boolean {
44
- if (this == = other) return true
45
- if (other == null || this ::class != other::class ) return false
46
- other as CborNegativeInt
47
- return value == other.value
48
- }
55
+ override fun equals (other : Any? ): Boolean =
56
+ other is CborNegativeInt && other.value == value && other.tags.contentEquals(tags)
49
57
50
- override fun hashCode (): Int = value.hashCode()
58
+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
51
59
}
52
60
53
61
/* *
54
62
* Class representing unsigned CBOR integer (major type 0).
55
63
*/
56
64
@Serializable(with = CborUIntSerializer ::class )
57
- public class CborPositiveInt (public val value : ULong ) : CborPrimitive() {
65
+ public class CborPositiveInt (
66
+ public val value : ULong ,
67
+ tags : ULongArray = ulongArrayOf()
68
+ ) : CborPrimitive(tags) {
58
69
59
- override fun equals (other : Any? ): Boolean {
60
- if (this == = other) return true
61
- if (other == null || this ::class != other::class ) return false
62
- other as CborPositiveInt
63
- return value == other.value
64
- }
70
+ override fun equals (other : Any? ): Boolean =
71
+ other is CborPositiveInt && other.value == value && other.tags.contentEquals(tags)
65
72
66
- override fun hashCode (): Int = value.hashCode()
73
+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
67
74
}
68
75
69
76
/* *
70
77
* Class representing CBOR floating point value (major type 7).
71
78
*/
72
79
@Serializable(with = CborDoubleSerializer ::class )
73
- public class CborDouble (public val value : Double ) : CborPrimitive() {
80
+ public class CborDouble (
81
+ public val value : Double ,
82
+ tags : ULongArray = ulongArrayOf()
83
+ ) : CborPrimitive(tags) {
74
84
75
- override fun equals (other : Any? ): Boolean {
76
- if (this == = other) return true
77
- if (other == null || this ::class != other::class ) return false
78
- other as CborDouble
79
- return value == other.value
80
- }
85
+ override fun equals (other : Any? ): Boolean =
86
+ other is CborDouble && other.value == value && other.tags.contentEquals(tags)
81
87
82
- override fun hashCode (): Int = value.hashCode()
88
+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
83
89
}
84
90
85
-
86
91
/* *
87
92
* Class representing CBOR string value.
88
93
*/
89
94
@Serializable(with = CborStringSerializer ::class )
90
- public class CborString (public val value : String ) : CborPrimitive() {
95
+ public class CborString (
96
+ public val value : String ,
97
+ tags : ULongArray = ulongArrayOf()
98
+ ) : CborPrimitive(tags) {
91
99
92
- override fun equals (other : Any? ): Boolean {
93
- if (this == = other) return true
94
- if (other == null || this ::class != other::class ) return false
95
- other as CborString
96
- return value == other.value
97
- }
100
+ override fun equals (other : Any? ): Boolean =
101
+ other is CborString && other.value == value && other.tags.contentEquals(tags)
98
102
99
- override fun hashCode (): Int = value.hashCode()
103
+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
100
104
}
101
105
102
106
/* *
103
107
* Class representing CBOR boolean value.
104
108
*/
105
109
@Serializable(with = CborBooleanSerializer ::class )
106
- public class CborBoolean (private val value : Boolean ) : CborPrimitive() {
110
+ public class CborBoolean (
111
+ private val value : Boolean ,
112
+ tags : ULongArray = ulongArrayOf()
113
+ ) : CborPrimitive(tags) {
107
114
108
115
/* *
109
116
* Returns the boolean value.
110
117
*/
111
118
public val boolean: Boolean get() = value
112
119
113
- override fun equals (other : Any? ): Boolean {
114
- if (this == = other) return true
115
- if (other == null || this ::class != other::class ) return false
116
- other as CborBoolean
117
- return value == other.value
118
- }
120
+ override fun equals (other : Any? ): Boolean =
121
+ other is CborBoolean && other.value == value && other.tags.contentEquals(tags)
119
122
120
- override fun hashCode (): Int = value.hashCode()
123
+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
121
124
}
122
125
123
126
/* *
124
127
* Class representing CBOR byte string value.
125
128
*/
126
129
@Serializable(with = CborByteStringSerializer ::class )
127
- public class CborByteString (private val value : ByteArray ) : CborPrimitive() {
130
+ public class CborByteString (
131
+ private val value : ByteArray ,
132
+ tags : ULongArray = ulongArrayOf()
133
+ ) : CborPrimitive(tags) {
128
134
129
135
/* *
130
136
* Returns the byte array value.
131
137
*/
132
138
public val bytes: ByteArray get() = value.copyOf()
133
139
134
- override fun equals (other : Any? ): Boolean {
135
- if (this == = other) return true
136
- if (other == null || this ::class != other::class ) return false
137
- other as CborByteString
138
- return value.contentEquals(other.value)
139
- }
140
+ override fun equals (other : Any? ): Boolean =
141
+ other is CborByteString && other.value.contentEquals(value) && other.tags.contentEquals(tags)
140
142
141
- override fun hashCode (): Int = value.contentHashCode()
143
+ override fun hashCode (): Int = value.contentHashCode() * 31 + tags.contentHashCode()
142
144
}
143
145
144
146
/* *
145
147
* Class representing CBOR `null` value
146
148
*/
147
149
@Serializable(with = CborNullSerializer ::class )
148
- public object CborNull : CborPrimitive() {
150
+ public class CborNull (tags : ULongArray =ulongArrayOf()) : CborPrimitive(tags) {
151
+ // Note: CborNull is an object, so it cannot have constructor parameters for tags
152
+ // If tags are needed for null values, this would need to be changed to a class
153
+ override fun equals (other : Any? ): Boolean {
154
+ if (this == = other) return true
155
+ if (other !is CborNull ) return false
156
+ return true
157
+ }
158
+
159
+ override fun hashCode (): Int {
160
+ return this ::class .hashCode()
161
+ }
149
162
}
150
163
151
164
/* *
@@ -156,124 +169,34 @@ public object CborNull : CborPrimitive() {
156
169
*/
157
170
@Serializable(with = CborMapSerializer ::class )
158
171
public class CborMap (
159
- private val content : Map <CborElement , CborElement >
160
- ) : CborElement(), Map<CborElement, CborElement> by content {
161
- public override fun equals (other : Any? ): Boolean {
162
- if (this == = other) return true
163
- if (other == null || this ::class != other::class ) return false
164
- other as CborMap
165
- return content == other.content
166
- }
167
-
168
- public override fun hashCode (): Int = content.hashCode()
169
- public override fun toString (): String {
170
- return content.entries.joinToString(
171
- separator = " , " ,
172
- prefix = " {" ,
173
- postfix = " }" ,
174
- transform = { (k, v) -> " $k : $v " }
175
- )
176
- }
172
+ private val content : Map <CborElement , CborElement >,
173
+ tags : ULongArray = ulongArrayOf()
174
+ ) : CborElement(tags), Map<CborElement, CborElement> by content {
175
+
176
+ public override fun equals (other : Any? ): Boolean =
177
+ other is CborMap && other.content == content && other.tags.contentEquals(tags)
178
+
179
+ public override fun hashCode (): Int = content.hashCode() * 31 + tags.contentHashCode()
180
+
181
+ public override fun toString (): String = content.toString()
177
182
}
178
183
179
184
/* *
180
- * Class representing CBOR array, consisting of indexed values, where value is arbitrary [CborElement]
185
+ * Class representing CBOR array, consisting of CBOR elements.
181
186
*
182
187
* Since this class also implements [List] interface, you can use
183
- * traditional methods like [List.get] or [List.getOrNull ] to obtain CBOR elements.
188
+ * traditional methods like [List.get] or [List.size ] to obtain CBOR elements.
184
189
*/
185
190
@Serializable(with = CborListSerializer ::class )
186
- public class CborList (private val content : List <CborElement >) : CborElement(), List<CborElement> by content {
187
- public override fun equals (other : Any? ): Boolean {
188
- if (this == = other) return true
189
- if (other == null || this ::class != other::class ) return false
190
- other as CborList
191
- return content == other.content
192
- }
193
-
194
- public override fun hashCode (): Int = content.hashCode()
195
- public override fun toString (): String = content.joinToString(prefix = " [" , postfix = " ]" , separator = " , " )
196
- }
197
-
198
- /* *
199
- * Convenience method to get current element as [CborPrimitive]
200
- * @throws IllegalArgumentException if current element is not a [CborPrimitive]
201
- */
202
- public val CborElement .cborPrimitive: CborPrimitive
203
- get() = this as ? CborPrimitive ? : error(" CborPrimitive" )
204
-
205
- /* *
206
- * Convenience method to get current element as [CborMap]
207
- * @throws IllegalArgumentException if current element is not a [CborMap]
208
- */
209
- public val CborElement .cborMap: CborMap
210
- get() = this as ? CborMap ? : error(" CborMap" )
211
-
212
- /* *
213
- * Convenience method to get current element as [CborList]
214
- * @throws IllegalArgumentException if current element is not a [CborList]
215
- */
216
- public val CborElement .cborList: CborList
217
- get() = this as ? CborList ? : error(" CborList" )
218
-
219
- /* *
220
- * Convenience method to get current element as [CborNull]
221
- * @throws IllegalArgumentException if current element is not a [CborNull]
222
- */
223
- public val CborElement .cborNull: CborNull
224
- get() = this as ? CborNull ? : error(" CborNull" )
225
-
226
- /* *
227
- * Convenience method to get current element as [CborNegativeInt]
228
- * @throws IllegalArgumentException if current element is not a [CborNegativeInt]
229
- */
230
- public val CborElement .cborNegativeInt: CborNegativeInt
231
- get() = this as ? CborNegativeInt ? : error(" CborNegativeInt" )
232
-
233
- /* *
234
- * Convenience method to get current element as [CborPositiveInt]
235
- * @throws IllegalArgumentException if current element is not a [CborPositiveInt]
236
- */
237
- public val CborElement .cborPositiveInt: CborPositiveInt
238
- get() = this as ? CborPositiveInt ? : error(" CborPositiveInt" )
239
-
240
- /* *
241
- * Convenience method to get current element as [CborDouble]
242
- * @throws IllegalArgumentException if current element is not a [CborDouble]
243
- */
244
- public val CborElement .cborDouble: CborDouble
245
- get() = this as ? CborDouble ? : error(" CborDouble" )
246
-
247
- /* *
248
- * Convenience method to get current element as [CborString]
249
- * @throws IllegalArgumentException if current element is not a [CborString]
250
- */
251
- public val CborElement .cborString: CborString
252
- get() = this as ? CborString ? : error(" CborString" )
253
-
254
- /* *
255
- * Convenience method to get current element as [CborBoolean]
256
- * @throws IllegalArgumentException if current element is not a [CborBoolean]
257
- */
258
- public val CborElement .cborBoolean: CborBoolean
259
- get() = this as ? CborBoolean ? : error(" CborBoolean" )
260
-
261
- /* *
262
- * Convenience method to get current element as [CborByteString]
263
- * @throws IllegalArgumentException if current element is not a [CborByteString]
264
- */
265
- public val CborElement .cborByteString: CborByteString
266
- get() = this as ? CborByteString ? : error(" CborByteString" )
267
-
268
- /* *
269
- * Creates a [CborMap] from the given map entries.
270
- */
271
- public fun CborMap (vararg pairs : Pair <CborElement , CborElement >): CborMap = CborMap (mapOf (* pairs))
272
-
273
- /* *
274
- * Creates a [CborList] from the given elements.
275
- */
276
- public fun CborList (vararg elements : CborElement ): CborList = CborList (listOf (* elements))
277
-
278
- private fun CborElement.error (element : String ): Nothing =
279
- throw IllegalArgumentException (" Element ${this ::class } is not a $element " )
191
+ public class CborList (
192
+ private val content : List <CborElement >,
193
+ tags : ULongArray = ulongArrayOf()
194
+ ) : CborElement(tags), List<CborElement> by content {
195
+
196
+ public override fun equals (other : Any? ): Boolean =
197
+ other is CborList && other.content == content && other.tags.contentEquals(tags)
198
+
199
+ public override fun hashCode (): Int = content.hashCode() * 31 + tags.contentHashCode()
200
+
201
+ public override fun toString (): String = content.toString()
202
+ }
0 commit comments