Skip to content

Commit 1a46b06

Browse files
committed
tree encoding close
1 parent 998f01e commit 1a46b06

File tree

5 files changed

+128
-99
lines changed

5 files changed

+128
-99
lines changed

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

Lines changed: 100 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -40,142 +40,156 @@ public sealed class CborElement(
4040
public var tags: ULongArray = tags
4141
internal set
4242

43+
override fun equals(other: Any?): Boolean {
44+
if (this === other) return true
45+
if (other !is CborElement) return false
46+
47+
if (!tags.contentEquals(other.tags)) return false
48+
49+
return true
50+
}
51+
52+
override fun hashCode(): Int {
53+
return tags.contentHashCode()
54+
}
55+
4356
}
4457

4558
/**
4659
* Class representing CBOR primitive value.
4760
* CBOR primitives include numbers, strings, booleans, byte arrays and special null value [CborNull].
4861
*/
4962
@Serializable(with = CborPrimitiveSerializer::class)
50-
public sealed class CborPrimitive(
63+
public sealed class CborPrimitive<T : Any>(
64+
public val value: T,
5165
tags: ULongArray = ulongArrayOf()
52-
) : CborElement(tags)
66+
) : CborElement(tags) {
67+
override fun equals(other: Any?): Boolean {
68+
if (this === other) return true
69+
if (other !is CborPrimitive<*>) return false
70+
if (!super.equals(other)) return false
71+
72+
if (value != other.value) return false
73+
74+
return true
75+
}
76+
77+
override fun hashCode(): Int {
78+
var result = super.hashCode()
79+
result = 31 * result + value.hashCode()
80+
return result
81+
}
82+
83+
override fun toString(): String {
84+
return "CborPrimitive(" +
85+
"kind=${value::class.simpleName}, " +
86+
"tags=${tags.joinToString()}, " +
87+
"value=$value" +
88+
")"
89+
}
90+
}
91+
92+
public sealed class CborInt<T : Any>(
93+
tags: ULongArray = ulongArrayOf(),
94+
value: T,
95+
) : CborPrimitive<T>(value, tags) {
96+
public companion object {
97+
public operator fun invoke(
98+
value: Long,
99+
tags: ULongArray = ulongArrayOf()
100+
): CborInt<*> = if (value >= 0) CborPositiveInt(value.toULong(), tags) else CborNegativeInt(value, tags)
101+
102+
public operator fun invoke(
103+
value: ULong,
104+
tags: ULongArray = ulongArrayOf()
105+
): CborInt<ULong> = CborPositiveInt(value, tags)
106+
}
107+
}
53108

54109
/**
55110
* Class representing signed CBOR integer (major type 1).
56111
*/
57112
@Serializable(with = CborIntSerializer::class)
58113
public class CborNegativeInt(
59-
public val value: Long,
114+
value: Long,
60115
tags: ULongArray = ulongArrayOf()
61-
) : CborPrimitive(tags) {
116+
) : CborInt<Long>(tags, value) {
62117
init {
63118
require(value < 0) { "Number must be negative: $value" }
64119
}
65-
66-
override fun equals(other: Any?): Boolean =
67-
other is CborNegativeInt && other.value == value && other.tags.contentEquals(tags)
68-
69-
override fun hashCode(): Int = value.hashCode() * 31 + tags.contentHashCode()
70120
}
71121

72122
/**
73123
* Class representing unsigned CBOR integer (major type 0).
74124
*/
75125
@Serializable(with = CborUIntSerializer::class)
76126
public class CborPositiveInt(
77-
public val value: ULong,
127+
value: ULong,
78128
tags: ULongArray = ulongArrayOf()
79-
) : CborPrimitive(tags) {
80-
81-
override fun equals(other: Any?): Boolean =
82-
other is CborPositiveInt && other.value == value && other.tags.contentEquals(tags)
83-
84-
override fun hashCode(): Int = value.hashCode() * 31 + tags.contentHashCode()
85-
}
86-
87-
public fun CborInt(
88-
value: Long,
89-
tags: ULongArray = ulongArrayOf()
90-
): CborPrimitive = if (value >= 0) CborPositiveInt(value.toULong(), tags) else CborNegativeInt(value, tags)
129+
) : CborInt<ULong>(tags, value)
91130

92131
/**
93132
* Class representing CBOR floating point value (major type 7).
94133
*/
95134
@Serializable(with = CborDoubleSerializer::class)
96135
public class CborDouble(
97-
public val value: Double,
136+
value: Double,
98137
tags: ULongArray = ulongArrayOf()
99-
) : CborPrimitive(tags) {
100-
101-
override fun equals(other: Any?): Boolean =
102-
other is CborDouble && other.value == value && other.tags.contentEquals(tags)
103-
104-
override fun hashCode(): Int = value.hashCode() * 31 + tags.contentHashCode()
105-
}
138+
) : CborPrimitive<Double>(value, tags)
106139

107140
/**
108141
* Class representing CBOR string value.
109142
*/
110143
@Serializable(with = CborStringSerializer::class)
111144
public class CborString(
112-
public val value: String,
145+
value: String,
113146
tags: ULongArray = ulongArrayOf()
114-
) : CborPrimitive(tags) {
115-
116-
override fun equals(other: Any?): Boolean =
117-
other is CborString && other.value == value && other.tags.contentEquals(tags)
118-
119-
override fun hashCode(): Int = value.hashCode() * 31 + tags.contentHashCode()
120-
}
147+
) : CborPrimitive<String>(value, tags)
121148

122149
/**
123150
* Class representing CBOR boolean value.
124151
*/
125152
@Serializable(with = CborBooleanSerializer::class)
126153
public class CborBoolean(
127-
private val value: Boolean,
154+
value: Boolean,
128155
tags: ULongArray = ulongArrayOf()
129-
) : CborPrimitive(tags) {
130-
131-
/**
132-
* Returns the boolean value.
133-
*/
134-
public val boolean: Boolean get() = value
135-
136-
override fun equals(other: Any?): Boolean =
137-
other is CborBoolean && other.value == value && other.tags.contentEquals(tags)
138-
139-
override fun hashCode(): Int = value.hashCode() * 31 + tags.contentHashCode()
140-
}
156+
) : CborPrimitive<Boolean>(value, tags)
141157

142158
/**
143159
* Class representing CBOR byte string value.
144160
*/
145161
@Serializable(with = CborByteStringSerializer::class)
146162
public class CborByteString(
147-
private val value: ByteArray,
163+
value: ByteArray,
148164
tags: ULongArray = ulongArrayOf()
149-
) : CborPrimitive(tags) {
150-
151-
/**
152-
* Returns the byte array value.
153-
*/
154-
public val bytes: ByteArray get() = value.copyOf()
165+
) : CborPrimitive<ByteArray>(value, tags) {
166+
override fun equals(other: Any?): Boolean {
167+
if (this === other) return true
168+
if (other !is CborByteString) return false
169+
if (!super.equals(other)) return false
170+
return value.contentEquals(other.value)
171+
}
172+
override fun toString(): String {
173+
return "CborPrimitive(" +
174+
"kind=${value::class.simpleName}, " +
175+
"tags=${tags.joinToString()}, " +
176+
"value=h'${value.toHexString()}" +
177+
")"
178+
}
155179

156-
override fun equals(other: Any?): Boolean =
157-
other is CborByteString && other.value.contentEquals(value) && other.tags.contentEquals(tags)
180+
override fun hashCode(): Int {
181+
var result = super.hashCode()
182+
result = 31 * result + (value.contentHashCode())
183+
return result
184+
}
158185

159-
override fun hashCode(): Int = value.contentHashCode() * 31 + tags.contentHashCode()
160186
}
161187

162188
/**
163189
* Class representing CBOR `null` value
164190
*/
165191
@Serializable(with = CborNullSerializer::class)
166-
public class CborNull(tags: ULongArray = ulongArrayOf()) : CborPrimitive(tags) {
167-
// Note: CborNull is an object, so it cannot have constructor parameters for tags
168-
// If tags are needed for null values, this would need to be changed to a class
169-
override fun equals(other: Any?): Boolean {
170-
if (this === other) return true
171-
if (other !is CborNull) return false
172-
return true
173-
}
174-
175-
override fun hashCode(): Int {
176-
return this::class.hashCode()
177-
}
178-
}
192+
public class CborNull(tags: ULongArray = ulongArrayOf()) : CborPrimitive<Unit>(Unit, tags)
179193

180194
/**
181195
* Class representing CBOR map, consisting of key-value pairs, where both key and value are arbitrary [CborElement]
@@ -193,8 +207,13 @@ public class CborMap(
193207
other is CborMap && other.content == content && other.tags.contentEquals(tags)
194208

195209
public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
210+
override fun toString(): String {
211+
return "CborMap(" +
212+
"tags=${tags.joinToString()}, " +
213+
"content=$content" +
214+
")"
215+
}
196216

197-
public override fun toString(): String = content.toString()
198217
}
199218

200219
/**
@@ -213,6 +232,11 @@ public class CborList(
213232
other is CborList && other.content == content && other.tags.contentEquals(tags)
214233

215234
public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
235+
override fun toString(): String {
236+
return "CborList(" +
237+
"tags=${tags.joinToString()}, " +
238+
"content=$content" +
239+
")"
240+
}
216241

217-
public override fun toString(): String = content.toString()
218242
}

formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborElementSerializers.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal object CborElementSerializer : KSerializer<CborElement> {
3636

3737
// Encode the value
3838
when (value) {
39-
is CborPrimitive -> encoder.encodeSerializableValue(CborPrimitiveSerializer, value)
39+
is CborPrimitive<*> -> encoder.encodeSerializableValue(CborPrimitiveSerializer, value)
4040
is CborMap -> encoder.encodeSerializableValue(CborMapSerializer, value)
4141
is CborList -> encoder.encodeSerializableValue(CborListSerializer, value)
4242
}
@@ -52,11 +52,11 @@ internal object CborElementSerializer : KSerializer<CborElement> {
5252
* Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborPrimitive].
5353
* It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
5454
*/
55-
internal object CborPrimitiveSerializer : KSerializer<CborPrimitive> {
55+
internal object CborPrimitiveSerializer : KSerializer<CborPrimitive<*>> {
5656
override val descriptor: SerialDescriptor =
5757
buildSerialDescriptor("kotlinx.serialization.cbor.CborPrimitive", PrimitiveKind.STRING)
5858

59-
override fun serialize(encoder: Encoder, value: CborPrimitive) {
59+
override fun serialize(encoder: Encoder, value: CborPrimitive<*>) {
6060
val cborEncoder = encoder.asCborEncoder()
6161

6262
cborEncoder.encodeTags(value)
@@ -72,9 +72,9 @@ internal object CborPrimitiveSerializer : KSerializer<CborPrimitive> {
7272
}
7373
}
7474

75-
override fun deserialize(decoder: Decoder): CborPrimitive {
75+
override fun deserialize(decoder: Decoder): CborPrimitive<*> {
7676
val result = decoder.asCborDecoder().decodeCborElement()
77-
if (result !is CborPrimitive) throw CborDecodingException("Unexpected CBOR element, expected CborPrimitive, had ${result::class}")
77+
if (result !is CborPrimitive<*>) throw CborDecodingException("Unexpected CBOR element, expected CborPrimitive, had ${result::class}")
7878
return result
7979
}
8080
}
@@ -176,7 +176,7 @@ public object CborBooleanSerializer : KSerializer<CborBoolean> {
176176

177177
override fun serialize(encoder: Encoder, value: CborBoolean) {
178178
encoder.asCborEncoder().encodeTags(value)
179-
encoder.encodeBoolean(value.boolean)
179+
encoder.encodeBoolean(value.value)
180180
}
181181

182182
override fun deserialize(decoder: Decoder): CborBoolean {
@@ -198,7 +198,7 @@ public object CborByteStringSerializer : KSerializer<CborByteString> {
198198
override fun serialize(encoder: Encoder, value: CborByteString) {
199199
val cborEncoder = encoder.asCborEncoder()
200200
cborEncoder.encodeTags(value)
201-
cborEncoder.encodeByteString(value.bytes)
201+
cborEncoder.encodeByteString(value.value)
202202
}
203203

204204
override fun deserialize(decoder: Decoder): CborByteString {

formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ internal sealed class CborWriter(
5151
if ((encodeByteArrayAsByteString || cbor.configuration.alwaysUseByteString)
5252
&& serializer.descriptor == ByteArraySerializer().descriptor
5353
) {
54-
getDestination().encodeByteString(value as ByteArray)
54+
encodeByteString(value as ByteArray)
5555
} else {
5656
encodeByteArrayAsByteString = encodeByteArrayAsByteString || serializer.descriptor.isInlineByteString()
5757
super<AbstractEncoder>.encodeSerializableValue(serializer, value)
@@ -191,7 +191,7 @@ internal class StructuredCborWriter(cbor: Cbor) : CborWriter(
191191
cbor
192192
) {
193193

194-
sealed class CborContainer(tags: ULongArray, elements: MutableList<CborElement>) {
194+
sealed class CborContainer(tags: ULongArray, elements: MutableList<CborElement>) {
195195
var elements = elements
196196
private set
197197

@@ -229,7 +229,7 @@ internal class StructuredCborWriter(cbor: Cbor) : CborWriter(
229229
private val stack = ArrayDeque<CborContainer>()
230230
private var currentElement: CborContainer? = null
231231

232-
fun finalize() =currentElement!!.finalize()
232+
fun finalize() = currentElement!!.finalize()
233233

234234
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
235235
val tags = descriptor.getObjectTags() ?: ulongArrayOf()
@@ -255,14 +255,17 @@ internal class StructuredCborWriter(cbor: Cbor) : CborWriter(
255255
}
256256
}
257257

258-
override fun getDestination() = TODO()
258+
override fun getDestination() = throw IllegalStateException("There is not byteArrayOutput")
259259

260260

261261
override fun incrementChildren() {/*NOOP*/
262262
}
263263

264264

265265
override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
266+
//we don't care for special encoding of an empty class, so we don't set this flag here
267+
// isClass = descriptor.getElementDescriptor(index).kind == StructureKind.CLASS
268+
encodeByteArrayAsByteString = descriptor.isByteString(index)
266269
//TODO check if cborelement and be done
267270
val name = descriptor.getElementName(index)
268271
if (!descriptor.hasArrayTag()) {
@@ -273,7 +276,7 @@ internal class StructuredCborWriter(cbor: Cbor) : CborWriter(
273276
val cborLabel = descriptor.getCborLabel(index)
274277
if (cbor.configuration.preferCborLabelsOverNames && cborLabel != null) {
275278
currentElement!!.add(
276-
CborInt(cborLabel, keyTags ?: ulongArrayOf())
279+
CborInt.invoke(value = cborLabel, tags = keyTags ?: ulongArrayOf())
277280
)
278281
} else {
279282
currentElement!!.add(CborString(name, keyTags ?: ulongArrayOf()))

0 commit comments

Comments
 (0)