Skip to content

Commit

Permalink
Merge pull request #57 from Peanuuutz/dev
Browse files Browse the repository at this point in the history
0.3.6 Add linuxArm64 target, complete TomlWriter, make TomlTable pretty-printable.
  • Loading branch information
Peanuuutz authored Oct 12, 2023
2 parents 4f68cc1 + eb38a41 commit 1f54b9f
Show file tree
Hide file tree
Showing 18 changed files with 600 additions and 112 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repositories {
}

dependencies {
implementation("net.peanuuutz.tomlkt:tomlkt:0.3.5")
implementation("net.peanuuutz.tomlkt:tomlkt:0.3.6")
}
```
</details>
Expand All @@ -33,7 +33,7 @@ repositories {
}
dependencies {
implementation "net.peanuuutz.tomlkt:tomlkt:0.3.5"
implementation "net.peanuuutz.tomlkt:tomlkt:0.3.6"
}
```
</details>
Expand All @@ -45,7 +45,7 @@ dependencies {
<dependency>
<groupId>net.peanuuutz.tomlkt</groupId>
<artifactId>tomlkt-jvm</artifactId>
<version>0.3.5</version>
<version>0.3.6</version>
</dependency>
```
</details>
Expand Down
5 changes: 5 additions & 0 deletions benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ dependencies {
jmh("com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.1")
jmh("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.1")
jmh("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.1")
// night config
jmh("com.electronwill.night-config:toml:3.6.0")
// tomlj
jmh("org.tomlj:tomlj:1.1.0")

// official JSON
val serializationVersion: String by rootProject
jmh("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
Expand Down
45 changes: 19 additions & 26 deletions benchmark/src/jmh/kotlin/test/Benchmark.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package test

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import kotlinx.serialization.decodeFromString
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Fork
Expand All @@ -25,17 +23,17 @@ object TomlObjects {
registerKotlinModule()
registerModule(JavaTimeModule())
}
val night = com.electronwill.nightconfig.toml.TomlParser()
}

/*
Benchmark Mode Cnt Score Error Units
Benchmark.jacksonWithType avgt 10 7753.556 ± 104.444 ns/op
Benchmark.jacksonWithClass avgt 10 7803.826 ± 312.212 ns/op
Benchmark.tomlktWithSerializer avgt 10 13638.573 ± 203.072 ns/op
Benchmark.tomlktWithoutSerializer avgt 10 13723.892 ± 320.052 ns/op
Benchmark.toml4j avgt 10 21123.156 ± 209.969 ns/op
Benchmark.ktomlWithSerializer avgt 10 47823.952 ± 722.429 ns/op
Benchmark.ktomlWithoutSerializer avgt 10 48057.838 ± 1090.022 ns/op
Benchmark Mode Cnt Score Error Units
Benchmark.jackson avgt 5 5175.377 ± 156.576 ns/op
Benchmark.night avgt 5 7278.401 ± 257.462 ns/op
Benchmark.tomlkt avgt 5 9494.927 ± 271.427 ns/op
Benchmark.toml4j avgt 5 15858.802 ± 374.109 ns/op
Benchmark.ktoml avgt 5 43655.068 ± 8584.812 ns/op
Benchmark.tomlj avgt 5 92314.157 ± 4917.454 ns/op
*/
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 4)
Expand All @@ -46,37 +44,32 @@ object TomlObjects {
@State(Scope.Thread)
class Benchmark {
@Benchmark
fun tomlktWithoutSerializer() {
TomlObjects.tomlkt.decodeFromString<Config>(SampleConfig)
}

@Benchmark
fun tomlktWithSerializer() {
TomlObjects.tomlkt.decodeFromString(Config.serializer(), SampleConfig)
fun tomlkt() {
TomlObjects.tomlkt.parseToTomlTable(SampleConfig)
}

@Benchmark
fun toml4j() {
TomlObjects.toml4j.read(SampleConfig).to(Config::class.java)
TomlObjects.toml4j.read(SampleConfig)
}

@Benchmark
fun ktomlWithoutSerializer() {
TomlObjects.ktoml.decodeFromString<Config>(SampleConfig)
fun ktoml() {
TomlObjects.ktoml.tomlParser.parseString(SampleConfig)
}

@Benchmark
fun ktomlWithSerializer() {
TomlObjects.ktoml.decodeFromString(Config.serializer(), SampleConfig)
fun jackson() {
TomlObjects.jackson.readTree(SampleConfig)
}

@Benchmark
fun jacksonWithClass() {
TomlObjects.jackson.readValue(SampleConfig, Config::class.java)
fun night() {
TomlObjects.night.parse(SampleConfig)
}

@Benchmark
fun jacksonWithType() {
TomlObjects.jackson.readValue<Config>(SampleConfig)
fun tomlj() {
org.tomlj.Toml.parse(SampleConfig)
}
}
5 changes: 5 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ kotlin {
macosArm64()
macosX64()
ios()
linuxArm64()
linuxX64()

sourceSets {
Expand Down Expand Up @@ -110,6 +111,10 @@ kotlin {
dependsOn(kotlinxMain)
}

val linuxArm64Main by getting {
dependsOn(kotlinxMain)
}

val linuxX64Main by getting {
dependsOn(kotlinxMain)
}
Expand Down
10 changes: 8 additions & 2 deletions core/src/commonMain/kotlin/net/peanuuutz/tomlkt/TomlDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ import kotlin.contracts.contract

/**
* A special [Decoder] which is used internally by decoding process of [Toml],
* providing an extra [decodeTomlElement] to decode [TomlElement] from the
* current position.
* providing an extra [decodeTomlElement] to decode [TomlElement] from current
* position.
*/
@SubclassOptInRequired(TomlSpecific::class)
public interface TomlDecoder : Decoder {
/**
* The [Toml] instance used to create this decoder.
*/
public val toml: Toml

/**
* Decodes [TomlElement] from current position.
*/
public fun decodeTomlElement(): TomlElement
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ import kotlin.contracts.contract

/**
* A special [Encoder] which is used internally by encoding process of [Toml],
* providing an extra [encodeTomlElement] to encode [TomlElement] to the current
* providing an extra [encodeTomlElement] to encode [TomlElement] to current
* position.
*/
@SubclassOptInRequired(TomlSpecific::class)
public interface TomlEncoder : Encoder {
/**
* The [Toml] instance used to create this encoder.
*/
public val toml: Toml

/**
* Encodes [value] to current position.
*/
public fun encodeTomlElement(value: TomlElement)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import kotlinx.serialization.descriptors.StructureKind.OBJECT
import kotlinx.serialization.descriptors.getContextualDescriptor
import net.peanuuutz.tomlkt.TomlConfig

private const val TomlTableSerialName: String = "net.peanuuutz.tomlkt.TomlTable"

internal val SerialDescriptor.isPrimitiveLike: Boolean
get() {
val kind = kind
Expand Down Expand Up @@ -60,12 +58,10 @@ internal val SerialDescriptor.isArrayOfTable: Boolean
internal val SerialDescriptor.isTableLike: Boolean
get() = isTable || isArrayOfTable

internal val SerialDescriptor.isExactlyTomlTable: Boolean
internal val SerialDescriptor.isTomlElement: Boolean
get() {
if (!isNullable) {
return false
}
return serialName.removeSuffix("?") == TomlTableSerialName
val actualSerialName = if (!isNullable) serialName else serialName.removeSuffix("?")
return actualSerialName == TomlElementSerialName
}

internal fun SerialDescriptor.findRealDescriptor(config: TomlConfig): SerialDescriptor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import net.peanuuutz.tomlkt.TomlInteger.Base
import net.peanuuutz.tomlkt.TomlInteger.Base.Dec
import kotlin.math.pow

internal typealias Path = List<String>

internal typealias MutablePath = MutableList<String>

internal const val Comment = '#'

internal const val KeySeparator = '.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ import net.peanuuutz.tomlkt.asTomlLiteral
import net.peanuuutz.tomlkt.asTomlNull
import net.peanuuutz.tomlkt.asTomlTable

// -------- TomlElementSerializer --------

internal const val TomlElementSerialName: String = "net.peanuuutz.tomlkt.TomlElement"

internal object TomlElementSerializer : KSerializer<TomlElement> {
override val descriptor: SerialDescriptor = buildSerialDescriptor(
serialName = "net.peanuuutz.tomlkt.TomlElement",
serialName = TomlElementSerialName,
kind = SerialKind.CONTEXTUAL
)

Expand All @@ -57,6 +61,8 @@ internal object TomlElementSerializer : KSerializer<TomlElement> {
}
}

// -------- TomlNullSerializer --------

internal object TomlNullSerializer : KSerializer<TomlNull> {
override val descriptor: SerialDescriptor = buildSerialDescriptor(
serialName = "net.peanuuutz.tomlkt.TomlNull",
Expand All @@ -72,6 +78,8 @@ internal object TomlNullSerializer : KSerializer<TomlNull> {
}
}

// -------- TomlLiteralSerializer --------

internal object TomlLiteralSerializer : KSerializer<TomlLiteral> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
serialName = "net.peanuuutz.tomlkt.TomlLiteral",
Expand All @@ -87,6 +95,8 @@ internal object TomlLiteralSerializer : KSerializer<TomlLiteral> {
}
}

// -------- TomlArraySerializer --------

internal object TomlArraySerializer : KSerializer<TomlArray> {
private val delegate: KSerializer<List<TomlElement>> = ListSerializer(
elementSerializer = TomlElement.serializer()
Expand All @@ -105,6 +115,8 @@ internal object TomlArraySerializer : KSerializer<TomlArray> {
}
}

// -------- TomlTableSerializer --------

internal object TomlTableSerializer : KSerializer<TomlTable> {
private val delegate: KSerializer<Map<String, TomlElement>> = MapSerializer(
keySerializer = String.serializer(),
Expand All @@ -116,10 +128,37 @@ internal object TomlTableSerializer : KSerializer<TomlTable> {
}

override fun serialize(encoder: Encoder, value: TomlTable) {
delegate.serialize(encoder.asTomlEncoder(), value)
// We sort the entries in a way that nested tables are at the end.
delegate.serialize(encoder.asTomlEncoder(), value.sorted())
}

override fun deserialize(decoder: Decoder): TomlTable {
return decoder.asTomlDecoder().decodeTomlElement().asTomlTable()
}
}

private val TomlTableEntrySorter: Comparator<Map.Entry<String, TomlElement>> = compareBy { (_, element) ->
when (element) {
is TomlNull, is TomlLiteral -> 0
is TomlArray -> 0
is TomlTable -> 1
}
}

private fun TomlTable.sorted(): Map<String, TomlElement> {
fun <K, V> Array<out Map.Entry<K, V>>.toMap(): Map<K, V> {
val map = LinkedHashMap<K, V>(size)
for ((k, v) in this) {
map[k] = v
}
return map
}

return if (size > 1) {
val entries = entries.toTypedArray()
entries.sortWith(TomlTableEntrySorter)
entries.toMap()
} else {
this
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package net.peanuuutz.tomlkt.internal

import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialKind
import net.peanuuutz.tomlkt.internal.parser.Path
import kotlin.reflect.KClass

// -------- Encoding --------
Expand Down Expand Up @@ -67,9 +66,10 @@ internal fun throwPolymorphicCollection(): Nothing {

internal class NullInArrayOfTableException(message: String) : TomlEncodingException(message)

internal fun throwNullInArrayOfTable(path: String): Nothing {
internal fun throwNullInArrayOfTable(path: Path): Nothing {
val pathString = path.joinToString(separator = ".")
val message = "Null is not allowed in array of table. Please annotate the corresponding property " +
"(at $path) with @TomlBlockArray or @TomlInline"
"(at $pathString) with @TomlBlockArray or @TomlInline"
throw NullInArrayOfTableException(message)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ private val UnsignedIntegerDescriptors: Set<SerialDescriptor> = setOf(
)

internal val SerialDescriptor.isUnsignedInteger: Boolean
get() = this.isInline && this in UnsignedIntegerDescriptors
get() = isInline && this in UnsignedIntegerDescriptors
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.internal.AbstractPolymorphicSerializer
import kotlinx.serialization.modules.SerializersModule
import net.peanuuutz.tomlkt.Toml
import net.peanuuutz.tomlkt.TomlArray
import net.peanuuutz.tomlkt.TomlElement
import net.peanuuutz.tomlkt.TomlEncoder
import net.peanuuutz.tomlkt.TomlNull
import net.peanuuutz.tomlkt.TomlTable
import net.peanuuutz.tomlkt.internal.encodePolymorphically
import net.peanuuutz.tomlkt.internal.findRealDescriptor
import net.peanuuutz.tomlkt.internal.isPrimitiveLike
Expand All @@ -52,6 +54,9 @@ internal abstract class AbstractTomlEncoder(
internal val Any?.isNullLike: Boolean
get() = this == null || this == TomlNull

internal val Any?.isTomlCollection: Boolean
get() = this is TomlArray || this is TomlTable

internal fun <T> AbstractTomlEncoder.encodeSerializableValuePolymorphically(
serializer: SerializationStrategy<T>,
value: T
Expand Down
Loading

0 comments on commit 1f54b9f

Please sign in to comment.