Skip to content

Commit 0fb27f2

Browse files
committed
feat(common): decodeBinaryString, allowing ISO8859-1 or Base64
1 parent a28d7c4 commit 0fb27f2

File tree

9 files changed

+73
-20
lines changed

9 files changed

+73
-20
lines changed

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,28 @@ interface Bencode : BinaryFormat {
2323

2424

2525
companion object : Bencode {
26-
private val defaultOptions = BencodeOptionsData()
26+
private val defaultOptions = BencodeConfigureScope0().build()
2727
override val options: BencodeOptions
2828
get() = defaultOptions
2929
}
3030

3131
}
3232

33-
fun Bencode(configure: BencodeConfigureScope.() -> Unit): Bencode {
34-
val scope = object : BencodeConfigureScope {
35-
override var floatStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow
36-
override var doubleStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow
37-
}
38-
scope.configure()
39-
return object : Bencode {
40-
override val options: BencodeOptions = BencodeOptionsData(scope.floatStrategy, scope.doubleStrategy)
41-
}
33+
fun Bencode(configure: BencodeConfigureScope.() -> Unit): Bencode = object : Bencode {
34+
override val options: BencodeOptions = BencodeConfigureScope0().apply(configure).build()
4235
}
4336

4437

4538
interface BencodeConfigureScope {
4639
var floatStrategy: FloatNumberStrategy
4740
var doubleStrategy: FloatNumberStrategy
41+
var binaryStringStrategy: BinaryStringStrategy
4842
}
4943

44+
private class BencodeConfigureScope0 : BencodeConfigureScope {
45+
override var floatStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow
46+
override var doubleStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow
47+
override var binaryStringStrategy: BinaryStringStrategy = BinaryStringStrategy.ISO88591
48+
fun build() = BencodeOptionsData(floatStrategy, doubleStrategy, binaryStringStrategy)
49+
}
5050

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ interface BencodeCompositeDecoder : CompositeDecoder {
77
fun decodeByteArrayElement(descriptor: SerialDescriptor, index: Int): ByteArray
88
fun reportError(message: String, descriptor: SerialDescriptor, index: Int): Nothing =
99
throw BencodeDecodeException(-1, message)
10+
val options: BencodeOptions
1011
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.serialization.encoding.Decoder
44

55
interface BencodeDecoder : Decoder {
66
fun decodeByteArray(): ByteArray
7-
fun decodeStringIso88591(): String
7+
fun decodeBinaryString(strategy: BinaryStringStrategy): String
88
fun reportError(message: String): Nothing = throw BencodeDecodeException(-1, message)
9+
val options: BencodeOptions
910
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import kotlinx.serialization.modules.SerializersModule
1010

1111
@OptIn(ExperimentalSerializationApi::class)
1212
internal class BencodeDecoder0(
13-
private val lexer: Lexer, override val serializersModule: SerializersModule, val options: BencodeOptions
13+
private val lexer: Lexer,
14+
override val serializersModule: SerializersModule,
15+
override val options: BencodeOptions,
1416
) : BencodeDecoder, BencodeCompositeDecoder {
1517

1618
private fun unsupported(kind: String): Nothing = throw BencodeDecodeException(
@@ -68,8 +70,9 @@ internal class BencodeDecoder0(
6870

6971
override fun decodeShortElement(descriptor: SerialDescriptor, index: Int) = el(descriptor, index, ::decodeShort)
7072
override fun decodeStringElement(descriptor: SerialDescriptor, index: Int) = el(descriptor, index) {
71-
if (descriptor.getElementAnnotations(index).any { it is StringInIso88591 }) {
72-
decodeStringIso88591()
73+
val anno = descriptor.binaryStringAnnotation(index)
74+
if (anno != null) {
75+
decodeBinaryString(anno.strategy)
7376
} else {
7477
decodeString()
7578
}
@@ -90,7 +93,8 @@ internal class BencodeDecoder0(
9093
override fun decodeNull(): Nothing? = null
9194
override fun decodeShort(): Short = decodeInt().toShort()
9295
override fun decodeString(): String = lexer.nextBytes().decodeToString()
93-
override fun decodeStringIso88591(): String = bytes2StringIso88591(lexer.nextBytes())
96+
override fun decodeBinaryString(strategy: BinaryStringStrategy): String =
97+
options.binaryStringStrategy.decodeString(strategy)
9498
override fun reportError(message: String): Nothing {
9599
throw BencodeDecodeException(lexer.pos(), message)
96100
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package space.iseki.bencoding
33
interface BencodeOptions {
44
val floatStrategy: FloatNumberStrategy
55
val doubleStrategy: FloatNumberStrategy
6+
val binaryStringStrategy: BinaryStringStrategy
67
}
78

89
internal data class BencodeOptionsData(
9-
override val floatStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow,
10-
override val doubleStrategy: FloatNumberStrategy = FloatNumberStrategy.Disallow,
10+
override val floatStrategy: FloatNumberStrategy,
11+
override val doubleStrategy: FloatNumberStrategy,
12+
override val binaryStringStrategy: BinaryStringStrategy,
1113
) : BencodeOptions
1214

src/commonMain/kotlin/space/iseki/bencoding/StringInIso88591.kt renamed to src/commonMain/kotlin/space/iseki/bencoding/BinaryString.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package space.iseki.bencoding
22

33
import kotlinx.serialization.ExperimentalSerializationApi
44
import kotlinx.serialization.MetaSerializable
5+
import kotlinx.serialization.SerialInfo
56

67
@OptIn(ExperimentalSerializationApi::class)
78
@Target(AnnotationTarget.PROPERTY)
89
@Retention(AnnotationRetention.RUNTIME)
10+
@SerialInfo
911
@MetaSerializable
10-
annotation class StringInIso88591
12+
annotation class BinaryString(val strategy: BinaryStringStrategy = BinaryStringStrategy.Default)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package space.iseki.bencoding
2+
3+
import kotlin.io.encoding.ExperimentalEncodingApi
4+
5+
/**
6+
* Represent the strategy to handle the binary data in [String].
7+
*
8+
* Because the bencoding standard and the serialization framework does not support [ByteArray], we convert it to [String] when decoding/encoding.
9+
* But many platforms haven't a standard way to store binary data in [String], so we provide some strategies to handle it.
10+
*/
11+
enum class BinaryStringStrategy {
12+
/**
13+
* Use ISO-8859-1 to encode/decode the binary data.
14+
*/
15+
ISO88591,
16+
17+
/**
18+
* Use Base64 to encode/decode the binary data.
19+
*/
20+
Base64,
21+
22+
/**
23+
* Use the default strategy that configured in the [BencodeOptions].
24+
*/
25+
Default, ;
26+
27+
context(BencodeDecoder)
28+
@OptIn(ExperimentalEncodingApi::class)
29+
internal fun decodeString(strategy: BinaryStringStrategy): String {
30+
return when (if (strategy == Default) options.binaryStringStrategy else strategy) {
31+
ISO88591, Default -> bytes2StringIso88591(decodeByteArray())
32+
Base64 -> kotlin.io.encoding.Base64.encode(decodeByteArray())
33+
}
34+
}
35+
36+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ enum class FloatNumberStrategy {
2828
/**
2929
* Allow the [Double] and [Float] number, but convert them to decimal-string when encoding/decoding.
3030
*/
31-
DecimalString,
32-
;
31+
DecimalString, ;
3332

3433
context(BencodeDecoder)
3534
internal fun decodeDouble(): Double {
@@ -71,3 +70,4 @@ enum class FloatNumberStrategy {
7170
}
7271
}
7372
}
73+

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
package space.iseki.bencoding
44

5+
import kotlinx.serialization.ExperimentalSerializationApi
6+
import kotlinx.serialization.descriptors.SerialDescriptor
7+
58
internal expect fun bytes2Long(bytes: ByteArray, off: Int, len: Int): Long
69
internal expect fun bytes2StringIso88591(bytes: ByteArray, off: Int = 0, len: Int = bytes.size): String
710
internal expect fun string2BytesIso88591(s: String): ByteArray
11+
12+
@OptIn(ExperimentalSerializationApi::class)
13+
internal fun SerialDescriptor.binaryStringAnnotation(childIndex: Int) =
14+
getElementAnnotations(childIndex).firstOrNull { it is BinaryString } as BinaryString?

0 commit comments

Comments
 (0)