33 */
44
55@file:Suppress(" unused" )
6+ @file:OptIn(ExperimentalUnsignedTypes ::class )
67
78package kotlinx.serialization.cbor
89
@@ -20,132 +21,144 @@ import kotlinx.serialization.cbor.internal.*
2021 * The whole hierarchy is [serializable][Serializable] only by [Cbor] format.
2122 */
2223@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+ )
2433
2534/* *
2635 * Class representing CBOR primitive value.
2736 * CBOR primitives include numbers, strings, booleans, byte arrays and special null value [CborNull].
2837 */
2938@Serializable(with = CborPrimitiveSerializer ::class )
30- public sealed class CborPrimitive : CborElement () {
31-
32- }
39+ public sealed class CborPrimitive (
40+ tags : ULongArray = ulongArrayOf()
41+ ) : CborElement(tags)
3342
3443/* *
3544 * Class representing signed CBOR integer (major type 1).
3645 */
3746@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) {
3951 init {
4052 require(value < 0 ) { " Number must be negative: $value " }
4153 }
4254
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)
4957
50- override fun hashCode (): Int = value.hashCode()
58+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
5159}
5260
5361/* *
5462 * Class representing unsigned CBOR integer (major type 0).
5563 */
5664@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) {
5869
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)
6572
66- override fun hashCode (): Int = value.hashCode()
73+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
6774}
6875
6976/* *
7077 * Class representing CBOR floating point value (major type 7).
7178 */
7279@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) {
7484
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)
8187
82- override fun hashCode (): Int = value.hashCode()
88+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
8389}
8490
85-
8691/* *
8792 * Class representing CBOR string value.
8893 */
8994@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) {
9199
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)
98102
99- override fun hashCode (): Int = value.hashCode()
103+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
100104}
101105
102106/* *
103107 * Class representing CBOR boolean value.
104108 */
105109@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) {
107114
108115 /* *
109116 * Returns the boolean value.
110117 */
111118 public val boolean: Boolean get() = value
112119
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)
119122
120- override fun hashCode (): Int = value.hashCode()
123+ override fun hashCode (): Int = value.hashCode() * 31 + tags.contentHashCode()
121124}
122125
123126/* *
124127 * Class representing CBOR byte string value.
125128 */
126129@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) {
128134
129135 /* *
130136 * Returns the byte array value.
131137 */
132138 public val bytes: ByteArray get() = value.copyOf()
133139
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)
140142
141- override fun hashCode (): Int = value.contentHashCode()
143+ override fun hashCode (): Int = value.contentHashCode() * 31 + tags.contentHashCode()
142144}
143145
144146/* *
145147 * Class representing CBOR `null` value
146148 */
147149@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+ }
149162}
150163
151164/* *
@@ -156,124 +169,34 @@ public object CborNull : CborPrimitive() {
156169 */
157170@Serializable(with = CborMapSerializer ::class )
158171public 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()
177182}
178183
179184/* *
180- * Class representing CBOR array, consisting of indexed values, where value is arbitrary [CborElement]
185+ * Class representing CBOR array, consisting of CBOR elements.
181186 *
182187 * 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.
184189 */
185190@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