Skip to content

Commit 5857ef0

Browse files
committed
baseline tagging
1 parent d561069 commit 5857ef0

File tree

7 files changed

+242
-248
lines changed

7 files changed

+242
-248
lines changed

formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ public sealed class Cbor(
3434
CborConfiguration(
3535
encodeDefaults = false,
3636
ignoreUnknownKeys = false,
37-
encodeKeyTags = false,
38-
encodeValueTags = false,
39-
encodeObjectTags = false,
40-
verifyKeyTags = false,
41-
verifyValueTags = false,
42-
verifyObjectTags = false,
37+
encodeKeyTags = true,
38+
encodeValueTags = true,
39+
encodeObjectTags = true,
40+
verifyKeyTags = true,
41+
verifyValueTags = true,
42+
verifyObjectTags = true,
4343
useDefiniteLengthEncoding = false,
4444
preferCborLabelsOverNames = false,
4545
alwaysUseByteString = false

formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborElement.kt

Lines changed: 91 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
@file:Suppress("unused")
6+
@file:OptIn(ExperimentalUnsignedTypes::class)
67

78
package 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)
158171
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()
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

Comments
 (0)