Skip to content

Commit da79034

Browse files
committed
polish encoder
1 parent 74db7b0 commit da79034

File tree

5 files changed

+317
-10
lines changed

5 files changed

+317
-10
lines changed

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,23 +166,23 @@ public class CborByteString(
166166
override fun equals(other: Any?): Boolean {
167167
if (this === other) return true
168168
if (other !is CborByteString) return false
169-
if (!super.equals(other)) return false
169+
if (!tags.contentEquals(other.tags)) return false
170170
return value.contentEquals(other.value)
171171
}
172+
173+
override fun hashCode(): Int {
174+
var result = tags.contentHashCode()
175+
result = 31 * result + (value.contentHashCode())
176+
return result
177+
}
178+
172179
override fun toString(): String {
173180
return "CborPrimitive(" +
174181
"kind=${value::class.simpleName}, " +
175182
"tags=${tags.joinToString()}, " +
176183
"value=h'${value.toHexString()}" +
177184
")"
178185
}
179-
180-
override fun hashCode(): Int {
181-
var result = super.hashCode()
182-
result = 31 * result + (value.contentHashCode())
183-
return result
184-
}
185-
186186
}
187187

188188
/**
@@ -207,6 +207,7 @@ public class CborMap(
207207
other is CborMap && other.content == content && other.tags.contentEquals(tags)
208208

209209
public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
210+
210211
override fun toString(): String {
211212
return "CborMap(" +
212213
"tags=${tags.joinToString()}, " +
@@ -232,6 +233,7 @@ public class CborList(
232233
other is CborList && other.content == content && other.tags.contentEquals(tags)
233234

234235
public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
236+
235237
override fun toString(): String {
236238
return "CborList(" +
237239
"tags=${tags.joinToString()}, " +
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/*
2+
* Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.cbor
6+
7+
import kotlin.test.*
8+
9+
class CborElementEqualityTest {
10+
11+
@Test
12+
fun testCborPositiveIntEquality() {
13+
val int1 = CborPositiveInt(42u)
14+
val int2 = CborPositiveInt(42u)
15+
val int3 = CborPositiveInt(43u)
16+
val int4 = CborPositiveInt(42u, ulongArrayOf(1u))
17+
18+
// Same values should be equal
19+
assertEquals(int1, int2)
20+
assertEquals(int1.hashCode(), int2.hashCode())
21+
22+
// Different values should not be equal
23+
assertNotEquals(int1, int3)
24+
25+
// Different tags should not be equal
26+
assertNotEquals(int1, int4)
27+
28+
// Null comparison
29+
assertNotEquals(int1, null as CborElement?)
30+
31+
// Different type comparison
32+
assertNotEquals(int1 as CborElement, CborString("42"))
33+
assertNotEquals(int1, CborString("42") as CborElement)
34+
}
35+
36+
@Test
37+
fun testCborNegativeIntEquality() {
38+
val int1 = CborNegativeInt(-42)
39+
val int2 = CborNegativeInt(-42)
40+
val int3 = CborNegativeInt(-43)
41+
val int4 = CborNegativeInt(-42, ulongArrayOf(1u))
42+
43+
assertEquals(int1, int2)
44+
assertEquals(int1.hashCode(), int2.hashCode())
45+
assertNotEquals(int1, int3)
46+
assertNotEquals(int1, int4)
47+
assertNotEquals(int1, null as CborElement?)
48+
assertNotEquals(int1, CborPositiveInt(42u) as CborElement)
49+
assertNotEquals(int1 as CborElement, CborPositiveInt(42u))
50+
}
51+
52+
@Test
53+
fun testCborDoubleEquality() {
54+
val double1 = CborDouble(3.14)
55+
val double2 = CborDouble(3.14)
56+
val double3 = CborDouble(2.71)
57+
val double4 = CborDouble(3.14, ulongArrayOf(1u))
58+
59+
assertEquals(double1, double2)
60+
assertEquals(double1.hashCode(), double2.hashCode())
61+
assertNotEquals(double1, double3)
62+
assertNotEquals(double1, double4)
63+
assertNotEquals(double1, null as CborElement?)
64+
assertNotEquals(double1 as CborElement, CborString("3.14"))
65+
assertNotEquals(double1, CborString("3.14") as CborElement)
66+
}
67+
68+
@Test
69+
fun testCborStringEquality() {
70+
val string1 = CborString("hello")
71+
val string2 = CborString("hello")
72+
val string3 = CborString("world")
73+
val string4 = CborString("hello", ulongArrayOf(1u))
74+
75+
assertEquals(string1, string2)
76+
assertEquals(string1.hashCode(), string2.hashCode())
77+
assertNotEquals(string1, string3)
78+
assertNotEquals(string1, string4)
79+
assertNotEquals(string1, null as CborElement?)
80+
assertNotEquals(string1 as CborElement, CborPositiveInt(123u))
81+
assertNotEquals(string1, CborPositiveInt(123u) as CborElement)
82+
}
83+
84+
@Test
85+
fun testCborBooleanEquality() {
86+
val bool1 = CborBoolean(true)
87+
val bool2 = CborBoolean(true)
88+
val bool3 = CborBoolean(false)
89+
val bool4 = CborBoolean(true, ulongArrayOf(1u))
90+
91+
assertEquals(bool1, bool2)
92+
assertEquals(bool1.hashCode(), bool2.hashCode())
93+
assertNotEquals(bool1, bool3)
94+
assertNotEquals(bool1, bool4)
95+
assertNotEquals(bool1, null as CborElement?)
96+
assertNotEquals(bool1 as CborElement, CborString("true"))
97+
assertNotEquals(bool1, CborString("true") as CborElement)
98+
}
99+
100+
@Test
101+
fun testCborByteStringEquality() {
102+
val bytes1 = byteArrayOf(1, 2, 3)
103+
val bytes2 = byteArrayOf(1, 2, 3)
104+
val bytes3 = byteArrayOf(4, 5, 6)
105+
106+
val byteString1 = CborByteString(bytes1)
107+
val byteString2 = CborByteString(bytes2)
108+
val byteString3 = CborByteString(bytes3)
109+
val byteString4 = CborByteString(bytes1, ulongArrayOf(1u))
110+
111+
assertEquals(byteString1, byteString2)
112+
assertEquals(byteString1.hashCode(), byteString2.hashCode())
113+
assertNotEquals(byteString1, byteString3)
114+
assertNotEquals(byteString1, byteString4)
115+
assertNotEquals(byteString1, null as CborElement?)
116+
assertNotEquals(byteString1 as CborElement, CborString("123"))
117+
assertNotEquals(byteString1, CborString("123") as CborElement)
118+
}
119+
120+
@Test
121+
fun testCborNullEquality() {
122+
val null1 = CborNull()
123+
val null2 = CborNull()
124+
val null3 = CborNull(ulongArrayOf(1u))
125+
126+
assertEquals(null1, null2)
127+
assertEquals(null1.hashCode(), null2.hashCode())
128+
assertNotEquals(null1, null3)
129+
assertNotEquals(null1, null as CborElement?)
130+
assertNotEquals(null1 as CborElement, CborString("null"))
131+
assertNotEquals(null1, CborString("null") as CborElement)
132+
}
133+
134+
@Test
135+
fun testCborListEquality() {
136+
val list1 = CborList(listOf(CborPositiveInt(1u), CborString("test")))
137+
val list2 = CborList(listOf(CborPositiveInt(1u), CborString("test")))
138+
val list3 = CborList(listOf(CborPositiveInt(2u), CborString("test")))
139+
val list4 = CborList(listOf(CborPositiveInt(1u), CborString("test")), ulongArrayOf(1u))
140+
val list5 = CborList(listOf(CborPositiveInt(1u)))
141+
142+
assertEquals(list1, list2)
143+
assertEquals(list1.hashCode(), list2.hashCode())
144+
assertNotEquals(list1, list3)
145+
assertNotEquals(list1, list4)
146+
assertNotEquals(list1, list5)
147+
assertNotEquals(list1, null as CborElement?)
148+
assertNotEquals(list1 as CborElement, CborString("list"))
149+
assertNotEquals(list1, CborString("list") as CborElement)
150+
}
151+
152+
@Test
153+
fun testCborMapEquality() {
154+
val map1 = CborMap(mapOf(
155+
CborString("key1") to CborPositiveInt(1u),
156+
CborString("key2") to CborString("value")
157+
))
158+
val map2 = CborMap(mapOf(
159+
CborString("key1") to CborPositiveInt(1u),
160+
CborString("key2") to CborString("value")
161+
))
162+
val map3 = CborMap(mapOf(
163+
CborString("key1") to CborPositiveInt(2u),
164+
CborString("key2") to CborString("value")
165+
))
166+
val map4 = CborMap(mapOf(
167+
CborString("key1") to CborPositiveInt(1u),
168+
CborString("key2") to CborString("value")
169+
), ulongArrayOf(1u))
170+
val map5 = CborMap(mapOf(
171+
CborString("key1") to CborPositiveInt(1u)
172+
))
173+
174+
assertEquals(map1, map2)
175+
assertEquals(map1.hashCode(), map2.hashCode())
176+
assertNotEquals(map1, map3)
177+
assertNotEquals(map1, map4)
178+
assertNotEquals(map1, map5)
179+
assertNotEquals(map1, null as CborElement?)
180+
assertNotEquals(map1 as CborElement, CborString("map"))
181+
assertNotEquals(map1, CborString("map") as CborElement)
182+
}
183+
184+
@Test
185+
fun testTagsEquality() {
186+
val tags1 = ulongArrayOf(1u, 2u, 3u)
187+
val tags2 = ulongArrayOf(1u, 2u, 3u)
188+
val tags3 = ulongArrayOf(1u, 2u, 4u)
189+
190+
val string1 = CborString("test", tags1)
191+
val string2 = CborString("test", tags2)
192+
val string3 = CborString("test", tags3)
193+
194+
assertEquals(string1, string2)
195+
assertEquals(string1.hashCode(), string2.hashCode())
196+
assertNotEquals(string1, string3)
197+
}
198+
199+
@Test
200+
fun testEmptyCollectionsEquality() {
201+
val emptyList1 = CborList(emptyList())
202+
val emptyList2 = CborList(emptyList())
203+
val emptyMap1 = CborMap(emptyMap())
204+
val emptyMap2 = CborMap(emptyMap())
205+
206+
assertEquals(emptyList1, emptyList2)
207+
assertEquals(emptyList1.hashCode(), emptyList2.hashCode())
208+
assertEquals(emptyMap1, emptyMap2)
209+
assertEquals(emptyMap1.hashCode(), emptyMap2.hashCode())
210+
assertNotEquals(emptyList1 as CborElement, emptyMap1)
211+
assertNotEquals(emptyList1, emptyMap1 as CborElement)
212+
}
213+
214+
@Test
215+
fun testNestedStructureEquality() {
216+
val nested1 = CborMap(mapOf(
217+
CborString("list") to CborList(listOf(
218+
CborPositiveInt(1u),
219+
CborMap(mapOf(CborString("inner") to CborNull()))
220+
))
221+
))
222+
val nested2 = CborMap(mapOf(
223+
CborString("list") to CborList(listOf(
224+
CborPositiveInt(1u),
225+
CborMap(mapOf(CborString("inner") to CborNull()))
226+
))
227+
))
228+
val nested3 = CborMap(mapOf(
229+
CborString("list") to CborList(listOf(
230+
CborPositiveInt(2u),
231+
CborMap(mapOf(CborString("inner") to CborNull()))
232+
))
233+
))
234+
235+
assertEquals(nested1, nested2)
236+
assertEquals(nested1.hashCode(), nested2.hashCode())
237+
assertNotEquals(nested1, nested3)
238+
}
239+
240+
@Test
241+
fun testReflexiveEquality() {
242+
val elements = listOf(
243+
CborPositiveInt(42u),
244+
CborNegativeInt(-42),
245+
CborDouble(3.14),
246+
CborString("test"),
247+
CborBoolean(true),
248+
CborByteString(byteArrayOf(1, 2, 3)),
249+
CborNull(),
250+
CborList(listOf(CborPositiveInt(1u))),
251+
CborMap(mapOf(CborString("key") to CborPositiveInt(1u)))
252+
)
253+
254+
elements.forEach { element ->
255+
assertEquals(element, element, "Element should be equal to itself")
256+
assertEquals(element.hashCode(), element.hashCode(), "Hash code should be consistent")
257+
}
258+
}
259+
260+
@Test
261+
fun testSymmetricEquality() {
262+
val pairs = listOf(
263+
CborPositiveInt(42u) to CborPositiveInt(42u),
264+
CborNegativeInt(-42) to CborNegativeInt(-42),
265+
CborDouble(3.14) to CborDouble(3.14),
266+
CborString("test") to CborString("test"),
267+
CborBoolean(true) to CborBoolean(true),
268+
CborByteString(byteArrayOf(1, 2, 3)) to CborByteString(byteArrayOf(1, 2, 3)),
269+
CborNull() to CborNull(),
270+
CborList(listOf(CborPositiveInt(1u))) to CborList(listOf(CborPositiveInt(1u))),
271+
CborMap(mapOf(CborString("key") to CborPositiveInt(1u))) to CborMap(mapOf(CborString("key") to CborPositiveInt(1u)))
272+
)
273+
274+
pairs.forEach { (a, b) ->
275+
assertEquals(a, b, "a should equal b")
276+
assertEquals(b, a, "b should equal a (symmetry)")
277+
assertEquals(a.hashCode(), b.hashCode(), "Hash codes should be equal")
278+
}
279+
}
280+
281+
@Test
282+
fun testTransitiveEquality() {
283+
val a = CborString("test")
284+
val b = CborString("test")
285+
val c = CborString("test")
286+
287+
assertEquals(a, b)
288+
assertEquals(b, c)
289+
assertEquals(a, c, "Transitivity: if a==b and b==c, then a==c")
290+
}
291+
}

formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,7 @@ class CborElementTest {
333333
assertEquals(1, decodedElement.tags.size)
334334
assertEquals(42u, decodedElement.tags.first())
335335
}
336+
337+
338+
336339
}

formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@ class CborTaggedTest {
571571
assertEquals(referenceHexString, cbor.encodeToHexString(ClassAsTagged.serializer(), reference))
572572
assertEquals(reference, cbor.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString))
573573

574+
val struct = Cbor.CoseCompliant.encodeToCbor(reference)
575+
assertEquals(Cbor.decodeFromByteArray<CborElement>(referenceHexString.hexToByteArray()), struct)
576+
574577
assertEquals(
575578
reference,
576579
Cbor { verifyObjectTags = false }.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString)

formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,14 @@ class CbrWriterTest {
5151
HexConverter.parseHexBinary("cafe"),
5252
HexConverter.parseHexBinary("cafe")
5353
)
54+
val encoded =
55+
"a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521"
5456
assertEquals(
55-
"a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521",
57+
encoded,
5658
Cbor { useDefiniteLengthEncoding = true }.encodeToHexString(TypesUmbrella.serializer(), test)
5759
)
60+
val struct = Cbor.encodeToCbor(test)
61+
assertEquals(Cbor.decodeFromByteArray<CborElement>(encoded.hexToByteArray()), struct)
5862
}
5963

6064
@Test
@@ -67,10 +71,14 @@ class CbrWriterTest {
6771
true,
6872
'a'
6973
)
74+
val encoded =
75+
"bf63696e741a00018894646c6f6e671b7fffffffffffffff65666c6f6174fa4228000066646f75626c65fb4271fb0c5a2b700067626f6f6c65616ef564636861721861ff"
7076
assertEquals(
71-
"bf63696e741a00018894646c6f6e671b7fffffffffffffff65666c6f6174fa4228000066646f75626c65fb4271fb0c5a2b700067626f6f6c65616ef564636861721861ff",
77+
encoded,
7278
Cbor.encodeToHexString(NumberTypesUmbrella.serializer(), test)
7379
)
80+
val struct = Cbor.encodeToCbor(test)
81+
assertEquals(Cbor.decodeFromByteArray<CborElement>(encoded.hexToByteArray()), struct)
7482
}
7583

7684
@Test

0 commit comments

Comments
 (0)