Skip to content

Commit 5dd8e63

Browse files
committed
feat: decoder
1 parent a2328a0 commit 5dd8e63

File tree

9 files changed

+249
-114
lines changed

9 files changed

+249
-114
lines changed

buildSrc/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ dependencies {
1212
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21")
1313
implementation("org.jetbrains.kotlin:kotlin-serialization:1.8.21")
1414
implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.8.10")
15+
implementation("org.jetbrains.kotlinx:kover-gradle-plugin:0.7.1")
1516
}

buildSrc/src/main/kotlin/kmm-convention.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2-
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
32

43
plugins {
54
kotlin("multiplatform")
65
kotlin("plugin.serialization")
76
idea
7+
id("org.jetbrains.kotlinx.kover")
88
}
99

1010
repositories {

src/commonMain/kotlin/space/iseki/bencoding/BencodingDecoder.kt

Lines changed: 0 additions & 73 deletions
This file was deleted.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package space.iseki.bencoding
2+
3+
import kotlinx.serialization.DeserializationStrategy
4+
import kotlinx.serialization.ExperimentalSerializationApi
5+
import kotlinx.serialization.descriptors.SerialDescriptor
6+
import kotlinx.serialization.encoding.CompositeDecoder
7+
import kotlinx.serialization.encoding.Decoder
8+
import kotlinx.serialization.modules.EmptySerializersModule
9+
import kotlinx.serialization.modules.SerializersModule
10+
11+
internal class BencodingDecoderImpl(private val input: I) : DecoderHelper {
12+
override fun decodeBytes(): ByteArray {
13+
return input.readText()
14+
}
15+
16+
override fun decodeNumber(): Long {
17+
return input.readNumber()
18+
}
19+
20+
@OptIn(ExperimentalSerializationApi::class)
21+
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
22+
return when (val s = input.lookahead()) {
23+
Symbol.Dict -> {
24+
input.skip()
25+
object : CompositeDecoderHelper, DecoderHelper by this {
26+
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
27+
do {
28+
when (val s = input.lookahead()) {
29+
Symbol.Text -> {
30+
val idx = descriptor.getElementIndex(decodeString())
31+
if (idx == CompositeDecoder.UNKNOWN_NAME) {
32+
skipCurrentValue()
33+
} else {
34+
return idx
35+
}
36+
}
37+
38+
Symbol.End -> return CompositeDecoder.DECODE_DONE
39+
else -> throw BencodingSerializationException("expect a string(dict key), get $s at ${input.pos}")
40+
}
41+
} while (true)
42+
}
43+
44+
override fun endStructure(descriptor: SerialDescriptor) {
45+
skipCurrentStructure()
46+
}
47+
48+
}
49+
}
50+
51+
Symbol.List -> {
52+
input.skip()
53+
object : CompositeDecoderHelper, DecoderHelper by this {
54+
var i = 0
55+
56+
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
57+
return when (input.lookahead()) {
58+
Symbol.End -> CompositeDecoder.DECODE_DONE
59+
Symbol.EOF -> throw BencodingSerializationException("EOF when decode list")
60+
else -> i++
61+
62+
}
63+
}
64+
65+
override fun endStructure(descriptor: SerialDescriptor) {
66+
skipCurrentStructure()
67+
}
68+
}
69+
}
70+
71+
else -> throw BencodingSerializationException("expect a structured token, get $s at ${input.pos}")
72+
}
73+
}
74+
75+
private fun skipCurrentValue() {
76+
var counter = 0
77+
do {
78+
when (input.lookahead()) {
79+
Symbol.End -> counter--
80+
Symbol.Dict, Symbol.List -> counter++
81+
Symbol.EOF -> break
82+
else -> Unit
83+
}
84+
input.skip()
85+
} while (counter > 0)
86+
}
87+
88+
private fun skipCurrentStructure() {
89+
var counter = 1
90+
while (counter > 0) {
91+
when (input.lookahead()) {
92+
Symbol.End -> counter--
93+
Symbol.Dict, Symbol.List -> counter++
94+
Symbol.EOF -> break
95+
else -> Unit
96+
}
97+
input.skip()
98+
}
99+
}
100+
}
101+
102+
interface BencodingDecoder {
103+
fun decodeBytes(): ByteArray
104+
fun decodeNumber(): Long
105+
}
106+
107+
private interface DecoderHelper : Decoder, BencodingDecoder {
108+
override fun decodeBytes(): ByteArray
109+
override fun decodeNumber(): Long
110+
111+
override val serializersModule: SerializersModule
112+
get() = EmptySerializersModule()
113+
114+
override fun decodeString(): String = decodeBytes().decodeToString()
115+
override fun decodeBoolean(): Boolean = decodeString().toBooleanStrict()
116+
override fun decodeByte(): Byte = decodeNumber().toByte()
117+
override fun decodeChar(): Char = decodeNumber().toInt().toChar()
118+
override fun decodeDouble(): Double = decodeNumber().toDouble()
119+
override fun decodeFloat(): Float = decodeNumber().toFloat()
120+
override fun decodeInt(): Int = decodeNumber().toInt()
121+
override fun decodeLong(): Long = decodeNumber()
122+
override fun decodeShort(): Short = decodeNumber().toShort()
123+
override fun decodeInline(descriptor: SerialDescriptor): Decoder = this
124+
125+
@ExperimentalSerializationApi
126+
override fun decodeNull(): Nothing? = throw UnsupportedOperationException("decode null is not supported")
127+
128+
@ExperimentalSerializationApi
129+
override fun decodeNotNullMark(): Boolean = false
130+
131+
@ExperimentalSerializationApi
132+
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? =
133+
deserializer.deserialize(this)
134+
135+
override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T =
136+
deserializer.deserialize(this)
137+
138+
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
139+
throw UnsupportedOperationException("decode enum is not supported")
140+
}
141+
142+
private interface CompositeDecoderHelper : CompositeDecoder, Decoder {
143+
override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = decodeBoolean()
144+
override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte = decodeByte()
145+
override fun decodeCharElement(descriptor: SerialDescriptor, index: Int): Char = decodeChar()
146+
override fun decodeDoubleElement(descriptor: SerialDescriptor, index: Int): Double = decodeDouble()
147+
override fun decodeFloatElement(descriptor: SerialDescriptor, index: Int): Float = decodeFloat()
148+
override fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder = decodeInline(descriptor)
149+
override fun decodeIntElement(descriptor: SerialDescriptor, index: Int): Int = decodeInt()
150+
override fun decodeLongElement(descriptor: SerialDescriptor, index: Int): Long = decodeLong()
151+
override fun decodeShortElement(descriptor: SerialDescriptor, index: Int): Short = decodeShort()
152+
override fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String = decodeString()
153+
154+
@ExperimentalSerializationApi
155+
override fun <T : Any> decodeNullableSerializableElement(
156+
descriptor: SerialDescriptor,
157+
index: Int,
158+
deserializer: DeserializationStrategy<T?>,
159+
previousValue: T?
160+
): T? = deserializer.deserialize(this)
161+
162+
override fun <T> decodeSerializableElement(
163+
descriptor: SerialDescriptor,
164+
index: Int,
165+
deserializer: DeserializationStrategy<T>,
166+
previousValue: T?
167+
): T = deserializer.deserialize(this)
168+
}
169+
170+

src/commonMain/kotlin/space/iseki/bencoding/CompositeDecoderHelper.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/commonMain/kotlin/space/iseki/bencoding/IO.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package space.iseki.bencoding
22

33
internal enum class Symbol {
4-
List, Dict, Integer, Text, End,EOF
4+
List,
5+
Dict,
6+
Integer,
7+
Text,
8+
End,
9+
EOF,
510
}
611

712
internal interface I {
13+
val pos: Int
814
fun lookahead(): Symbol
915
fun readText(): ByteArray
1016
fun readNumber(): Long

src/jvmMain/kotlin/space/iseki/bencoding/InputStreamInput.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package space.iseki.bencoding
22

3+
import kotlinx.serialization.KSerializer
4+
import kotlinx.serialization.serializer
35
import java.io.InputStream
46

57
private const val UNINITIALIZED = -2
68
private const val EOF = -1
79

810
internal class InputStreamI(private val inputStream: InputStream) : I {
11+
override var pos: Int = 0
12+
private set
913

1014
override fun lookahead(): Symbol = when (la()) {
1115
'l'.code -> Symbol.List
@@ -17,7 +21,7 @@ internal class InputStreamI(private val inputStream: InputStream) : I {
1721
else -> unrecognizedInput()
1822
}
1923

20-
override fun readText(): ByteArray = inputStream.readNBytes(readLength())
24+
override fun readText(): ByteArray = readLength().let { n -> inputStream.readNBytes(n).also { pos += n } }
2125

2226
override fun readNumber(): Long {
2327
var n = 0L
@@ -45,20 +49,20 @@ internal class InputStreamI(private val inputStream: InputStream) : I {
4549
when (lookahead()) {
4650
Symbol.EOF -> return
4751
Symbol.Dict, Symbol.List, Symbol.End -> read()
48-
Symbol.Text -> inputStream.skipNBytes(readLength().toLong())
52+
Symbol.Text -> readLength().let { n -> inputStream.skipNBytes(n.toLong()); pos += n }
4953
Symbol.Integer -> readNumber()
5054
}
5155
}
5256

5357
private var _la = UNINITIALIZED
5458

5559
private fun la() = when (_la) {
56-
UNINITIALIZED -> inputStream.read().also { _la = it }
60+
UNINITIALIZED -> inputStream.read().also { _la = it;pos++ }
5761
else -> _la
5862
}
5963

6064
private fun read() = when (_la) {
61-
UNINITIALIZED -> inputStream.read()
65+
UNINITIALIZED -> inputStream.read().also { pos++ }
6266
else -> _la.also { _la = UNINITIALIZED }
6367
}
6468

@@ -80,7 +84,12 @@ internal class InputStreamI(private val inputStream: InputStream) : I {
8084
else -> "unrecognized input, during $during"
8185
}.let { throw BencodingSerializationException(it) }
8286

87+
@Suppress("SameParameterValue")
8388
private fun error(msg: String): Nothing = throw BencodingSerializationException(msg)
8489

8590
}
8691

92+
inline fun <reified T> InputStream.decodeInBencoding() = decodeInBencoding(serializer<T>())
93+
94+
fun <T> InputStream.decodeInBencoding(serializer: KSerializer<T>) =
95+
BencodingDecoderImpl(InputStreamI(this)).decodeSerializableValue(serializer)

0 commit comments

Comments
 (0)