Skip to content

Commit bf0432d

Browse files
committed
some cleaning, added a really quick test
1 parent 139d433 commit bf0432d

File tree

2 files changed

+124
-46
lines changed

2 files changed

+124
-46
lines changed

core/commonMain/src/kotlinx/serialization/encoding/ReorderingCompositeEncoder.kt

+52-46
Original file line numberDiff line numberDiff line change
@@ -15,89 +15,88 @@ import kotlinx.serialization.modules.*
1515
* This encoder will replicate the behavior of a standard encoding, but calling the `encode*Element` methods in
1616
* the order defined by [mapElementIndex]. It first buffers each `encode*Element` calls in an array following
1717
* the given indexes using [mapElementIndex], then when [endStructure] is called, it encodes the buffered calls
18-
* in the expected order by replaying the previous calls on a concrete [CompositeEncoder] provided
19-
* by [beginStructure].
18+
* in the expected order by replaying the previous calls on the given [compositeEncoderDelegate].
2019
*
21-
* @param structureDescriptor descriptor of the structure being encoded
22-
* @param beginStructure provides the [CompositeEncoder] to be used to encode the given descriptor's elements in the expected order.
23-
* @param mapElementIndex maps the element index to the new reordered index (zero-based). If this mapper provides the same index for multiple elements, only the last one will be encoded as the previous ones will be overridden.
20+
* @param compositeEncoderDelegate the [CompositeEncoder] to be used to encode the given descriptor's elements in the expected order.
21+
* @param structureDescriptor descriptor of the structure being encoded and reordered
22+
* @param mapElementIndex maps the element index to a new positional zero-based index. If this mapper provides the same index for multiple elements, only the last one will be encoded as the previous ones will be overridden. The mapped index just helps to reorder the elements, but the reordered `encode*Element` method calls will still pass the original element index.
2423
*/
2524
@ExperimentalSerializationApi
2625
internal class ReorderingCompositeEncoder(
2726
structureDescriptor: SerialDescriptor,
28-
private val beginStructure: (SerialDescriptor) -> CompositeEncoder,
27+
private val compositeEncoderDelegate: CompositeEncoder,
2928
private val mapElementIndex: (SerialDescriptor, Int) -> Int,
3029
) : CompositeEncoder {
31-
// Each time we encode a field, if the next expected schema field index is not the good one, it is buffered until it's the time to encode it
3230
private var bufferedCalls = Array<BufferedCall?>(structureDescriptor.elementsCount) { null }
33-
private var callShouldEncodeElementDefault = false
3431

3532
override val serializersModule: SerializersModule
3633
// No need to return a serializers module as it's not used during buffering
3734
get() = EmptySerializersModule()
3835

3936
private data class BufferedCall(
4037
val originalElementIndex: Int,
41-
val encoder: CompositeEncoder.() -> Unit,
38+
val encoder: () -> Unit,
4239
)
4340

44-
private inline fun bufferEncoding(
41+
private fun bufferEncoding(
4542
descriptor: SerialDescriptor,
4643
index: Int,
47-
crossinline encoder: CompositeEncoder.() -> Unit
44+
encoder: () -> Unit
4845
) {
49-
if (callShouldEncodeElementDefault) {
50-
bufferedCalls[mapElementIndex(descriptor, index)] = BufferedCall(index) {
51-
if (shouldEncodeElementDefault(descriptor, index)) {
52-
encoder()
53-
}
54-
}
55-
} else {
56-
bufferedCalls[mapElementIndex(descriptor, index)] = BufferedCall(index) {
57-
encoder()
58-
}
59-
}
60-
callShouldEncodeElementDefault = false
46+
bufferedCalls[mapElementIndex(descriptor, index)] = BufferedCall(index, encoder)
6147
}
6248

6349
override fun endStructure(descriptor: SerialDescriptor) {
6450
encodeBufferedFields(descriptor)
6551
}
6652

6753
private fun encodeBufferedFields(descriptor: SerialDescriptor) {
68-
val classEncoder = beginStructure(descriptor)
69-
bufferedCalls.forEach { fieldToEncode ->
70-
fieldToEncode?.encoder?.invoke(classEncoder)
54+
bufferedCalls.filterNotNull().forEach { fieldToEncode ->
55+
fieldToEncode.encoder()
7156
}
72-
classEncoder.endStructure(descriptor)
57+
compositeEncoderDelegate.endStructure(descriptor)
7358
}
7459

7560
override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) {
76-
bufferEncoding(descriptor, index) { encodeBooleanElement(descriptor, index, value) }
61+
bufferEncoding(descriptor, index) {
62+
compositeEncoderDelegate.encodeBooleanElement(descriptor, index, value)
63+
}
7764
}
7865

7966
override fun encodeByteElement(descriptor: SerialDescriptor, index: Int, value: Byte) {
80-
bufferEncoding(descriptor, index) { encodeByteElement(descriptor, index, value) }
67+
bufferEncoding(descriptor, index) {
68+
compositeEncoderDelegate.encodeByteElement(descriptor, index, value)
69+
}
8170
}
8271

8372
override fun encodeCharElement(descriptor: SerialDescriptor, index: Int, value: Char) {
84-
bufferEncoding(descriptor, index) { encodeCharElement(descriptor, index, value) }
73+
bufferEncoding(descriptor, index) {
74+
compositeEncoderDelegate.encodeCharElement(descriptor, index, value)
75+
}
8576
}
8677

8778
override fun encodeDoubleElement(descriptor: SerialDescriptor, index: Int, value: Double) {
88-
bufferEncoding(descriptor, index) { encodeDoubleElement(descriptor, index, value) }
79+
bufferEncoding(descriptor, index) {
80+
compositeEncoderDelegate.encodeDoubleElement(descriptor, index, value)
81+
}
8982
}
9083

9184
override fun encodeFloatElement(descriptor: SerialDescriptor, index: Int, value: Float) {
92-
bufferEncoding(descriptor, index) { encodeFloatElement(descriptor, index, value) }
85+
bufferEncoding(descriptor, index) {
86+
compositeEncoderDelegate.encodeFloatElement(descriptor, index, value)
87+
}
9388
}
9489

9590
override fun encodeIntElement(descriptor: SerialDescriptor, index: Int, value: Int) {
96-
bufferEncoding(descriptor, index) { encodeIntElement(descriptor, index, value) }
91+
bufferEncoding(descriptor, index) {
92+
compositeEncoderDelegate.encodeIntElement(descriptor, index, value)
93+
}
9794
}
9895

9996
override fun encodeLongElement(descriptor: SerialDescriptor, index: Int, value: Long) {
100-
bufferEncoding(descriptor, index) { encodeLongElement(descriptor, index, value) }
97+
bufferEncoding(descriptor, index) {
98+
compositeEncoderDelegate.encodeLongElement(descriptor, index, value)
99+
}
101100
}
102101

103102
override fun <T : Any> encodeNullableSerializableElement(
@@ -106,7 +105,9 @@ internal class ReorderingCompositeEncoder(
106105
serializer: SerializationStrategy<T>,
107106
value: T?
108107
) {
109-
bufferEncoding(descriptor, index) { encodeNullableSerializableElement(descriptor, index, serializer, value) }
108+
bufferEncoding(descriptor, index) {
109+
compositeEncoderDelegate.encodeNullableSerializableElement(descriptor, index, serializer, value)
110+
}
110111
}
111112

112113
override fun <T> encodeSerializableElement(
@@ -115,39 +116,44 @@ internal class ReorderingCompositeEncoder(
115116
serializer: SerializationStrategy<T>,
116117
value: T
117118
) {
118-
bufferEncoding(descriptor, index) { encodeSerializableElement(descriptor, index, serializer, value) }
119+
bufferEncoding(descriptor, index) {
120+
compositeEncoderDelegate.encodeSerializableElement(descriptor, index, serializer, value)
121+
}
119122
}
120123

121124
override fun encodeShortElement(descriptor: SerialDescriptor, index: Int, value: Short) {
122-
bufferEncoding(descriptor, index) { encodeShortElement(descriptor, index, value) }
125+
bufferEncoding(descriptor, index) {
126+
compositeEncoderDelegate.encodeShortElement(descriptor, index, value)
127+
}
123128
}
124129

125130
override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String) {
126-
bufferEncoding(descriptor, index) { encodeStringElement(descriptor, index, value) }
131+
bufferEncoding(descriptor, index) {
132+
compositeEncoderDelegate.encodeStringElement(descriptor, index, value)
133+
}
127134
}
128135

129136
override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder {
130137
return BufferingInlineEncoder(descriptor, index)
131138
}
132139

133140
override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean {
134-
callShouldEncodeElementDefault = true
135-
return true
141+
return compositeEncoderDelegate.shouldEncodeElementDefault(descriptor, index)
136142
}
137143

138144
private inner class BufferingInlineEncoder(
139145
private val descriptor: SerialDescriptor,
140146
private val elementIndex: Int,
141147
) : Encoder {
142-
private var nullable = false
148+
private var encodeNotNullMarkCalled = false
143149

144150
override val serializersModule: SerializersModule
145151
get() = this@ReorderingCompositeEncoder.serializersModule
146152

147153
private fun bufferEncoding(encoder: Encoder.() -> Unit) {
148154
bufferEncoding(descriptor, elementIndex) {
149155
encodeInlineElement(descriptor, elementIndex).apply {
150-
if (nullable) {
156+
if (encodeNotNullMarkCalled) {
151157
encodeNotNullMark()
152158
}
153159
encoder()
@@ -156,7 +162,7 @@ internal class ReorderingCompositeEncoder(
156162
}
157163

158164
override fun encodeNotNullMark() {
159-
nullable = true
165+
encodeNotNullMarkCalled = true
160166
}
161167

162168
override fun <T : Any> encodeNullableSerializableValue(serializer: SerializationStrategy<T>, value: T?) {
@@ -231,10 +237,10 @@ internal class ReorderingCompositeEncoder(
231237
* Encodes the [structureDescriptor] elements in a specific order provided by [elementIndexMapper].
232238
*
233239
* @param structureDescriptor descriptor of the structure being encoded and reordered
234-
* @param elementIndexMapper maps the element index to the new reordered index (zero-based). If this mapper provides the same index for multiple elements, only the last one will be encoded as the previous ones will be overridden.
240+
* @param elementIndexMapper maps the element index to a new positional zero-based index. If this mapper provides the same index for multiple elements, only the last one will be encoded as the previous ones will be overridden. The mapped index just helps to reorder the elements, but the reordered `encode*Element` method calls will still pass the original element index.
235241
*/
236242
@ExperimentalSerializationApi
237-
public fun CompositeEncoder.reorderElements(
243+
public fun CompositeEncoder.encodeReorderingElements(
238244
structureDescriptor: SerialDescriptor,
239245
elementIndexMapper: (SerialDescriptor, Int) -> Int,
240-
): CompositeEncoder = ReorderingCompositeEncoder(structureDescriptor, { this }, elementIndexMapper)
246+
): CompositeEncoder = ReorderingCompositeEncoder(structureDescriptor, this, elementIndexMapper)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.encoding
6+
7+
import kotlinx.serialization.*
8+
import kotlinx.serialization.descriptors.*
9+
import kotlinx.serialization.modules.*
10+
import kotlin.jvm.*
11+
import kotlin.test.*
12+
13+
class ReorderingCompositeEncoderTest {
14+
@Test
15+
fun shouldReorderWell() {
16+
val first = 42
17+
val second = "string"
18+
val third = true
19+
val value = OuterToBeReordered(Middle(Inner(second, third)), first)
20+
21+
val encoder = ReorderingEncoder()
22+
23+
OuterToBeReordered.serializer().serialize(encoder, value)
24+
25+
assertContentEquals(actual = encoder.encodedValues, expected = listOf(first, second, third))
26+
}
27+
28+
private class ReorderingEncoder(
29+
val encodedValues: MutableList<Any?> = mutableListOf()
30+
) : AbstractEncoder() {
31+
override val serializersModule: SerializersModule
32+
get() = EmptySerializersModule()
33+
34+
override fun encodeValue(value: Any) {
35+
encodedValues.add(value)
36+
}
37+
38+
override fun encodeNull() {
39+
encodedValues.add(null)
40+
}
41+
42+
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
43+
return BasicCompositeEncoder().encodeReorderingElements(descriptor) { _, index ->
44+
// invert fields
45+
if (index == 0) 1 else 0
46+
}
47+
}
48+
49+
private inner class BasicCompositeEncoder : AbstractEncoder() {
50+
override val serializersModule: SerializersModule
51+
get() = EmptySerializersModule()
52+
53+
override fun encodeValue(value: Any) {
54+
encodedValues.add(value)
55+
}
56+
57+
override fun encodeNull() {
58+
encodedValues.add(null)
59+
}
60+
}
61+
}
62+
63+
@Serializable
64+
private data class OuterToBeReordered(val a: Middle, val b: Int)
65+
66+
@JvmInline
67+
@Serializable
68+
private value class Middle(val i: Inner)
69+
70+
@Serializable
71+
private data class Inner(val c: String, val d: Boolean)
72+
}

0 commit comments

Comments
 (0)