Skip to content

Commit

Permalink
Add Serialization Configuration for Char and Upgrade Kotlin (#75)
Browse files Browse the repository at this point in the history
* fix: Wrong serialization of strings containing commas (#68)

* feat: The configuring of char serialization

feat: Add char serialization using Unicode code points
fix: Correct serialization for chars with special characters

* fix: switch JS backend from BOTH to IR

* feat: Upgrade Kotlin to 2.0.0

Also:
Updated kotlinx.serialization to 1.7.0 for compatibility

* refactor: use `contextualDecodingException` when failing to decode a char

* refactor: renamed elements of CharSerialization

* test: add test for char serialization
  • Loading branch information
HatoYuze authored Feb 10, 2025
1 parent 5fa3c52 commit c2ec872
Show file tree
Hide file tree
Showing 16 changed files with 235 additions and 164 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.idea/
.gradle/
.kotlin/
build/
.idea_modules/
hs_err_pid*
*.hprof
local.properties

25 changes: 13 additions & 12 deletions benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
java
kotlin("jvm")
kotlin("kapt")
kotlin("plugin.serialization")
id("me.champeau.gradle.jmh")
id("me.champeau.jmh")
}
apply(plugin = "me.champeau.gradle.jmh")
apply(plugin = "me.champeau.jmh")

dependencies {
api(kotlin("stdlib-jdk8"))
api("org.openjdk.jmh:jmh-core:1.23")
api("org.openjdk.jmh:jmh-generator-annprocess:1.21")
api("org.openjdk.jmh:jmh-core:1.37")
api("org.openjdk.jmh:jmh-generator-annprocess:1.37")
api(project(":yamlkt"))
api(kotlinx("serialization-core", Versions.serialization))
api(kotlinx("serialization-json", Versions.serialization))
kapt("org.openjdk.jmh:jmh-generator-annprocess:1.21")
kapt("org.openjdk.jmh:jmh-generator-annprocess:1.37")
api("com.charleskorn.kaml:kaml:0.17.0")
api("org.yaml:snakeyaml:1.26")
api("com.google.code.gson:gson:2.8.6")
api("com.alibaba:fastjson:1.2.75")
api("com.google.code.gson:gson:2.11.0")
api("com.alibaba:fastjson:1.2.83") // Next major: 2.0.51
}


group = ""

jmh {
include = listOf("DeserializingTest")
includes.set(listOf("DeserializingTest"))
}

val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
compileKotlin.compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
compileTestKotlin.compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
}
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
kotlin("multiplatform") version Versions.kotlin apply false
kotlin("plugin.serialization") version Versions.kotlin apply false

id("me.champeau.gradle.jmh") version "0.5.3" apply false
id("me.champeau.jmh") version "0.7.2" apply false
}

allprojects {
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
object Versions {
const val version = "0.13.0"

const val kotlin = "1.8.0"
const val serialization = "1.5.0"
const val kotlin = "2.0.0"
const val serialization = "1.7.0"

const val mavenCentralPublish = "1.0.0-dev-3"
}
Expand Down
5 changes: 2 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# style guide
kotlin.code.style=official
kotlin.incremental.multiplatform=true
kotlin.js.compiler=both
kotlin.native.ignoreDisabledTargets=true
systemProp.org.gradle.internal.publish.checksums.insecure=true
org.gradle.vfs.watch=true

kotlin.mpp.enableCompatibilityMetadataVariant=true
kotlin.mpp.enableCInteropCommonization=true
org.gradle.jvmargs=-Xmx2g "-XX:MaxMetaspaceSize=2g"
kotlin.mpp.enableCInteropCommonization=true
5 changes: 5 additions & 0 deletions karma/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
config.client = config.client || {}
config.client.mocha = config.client.mocha || {}
config.client.mocha.timeout = '30s'
config.browserNoActivityTimeout = 30000
config.browserDisconnectTimeout = 30000
174 changes: 37 additions & 137 deletions yamlkt/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
@file:Suppress("UNUSED_VARIABLE")

import org.apache.tools.ant.taskdefs.condition.Os
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet

plugins {
id("me.him188.maven-central-publish")
kotlin("multiplatform")
Expand All @@ -13,129 +8,47 @@ plugins {
kotlin {
explicitApi()

targets {
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
}
js(BOTH) {
compilations.all {
kotlinOptions {
moduleKind = "umd"
sourceMap = true
metaInfo = true
jvmToolchain(8)
jvm()
js {
browser {
testTask {
useKarma {
useChromeHeadless()
useConfigDirectory(rootDir.resolve("karma"))
}
}
browser()
nodejs()
}


val ideaActive = System.getProperty("idea.active") == "true" && System.getProperty("publication.test") != "true"

val nativeMainSets = mutableListOf<KotlinSourceSet>()
val nativeTestSets = mutableListOf<KotlinSourceSet>()

if (ideaActive) {
when {
Os.isFamily(Os.FAMILY_MAC) -> if (Os.isArch("aarch64")) macosArm64("native") else macosX64("native")
Os.isFamily(Os.FAMILY_WINDOWS) -> mingwX64("native")
else -> linuxX64("native")
}
} else {
// https://kotlinlang.org/docs/native-target-support.html
// Updated for Kotlin 1.8.0, serialization 1.5.0
//kotlinx-serialization-core-iosarm32/ - -
//kotlinx-serialization-core-iosarm64/ - -
//kotlinx-serialization-core-iossimulatorarm64/ - -
//kotlinx-serialization-core-iosx64/ - -
//kotlinx-serialization-core-js/ - -
//kotlinx-serialization-core-jvm/ - -
//kotlinx-serialization-core-linuxarm32hfp/ - -
//kotlinx-serialization-core-linuxarm64/ - -
//kotlinx-serialization-core-linuxx64/ - -
//kotlinx-serialization-core-macosarm64/ - -
//kotlinx-serialization-core-macosx64/ - -
//kotlinx-serialization-core-metadata/ - -
//kotlinx-serialization-core-mingwx64/ - -
//kotlinx-serialization-core-mingwx86/ - -
//kotlinx-serialization-core-tvosarm64/ - -
//kotlinx-serialization-core-tvossimulatorarm64/ - -
//kotlinx-serialization-core-tvosx64/ - -
//kotlinx-serialization-core-watchosarm32/ - -
//kotlinx-serialization-core-watchosarm64/ - -
//kotlinx-serialization-core-watchossimulatora.../ - -
//kotlinx-serialization-core-watchosx64/ - -
//kotlinx-serialization-core-watchosx86/
// Commented ones are not supported by kotlinx-coroutines-core
val nativeTargets: List<String> = arrayOf(
// Tier 1:
"linuxX64",
"macosX64",
"macosArm64",
"iosSimulatorArm64",
"iosX64",

// Tier 2:
"linuxArm64",
// "watchosSimulatorArm64",
"watchosX64w",
"wwatchosArm32",
"watchosArm64",
"tvosSimulatorArm64",
"tvosX64",
"tvosArm64",
"iosArm64",

// Tier 3:
// "androidNativeArm32",
// "androidNativeArm64",
// "androidNativeX86",
// "androidNativeX64",
"mingwX64",
// "watchosDeviceArm64",

// Deprecated:
"iosArm32",
"watchosX86",
// "wasm32",
"mingwX86",
"linuxArm32Hfp",
// "linuxMips32",
// "linuxMipsel32",
).flatMap { it.split(", ") }
presets.filter { it.name in nativeTargets }
.forEach { preset ->
val target = targetFromPreset(preset, preset.name)
nativeMainSets.add(target.compilations[org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.MAIN_COMPILATION_NAME].kotlinSourceSets.first())
nativeTestSets.add(target.compilations[org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.TEST_COMPILATION_NAME].kotlinSourceSets.first())
}

sourceSets {
if (!ideaActive) {
configure(nativeMainSets) {
dependsOn(sourceSets.maybeCreate("nativeMain"))
}

configure(nativeTestSets) {
dependsOn(sourceSets.maybeCreate("nativeTest"))
}
}
}
}

/*
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}*/
}

// https://kotlinlang.org/docs/native-target-support.html
// Updated for Kotlin 2.0.0, serialization 1.7.0
// Commented ones are not supported by kotlinx-coroutines-core

// Tier 1:
macosX64()
macosArm64()
iosSimulatorArm64()
iosX64()

// Tier 2:
linuxX64()
linuxArm64()
watchosSimulatorArm64()
watchosX64()
watchosArm32()
watchosArm64()
tvosSimulatorArm64()
tvosX64()
tvosArm64()
iosArm64()

// Tier 3:
androidNativeArm32()
androidNativeArm64()
androidNativeX86()
androidNativeX64()

sourceSets {
val serializationVersion: String = Versions.serialization

Expand Down Expand Up @@ -166,30 +79,17 @@ kotlin {
api(kotlin("reflect"))
}
}

val jvmMain by getting
val jvmTest by getting {
dependencies {
api(kotlin("test-junit"))
api("com.charleskorn.kaml:kaml:0.34.0")
api("org.yaml:snakeyaml:1.26")
}
}

val jsMain by getting
val jsTest by getting

val nativeMain by getting {
dependsOn(commonMain)
}

val nativeTest by getting {
dependsOn(commonTest)
}
}
}

mavenCentralPublish {
singleDevGithubProject("Him188", "yamlkt")
licenseFromGitHubProject("Apache-2.0", "master")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ public class YamlBuilder internal constructor(
@JvmField
public var stringSerialization: StringSerialization = conf.stringSerialization

/**
* Configure how to serialize chars
* */
@JvmField
public var charSerialization: CharSerialization = conf.charSerialization

/**
* The value set for `null` serialization.
* Default: serialize `null` as "null"
Expand Down Expand Up @@ -80,6 +86,42 @@ public class YamlBuilder internal constructor(
@JvmField
public var listSerialization: ListSerialization = conf.listSerialization

/**
* The suggested format for [Char] serialization.
*
* [Char] isn't always serialized in this format, depending on the content.
*
* Some escape sequences of special characters will be processed as escaped characters _(such as '\n')_
* */
public enum class CharSerialization {
/**
* Quote all [Char]s with `'`
*
* If a value can't be serialized using single quotation, it will use [DOUBLE_QUOTATION]
*/
SINGLE_QUOTATION,

/**
* Quote all [Char]s with `"`
*/
DOUBLE_QUOTATION,

/**
* Encode [Char]s as their [code][Char.code] in integer.
* _(It will work like [Byte])_
*
* For example, the character 'A' will be converted to 65
*/
UNICODE_CODE,

/**
* Don't quote any [Char].
*
* When escaping is necessary, it defaults to using [SINGLE_QUOTATION]
*/
PLAIN,
}


/**
* The suggested format for [String] serialization.
Expand Down Expand Up @@ -213,6 +255,7 @@ public class YamlBuilder internal constructor(
nonStrictNumber,
encodeDefaultValues,
stringSerialization,
charSerialization,
nullSerialization,
mapSerialization,
classSerialization,
Expand All @@ -229,6 +272,7 @@ internal class YamlConfigurationInternal internal constructor(
// encoding
@JvmField val encodeDefaultValues: Boolean = true,
@JvmField val stringSerialization: YamlBuilder.StringSerialization = YamlBuilder.StringSerialization.NONE,
@JvmField val charSerialization: YamlBuilder.CharSerialization = YamlBuilder.CharSerialization.PLAIN,
@JvmField val nullSerialization: YamlBuilder.NullSerialization = YamlBuilder.NullSerialization.NULL,
@JvmField val mapSerialization: MapSerialization = MapSerialization.BLOCK_MAP,
@JvmField val classSerialization: MapSerialization = MapSerialization.BLOCK_MAP,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ internal object BinaryConverter {
}
}

internal fun Long.limitToChar(): Char {
if (this in Char.MIN_VALUE.code.toLong()..Char.MAX_VALUE.code.toLong()) return toInt().toChar()
error("value is too large for byte: $this")
}

internal fun Long.limitToByte(): Byte {
if (this in Byte.MIN_VALUE.toLong()..Byte.MAX_VALUE.toLong()) return this.toByte()
Expand Down
Loading

0 comments on commit c2ec872

Please sign in to comment.