diff --git a/CHANGELOG.md b/CHANGELOG.md
index 216d595784..19934a5032 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+1.9.0 / 2025-06-27
+==================
+
+This release updates Kotlin version to 2.2.0, includes several bugfixes and provides serializers for kotlin.time.Instant.
+
+## Add kotlin.time.Instant serializers
+
+Instant class was moved from kotlinx-datetime library to Kotlin standard library.
+As a result, kotlinx-datetime 0.7.0 no longer has serializers for the Instant class.
+To use new kotlin.time.Instant class in your @Serializable classes,
+you can use this 1.9.0 kotlinx-serialization version (Kotlin 2.2 is required).
+You can choose between default `InstantSerializer` which uses its string representation,
+or specify `InstantComponentSerializer` that represents instant as its components.
+See details in the [PR](https://github.com/Kotlin/kotlinx.serialization/pull/2945).
+
+## Other bugfixes
+ * Fix resize in JsonPath (#2995)
+ * Fixed proguard rules for obfuscation to work correctly (#2983)
+
1.8.1 / 2025-03-31
==================
diff --git a/README.md b/README.md
index febcb102bf..2da3b067cc 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[](http://www.apache.org/licenses/LICENSE-2.0)
[](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxSerialization_Ko&guest=1)
-[](http://kotlinlang.org)
-[](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.8.1)
+[](http://kotlinlang.org)
+[](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.9.0)
[](https://kotlinlang.org/api/kotlinx.serialization/)
[](https://kotlinlang.slack.com/messages/serialization/)
@@ -94,8 +94,8 @@ Kotlin DSL:
```kotlin
plugins {
- kotlin("jvm") version "2.1.20" // or kotlin("multiplatform") or any other kotlin plugin
- kotlin("plugin.serialization") version "2.1.20"
+ kotlin("jvm") version "2.2.0" // or kotlin("multiplatform") or any other kotlin plugin
+ kotlin("plugin.serialization") version "2.2.0"
}
```
@@ -103,8 +103,8 @@ Groovy DSL:
```gradle
plugins {
- id 'org.jetbrains.kotlin.multiplatform' version '2.1.20'
- id 'org.jetbrains.kotlin.plugin.serialization' version '2.1.20'
+ id 'org.jetbrains.kotlin.multiplatform' version '2.2.0'
+ id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.0'
}
```
@@ -122,7 +122,7 @@ buildscript {
repositories { mavenCentral() }
dependencies {
- val kotlinVersion = "2.1.20"
+ val kotlinVersion = "2.2.0"
classpath(kotlin("gradle-plugin", version = kotlinVersion))
classpath(kotlin("serialization", version = kotlinVersion))
}
@@ -133,7 +133,7 @@ Groovy DSL:
```gradle
buildscript {
- ext.kotlin_version = '2.1.20'
+ ext.kotlin_version = '2.2.0'
repositories { mavenCentral() }
dependencies {
@@ -163,7 +163,7 @@ repositories {
}
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
}
```
@@ -175,7 +175,7 @@ repositories {
}
dependencies {
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0"
}
```
@@ -265,8 +265,8 @@ Ensure the proper version of Kotlin and serialization version:
```xml
- 2.1.20
- 1.8.1
+ 2.2.0
+ 1.9.0
```
diff --git a/build.gradle.kts b/build.gradle.kts
index f2cfd85363..678d1cd829 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -21,13 +21,7 @@ plugins {
repositories {
mavenCentral()
- maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
- // kotlin-dev with space redirector
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
- // For Dokka that depends on kotlinx-html
- maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
- // For local development
- mavenLocal()
+ maven("https://redirector.kotlinlang.org/maven/dev")
}
// == common projects settings setup
@@ -59,7 +53,7 @@ allprojects {
}
repositories {
mavenCentral()
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+ maven("https://redirector.kotlinlang.org/maven/dev")
}
}
@@ -76,12 +70,13 @@ apiValidation {
knit {
siteRoot = "https://kotlinlang.org/api/kotlinx.serialization"
- moduleDocs = "build/dokka/htmlMultiModule"
+ moduleDocs = "build/dokka-module/html/module"
+ dokkaMultiModuleRoot = "build/dokka/html/"
}
// Build API docs for all modules with dokka before running Knit
tasks.named("knitPrepare") {
- dependsOn("dokka")
+ dependsOn("dokkaGenerate")
}
@@ -133,17 +128,14 @@ subprojects {
}
}
-// Knit relies on Dokka task and it's pretty convenient
-tasks.register("dokka") {
- dependsOn("dokkaHtmlMultiModule")
-}
-
-tasks.withType().named("dokkaHtmlMultiModule") {
- pluginsMapConfiguration.put("org.jetbrains.dokka.base.DokkaBase", """{ "templatesDir": "${projectDir.toString().replace("\\", "/")}/dokka-templates" }""")
-}
-
+// apply conventions to root module and setup dependencies for aggregation
+apply(plugin = "dokka-conventions")
dependencies {
- dokkaPlugin(libs.dokka.pathsaver)
+ subprojects.forEach {
+ if (it.name in documentedSubprojects) {
+ dokka(it)
+ }
+ }
}
// == NPM setup ==
@@ -157,19 +149,23 @@ logger.warn("Project is using Kotlin Gradle plugin version: ${project.getKotlinP
// == projects lists and flags ==
// getters are required because of variable lazy initialization in Gradle
-val unpublishedProjects get() = setOf(
- "benchmark",
- "guide",
- "kotlinx-serialization-json-tests",
- "proto-test-model",
-)
+val unpublishedProjects
+ get() = setOf(
+ "benchmark",
+ "guide",
+ "kotlinx-serialization-json-tests",
+ "proto-test-model",
+ )
val excludedFromBomProjects get() = unpublishedProjects + "kotlinx-serialization-bom"
-val documentedSubprojects get() = setOf("kotlinx-serialization-core",
- "kotlinx-serialization-json",
- "kotlinx-serialization-json-okio",
- "kotlinx-serialization-json-io",
- "kotlinx-serialization-cbor",
- "kotlinx-serialization-properties",
- "kotlinx-serialization-hocon",
- "kotlinx-serialization-protobuf")
+val documentedSubprojects
+ get() = setOf(
+ "kotlinx-serialization-core",
+ "kotlinx-serialization-json",
+ "kotlinx-serialization-json-okio",
+ "kotlinx-serialization-json-io",
+ "kotlinx-serialization-cbor",
+ "kotlinx-serialization-properties",
+ "kotlinx-serialization-hocon",
+ "kotlinx-serialization-protobuf"
+ )
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 529b81c127..5c73ad8bd5 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -26,15 +26,8 @@ repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots")
}
- // kotlin-dev with space redirector
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
-
- maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
- // For Dokka that depends on kotlinx-html
- maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
-
mavenCentral()
- mavenLocal()
+ maven("https://redirector.kotlinlang.org/maven/dev")
}
kotlin {
diff --git a/buildSrc/src/main/kotlin/Java9Modularity.kt b/buildSrc/src/main/kotlin/Java9Modularity.kt
index 41558c2cce..abe933bb30 100644
--- a/buildSrc/src/main/kotlin/Java9Modularity.kt
+++ b/buildSrc/src/main/kotlin/Java9Modularity.kt
@@ -175,6 +175,7 @@ object Java9Modularity {
val taskKotlinLanguageVersion = compilerOptions.languageVersion.orElse(KotlinVersion.DEFAULT)
@OptIn(InternalKotlinGradlePluginApi::class)
+ @Suppress("DEPRECATION")
if (taskKotlinLanguageVersion.get() < KotlinVersion.KOTLIN_2_0) {
// part of work-around for https://youtrack.jetbrains.com/issue/KT-60541
@Suppress("INVISIBLE_MEMBER")
diff --git a/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts b/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
index d30137276b..d64d46a911 100644
--- a/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
@@ -1,10 +1,3 @@
-/*
- * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-import org.jetbrains.dokka.gradle.*
-import java.net.URI
-
/*
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@@ -18,62 +11,24 @@ dependencies {
dokkaPlugin(provider { extens.getByType().named("libs").findLibrary("dokka.pathsaver").get().get() })
}
-tasks.withType().named("dokkaHtmlPartial") {
- outputDirectory.set(file("build/dokka"))
-
-
- pluginsMapConfiguration.put("org.jetbrains.dokka.base.DokkaBase", """{ "templatesDir": "${rootDir.resolve("dokka-templates").canonicalPath.replace('\\', '/')}" }""")
-
- dokkaSourceSets {
- configureEach {
- includes.from(rootDir.resolve("dokka/moduledoc.md").path)
-
- perPackageOption {
- matchingRegex.set("kotlinx\\.serialization(\$|\\.).*")
- reportUndocumented.set(true)
- skipDeprecated.set(true)
- }
-
- // Internal API
- perPackageOption {
- matchingRegex.set("kotlinx\\.serialization.internal(\$|\\.).*")
- suppress.set(true)
- }
-
- // Internal JSON API
- perPackageOption {
- matchingRegex.set("kotlinx\\.serialization.json.internal(\$|\\.).*")
- suppress.set(true)
- reportUndocumented.set(false)
- }
-
- // Workaround for typealias
- perPackageOption {
- matchingRegex.set("kotlinx\\.serialization.protobuf.internal(\$|\\.).*")
- suppress.set(true)
- reportUndocumented.set(false)
- }
-
- // Deprecated migrations
- perPackageOption {
- matchingRegex.set("kotlinx\\.protobuf(\$|\\.).*")
- reportUndocumented.set(true)
- skipDeprecated.set(true)
- }
+dokka {
+ pluginsConfiguration.html {
+ templatesDir = rootDir.resolve("dokka-templates")
+ }
- // Deprecated migrations
- perPackageOption {
- matchingRegex.set("org\\.jetbrains\\.kotlinx\\.serialization\\.config(\$|\\.).*")
- reportUndocumented.set(false)
- skipDeprecated.set(true)
- }
+ dokkaSourceSets.configureEach {
+ includes.from(rootDir.resolve("dokka/moduledoc.md").path)
+ reportUndocumented = true
+ skipDeprecated = true
- sourceLink {
- localDirectory.set(rootDir)
+ perPackageOption {
+ matchingRegex = ".*\\.internal(\\..*)?"
+ suppress = true
+ }
- remoteUrl.set(URI("https://github.com/Kotlin/kotlinx.serialization/tree/master").toURL())
- remoteLineSuffix.set("#L")
- }
+ sourceLink {
+ localDirectory = rootDir
+ remoteUrl("https://github.com/Kotlin/kotlinx.serialization/tree/master")
}
}
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/global-compiler-options.gradle.kts b/buildSrc/src/main/kotlin/global-compiler-options.gradle.kts
index c0e95c3586..aa70b9ba55 100644
--- a/buildSrc/src/main/kotlin/global-compiler-options.gradle.kts
+++ b/buildSrc/src/main/kotlin/global-compiler-options.gradle.kts
@@ -3,6 +3,12 @@
*/
import org.jetbrains.kotlin.gradle.tasks.*
+import kotlin.collections.joinToString
+
+val kotlinAdditionalCliOptions = providers.gradleProperty("kotlin_additional_cli_options")
+ .orNull?.let { options ->
+ options.removeSurrounding("\"").split(" ").filter { it.isNotBlank() }
+ }
val globalCompilerArgs
get() = listOf(
@@ -17,6 +23,7 @@ tasks.withType(KotlinCompilationTask::class).configureEach {
compilerOptions {
// Unconditional compiler options
freeCompilerArgs.addAll(globalCompilerArgs)
+ kotlinAdditionalCliOptions?.forEach { option -> freeCompilerArgs.add(option) }
val isMainTaskName = name.startsWith("compileKotlin")
if (isMainTaskName) {
@@ -26,16 +33,7 @@ tasks.withType(KotlinCompilationTask::class).configureEach {
null -> true // Werror is enabled by default
else -> throw GradleException("Invalid kotlin_Werror_override value. Use 'enable' or 'disable'")
}
-
allWarningsAsErrors = werrorEnabled
-
- // Add extra compiler options when -Werror is disabled
- if (!werrorEnabled) {
- freeCompilerArgs.addAll(
- "-Wextra",
- "-Xuse-fir-experimental-checkers"
- )
- }
}
}
}
@@ -46,3 +44,10 @@ tasks.withType().configureEach {
tasks.withType().configureEach {
compilerOptions { freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR") }
}
+
+tasks.withType>().configureEach {
+ doFirst {
+ logger.info("Added Kotlin compiler flags: ${compilerOptions.freeCompilerArgs.get().joinToString(", ")}")
+ logger.info("allWarningsAsErrors=${compilerOptions.allWarningsAsErrors.get()}")
+ }
+}
diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api
index c8d0d35d77..1b15d35953 100644
--- a/core/api/kotlinx-serialization-core.api
+++ b/core/api/kotlinx-serialization-core.api
@@ -202,9 +202,19 @@ public final class kotlinx/serialization/builtins/BuiltinSerializersKt {
public static final fun serializer (Lkotlin/jvm/internal/ShortCompanionObject;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Lkotlin/jvm/internal/StringCompanionObject;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Lkotlin/time/Duration$Companion;)Lkotlinx/serialization/KSerializer;
+ public static final fun serializer (Lkotlin/time/Instant$Companion;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Lkotlin/uuid/Uuid$Companion;)Lkotlinx/serialization/KSerializer;
}
+public final class kotlinx/serialization/builtins/InstantComponentSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/builtins/InstantComponentSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlin/time/Instant;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/time/Instant;)V
+}
+
public final class kotlinx/serialization/builtins/LongAsStringSerializer : kotlinx/serialization/KSerializer {
public static final field INSTANCE Lkotlinx/serialization/builtins/LongAsStringSerializer;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Long;
@@ -361,6 +371,7 @@ public abstract class kotlinx/serialization/encoding/AbstractDecoder : kotlinx/s
public final fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
public fun decodeChar ()C
public final fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
public fun decodeDouble ()D
public final fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
public fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
@@ -375,7 +386,10 @@ public abstract class kotlinx/serialization/encoding/AbstractDecoder : kotlinx/s
public fun decodeNotNullMark ()Z
public fun decodeNull ()Ljava/lang/Void;
public final fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSequentially ()Z
public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
public static synthetic fun decodeSerializableValue$default (Lkotlinx/serialization/encoding/AbstractDecoder;Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;
public fun decodeShort ()S
@@ -388,6 +402,7 @@ public abstract class kotlinx/serialization/encoding/AbstractDecoder : kotlinx/s
public abstract class kotlinx/serialization/encoding/AbstractEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
public fun ()V
+ public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
public fun encodeBoolean (Z)V
public final fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
@@ -407,15 +422,19 @@ public abstract class kotlinx/serialization/encoding/AbstractEncoder : kotlinx/s
public final fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V
public fun encodeLong (J)V
public final fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V
+ public fun encodeNotNullMark ()V
public fun encodeNull ()V
public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
public fun encodeShort (S)V
public final fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
public fun encodeString (Ljava/lang/String;)V
public final fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V
public fun encodeValue (Ljava/lang/Object;)V
public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
}
public abstract interface class kotlinx/serialization/encoding/ChunkedDecoder {
@@ -795,6 +814,15 @@ public final class kotlinx/serialization/internal/InlineClassDescriptorKt {
public static final fun InlinePrimitiveDescriptor (Ljava/lang/String;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/descriptors/SerialDescriptor;
}
+public final class kotlinx/serialization/internal/InstantSerializer : kotlinx/serialization/KSerializer {
+ public static final field INSTANCE Lkotlinx/serialization/internal/InstantSerializer;
+ public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+ public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlin/time/Instant;
+ public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+ public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+ public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/time/Instant;)V
+}
+
public final class kotlinx/serialization/internal/IntArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
}
@@ -985,6 +1013,8 @@ public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : ko
public fun getSerialNames ()Ljava/util/Set;
public fun hashCode ()I
public fun isElementOptional (I)Z
+ public fun isInline ()Z
+ public fun isNullable ()Z
public final fun pushAnnotation (Ljava/lang/annotation/Annotation;)V
public final fun pushClassAnnotation (Ljava/lang/annotation/Annotation;)V
public fun toString ()Ljava/lang/String;
@@ -1075,6 +1105,7 @@ public abstract class kotlinx/serialization/internal/TaggedDecoder : kotlinx/ser
public final fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
public final fun decodeChar ()C
public final fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
public final fun decodeDouble ()D
public final fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
public final fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
@@ -1089,7 +1120,10 @@ public abstract class kotlinx/serialization/internal/TaggedDecoder : kotlinx/ser
public fun decodeNotNullMark ()Z
public final fun decodeNull ()Ljava/lang/Void;
public final fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSequentially ()Z
public final fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
protected fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
public final fun decodeShort ()S
public final fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S
@@ -1120,6 +1154,7 @@ public abstract class kotlinx/serialization/internal/TaggedDecoder : kotlinx/ser
public abstract class kotlinx/serialization/internal/TaggedEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
public fun ()V
+ public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
public final fun encodeBoolean (Z)V
public final fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
@@ -1141,7 +1176,9 @@ public abstract class kotlinx/serialization/internal/TaggedEncoder : kotlinx/ser
public fun encodeNotNullMark ()V
public fun encodeNull ()V
public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
public final fun encodeShort (S)V
public final fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
public final fun encodeString (Ljava/lang/String;)V
@@ -1168,6 +1205,7 @@ public abstract class kotlinx/serialization/internal/TaggedEncoder : kotlinx/ser
protected abstract fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
protected final fun popTag ()Ljava/lang/Object;
protected final fun pushTag (Ljava/lang/Object;)V
+ public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
}
public final class kotlinx/serialization/internal/TripleSerializer : kotlinx/serialization/KSerializer {
@@ -1314,6 +1352,7 @@ public final class kotlinx/serialization/modules/SerializersModuleBuilder : kotl
public fun contextual (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
public final fun include (Lkotlinx/serialization/modules/SerializersModule;)V
public fun polymorphic (Lkotlin/reflect/KClass;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
+ public fun polymorphicDefault (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
public fun polymorphicDefaultDeserializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
public fun polymorphicDefaultSerializer (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)V
}
diff --git a/core/api/kotlinx-serialization-core.klib.api b/core/api/kotlinx-serialization-core.klib.api
index 041c115e26..5aefad0f2f 100644
--- a/core/api/kotlinx-serialization-core.klib.api
+++ b/core/api/kotlinx-serialization-core.klib.api
@@ -890,6 +890,14 @@ sealed class kotlinx.serialization.modules/SerializersModule { // kotlinx.serial
final fun <#A1: kotlin/Any> getContextual(kotlin.reflect/KClass<#A1>): kotlinx.serialization/KSerializer<#A1>? // kotlinx.serialization.modules/SerializersModule.getContextual|getContextual(kotlin.reflect.KClass<0:0>){0§}[0]
}
+final object kotlinx.serialization.builtins/InstantComponentSerializer : kotlinx.serialization/KSerializer { // kotlinx.serialization.builtins/InstantComponentSerializer|null[0]
+ final val descriptor // kotlinx.serialization.builtins/InstantComponentSerializer.descriptor|{}descriptor[0]
+ final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.builtins/InstantComponentSerializer.descriptor.|(){}[0]
+
+ final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin.time/Instant // kotlinx.serialization.builtins/InstantComponentSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+ final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin.time/Instant) // kotlinx.serialization.builtins/InstantComponentSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.time.Instant){}[0]
+}
+
final object kotlinx.serialization.builtins/LongAsStringSerializer : kotlinx.serialization/KSerializer { // kotlinx.serialization.builtins/LongAsStringSerializer|null[0]
final val descriptor // kotlinx.serialization.builtins/LongAsStringSerializer.descriptor|{}descriptor[0]
final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.builtins/LongAsStringSerializer.descriptor.|(){}[0]
@@ -956,6 +964,14 @@ final object kotlinx.serialization.internal/FloatSerializer : kotlinx.serializat
final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Float) // kotlinx.serialization.internal/FloatSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Float){}[0]
}
+final object kotlinx.serialization.internal/InstantSerializer : kotlinx.serialization/KSerializer { // kotlinx.serialization.internal/InstantSerializer|null[0]
+ final val descriptor // kotlinx.serialization.internal/InstantSerializer.descriptor|{}descriptor[0]
+ final fun (): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/InstantSerializer.descriptor.|(){}[0]
+
+ final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin.time/Instant // kotlinx.serialization.internal/InstantSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+ final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin.time/Instant) // kotlinx.serialization.internal/InstantSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.time.Instant){}[0]
+}
+
final object kotlinx.serialization.internal/IntArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer, kotlinx.serialization/KSerializer // kotlinx.serialization.internal/IntArraySerializer|null[0]
final object kotlinx.serialization.internal/IntSerializer : kotlinx.serialization/KSerializer { // kotlinx.serialization.internal/IntSerializer|null[0]
@@ -1074,6 +1090,7 @@ final val kotlinx.serialization.modules/EmptySerializersModule // kotlinx.serial
final fun (): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/EmptySerializersModule.|(){}[0]
final fun (kotlin.time/Duration.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer // kotlinx.serialization.builtins/serializer|serializer@kotlin.time.Duration.Companion(){}[0]
+final fun (kotlin.time/Instant.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer // kotlinx.serialization.builtins/serializer|serializer@kotlin.time.Instant.Companion(){}[0]
final fun (kotlin.uuid/Uuid.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer // kotlinx.serialization.builtins/serializer|serializer@kotlin.uuid.Uuid.Companion(){}[0]
final fun (kotlin/Boolean.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer // kotlinx.serialization.builtins/serializer|serializer@kotlin.Boolean.Companion(){}[0]
final fun (kotlin/Byte.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer // kotlinx.serialization.builtins/serializer|serializer@kotlin.Byte.Companion(){}[0]
diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt
index 4e44d3d2b2..358bbe7956 100644
--- a/core/commonMain/src/kotlinx/serialization/Serializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt
@@ -23,7 +23,7 @@ import kotlin.reflect.*
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
* For example, `serializer>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
* Star projections in [T]'s type arguments are prohibited.
@@ -42,7 +42,7 @@ public inline fun serializer(): KSerializer {
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
* For example, `serializer>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
* Star projections in [T]'s type arguments are prohibited.
@@ -60,12 +60,17 @@ public inline fun SerializersModule.serializer(): KSerializer {
*
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
- * For example, `serializer>>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * For example, `serializer(typeOf>())` returns [KSerializer] that is able
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [type]'s type arguments is not used by the serialization and is not taken into account.
* Star projections in [type]'s arguments are prohibited.
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KType], even though the type allows passing such a value.
+ * Consider using the `serializer()` overload accepting a type argument (for example, `serializer>()`),
+ * which returns the serializer with the correct type.
+ *
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
*/
@@ -80,11 +85,16 @@ public fun serializer(type: KType): KSerializer = EmptySerializersModule()
* The nullability of returned serializer is specified using the [isNullable].
*
* Note that it is impossible to create an array serializer with this method,
- * as array serializer needs additional information: type token for an element type.
+ * as an array serializer needs additional information: type token for an element type.
* To create array serializer, use overload with [KType] or [ArraySerializer] directly.
*
* Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KClass], even though the type allows passing such a value.
+ * Consider using the `serializer()` overload accepting a type argument (for example, `serializer>()`),
+ * which returns the serializer with the correct type.
+ *
* @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable)
* @throws SerializationException if [kClass] is a `kotlin.Array`
* @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
@@ -102,12 +112,15 @@ public fun serializer(
*
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
- * For example, `serializerOrNull>>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * For example, `serializerOrNull(typeOf>())` returns [KSerializer] that is able
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
* Star projections in [type]'s arguments are prohibited.
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KType], even though the type allows passing such a value.
+ *
* @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
*/
@@ -120,12 +133,18 @@ public fun serializerOrNull(type: KType): KSerializer? = EmptySerializersM
*
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
- * For example, `serializer>>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * For example, `serializer(typeOf>())` returns [KSerializer] that is able
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
* Star projections in [type]'s arguments are prohibited.
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KType], even though the type allows passing such a value.
+ * Consider using the `serializer()` overload accepting a type argument
+ * (for example, `module.serializer>()`),
+ * which returns the serializer with the correct type.
+ *
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
*/
@@ -143,11 +162,17 @@ public fun SerializersModule.serializer(type: KType): KSerializer =
* The nullability of returned serializer is specified using the [isNullable].
*
* Note that it is impossible to create an array serializer with this method,
- * as array serializer needs additional information: type token for an element type.
+ * as an array serializer needs additional information: type token for an element type.
* To create array serializer, use overload with [KType] or [ArraySerializer] directly.
*
* Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KClass], even though the type allows passing such a value.
+ * Consider using the `serializer()` overload accepting a type argument
+ * (for example, `module.serializer>()`),
+ * which returns the serializer with the correct type.
+ *
* @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable and is not registered in [this] module)
* @throws SerializationException if [kClass] is a `kotlin.Array`
* @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
@@ -168,12 +193,15 @@ public fun SerializersModule.serializer(
*
* This overload works with full type information, including type arguments and nullability,
* and is a recommended way to retrieve a serializer.
- * For example, `serializerOrNull>>()` returns [KSerializer] that is able
- * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
+ * For example, `serializerOrNull(typeOf>())` returns [KSerializer] that is able
+ * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
*
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
* Star projections in [type]'s arguments are prohibited.
*
+ * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives
+ * a value that's not a valid instance of the [KType], even though the type allows passing such a value.
+ *
* @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module).
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
*/
@@ -276,24 +304,24 @@ internal fun SerializersModule.serializersForParameters(
* The given class must be annotated with [Serializable] or be one of the built-in types.
*
* This method uses platform-specific reflection available for the given erased `KClass`
- * and is not recommended to use this method for anything, but last-ditch resort, e.g.
- * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
+ * and is not recommended to use this method for anything, but last-ditch resort, e.g.,
+ * when all type info is lost, your application has crashed, and it is the final attempt to log or send some serializable data.
*
* The recommended way to retrieve the serializer is inline [serializer] function and [`serializer(KType)`][serializer]
*
* This API is not guaranteed to work consistently across different platforms or
- * to work in cases that slightly differ from "plain @Serializable class" and have platform and reflection specific limitations.
+ * to work in cases that slightly differ from "plain @Serializable class" and have platform- and reflection-specific limitations.
*
* ### Constraints
* This paragraph explains known (but not all!) constraints of the `serializer()` implementation.
- * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
+ * Please note that they are not bugs but implementation restrictions that we cannot work around.
*
* * This method may behave differently on JVM, JS and Native because of runtime reflection differences
* * Serializers for classes with generic parameters are ignored by this method
- * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
- * * Serializers for classes with named companion objects are not lookuped consistently
+ * * External serializers generated with `Serializer(forClass = )` are not looked up consistently
+ * * Serializers for classes with named companion objects are not looked up consistently
*
- * @throws SerializationException if serializer can't be found.
+ * @throws SerializationException if the serializer can't be found.
*/
@InternalSerializationApi
public fun KClass.serializer(): KSerializer = serializerOrNull() ?: serializerNotRegistered()
@@ -302,20 +330,20 @@ public fun KClass.serializer(): KSerializer = serializerOrNull()
* Retrieves a [KSerializer] for the given [KClass] or returns `null` if none is found.
* The given class must be annotated with [Serializable] or be one of the built-in types.
* This method uses platform-specific reflection available for the given erased `KClass`
- * and it is not recommended to use this method for anything, but last-ditch resort, e.g.
- * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
+ * and it is not recommended to use this method for anything, but last-ditch resort, e.g.,
+ * when all type info is lost, your application has crashed, and it is the final attempt to log or send some serializable data.
*
* This API is not guaranteed to work consistently across different platforms or
* to work in cases that slightly differ from "plain @Serializable class".
*
* ### Constraints
* This paragraph explains known (but not all!) constraints of the `serializerOrNull()` implementation.
- * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
+ * Please note that they are not bugs but implementation restrictions that we cannot work around.
*
* * This method may behave differently on JVM, JS and Native because of runtime reflection differences
* * Serializers for classes with generic parameters are ignored by this method
- * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
- * * Serializers for classes with named companion objects are not lookuped consistently
+ * * External serializers generated with `Serializer(forClass = )` are not looked up consistently
+ * * Serializers for classes with named companion objects are not looked up consistently
*/
@InternalSerializationApi
public fun KClass.serializerOrNull(): KSerializer? =
diff --git a/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
index c481e3adf8..aedd4fe874 100644
--- a/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
@@ -10,6 +10,8 @@ import kotlinx.serialization.internal.*
import kotlin.reflect.*
import kotlinx.serialization.descriptors.*
import kotlin.time.Duration
+import kotlin.time.ExperimentalTime
+import kotlin.time.Instant
import kotlin.uuid.*
/**
@@ -245,12 +247,30 @@ public fun UShort.Companion.serializer(): KSerializer = UShortSerializer
/**
* Returns serializer for [Duration].
- * It is serialized as a string that represents a duration in the ISO-8601-2 format.
+ * It is serialized as a string that represents a duration in the format used by [Duration.toIsoString],
+ * that is, the ISO-8601-2 format.
*
- * The result of serialization is similar to calling [Duration.toIsoString], for deserialization is [Duration.parseIsoString].
+ * For deserialization, [Duration.parseIsoString] is used.
+ *
+ * @see Duration.toIsoString
+ * @see Duration.parseIsoString
*/
public fun Duration.Companion.serializer(): KSerializer = DurationSerializer
+/**
+ * Returns serializer for [Instant].
+ * It is serialized as a string that represents an instant in the format used by [Instant.toString]
+ * and described in ISO-8601-1:2019, 5.4.2.1b).
+ *
+ * Deserialization is case-insensitive.
+ * More details can be found in the documentation of [Instant.toString] and [Instant.parse] functions.
+ *
+ * @see Instant.toString
+ * @see Instant.parse
+ */
+@ExperimentalTime
+public fun Instant.Companion.serializer(): KSerializer = InstantSerializer
+
/**
* Returns serializer for [Uuid].
* Serializer operates with a standard UUID string representation, also known as "hex-and-dash" format —
diff --git a/core/commonMain/src/kotlinx/serialization/builtins/InstantComponentSerializer.kt b/core/commonMain/src/kotlinx/serialization/builtins/InstantComponentSerializer.kt
new file mode 100644
index 0000000000..3aaedccb9e
--- /dev/null
+++ b/core/commonMain/src/kotlinx/serialization/builtins/InstantComponentSerializer.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.builtins
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlin.time.ExperimentalTime
+import kotlin.time.Instant
+
+/**
+ * Serializer that encodes and decodes [Instant] as its second and nanosecond components of the Unix time.
+ *
+ * JSON example: `{"epochSeconds":1607505416,"nanosecondsOfSecond":124000}`.
+ */
+@ExperimentalTime
+public object InstantComponentSerializer : KSerializer {
+
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("kotlinx.serialization.InstantComponentSerializer") {
+ element("epochSeconds")
+ element("nanosecondsOfSecond", isOptional = true)
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun deserialize(decoder: Decoder): Instant =
+ decoder.decodeStructure(descriptor) {
+ var epochSecondsNotSeen = true
+ var epochSeconds: Long = 0
+ var nanosecondsOfSecond = 0
+ while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> {
+ epochSecondsNotSeen = false
+ epochSeconds = decodeLongElement(descriptor, 0)
+ }
+ 1 -> nanosecondsOfSecond = decodeIntElement(descriptor, 1)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> throw SerializationException("Unexpected index: $index")
+ }
+ }
+ if (epochSecondsNotSeen) throw MissingFieldException(
+ missingField = "epochSeconds",
+ serialName = descriptor.serialName
+ )
+ Instant.fromEpochSeconds(epochSeconds, nanosecondsOfSecond)
+ }
+
+ @OptIn(ExperimentalSerializationApi::class)
+ override fun serialize(encoder: Encoder, value: Instant) {
+ encoder.encodeStructure(descriptor) {
+ encodeLongElement(descriptor, 0, value.epochSeconds)
+ if (value.nanosecondsOfSecond != 0 || shouldEncodeElementDefault(descriptor, 1)) {
+ encodeIntElement(descriptor, 1, value.nanosecondsOfSecond)
+ }
+ }
+ }
+
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt b/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
index fbc5dc1476..8db48f72e9 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
@@ -10,6 +10,8 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.time.Duration
+import kotlin.time.ExperimentalTime
+import kotlin.time.Instant
import kotlin.uuid.*
@@ -39,6 +41,20 @@ internal object NothingSerializer : KSerializer {
}
}
+@PublishedApi
+@ExperimentalTime
+internal object InstantSerializer : KSerializer {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.time.Instant", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: Instant) {
+ encoder.encodeString(value.toString())
+ }
+
+ override fun deserialize(decoder: Decoder): Instant {
+ return Instant.parse(decoder.decodeString())
+ }
+}
+
@PublishedApi
@ExperimentalUuidApi
internal object UuidSerializer: KSerializer {
diff --git a/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt b/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt
index 859818aa29..6115f8fd1d 100644
--- a/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/BasicTypesSerializationTest.kt
@@ -10,7 +10,6 @@ import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
import kotlinx.serialization.modules.*
-import kotlinx.serialization.test.*
import kotlin.test.*
import kotlin.time.Duration
diff --git a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
index e423afca2a..63f48bcb34 100644
--- a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
@@ -26,7 +26,7 @@ internal actual fun KClass.compiledSerializerImpl(): KSerializer
else this.js.asDynamic().Companion?.serializer()
) as? KSerializer
-internal actual fun KClass.isInterface(): Boolean = isInterface
+internal actual fun KClass.isInterface(): Boolean = isInterfaceHack
internal actual fun createCache(factory: (KClass<*>) -> KSerializer?): SerializerCache {
return object: SerializerCache {
@@ -73,15 +73,18 @@ internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClas
* WARNING: may be broken in arbitrary time in the future without notice
*
* Should be eventually replaced with compiler intrinsics
+ *
+ * TODO: Remove this when KT-78581 lands into a 2.2.20 release
*/
-private val KClass<*>.isInterface: Boolean
+private val KClass<*>.isInterfaceHack: Boolean
get(): Boolean {
// .js throws an exception for Nothing
if (this === Nothing::class) return false
return js.asDynamic().`$metadata$`?.kind == "interface"
}
-@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class,
+ ExperimentalTime::class)
internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
String::class to String.serializer(),
Char::class to Char.serializer(),
@@ -111,5 +114,6 @@ internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
Unit::class to Unit.serializer(),
Nothing::class to NothingSerializer(),
Duration::class to Duration.serializer(),
+ Instant::class to Instant.serializer(),
Uuid::class to Uuid.serializer()
)
diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
index b838a0fe52..65d38fba70 100644
--- a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
@@ -201,6 +201,9 @@ internal actual fun initBuiltins(): Map, KSerializer<*>> = buildMap {
}
@OptIn(ExperimentalUuidApi::class)
loadSafe { put(Uuid::class, Uuid.serializer()) }
+
+ @OptIn(ExperimentalTime::class)
+ loadSafe { put(Instant::class, Instant.serializer()) }
}
// Reference classes in [block] ignoring any exceptions related to class loading
diff --git a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
index c2e9fdd7e0..7e2423a76e 100644
--- a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
@@ -75,7 +75,8 @@ private fun arrayOfAnyNulls(size: Int): Array = arrayOfNulls(size) a
internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClass == Array::class
-@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class,
+ ExperimentalTime::class)
internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
String::class to String.serializer(),
Char::class to Char.serializer(),
@@ -105,5 +106,6 @@ internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
Unit::class to Unit.serializer(),
Nothing::class to NothingSerializer(),
Duration::class to Duration.serializer(),
+ Instant::class to Instant.serializer(),
Uuid::class to Uuid.serializer()
)
diff --git a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
index ecfee8ede8..381d6a4d68 100644
--- a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
@@ -65,7 +65,8 @@ internal actual fun ArrayList.toNativeArrayImpl(eClass: KCl
internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClass == Array::class
-@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class,
+ ExperimentalTime::class)
internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
String::class to String.serializer(),
Char::class to Char.serializer(),
@@ -95,5 +96,6 @@ internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf(
Unit::class to Unit.serializer(),
Nothing::class to NothingSerializer(),
Duration::class to Duration.serializer(),
+ Instant::class to Instant.serializer(),
Uuid::class to Uuid.serializer()
)
diff --git a/formats/cbor/api/kotlinx-serialization-cbor.api b/formats/cbor/api/kotlinx-serialization-cbor.api
index 8b580321fb..e1e37801f6 100644
--- a/formats/cbor/api/kotlinx-serialization-cbor.api
+++ b/formats/cbor/api/kotlinx-serialization-cbor.api
@@ -1,7 +1,7 @@
public abstract interface annotation class kotlinx/serialization/cbor/ByteString : java/lang/annotation/Annotation {
}
-public synthetic class kotlinx/serialization/cbor/ByteString$Impl : kotlinx/serialization/cbor/ByteString {
+public final synthetic class kotlinx/serialization/cbor/ByteString$Impl : kotlinx/serialization/cbor/ByteString {
public fun ()V
}
@@ -21,7 +21,7 @@ public final class kotlinx/serialization/cbor/Cbor$Default : kotlinx/serializati
public abstract interface annotation class kotlinx/serialization/cbor/CborArray : java/lang/annotation/Annotation {
}
-public synthetic class kotlinx/serialization/cbor/CborArray$Impl : kotlinx/serialization/cbor/CborArray {
+public final synthetic class kotlinx/serialization/cbor/CborArray$Impl : kotlinx/serialization/cbor/CborArray {
public fun ()V
}
@@ -96,7 +96,7 @@ public abstract interface annotation class kotlinx/serialization/cbor/CborLabel
public abstract fun label ()J
}
-public synthetic class kotlinx/serialization/cbor/CborLabel$Impl : kotlinx/serialization/cbor/CborLabel {
+public final synthetic class kotlinx/serialization/cbor/CborLabel$Impl : kotlinx/serialization/cbor/CborLabel {
public fun (J)V
public final synthetic fun label ()J
}
@@ -125,7 +125,7 @@ public abstract interface annotation class kotlinx/serialization/cbor/KeyTags :
public abstract fun tags ()[J
}
-public synthetic class kotlinx/serialization/cbor/KeyTags$Impl : kotlinx/serialization/cbor/KeyTags {
+public final synthetic class kotlinx/serialization/cbor/KeyTags$Impl : kotlinx/serialization/cbor/KeyTags {
public synthetic fun ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final synthetic fun tags ()[J
}
@@ -134,7 +134,7 @@ public abstract interface annotation class kotlinx/serialization/cbor/ObjectTags
public abstract fun tags ()[J
}
-public synthetic class kotlinx/serialization/cbor/ObjectTags$Impl : kotlinx/serialization/cbor/ObjectTags {
+public final synthetic class kotlinx/serialization/cbor/ObjectTags$Impl : kotlinx/serialization/cbor/ObjectTags {
public synthetic fun ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final synthetic fun tags ()[J
}
@@ -143,7 +143,7 @@ public abstract interface annotation class kotlinx/serialization/cbor/ValueTags
public abstract fun tags ()[J
}
-public synthetic class kotlinx/serialization/cbor/ValueTags$Impl : kotlinx/serialization/cbor/ValueTags {
+public final synthetic class kotlinx/serialization/cbor/ValueTags$Impl : kotlinx/serialization/cbor/ValueTags {
public synthetic fun ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final synthetic fun tags ()[J
}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
index 21293a9231..7b618a8c3b 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
@@ -88,8 +88,27 @@ public sealed class Cbor(
val reader = CborReader(this, CborParser(stream, configuration.verifyObjectTags))
return reader.decodeSerializableValue(deserializer)
}
+
+ public fun decodeFromCbor(deserializer: DeserializationStrategy, element: CborElement): T {
+ val reader = CborReader(this, StructuredCborParser(element, configuration.verifyObjectTags))
+ return reader.decodeSerializableValue(deserializer)
+ }
+
+ public fun encodeToCbor(serializer: SerializationStrategy, value: T): CborElement {
+ val writer = StructuredCborWriter(this)
+ writer.encodeSerializableValue(serializer, value)
+ return writer.finalize()
+ }
}
+@ExperimentalSerializationApi
+public inline fun Cbor.encodeToCbor(value: T): CborElement =
+ encodeToCbor(serializersModule.serializer(), value)
+
+@ExperimentalSerializationApi
+public inline fun Cbor.decodeFromCbor(element: CborElement): T =
+ decodeFromCbor(serializersModule.serializer(), element)
+
@OptIn(ExperimentalSerializationApi::class)
private class CborImpl(
configuration: CborConfiguration,
@@ -108,18 +127,20 @@ private class CborImpl(
public fun Cbor(from: Cbor = Cbor, builderAction: CborBuilder.() -> Unit): Cbor {
val builder = CborBuilder(from)
builder.builderAction()
- return CborImpl(CborConfiguration(
- builder.encodeDefaults,
- builder.ignoreUnknownKeys,
- builder.encodeKeyTags,
- builder.encodeValueTags,
- builder.encodeObjectTags,
- builder.verifyKeyTags,
- builder.verifyValueTags,
- builder.verifyObjectTags,
- builder.useDefiniteLengthEncoding,
- builder.preferCborLabelsOverNames,
- builder.alwaysUseByteString),
+ return CborImpl(
+ CborConfiguration(
+ builder.encodeDefaults,
+ builder.ignoreUnknownKeys,
+ builder.encodeKeyTags,
+ builder.encodeValueTags,
+ builder.encodeObjectTags,
+ builder.verifyKeyTags,
+ builder.verifyValueTags,
+ builder.verifyObjectTags,
+ builder.useDefiniteLengthEncoding,
+ builder.preferCborLabelsOverNames,
+ builder.alwaysUseByteString
+ ),
builder.serializersModule
)
}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt
index 13a773f3fa..da6bde3c06 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt
@@ -31,4 +31,15 @@ public interface CborDecoder : Decoder {
* Exposes the current [Cbor] instance and all its configuration flags. Useful for low-level custom serializers.
*/
public val cbor: Cbor
+
+ /**
+ * Decodes the next element in the current input as [CborElement].
+ * The type of the decoded element depends on the current state of the input and, when received
+ * by [serializer][KSerializer] in its [KSerializer.serialize] method, the type of the token directly matches
+ * the [kind][SerialDescriptor.kind].
+ *
+ * This method is allowed to invoke only as the part of the whole deserialization process of the class,
+ * calling this method after invoking [beginStructure] or any `decode*` method will lead to unspecified behaviour.
+ */
+ public fun decodeCborElement(): CborElement
}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborElement.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborElement.kt
new file mode 100644
index 0000000000..c6f610b18d
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborElement.kt
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("unused")
+@file:OptIn(ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.internal.*
+
+/**
+ * Class representing single CBOR element.
+ * Can be [CborPrimitive], [CborMap] or [CborList].
+ *
+ * [CborElement.toString] properly prints CBOR tree as a human-readable representation.
+ * Whole hierarchy is serializable, but only when used with [Cbor] as [CborElement] is purely CBOR-specific structure
+ * which has a meaningful schemaless semantics only for CBOR.
+ *
+ * The whole hierarchy is [serializable][Serializable] only by [Cbor] format.
+ */
+@Serializable(with = CborElementSerializer::class)
+public sealed class CborElement(
+ /**
+ * CBOR tags associated with this element.
+ * Tags are optional semantic tagging of other major types (major type 6).
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
+ */
+ @OptIn(ExperimentalUnsignedTypes::class)
+ tags: ULongArray = ulongArrayOf()
+
+) {
+ /**
+ * CBOR tags associated with this element.
+ * Tags are optional semantic tagging of other major types (major type 6).
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
+ */
+ @OptIn(ExperimentalUnsignedTypes::class)
+ public var tags: ULongArray = tags
+ internal set
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is CborElement) return false
+
+ if (!tags.contentEquals(other.tags)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return tags.contentHashCode()
+ }
+
+}
+
+/**
+ * Class representing CBOR primitive value.
+ * CBOR primitives include numbers, strings, booleans, byte arrays and special null value [CborNull].
+ */
+@Serializable(with = CborPrimitiveSerializer::class)
+public sealed class CborPrimitive(
+ public val value: T,
+ tags: ULongArray = ulongArrayOf()
+) : CborElement(tags) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is CborPrimitive<*>) return false
+ if (!super.equals(other)) return false
+
+ if (value != other.value) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = super.hashCode()
+ result = 31 * result + value.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "CborPrimitive(" +
+ "kind=${value::class.simpleName}, " +
+ "tags=${tags.joinToString()}, " +
+ "value=$value" +
+ ")"
+ }
+}
+
+public sealed class CborInt(
+ tags: ULongArray = ulongArrayOf(),
+ value: T,
+) : CborPrimitive(value, tags) {
+ public companion object {
+ public operator fun invoke(
+ value: Long,
+ tags: ULongArray = ulongArrayOf()
+ ): CborInt<*> = if (value >= 0) CborPositiveInt(value.toULong(), tags) else CborNegativeInt(value, tags)
+
+ public operator fun invoke(
+ value: ULong,
+ tags: ULongArray = ulongArrayOf()
+ ): CborInt = CborPositiveInt(value, tags)
+ }
+}
+
+/**
+ * Class representing signed CBOR integer (major type 1).
+ */
+@Serializable(with = CborIntSerializer::class)
+public class CborNegativeInt(
+ value: Long,
+ tags: ULongArray = ulongArrayOf()
+) : CborInt(tags, value) {
+ init {
+ require(value < 0) { "Number must be negative: $value" }
+ }
+}
+
+/**
+ * Class representing unsigned CBOR integer (major type 0).
+ */
+@Serializable(with = CborUIntSerializer::class)
+public class CborPositiveInt(
+ value: ULong,
+ tags: ULongArray = ulongArrayOf()
+) : CborInt(tags, value)
+
+/**
+ * Class representing CBOR floating point value (major type 7).
+ */
+@Serializable(with = CborDoubleSerializer::class)
+public class CborDouble(
+ value: Double,
+ tags: ULongArray = ulongArrayOf()
+) : CborPrimitive(value, tags)
+
+/**
+ * Class representing CBOR string value.
+ */
+@Serializable(with = CborStringSerializer::class)
+public class CborString(
+ value: String,
+ tags: ULongArray = ulongArrayOf()
+) : CborPrimitive(value, tags)
+
+/**
+ * Class representing CBOR boolean value.
+ */
+@Serializable(with = CborBooleanSerializer::class)
+public class CborBoolean(
+ value: Boolean,
+ tags: ULongArray = ulongArrayOf()
+) : CborPrimitive(value, tags)
+
+/**
+ * Class representing CBOR byte string value.
+ */
+@Serializable(with = CborByteStringSerializer::class)
+public class CborByteString(
+ value: ByteArray,
+ tags: ULongArray = ulongArrayOf()
+) : CborPrimitive(value, tags) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is CborByteString) return false
+ if (!tags.contentEquals(other.tags)) return false
+ return value.contentEquals(other.value)
+ }
+
+ override fun hashCode(): Int {
+ var result = tags.contentHashCode()
+ result = 31 * result + (value.contentHashCode())
+ return result
+ }
+
+ override fun toString(): String {
+ return "CborPrimitive(" +
+ "kind=${value::class.simpleName}, " +
+ "tags=${tags.joinToString()}, " +
+ "value=h'${value.toHexString()}" +
+ ")"
+ }
+}
+
+/**
+ * Class representing CBOR `null` value
+ */
+@Serializable(with = CborNullSerializer::class)
+public class CborNull(tags: ULongArray = ulongArrayOf()) : CborPrimitive(Unit, tags)
+
+/**
+ * Class representing CBOR map, consisting of key-value pairs, where both key and value are arbitrary [CborElement]
+ *
+ * Since this class also implements [Map] interface, you can use
+ * traditional methods like [Map.get] or [Map.getValue] to obtain CBOR elements.
+ */
+@Serializable(with = CborMapSerializer::class)
+public class CborMap(
+ private val content: Map,
+ tags: ULongArray = ulongArrayOf()
+) : CborElement(tags), Map by content {
+
+ public override fun equals(other: Any?): Boolean =
+ other is CborMap && other.content == content && other.tags.contentEquals(tags)
+
+ public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
+
+ override fun toString(): String {
+ return "CborMap(" +
+ "tags=${tags.joinToString()}, " +
+ "content=$content" +
+ ")"
+ }
+
+}
+
+/**
+ * Class representing CBOR array, consisting of CBOR elements.
+ *
+ * Since this class also implements [List] interface, you can use
+ * traditional methods like [List.get] or [List.size] to obtain CBOR elements.
+ */
+@Serializable(with = CborListSerializer::class)
+public class CborList(
+ private val content: List,
+ tags: ULongArray = ulongArrayOf()
+) : CborElement(tags), List by content {
+
+ public override fun equals(other: Any?): Boolean =
+ other is CborList && other.content == content && other.tags.contentEquals(tags)
+
+ public override fun hashCode(): Int = content.hashCode() * 31 + tags.contentHashCode()
+
+ override fun toString(): String {
+ return "CborList(" +
+ "tags=${tags.joinToString()}, " +
+ "content=$content" +
+ ")"
+ }
+
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborElementSerializers.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborElementSerializers.kt
new file mode 100644
index 0000000000..3c968d0714
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborElementSerializers.kt
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.cbor.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborElement].
+ * It can only be used by with [Cbor] format and its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborElementSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.cbor.CborElement", PolymorphicKind.SEALED) {
+ // Resolve cyclic dependency in descriptors by late binding
+ element("CborPrimitive", defer { CborPrimitiveSerializer.descriptor })
+ element("CborNull", defer { CborNullSerializer.descriptor })
+ element("CborString", defer { CborStringSerializer.descriptor })
+ element("CborBoolean", defer { CborBooleanSerializer.descriptor })
+ element("CborByteString", defer { CborByteStringSerializer.descriptor })
+ element("CborMap", defer { CborMapSerializer.descriptor })
+ element("CborList", defer { CborListSerializer.descriptor })
+ element("CborDouble", defer { CborDoubleSerializer.descriptor })
+ element("CborInt", defer { CborIntSerializer.descriptor })
+ element("CborUInt", defer { CborUIntSerializer.descriptor })
+ }
+
+ override fun serialize(encoder: Encoder, value: CborElement) {
+ encoder.asCborEncoder()
+
+ // Encode the value
+ when (value) {
+ is CborPrimitive<*> -> encoder.encodeSerializableValue(CborPrimitiveSerializer, value)
+ is CborMap -> encoder.encodeSerializableValue(CborMapSerializer, value)
+ is CborList -> encoder.encodeSerializableValue(CborListSerializer, value)
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): CborElement {
+ val input = decoder.asCborDecoder()
+ return input.decodeCborElement()
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborPrimitive].
+ * It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborPrimitiveSerializer : KSerializer> {
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.cbor.CborPrimitive", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: CborPrimitive<*>) {
+ val cborEncoder = encoder.asCborEncoder()
+
+ cborEncoder.encodeTags(value)
+
+ when (value) {
+ is CborNull -> encoder.encodeSerializableValue(CborNullSerializer, value)
+ is CborString -> encoder.encodeSerializableValue(CborStringSerializer, value)
+ is CborBoolean -> encoder.encodeSerializableValue(CborBooleanSerializer, value)
+ is CborByteString -> encoder.encodeSerializableValue(CborByteStringSerializer, value)
+ is CborDouble -> encoder.encodeSerializableValue(CborDoubleSerializer, value)
+ is CborNegativeInt -> encoder.encodeSerializableValue(CborIntSerializer, value)
+ is CborPositiveInt -> encoder.encodeSerializableValue(CborUIntSerializer, value)
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): CborPrimitive<*> {
+ val result = decoder.asCborDecoder().decodeCborElement()
+ if (result !is CborPrimitive<*>) throw CborDecodingException("Unexpected CBOR element, expected CborPrimitive, had ${result::class}")
+ return result
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborNull].
+ * It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborNullSerializer : KSerializer {
+
+ override val descriptor: SerialDescriptor =
+ buildSerialDescriptor("kotlinx.serialization.cbor.CborNull", SerialKind.ENUM)
+
+ override fun serialize(encoder: Encoder, value: CborNull) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeNull()
+ }
+
+ override fun deserialize(decoder: Decoder): CborNull {
+ decoder.asCborDecoder()
+ if (decoder.decodeNotNullMark()) {
+ throw CborDecodingException("Expected 'null' literal")
+ }
+ decoder.decodeNull()
+ return CborNull()
+ }
+}
+
+internal object CborIntSerializer : KSerializer {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.serialization.cbor.CborInt", PrimitiveKind.LONG)
+
+ override fun serialize(encoder: Encoder, value: CborNegativeInt) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeLong(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborNegativeInt {
+ decoder.asCborDecoder()
+ return CborNegativeInt(decoder.decodeLong())
+ }
+}
+
+internal object CborUIntSerializer : KSerializer {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("CborUInt", PrimitiveKind.LONG)
+
+ override fun serialize(encoder: Encoder, value: CborPositiveInt) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeInline(descriptor).encodeSerializableValue(ULong.serializer(), value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborPositiveInt {
+ decoder.asCborDecoder()
+ return CborPositiveInt(decoder.decodeInline(descriptor).decodeSerializableValue(ULong.serializer()))
+ }
+}
+
+internal object CborDoubleSerializer : KSerializer {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlinx.serialization.cbor.CborDouble", PrimitiveKind.DOUBLE)
+
+ override fun serialize(encoder: Encoder, value: CborDouble) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeDouble(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborDouble {
+ decoder.asCborDecoder()
+ return CborDouble(decoder.decodeDouble())
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborString].
+ * It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborStringSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.cbor.CborString", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: CborString) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeString(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborString {
+ val cborDecoder = decoder.asCborDecoder()
+ val element = cborDecoder.decodeCborElement()
+ if (element !is CborString) throw CborDecodingException("Unexpected CBOR element, expected CborString, had ${element::class}")
+ return element
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborBoolean].
+ * It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborBooleanSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.cbor.CborBoolean", PrimitiveKind.BOOLEAN)
+
+ override fun serialize(encoder: Encoder, value: CborBoolean) {
+ encoder.asCborEncoder().encodeTags(value)
+ encoder.encodeBoolean(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborBoolean {
+ val cborDecoder = decoder.asCborDecoder()
+ val element = cborDecoder.decodeCborElement()
+ if (element !is CborBoolean) throw CborDecodingException("Unexpected CBOR element, expected CborBoolean, had ${element::class}")
+ return element
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborByteString].
+ * It can only be used by with [Cbor] format and its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborByteStringSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("kotlinx.serialization.cbor.CborByteString", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: CborByteString) {
+ val cborEncoder = encoder.asCborEncoder()
+ cborEncoder.encodeTags(value)
+ cborEncoder.encodeByteString(value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborByteString {
+ val cborDecoder = decoder.asCborDecoder()
+ val element = cborDecoder.decodeCborElement()
+ if (element !is CborByteString) throw CborDecodingException("Unexpected CBOR element, expected CborByteString, had ${element::class}")
+ return element
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborMap].
+ * It can only be used by with [Cbor] format and its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborMapSerializer : KSerializer {
+ private object CborMapDescriptor :
+ SerialDescriptor by MapSerializer(CborElementSerializer, CborElementSerializer).descriptor {
+ @ExperimentalSerializationApi
+ override val serialName: String = "kotlinx.serialization.cbor.CborMap"
+ }
+
+ override val descriptor: SerialDescriptor = CborMapDescriptor
+
+ override fun serialize(encoder: Encoder, value: CborMap) {
+ val cborEncoder = encoder.asCborEncoder()
+ cborEncoder.encodeTags(value)
+ MapSerializer(CborElementSerializer, CborElementSerializer).serialize(encoder, value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborMap {
+ decoder.asCborDecoder()
+ return CborMap(MapSerializer(CborElementSerializer, CborElementSerializer).deserialize(decoder))
+ }
+}
+
+/**
+ * Serializer object providing [SerializationStrategy] and [DeserializationStrategy] for [CborList].
+ * It can only be used by with [Cbor] format an its input ([CborDecoder] and [CborEncoder]).
+ */
+internal object CborListSerializer : KSerializer {
+ private object CborListDescriptor : SerialDescriptor by ListSerializer(CborElementSerializer).descriptor {
+ @ExperimentalSerializationApi
+ override val serialName: String = "kotlinx.serialization.cbor.CborList"
+ }
+
+ override val descriptor: SerialDescriptor = CborListDescriptor
+
+ override fun serialize(encoder: Encoder, value: CborList) {
+ val cborEncoder = encoder.asCborEncoder()
+ cborEncoder.encodeTags(value)
+ ListSerializer(CborElementSerializer).serialize(encoder, value)
+ }
+
+ override fun deserialize(decoder: Decoder): CborList {
+ decoder.asCborDecoder()
+ return CborList(ListSerializer(CborElementSerializer).deserialize(decoder))
+ }
+}
+
+
+internal fun Decoder.asCborDecoder(): CborDecoder = this as? CborDecoder
+ ?: throw IllegalStateException(
+ "This serializer can be used only with Cbor format." +
+ "Expected Decoder to be CborDecoder, got ${this::class}"
+ )
+
+/*need to expose writer to access encodeTag()*/
+internal fun Encoder.asCborEncoder() = this as? CborWriter
+ ?: throw IllegalStateException(
+ "This serializer can be used only with Cbor format." +
+ "Expected Encoder to be CborEncoder, got ${this::class}"
+ )
+
+/**
+ * Returns serial descriptor that delegates all the calls to descriptor returned by [deferred] block.
+ * Used to resolve cyclic dependencies between recursive serializable structures.
+ */
+@OptIn(ExperimentalSerializationApi::class)
+private fun defer(deferred: () -> SerialDescriptor): SerialDescriptor = object : SerialDescriptor {
+ private val original: SerialDescriptor by lazy(deferred)
+
+ override val serialName: String
+ get() = original.serialName
+ override val kind: SerialKind
+ get() = original.kind
+ override val elementsCount: Int
+ get() = original.elementsCount
+
+ override fun getElementName(index: Int): String = original.getElementName(index)
+ override fun getElementIndex(name: String): Int = original.getElementIndex(name)
+ override fun getElementAnnotations(index: Int): List = original.getElementAnnotations(index)
+ override fun getElementDescriptor(index: Int): SerialDescriptor = original.getElementDescriptor(index)
+ override fun isElementOptional(index: Int): Boolean = original.isElementOptional(index)
+}
+
+private fun CborWriter.encodeTags(value: CborElement) { // Encode tags if present
+ if (value.tags.isNotEmpty()) {
+ for (tag in value.tags) {
+ encodeTag(tag)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborParserInterface.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborParserInterface.kt
new file mode 100644
index 0000000000..14296e519a
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborParserInterface.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+
+/**
+ * Common interface for CBOR parsers that can read CBOR data from different sources.
+ */
+internal sealed interface CborParserInterface {
+ // Basic state checks
+ fun isNull(): Boolean
+ fun isEnd(): Boolean
+ fun end()
+
+ // Collection operations
+ fun startArray(tags: ULongArray? = null): Int
+ fun startMap(tags: ULongArray? = null): Int
+
+ // Value reading operations
+ fun nextNull(tags: ULongArray? = null): Nothing?
+ fun nextBoolean(tags: ULongArray? = null): Boolean
+ fun nextNumber(tags: ULongArray? = null): Long
+ fun nextString(tags: ULongArray? = null): String
+ fun nextByteString(tags: ULongArray? = null): ByteArray
+ fun nextDouble(tags: ULongArray? = null): Double
+ fun nextFloat(tags: ULongArray? = null): Float
+
+ // Map key operations
+ fun nextTaggedStringOrNumber(): Triple
+
+ // Skip operations
+ fun skipElement(tags: ULongArray?)
+
+ // Tag verification
+ fun verifyTagsAndThrow(expected: ULongArray, actual: ULongArray?)
+
+ // Additional methods needed for CborTreeReader
+ fun nextTag(): ULong
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborTreeReader.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborTreeReader.kt
new file mode 100644
index 0000000000..c5fe746454
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/CborTreeReader.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.*
+
+/**
+ * [CborTreeReader] reads CBOR data from [parser] and constructs a [CborElement] tree.
+ */
+internal class CborTreeReader(
+ //no config values make sense here, because we have no "schema".
+ //we cannot validate tags, or disregard nulls, can we?!
+ //still, this needs to go here, in case it evolves to a point where we need to respect certain config values
+ private val configuration: CborConfiguration,
+ private val parser: CborParser
+) {
+ /**
+ * Reads the next CBOR element from the parser.
+ */
+ fun read(): CborElement {
+ // Read any tags before the actual value
+ val tags = readTags()
+
+ val result = when (parser.curByte shr 5) { // Get major type from the first 3 bits
+ 0 -> { // Major type 0: unsigned integer
+ val value = parser.nextNumber()
+ CborPositiveInt(value.toULong(), tags)
+ }
+
+ 1 -> { // Major type 1: negative integer
+ val value = parser.nextNumber()
+ CborNegativeInt(value, tags)
+ }
+
+ 2 -> { // Major type 2: byte string
+ CborByteString(parser.nextByteString(), tags)
+ }
+
+ 3 -> { // Major type 3: text string
+ CborString(parser.nextString(), tags)
+ }
+
+ 4 -> { // Major type 4: array
+ readArray(tags)
+ }
+
+ 5 -> { // Major type 5: map
+ readMap(tags)
+ }
+
+ 7 -> { // Major type 7: simple/float/break
+ when (parser.curByte) {
+ 0xF4 -> {
+ parser.readByte() // Advance parser position
+ CborBoolean(false, tags)
+ }
+
+ 0xF5 -> {
+ parser.readByte() // Advance parser position
+ CborBoolean(true, tags)
+ }
+
+ 0xF6, 0xF7 -> {
+ parser.nextNull()
+ CborNull(tags)
+ }
+ // Half/Float32/Float64
+ NEXT_HALF, NEXT_FLOAT, NEXT_DOUBLE -> CborDouble(parser.nextDouble(), tags)
+ else -> throw CborDecodingException(
+ "Invalid simple value or float type: ${parser.curByte.toString(16)}"
+ )
+ }
+ }
+
+ else -> throw CborDecodingException("Invalid CBOR major type: ${parser.curByte shr 5}")
+ }
+ return result
+ }
+
+ /**
+ * Reads any tags preceding the current value.
+ * @return An array of tags, possibly empty
+ */
+ @OptIn(ExperimentalUnsignedTypes::class)
+ private fun readTags(): ULongArray {
+ val tags = mutableListOf()
+
+ // Read tags (major type 6) until we encounter a non-tag
+ while ((parser.curByte shr 5) == 6) { // Major type 6: tag
+ val tag = parser.nextTag()
+ tags.add(tag)
+ }
+
+ return tags.toULongArray()
+ }
+
+
+ private fun readArray(tags: ULongArray): CborList {
+ val size = parser.startArray()
+ val elements = mutableListOf()
+
+ if (size >= 0) {
+ // Definite length array
+ repeat(size) {
+ elements.add(read())
+ }
+ } else {
+ // Indefinite length array
+ while (!parser.isEnd()) {
+ elements.add(read())
+ }
+ parser.end()
+ }
+
+ return CborList(elements, tags)
+ }
+
+ private fun readMap(tags: ULongArray): CborMap {
+ val size = parser.startMap()
+ val elements = mutableMapOf()
+
+ if (size >= 0) {
+ // Definite length map
+ repeat(size) {
+ val key = read()
+ val value = read()
+ elements[key] = value
+ }
+ } else {
+ // Indefinite length map
+ while (!parser.isEnd()) {
+ val key = read()
+ val value = read()
+ elements[key] = value
+ }
+ parser.end()
+ }
+
+ return CborMap(elements, tags)
+ }
+}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt
index 88075db26f..ef4e818901 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt
@@ -12,9 +12,16 @@ import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.modules.*
-internal open class CborReader(override val cbor: Cbor, protected val parser: CborParser) : AbstractDecoder(),
+internal open class CborReader(override val cbor: Cbor, protected val parser: CborParserInterface) : AbstractDecoder(),
CborDecoder {
+ override fun decodeCborElement(): CborElement =
+ when(parser) {
+ is CborParser -> CborTreeReader(cbor.configuration, parser).read()
+ is StructuredCborParser -> parser.element
+ }
+
+
protected var size = -1
private set
protected var finiteMode = false
@@ -151,14 +158,14 @@ internal open class CborReader(override val cbor: Cbor, protected val parser: Cb
}
}
-internal class CborParser(private val input: ByteArrayInput, private val verifyObjectTags: Boolean) {
- private var curByte: Int = -1
+internal class CborParser(private val input: ByteArrayInput, private val verifyObjectTags: Boolean) : CborParserInterface {
+ var curByte: Int = -1
init {
readByte()
}
- private fun readByte(): Int {
+ fun readByte(): Int {
curByte = input.read()
return curByte
}
@@ -170,9 +177,35 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
readByte()
}
- fun isNull() = (curByte == NULL || curByte == EMPTY_MAP)
+ override fun isNull() = (curByte == NULL || curByte == EMPTY_MAP || curByte == -1)
+
+ // Add this method to CborParser class
+ private fun readUnsignedValueFromAdditionalInfo(additionalInfo: Int): Long {
+ return when (additionalInfo) {
+ in 0..23 -> additionalInfo.toLong()
+ 24 -> {
+ val nextByte = readByte()
+ if (nextByte == -1) throw CborDecodingException("Unexpected EOF")
+ nextByte.toLong() and 0xFF
+ }
+
+ 25 -> input.readExact(2)
+ 26 -> input.readExact(4)
+ 27 -> input.readExact(8)
+ else -> throw CborDecodingException("Invalid additional info: $additionalInfo")
+ }
+ }
+
+ override fun nextTag(): ULong {
+ if ((curByte shr 5) != 6) {
+ throw CborDecodingException("Expected tag (major type 6), got major type ${curByte shr 5}")
+ }
+
+ val additionalInfo = curByte and 0x1F
+ return readUnsignedValueFromAdditionalInfo(additionalInfo).toULong().also { skipByte(curByte) }
+ }
- fun nextNull(tags: ULongArray? = null): Nothing? {
+ override fun nextNull(tags: ULongArray?): Nothing? {
processTags(tags)
if (curByte == NULL) {
skipByte(NULL)
@@ -182,7 +215,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return null
}
- fun nextBoolean(tags: ULongArray? = null): Boolean {
+ override fun nextBoolean(tags: ULongArray?): Boolean {
processTags(tags)
val ans = when (curByte) {
TRUE -> true
@@ -193,9 +226,9 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return ans
}
- fun startArray(tags: ULongArray? = null) = startSized(tags, BEGIN_ARRAY, HEADER_ARRAY, "array")
+ override fun startArray(tags: ULongArray?) = startSized(tags, BEGIN_ARRAY, HEADER_ARRAY, "array")
- fun startMap(tags: ULongArray? = null) = startSized(tags, BEGIN_MAP, HEADER_MAP, "map")
+ override fun startMap(tags: ULongArray?) = startSized(tags, BEGIN_MAP, HEADER_MAP, "map")
private fun startSized(
tags: ULongArray?,
@@ -215,11 +248,11 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return size
}
- fun isEnd() = curByte == BREAK
+ override fun isEnd() = curByte == BREAK
- fun end() = skipByte(BREAK)
+ override fun end() = skipByte(BREAK)
- fun nextByteString(tags: ULongArray? = null): ByteArray {
+ override fun nextByteString(tags: ULongArray?): ByteArray {
processTags(tags)
if ((curByte and 0b111_00000) != HEADER_BYTE_STRING)
throw CborDecodingException("start of byte string", curByte)
@@ -228,7 +261,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return arr
}
- fun nextString(tags: ULongArray? = null) = nextTaggedString(tags).first
+ override fun nextString(tags: ULongArray?) = nextTaggedString(tags).first
//used for reading the tag names and names of tagged keys (of maps, and serialized classes)
private fun nextTaggedString(tags: ULongArray?): Pair {
@@ -282,7 +315,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
}
}
- internal fun verifyTagsAndThrow(expected: ULongArray, actual: ULongArray?) {
+ override fun verifyTagsAndThrow(expected: ULongArray, actual: ULongArray?) {
if (!expected.contentEquals(actual))
throw CborDecodingException(
"CBOR tags ${actual?.contentToString()} do not match expected tags ${expected.contentToString()}"
@@ -292,7 +325,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
/**
* Used for reading the tags and either string (element name) or number (serial label)
*/
- fun nextTaggedStringOrNumber(): Triple {
+ override fun nextTaggedStringOrNumber(): Triple {
val collectedTags = processTags(null)
if ((curByte and 0b111_00000) == HEADER_STRING) {
val arr = readBytes()
@@ -306,7 +339,8 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
}
}
- fun nextNumber(tags: ULongArray? = null): Long {
+
+ override fun nextNumber(tags: ULongArray?): Long {
processTags(tags)
val res = readNumber()
readByte()
@@ -314,22 +348,11 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
}
private fun readNumber(): Long {
- val value = curByte and 0b000_11111
+ val additionalInfo = curByte and 0b000_11111
val negative = (curByte and 0b111_00000) == HEADER_NEGATIVE.toInt()
- val bytesToRead = when (value) {
- 24 -> 1
- 25 -> 2
- 26 -> 4
- 27 -> 8
- else -> 0
- }
- if (bytesToRead == 0) {
- return if (negative) -(value + 1).toLong()
- else value.toLong()
- }
- val res = input.readExact(bytesToRead)
- return if (negative) -(res + 1)
- else res
+
+ val value = readUnsignedValueFromAdditionalInfo(additionalInfo)
+ return if (negative) -(value + 1) else value
}
private fun ByteArrayInput.readExact(bytes: Int): Long {
@@ -350,7 +373,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return array
}
- fun nextFloat(tags: ULongArray? = null): Float {
+ override fun nextFloat(tags: ULongArray?): Float {
processTags(tags)
val res = when (curByte) {
NEXT_FLOAT -> Float.fromBits(readInt())
@@ -361,7 +384,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
return res
}
- fun nextDouble(tags: ULongArray? = null): Double {
+ override fun nextDouble(tags: ULongArray?): Double {
processTags(tags)
val res = when (curByte) {
NEXT_DOUBLE -> Double.fromBits(readLong())
@@ -409,7 +432,7 @@ internal class CborParser(private val input: ByteArrayInput, private val verifyO
* been skipped, the "length stack" is [pruned][prune]. For indefinite length elements, a special marker is added to
* the "length stack" which is only popped from the "length stack" when a CBOR [break][isEnd] is encountered.
*/
- fun skipElement(tags: ULongArray?) {
+ override fun skipElement(tags: ULongArray?) {
val lengthStack = mutableListOf()
processTags(tags)
@@ -531,14 +554,179 @@ private fun Iterable.flatten(): ByteArray {
return output
}
+private typealias ElementHolder = Pair, CborElement>
+private val ElementHolder.tags: MutableList get() = first
+private val ElementHolder.element: CborElement get() = second
+internal class StructuredCborParser(val element: CborElement, private val verifyObjectTags: Boolean) : CborParserInterface {
+
+
+ internal var current: ElementHolder = element.tags.toMutableList() to element
+ private var listIterator: Iterator? = null
-private class CborMapReader(cbor: Cbor, decoder: CborParser) : CborListReader(cbor, decoder) {
+ // Implementation of methods needed for CborTreeReader
+ override fun nextTag(): ULong {
+ if (current.tags.isEmpty()) {
+ throw CborDecodingException("Expected tag, but no tags found on current element")
+ }
+ return current.tags.removeFirst()
+ }
+
+ override fun isNull() : Boolean {
+ //TODO this is a bit wonky! if we are inside a map, we want to skip over the key, and check the value,
+ // so the below call is not what it should be!
+ processTags(null)
+ return current.element is CborNull
+ }
+
+ override fun isEnd() = when {
+ listIterator != null -> !listIterator!!.hasNext()
+ else -> false
+ }
+
+ override fun end() {
+ // Reset iterators when ending a structure
+ listIterator = null
+ }
+
+ override fun startArray(tags: ULongArray?): Int {
+ processTags(tags)
+ if (current.element !is CborList) {
+ throw CborDecodingException("Expected array, got ${current.element::class.simpleName}")
+ }
+
+ val list = current.element as CborList
+ listIterator = list.iterator()
+ return list.size
+ }
+
+ override fun startMap(tags: ULongArray?): Int {
+ processTags(tags)
+ if (current.element !is CborMap) {
+ throw CborDecodingException("Expected map, got ${current.element::class.simpleName}")
+ }
+
+ val map = current.element as CborMap
+ //zip key, value, key, value, ... pairs to mirror byte-layout of CBOR map
+ listIterator = map.entries.flatMap { listOf(it.key, it.value) }.iterator()
+ return map.size //cbor map size is the size of the map, not the doubled size of the flattened pairs
+ }
+
+ override fun nextNull(tags: ULongArray?): Nothing? {
+ processTags(tags)
+ if (current.element !is CborNull) {
+ throw CborDecodingException("Expected null, got ${current.element::class.simpleName}")
+ }
+ return null
+ }
+
+ override fun nextBoolean(tags: ULongArray?): Boolean {
+ processTags(tags)
+ if (current.element !is CborBoolean) {
+ throw CborDecodingException("Expected boolean, got ${current.element::class.simpleName}")
+ }
+ return (current.element as CborBoolean).value
+ }
+
+ override fun nextNumber(tags: ULongArray?): Long {
+ processTags(tags)
+ return when (current.element) {
+ is CborPositiveInt -> (current.element as CborPositiveInt).value.toLong()
+ is CborNegativeInt -> (current.element as CborNegativeInt).value
+ else -> throw CborDecodingException("Expected number, got ${current.element::class.simpleName}")
+ }
+ }
+
+ override fun nextString(tags: ULongArray?): String {
+ processTags(tags)
+
+ // Special handling for polymorphic serialization
+ // If we have a CborList with a string as first element, return that string
+ if (current.element is CborList && (current.element as CborList).isNotEmpty() && (current.element as CborList)[0] is CborString) {
+ val stringElement = (current.element as CborList)[0] as CborString
+ // Move to the next element (the map) for subsequent operations
+ current = (current.element as CborList)[1].tags.toMutableList() to (current.element as CborList)[1]
+ return stringElement.value
+ }
+
+ if (current.element !is CborString) {
+ throw CborDecodingException("Expected string, got ${current.element::class.simpleName}")
+ }
+ return (current.element as CborString).value
+ }
+
+ override fun nextByteString(tags: ULongArray?): ByteArray {
+ processTags(tags)
+ if (current.element !is CborByteString) {
+ throw CborDecodingException("Expected byte string, got ${current.element::class.simpleName}")
+ }
+ return (current.element as CborByteString).value
+ }
+
+ override fun nextDouble(tags: ULongArray?): Double {
+ processTags(tags)
+ return when (current.element) {
+ is CborDouble -> (current.element as CborDouble).value
+ else -> throw CborDecodingException("Expected double, got ${current.element::class.simpleName}")
+ }
+ }
+
+ override fun nextFloat(tags: ULongArray?): Float {
+ return nextDouble(tags).toFloat()
+ }
+
+ override fun nextTaggedStringOrNumber(): Triple {
+ val tags = processTags(null)
+
+ return when (val key = current.element) {
+ is CborString -> Triple(key.value, null, tags)
+ is CborPositiveInt -> Triple(null, key.value.toLong(), tags)
+ is CborNegativeInt -> Triple(null, key.value, tags)
+ else -> throw CborDecodingException("Expected string or number key, got ${key?.let { it::class.simpleName } ?: "null"}")
+ }
+ }
+
+ private fun processTags(tags: ULongArray?): ULongArray? {
+
+ // If we're in a list, advance to the next element
+ if (listIterator != null && listIterator!!.hasNext()) {
+ listIterator!!.next().let { current = it.tags.toMutableList() to it }
+ }
+
+ // Store collected tags for verification
+ val collectedTags = if (current.tags.isEmpty()) null else current.tags.toULongArray()
+
+ // Verify tags if needed
+ if (verifyObjectTags) {
+ tags?.let {
+ verifyTagsAndThrow(it, collectedTags)
+ }
+ }
+
+ return collectedTags
+ }
+
+ override fun verifyTagsAndThrow(expected: ULongArray, actual: ULongArray?) {
+ if (!expected.contentEquals(actual)) {
+ throw CborDecodingException(
+ "CBOR tags ${actual?.contentToString()} do not match expected tags ${expected.contentToString()}"
+ )
+ }
+ }
+
+ override fun skipElement(tags: ULongArray?) {
+ // Process tags but don't do anything with the element
+ processTags(tags)
+ }
+}
+
+
+private class CborMapReader(cbor: Cbor, decoder: CborParserInterface) : CborListReader(cbor, decoder) {
override fun skipBeginToken(objectTags: ULongArray?) =
setSize(parser.startMap(tags?.let { if (objectTags == null) it else ulongArrayOf(*it, *objectTags) }
- ?: objectTags) * 2)
+ ?: objectTags))
}
-private open class CborListReader(cbor: Cbor, decoder: CborParser) : CborReader(cbor, decoder) {
+private open class CborListReader(cbor: Cbor, decoder: CborParserInterface) : CborReader(cbor, decoder) {
private var ind = 0
override fun skipBeginToken(objectTags: ULongArray?) =
@@ -553,7 +741,6 @@ private open class CborListReader(cbor: Cbor, decoder: CborParser) : CborReader(
}
}
-
private val normalizeBaseBits = SINGLE_PRECISION_NORMALIZE_BASE.toBits()
@@ -617,4 +804,4 @@ private fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
" You can enable 'CborBuilder.ignoreUnknownKeys' property to ignore unknown keys"
)
return index
-}
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt
index eb5fc556a2..94a369c3a4 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt
@@ -26,15 +26,19 @@ private fun Stack.peek() = last()
// Split implementation to optimize base case
internal sealed class CborWriter(
override val cbor: Cbor,
- protected val output: ByteArrayOutput,
) : AbstractEncoder(), CborEncoder {
+
+ internal open fun encodeByteString(byteArray: ByteArray) {
+ getDestination().encodeByteString(byteArray)
+ }
+
protected var isClass = false
protected var encodeByteArrayAsByteString = false
class Data(val bytes: ByteArrayOutput, var elementCount: Int)
- protected abstract fun getDestination(): ByteArrayOutput
+ internal abstract fun getDestination(): ByteArrayOutput
override val serializersModule: SerializersModule
get() = cbor.serializersModule
@@ -46,7 +50,7 @@ internal sealed class CborWriter(
if ((encodeByteArrayAsByteString || cbor.configuration.alwaysUseByteString)
&& serializer.descriptor == ByteArraySerializer().descriptor
) {
- getDestination().encodeByteString(value as ByteArray)
+ encodeByteString(value as ByteArray)
} else {
encodeByteArrayAsByteString = encodeByteArrayAsByteString || serializer.descriptor.isInlineByteString()
super.encodeSerializableValue(serializer, value)
@@ -143,12 +147,14 @@ internal sealed class CborWriter(
incrementChildren() // needed for definite len encoding, NOOP for indefinite length encoding
return true
}
+
+ internal fun encodeTag(tag: ULong) = getDestination().encodeTag(tag)
}
// optimized indefinite length encoder
-internal class IndefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) : CborWriter(
- cbor, output
+internal class IndefiniteLengthCborWriter(cbor: Cbor, private val output: ByteArrayOutput) : CborWriter(
+ cbor
) {
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
@@ -179,8 +185,167 @@ internal class IndefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) :
}
+// optimized indefinite length encoder
+internal class StructuredCborWriter(cbor: Cbor) : CborWriter(
+ cbor
+) {
+
+ sealed class CborContainer(tags: ULongArray, elements: MutableList) {
+ var elements = elements
+ private set
+
+ var tags = tags
+ internal set
+
+
+ open fun add(element: CborElement) = elements.add(element)
+ class Map(tags: ULongArray, elements: MutableList = mutableListOf()) :
+ CborContainer(tags, elements)
+
+ class List(tags: ULongArray, elements: MutableList = mutableListOf()) :
+ CborContainer(tags, elements)
+
+ class Primitive(tags: ULongArray) : CborContainer(tags, elements = mutableListOf()) {
+ override fun add(element: CborElement): Boolean {
+ require(elements.isEmpty()) {"Implementation error. Please report a bug."}
+ return elements.add(element)
+ }
+ }
+
+ fun finalize() = when (this) {
+ is List -> CborList(content = elements, tags = tags)
+ is Map -> CborMap(
+ content = if (elements.isNotEmpty()) IntRange(0, elements.size / 2 - 1).associate {
+ elements[it * 2] to elements[it * 2 + 1]
+ } else mapOf(),
+ tags = tags
+ )
+
+ is Primitive -> elements.first().also { it.tags += tags }
+
+ }
+ }
+
+ private val stack = ArrayDeque()
+ private var currentElement: CborContainer? = null
+
+ fun finalize() = currentElement!!.finalize()
+
+ override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+ //TODO check if cborelement and be done
+ val tags = descriptor.getObjectTags() ?: ulongArrayOf()
+ val element = if (descriptor.hasArrayTag()) {
+ CborContainer.List(tags)
+ } else {
+ when (descriptor.kind) {
+ StructureKind.LIST, is PolymorphicKind -> CborContainer.List(tags)
+ is StructureKind.MAP -> CborContainer.Map(tags)
+ else -> CborContainer.Map(tags)
+ }
+ }
+ currentElement?.let { stack.add(it) }
+ currentElement = element
+ return this
+ }
+
+ override fun endStructure(descriptor: SerialDescriptor) {
+ val finalized = currentElement!!.finalize()
+ if (stack.isNotEmpty()) {
+ currentElement = stack.removeLast()
+ currentElement!!.add(finalized)
+ }
+ }
+
+ override fun getDestination() = throw IllegalStateException("There is not byteArrayOutput")
+
+
+ override fun incrementChildren() {/*NOOP*/
+ }
+
+
+ override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+ //we don't care for special encoding of an empty class, so we don't set this flag here
+ // isClass = descriptor.getElementDescriptor(index).kind == StructureKind.CLASS
+ encodeByteArrayAsByteString = descriptor.isByteString(index)
+ //TODO check if cborelement and be done
+ val name = descriptor.getElementName(index)
+ if (!descriptor.hasArrayTag()) {
+ val keyTags = descriptor.getKeyTags(index)
+
+ if ((descriptor.kind !is StructureKind.LIST) && (descriptor.kind !is StructureKind.MAP) && (descriptor.kind !is PolymorphicKind)) {
+ //indices are put into the name field. we don't want to write those, as it would result in double writes
+ val cborLabel = descriptor.getCborLabel(index)
+ if (cbor.configuration.preferCborLabelsOverNames && cborLabel != null) {
+ currentElement!!.add(
+ CborInt.invoke(value = cborLabel, tags = keyTags ?: ulongArrayOf())
+ )
+ } else {
+ currentElement!!.add(CborString(name, keyTags ?: ulongArrayOf()))
+ }
+ }
+ }
+
+ if (cbor.configuration.encodeValueTags) {
+ descriptor.getValueTags(index)?.let { valueTags ->
+ currentElement!!.tags += valueTags
+ }
+ }
+ return true
+ }
+
+
+ override fun encodeBoolean(value: Boolean) {
+ currentElement!!.add(CborBoolean(value))
+ }
+
+ override fun encodeByte(value: Byte) {
+ currentElement!!.add(CborInt(value.toLong()))
+ }
+
+ override fun encodeChar(value: Char) {
+ currentElement!!.add(CborInt(value.code.toLong()))
+ }
+
+ override fun encodeDouble(value: Double) {
+ currentElement!!.add(CborDouble(value))
+ }
+
+ override fun encodeFloat(value: Float) {
+ currentElement!!.add(CborDouble(value.toDouble()))
+ }
+
+ override fun encodeInt(value: Int) {
+ currentElement!!.add(CborInt(value.toLong()))
+ }
+
+ override fun encodeLong(value: Long) {
+ currentElement!!.add(CborInt(value))
+ }
+
+ override fun encodeShort(value: Short) {
+ currentElement!!.add(CborInt(value.toLong()))
+ }
+
+ override fun encodeString(value: String) {
+ currentElement!!.add(CborString(value))
+ }
+
+ override fun encodeByteString(byteArray: ByteArray) {
+ currentElement!!.add(CborByteString(byteArray))
+ }
+
+ override fun encodeNull() {
+ currentElement!!.add(CborNull())
+ }
+
+ override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
+ currentElement!!.add(CborString(enumDescriptor.getElementName(index)))
+ }
+
+}
+
//optimized definite length encoder
-internal class DefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) : CborWriter(cbor, output) {
+internal class DefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) : CborWriter(cbor) {
private val structureStack = Stack(Data(output, -1))
override fun getDestination(): ByteArrayOutput =
@@ -234,7 +399,7 @@ private fun ByteArrayOutput.startMap(size: ULong) {
composePositiveInline(size, HEADER_MAP)
}
-private fun ByteArrayOutput.encodeTag(tag: ULong) {
+internal fun ByteArrayOutput.encodeTag(tag: ULong) {
composePositiveInline(tag, HEADER_TAG)
}
@@ -329,4 +494,3 @@ private fun composeNegative(value: Long): ByteArray {
data[0] = data[0] or HEADER_NEGATIVE
return data
}
-
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt
index 92aee674be..c1d4d528b5 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt
@@ -17,7 +17,10 @@ class CborDecoderTest {
@Test
fun testDecodeSimpleObject() {
- assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), "bf616163737472ff"))
+ val hex = "bf616163737472ff"
+ assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), hex))
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(Simple("str"), Cbor.decodeFromCbor(Simple.serializer(), struct))
}
@Test
@@ -34,12 +37,19 @@ class CborDecoderTest {
HexConverter.parseHexBinary("cafe")
)
// with maps, lists & strings of indefinite length
+ val hex =
+ "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
assertEquals(
test, Cbor.decodeFromHexString(
TypesUmbrella.serializer(),
- "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
+ hex
)
)
+
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(test, Cbor.decodeFromCbor(TypesUmbrella.serializer(), struct))
+
+
// with maps, lists & strings of definite length
assertEquals(
test, Cbor.decodeFromHexString(
@@ -57,31 +67,43 @@ class CborDecoderTest {
* 44 # bytes(4)
* 01020304 # "\x01\x02\x03\x04"
*/
+ val hex = "a16a62797465537472696e674401020304"
+ val expected = NullableByteString(byteArrayOf(1, 2, 3, 4))
assertEquals(
- expected = NullableByteString(byteArrayOf(1, 2, 3, 4)),
+ expected = expected,
actual = Cbor.decodeFromHexString(
deserializer = NullableByteString.serializer(),
- hex = "a16a62797465537472696e674401020304"
+ hex = hex
)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, Cbor.decodeFromCbor(NullableByteString.serializer(), struct))
+
/* A1 # map(1)
* 6A # text(10)
* 62797465537472696E67 # "byteString"
* F6 # primitive(22)
*/
+ val hexNull = "a16a62797465537472696e67f6"
+ val expectedNull = NullableByteString(byteString = null)
assertEquals(
- expected = NullableByteString(byteString = null),
+ expected = expectedNull,
actual = Cbor.decodeFromHexString(
deserializer = NullableByteString.serializer(),
- hex = "a16a62797465537472696e67f6"
+ hex = hexNull
)
)
+
+ val structNull = Cbor.decodeFromHexString(hexNull)
+ assertEquals(expectedNull, Cbor.decodeFromCbor(NullableByteString.serializer(), structNull))
}
@Test
fun testNullables() {
Cbor.decodeFromHexString("a0")
+ val struct = Cbor.decodeFromHexString("a0")
+ assertEquals(NullableByteStringDefaultNull(), Cbor.decodeFromCbor(NullableByteStringDefaultNull.serializer(), struct))
}
/**
@@ -171,8 +193,10 @@ class CborDecoderTest {
@Test
fun testDecodeCborWithUnknownField() {
+ val hex = "bf616163313233616263393837ff"
+ val expected = Simple("123")
assertEquals(
- expected = Simple("123"),
+ expected = expected,
actual = ignoreUnknownKeys.decodeFromHexString(
deserializer = Simple.serializer(),
@@ -187,15 +211,20 @@ class CborDecoderTest {
* 393837 # "987"
* FF # primitive(*)
*/
- hex = "bf616163313233616263393837ff"
+ hex = hex
)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, ignoreUnknownKeys.decodeFromCbor(Simple.serializer(), struct))
+
}
@Test
fun testDecodeCborWithUnknownNestedIndefiniteFields() {
+ val hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
+ val expected = Simple("123")
assertEquals(
- expected = Simple("123"),
+ expected = expected,
actual = ignoreUnknownKeys.decodeFromHexString(
deserializer = Simple.serializer(),
@@ -225,9 +254,12 @@ class CborDecoderTest {
* FF # primitive(*)
* FF # primitive(*)
*/
- hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
+ hex = hex
)
)
+
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, ignoreUnknownKeys.decodeFromCbor(Simple.serializer(), struct))
}
/**
@@ -308,70 +340,107 @@ class CborDecoderTest {
* FF # primitive(*)
*/
+ val expected = SealedBox(
+ listOf(
+ SubSealedA("a"),
+ SubSealedB(1)
+ )
+ )
+ val hex =
+ "bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
assertEquals(
- expected = SealedBox(
- listOf(
- SubSealedA("a"),
- SubSealedB(1)
- )
- ),
+ expected = expected,
actual = ignoreUnknownKeys.decodeFromHexString(
SealedBox.serializer(),
- "bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
+ hex
)
)
+ val ref = ignoreUnknownKeys.encodeToCbor(expected)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, ignoreUnknownKeys.decodeFromCbor(SealedBox.serializer(), struct))
+
}
@Test
fun testReadCustomByteString() {
+ val expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33))
+ val hex = "bf617843112233ff"
assertEquals(
- expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
- actual = Cbor.decodeFromHexString("bf617843112233ff")
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, Cbor.decodeFromCbor(TypeWithCustomByteString.serializer(), struct))
+
}
@Test
fun testReadNullableCustomByteString() {
+ val hex = "bf617843112233ff"
+ val expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33))
assertEquals(
- expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
- actual = Cbor.decodeFromHexString("bf617843112233ff")
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, Cbor.decodeFromCbor(TypeWithNullableCustomByteString.serializer(), struct))
+
}
@Test
fun testReadNullCustomByteString() {
+ val hex = "bf6178f6ff"
+ val expected = TypeWithNullableCustomByteString(null)
assertEquals(
- expected = TypeWithNullableCustomByteString(null),
- actual = Cbor.decodeFromHexString("bf6178f6ff")
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, Cbor.decodeFromCbor(TypeWithNullableCustomByteString.serializer(), struct))
+
}
@Test
fun testReadValueClassWithByteString() {
+ val expected = byteArrayOf(0x11, 0x22, 0x33)
+ val hex = "43112233"
assertContentEquals(
- expected = byteArrayOf(0x11, 0x22, 0x33),
- actual = Cbor.decodeFromHexString("43112233").x
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex).x
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertContentEquals(expected, Cbor.decodeFromCbor(ValueClassWithByteString.serializer(), struct).x)
+
}
@Test
fun testReadValueClassCustomByteString() {
+ val expected = ValueClassWithCustomByteString(CustomByteString(0x11, 0x22, 0x33))
+ val hex = "43112233"
assertEquals(
- expected = ValueClassWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
- actual = Cbor.decodeFromHexString("43112233")
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex)
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertEquals(expected, Cbor.decodeFromCbor(ValueClassWithCustomByteString.serializer(), struct))
+
}
@Test
fun testReadValueClassWithUnlabeledByteString() {
+ val expected = byteArrayOf(
+ 0x11,
+ 0x22,
+ 0x33
+ )
+ val hex = "43112233"
assertContentEquals(
- expected = byteArrayOf(
- 0x11,
- 0x22,
- 0x33
- ),
- actual = Cbor.decodeFromHexString("43112233").x.x
+ expected = expected,
+ actual = Cbor.decodeFromHexString(hex).x.x
)
+ val struct = Cbor.decodeFromHexString(hex)
+ assertContentEquals(expected, Cbor.decodeFromCbor(ValueClassWithUnlabeledByteString.serializer(), struct).x.x)
+
}
}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementEqualityTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementEqualityTest.kt
new file mode 100644
index 0000000000..08868ebc9e
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementEqualityTest.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2017-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlin.test.*
+
+class CborElementEqualityTest {
+
+ @Test
+ fun testCborPositiveIntEquality() {
+ val int1 = CborPositiveInt(42u)
+ val int2 = CborPositiveInt(42u)
+ val int3 = CborPositiveInt(43u)
+ val int4 = CborPositiveInt(42u, ulongArrayOf(1u))
+
+ // Same values should be equal
+ assertEquals(int1, int2)
+ assertEquals(int1.hashCode(), int2.hashCode())
+
+ // Different values should not be equal
+ assertNotEquals(int1, int3)
+
+ // Different tags should not be equal
+ assertNotEquals(int1, int4)
+
+ // Null comparison
+ assertNotEquals(int1, null as CborElement?)
+
+ // Different type comparison
+ assertNotEquals(int1 as CborElement, CborString("42"))
+ assertNotEquals(int1, CborString("42") as CborElement)
+ }
+
+ @Test
+ fun testCborNegativeIntEquality() {
+ val int1 = CborNegativeInt(-42)
+ val int2 = CborNegativeInt(-42)
+ val int3 = CborNegativeInt(-43)
+ val int4 = CborNegativeInt(-42, ulongArrayOf(1u))
+
+ assertEquals(int1, int2)
+ assertEquals(int1.hashCode(), int2.hashCode())
+ assertNotEquals(int1, int3)
+ assertNotEquals(int1, int4)
+ assertNotEquals(int1, null as CborElement?)
+ assertNotEquals(int1, CborPositiveInt(42u) as CborElement)
+ assertNotEquals(int1 as CborElement, CborPositiveInt(42u))
+ }
+
+ @Test
+ fun testCborDoubleEquality() {
+ val double1 = CborDouble(3.14)
+ val double2 = CborDouble(3.14)
+ val double3 = CborDouble(2.71)
+ val double4 = CborDouble(3.14, ulongArrayOf(1u))
+
+ assertEquals(double1, double2)
+ assertEquals(double1.hashCode(), double2.hashCode())
+ assertNotEquals(double1, double3)
+ assertNotEquals(double1, double4)
+ assertNotEquals(double1, null as CborElement?)
+ assertNotEquals(double1 as CborElement, CborString("3.14"))
+ assertNotEquals(double1, CborString("3.14") as CborElement)
+ }
+
+ @Test
+ fun testCborStringEquality() {
+ val string1 = CborString("hello")
+ val string2 = CborString("hello")
+ val string3 = CborString("world")
+ val string4 = CborString("hello", ulongArrayOf(1u))
+
+ assertEquals(string1, string2)
+ assertEquals(string1.hashCode(), string2.hashCode())
+ assertNotEquals(string1, string3)
+ assertNotEquals(string1, string4)
+ assertNotEquals(string1, null as CborElement?)
+ assertNotEquals(string1 as CborElement, CborPositiveInt(123u))
+ assertNotEquals(string1, CborPositiveInt(123u) as CborElement)
+ }
+
+ @Test
+ fun testCborBooleanEquality() {
+ val bool1 = CborBoolean(true)
+ val bool2 = CborBoolean(true)
+ val bool3 = CborBoolean(false)
+ val bool4 = CborBoolean(true, ulongArrayOf(1u))
+
+ assertEquals(bool1, bool2)
+ assertEquals(bool1.hashCode(), bool2.hashCode())
+ assertNotEquals(bool1, bool3)
+ assertNotEquals(bool1, bool4)
+ assertNotEquals(bool1, null as CborElement?)
+ assertNotEquals(bool1 as CborElement, CborString("true"))
+ assertNotEquals(bool1, CborString("true") as CborElement)
+ }
+
+ @Test
+ fun testCborByteStringEquality() {
+ val bytes1 = byteArrayOf(1, 2, 3)
+ val bytes2 = byteArrayOf(1, 2, 3)
+ val bytes3 = byteArrayOf(4, 5, 6)
+
+ val byteString1 = CborByteString(bytes1)
+ val byteString2 = CborByteString(bytes2)
+ val byteString3 = CborByteString(bytes3)
+ val byteString4 = CborByteString(bytes1, ulongArrayOf(1u))
+
+ assertEquals(byteString1, byteString2)
+ assertEquals(byteString1.hashCode(), byteString2.hashCode())
+ assertNotEquals(byteString1, byteString3)
+ assertNotEquals(byteString1, byteString4)
+ assertNotEquals(byteString1, null as CborElement?)
+ assertNotEquals(byteString1 as CborElement, CborString("123"))
+ assertNotEquals(byteString1, CborString("123") as CborElement)
+ }
+
+ @Test
+ fun testCborNullEquality() {
+ val null1 = CborNull()
+ val null2 = CborNull()
+ val null3 = CborNull(ulongArrayOf(1u))
+
+ assertEquals(null1, null2)
+ assertEquals(null1.hashCode(), null2.hashCode())
+ assertNotEquals(null1, null3)
+ assertNotEquals(null1, null as CborElement?)
+ assertNotEquals(null1 as CborElement, CborString("null"))
+ assertNotEquals(null1, CborString("null") as CborElement)
+ }
+
+ @Test
+ fun testCborListEquality() {
+ val list1 = CborList(listOf(CborPositiveInt(1u), CborString("test")))
+ val list2 = CborList(listOf(CborPositiveInt(1u), CborString("test")))
+ val list3 = CborList(listOf(CborPositiveInt(2u), CborString("test")))
+ val list4 = CborList(listOf(CborPositiveInt(1u), CborString("test")), ulongArrayOf(1u))
+ val list5 = CborList(listOf(CborPositiveInt(1u)))
+
+ assertEquals(list1, list2)
+ assertEquals(list1.hashCode(), list2.hashCode())
+ assertNotEquals(list1, list3)
+ assertNotEquals(list1, list4)
+ assertNotEquals(list1, list5)
+ assertNotEquals(list1, null as CborElement?)
+ assertNotEquals(list1 as CborElement, CborString("list"))
+ assertNotEquals(list1, CborString("list") as CborElement)
+ }
+
+ @Test
+ fun testCborMapEquality() {
+ val map1 = CborMap(mapOf(
+ CborString("key1") to CborPositiveInt(1u),
+ CborString("key2") to CborString("value")
+ ))
+ val map2 = CborMap(mapOf(
+ CborString("key1") to CborPositiveInt(1u),
+ CborString("key2") to CborString("value")
+ ))
+ val map3 = CborMap(mapOf(
+ CborString("key1") to CborPositiveInt(2u),
+ CborString("key2") to CborString("value")
+ ))
+ val map4 = CborMap(mapOf(
+ CborString("key1") to CborPositiveInt(1u),
+ CborString("key2") to CborString("value")
+ ), ulongArrayOf(1u))
+ val map5 = CborMap(mapOf(
+ CborString("key1") to CborPositiveInt(1u)
+ ))
+
+ assertEquals(map1, map2)
+ assertEquals(map1.hashCode(), map2.hashCode())
+ assertNotEquals(map1, map3)
+ assertNotEquals(map1, map4)
+ assertNotEquals(map1, map5)
+ assertNotEquals(map1, null as CborElement?)
+ assertNotEquals(map1 as CborElement, CborString("map"))
+ assertNotEquals(map1, CborString("map") as CborElement)
+ }
+
+ @Test
+ fun testTagsEquality() {
+ val tags1 = ulongArrayOf(1u, 2u, 3u)
+ val tags2 = ulongArrayOf(1u, 2u, 3u)
+ val tags3 = ulongArrayOf(1u, 2u, 4u)
+
+ val string1 = CborString("test", tags1)
+ val string2 = CborString("test", tags2)
+ val string3 = CborString("test", tags3)
+
+ assertEquals(string1, string2)
+ assertEquals(string1.hashCode(), string2.hashCode())
+ assertNotEquals(string1, string3)
+ }
+
+ @Test
+ fun testEmptyCollectionsEquality() {
+ val emptyList1 = CborList(emptyList())
+ val emptyList2 = CborList(emptyList())
+ val emptyMap1 = CborMap(emptyMap())
+ val emptyMap2 = CborMap(emptyMap())
+
+ assertEquals(emptyList1, emptyList2)
+ assertEquals(emptyList1.hashCode(), emptyList2.hashCode())
+ assertEquals(emptyMap1, emptyMap2)
+ assertEquals(emptyMap1.hashCode(), emptyMap2.hashCode())
+ assertNotEquals(emptyList1 as CborElement, emptyMap1)
+ assertNotEquals(emptyList1, emptyMap1 as CborElement)
+ }
+
+ @Test
+ fun testNestedStructureEquality() {
+ val nested1 = CborMap(mapOf(
+ CborString("list") to CborList(listOf(
+ CborPositiveInt(1u),
+ CborMap(mapOf(CborString("inner") to CborNull()))
+ ))
+ ))
+ val nested2 = CborMap(mapOf(
+ CborString("list") to CborList(listOf(
+ CborPositiveInt(1u),
+ CborMap(mapOf(CborString("inner") to CborNull()))
+ ))
+ ))
+ val nested3 = CborMap(mapOf(
+ CborString("list") to CborList(listOf(
+ CborPositiveInt(2u),
+ CborMap(mapOf(CborString("inner") to CborNull()))
+ ))
+ ))
+
+ assertEquals(nested1, nested2)
+ assertEquals(nested1.hashCode(), nested2.hashCode())
+ assertNotEquals(nested1, nested3)
+ }
+
+ @Test
+ fun testReflexiveEquality() {
+ val elements = listOf(
+ CborPositiveInt(42u),
+ CborNegativeInt(-42),
+ CborDouble(3.14),
+ CborString("test"),
+ CborBoolean(true),
+ CborByteString(byteArrayOf(1, 2, 3)),
+ CborNull(),
+ CborList(listOf(CborPositiveInt(1u))),
+ CborMap(mapOf(CborString("key") to CborPositiveInt(1u)))
+ )
+
+ elements.forEach { element ->
+ assertEquals(element, element, "Element should be equal to itself")
+ assertEquals(element.hashCode(), element.hashCode(), "Hash code should be consistent")
+ }
+ }
+
+ @Test
+ fun testSymmetricEquality() {
+ val pairs = listOf(
+ CborPositiveInt(42u) to CborPositiveInt(42u),
+ CborNegativeInt(-42) to CborNegativeInt(-42),
+ CborDouble(3.14) to CborDouble(3.14),
+ CborString("test") to CborString("test"),
+ CborBoolean(true) to CborBoolean(true),
+ CborByteString(byteArrayOf(1, 2, 3)) to CborByteString(byteArrayOf(1, 2, 3)),
+ CborNull() to CborNull(),
+ CborList(listOf(CborPositiveInt(1u))) to CborList(listOf(CborPositiveInt(1u))),
+ CborMap(mapOf(CborString("key") to CborPositiveInt(1u))) to CborMap(mapOf(CborString("key") to CborPositiveInt(1u)))
+ )
+
+ pairs.forEach { (a, b) ->
+ assertEquals(a, b, "a should equal b")
+ assertEquals(b, a, "b should equal a (symmetry)")
+ assertEquals(a.hashCode(), b.hashCode(), "Hash codes should be equal")
+ }
+ }
+
+ @Test
+ fun testTransitiveEquality() {
+ val a = CborString("test")
+ val b = CborString("test")
+ val c = CborString("test")
+
+ assertEquals(a, b)
+ assertEquals(b, c)
+ assertEquals(a, c, "Transitivity: if a==b and b==c, then a==c")
+ }
+}
\ No newline at end of file
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementTest.kt
new file mode 100644
index 0000000000..9cd9e937f9
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborElementTest.kt
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class CborElementTest {
+
+ private val cbor = Cbor {}
+
+ /**
+ * Helper method to decode a hex string to a CborElement
+ */
+ private fun decodeHexToCborElement(hexString: String): CborElement {
+ val bytes = HexConverter.parseHexBinary(hexString.uppercase())
+ return cbor.decodeFromByteArray(bytes)
+ }
+
+ @Test
+ fun testCborNull() {
+ val nullElement = CborNull()
+ val nullBytes = cbor.encodeToByteArray(nullElement)
+ val decodedNull = cbor.decodeFromByteArray(nullBytes)
+ assertEquals(nullElement, decodedNull)
+ }
+
+ @Test
+ fun testCborNumber() {
+ val numberElement = CborPositiveInt(42u)
+ val numberBytes = cbor.encodeToByteArray(numberElement)
+ val decodedNumber = cbor.decodeFromByteArray(numberBytes)
+ assertEquals(numberElement, decodedNumber)
+ assertEquals(42u, (decodedNumber as CborPositiveInt).value)
+ }
+
+ @Test
+ fun testCborString() {
+ val stringElement = CborString("Hello, CBOR!")
+ val stringBytes = cbor.encodeToByteArray(stringElement)
+ val decodedString = cbor.decodeFromByteArray(stringBytes)
+ assertEquals(stringElement, decodedString)
+ assertEquals("Hello, CBOR!", (decodedString as CborString).value)
+ }
+
+ @Test
+ fun testCborBoolean() {
+ val booleanElement = CborBoolean(true)
+ val booleanBytes = cbor.encodeToByteArray(booleanElement)
+ val decodedBoolean = cbor.decodeFromByteArray(booleanBytes)
+ assertEquals(booleanElement, decodedBoolean)
+ assertEquals(true, (decodedBoolean as CborBoolean).value)
+ }
+
+ @Test
+ fun testCborByteString() {
+ val byteArray = byteArrayOf(1, 2, 3, 4, 5)
+ val byteStringElement = CborByteString(byteArray)
+ val byteStringBytes = cbor.encodeToByteArray(byteStringElement)
+ val decodedByteString = cbor.decodeFromByteArray(byteStringBytes)
+ assertEquals(byteStringElement, decodedByteString)
+ assertTrue((decodedByteString as CborByteString).value.contentEquals(byteArray))
+ }
+
+ @Test
+ fun testCborList() {
+ val listElement = CborList(
+ listOf(
+ CborPositiveInt(1u),
+ CborString("two"),
+ CborBoolean(true),
+ CborNull()
+ )
+ )
+ val listBytes = cbor.encodeToByteArray(listElement)
+ val decodedList = cbor.decodeFromByteArray(listBytes)
+
+ // Verify the type and size
+ assertTrue(decodedList is CborList)
+ assertEquals(4, decodedList.size)
+
+ // Verify individual elements
+ assertTrue(decodedList[0] is CborPositiveInt)
+ assertEquals(1u, (decodedList[0] as CborPositiveInt).value)
+
+ assertTrue(decodedList[1] is CborString)
+ assertEquals("two", (decodedList[1] as CborString).value)
+
+ assertTrue(decodedList[2] is CborBoolean)
+ assertEquals(true, (decodedList[2] as CborBoolean).value)
+
+ assertTrue(decodedList[3] is CborNull)
+ }
+
+ @Test
+ fun testCborMap() {
+ val mapElement = CborMap(
+ mapOf(
+ CborString("key1") to CborPositiveInt(42u),
+ CborString("key2") to CborString("value"),
+ CborPositiveInt(3u) to CborBoolean(true),
+ CborNull() to CborNull()
+ )
+ )
+ val mapBytes = cbor.encodeToByteArray(mapElement)
+ val decodedMap = cbor.decodeFromByteArray(mapBytes)
+
+ // Verify the type and size
+ assertTrue(decodedMap is CborMap)
+ assertEquals(4, decodedMap.size)
+
+ // Verify individual entries
+ assertTrue(decodedMap.containsKey(CborString("key1")))
+ val value1 = decodedMap[CborString("key1")]
+ assertTrue(value1 is CborPositiveInt)
+ assertEquals(42u, (value1 as CborPositiveInt).value)
+
+ assertTrue(decodedMap.containsKey(CborString("key2")))
+ val value2 = decodedMap[CborString("key2")]
+ assertTrue(value2 is CborString)
+ assertEquals("value", (value2 as CborString).value)
+
+ assertTrue(decodedMap.containsKey(CborPositiveInt(3u)))
+ val value3 = decodedMap[CborPositiveInt(3u)]
+ assertTrue(value3 is CborBoolean)
+ assertEquals(true, (value3 as CborBoolean).value)
+
+ assertTrue(decodedMap.containsKey(CborNull()))
+ val value4 = decodedMap[CborNull()]
+ assertTrue(value4 is CborNull)
+ }
+
+ @Test
+ fun testComplexNestedStructure() {
+ // Create a complex nested structure with maps and lists
+ val complexElement = CborMap(
+ mapOf(
+ CborString("primitives") to CborList(
+ listOf(
+ CborPositiveInt(123u),
+ CborString("text"),
+ CborBoolean(false),
+ CborByteString(byteArrayOf(10, 20, 30)),
+ CborNull()
+ )
+ ),
+ CborString("nested") to CborMap(
+ mapOf(
+ CborString("inner") to CborList(
+ listOf(
+ CborPositiveInt(1u),
+ CborPositiveInt(2u)
+ )
+ ),
+ CborString("empty") to CborList(emptyList())
+ )
+ )
+ )
+ )
+
+ val complexBytes = cbor.encodeToByteArray(complexElement)
+ val decodedComplex = cbor.decodeFromByteArray(complexBytes)
+
+ // Verify the type
+ assertTrue(decodedComplex is CborMap)
+
+ // Verify the primitives list
+ assertTrue(decodedComplex.containsKey(CborString("primitives")))
+ val primitivesValue = decodedComplex[CborString("primitives")]
+ assertTrue(primitivesValue is CborList)
+
+ assertEquals(5, primitivesValue.size)
+
+ assertTrue(primitivesValue[0] is CborPositiveInt)
+ assertEquals(123u, (primitivesValue[0] as CborPositiveInt).value)
+
+ assertTrue(primitivesValue[1] is CborString)
+ assertEquals("text", (primitivesValue[1] as CborString).value)
+
+ assertTrue(primitivesValue[2] is CborBoolean)
+ assertEquals(false, (primitivesValue[2] as CborBoolean).value)
+
+ assertTrue(primitivesValue[3] is CborByteString)
+ assertTrue((primitivesValue[3] as CborByteString).value.contentEquals(byteArrayOf(10, 20, 30)))
+
+ assertTrue(primitivesValue[4] is CborNull)
+
+ // Verify the nested map
+ assertTrue(decodedComplex.containsKey(CborString("nested")))
+ val nestedValue = decodedComplex[CborString("nested")]
+ assertTrue(nestedValue is CborMap)
+
+ assertEquals(2, nestedValue.size)
+
+ // Verify the inner list
+ assertTrue(nestedValue.containsKey(CborString("inner")))
+ val innerValue = nestedValue[CborString("inner")]
+ assertTrue(innerValue is CborList)
+
+ assertEquals(2, innerValue.size)
+
+ assertTrue(innerValue[0] is CborPositiveInt)
+ assertEquals(1u, (innerValue[0] as CborPositiveInt).value)
+
+ assertTrue(innerValue[1] is CborPositiveInt)
+ assertEquals(2u, (innerValue[1] as CborPositiveInt).value)
+
+ // Verify the empty list
+ assertTrue(nestedValue.containsKey(CborString("empty")))
+ val emptyValue = nestedValue[CborString("empty")]
+ assertTrue(emptyValue is CborList)
+ val empty = emptyValue
+
+ assertEquals(0, empty.size)
+ }
+
+ @Test
+ fun testDecodeIntegers() {
+ // Test data from CborParserTest.testParseIntegers
+ val element = decodeHexToCborElement("0C") as CborPositiveInt
+ assertEquals(12u, element.value)
+
+ }
+
+ @Test
+ fun testDecodeStrings() {
+ // Test data from CborParserTest.testParseStrings
+ val element = decodeHexToCborElement("6568656C6C6F")
+ assertTrue(element is CborString)
+ assertEquals("hello", element.value)
+
+ val longStringElement =
+ decodeHexToCborElement("7828737472696E672074686174206973206C6F6E676572207468616E2032332063686172616374657273")
+ assertTrue(longStringElement is CborString)
+ assertEquals("string that is longer than 23 characters", longStringElement.value)
+ }
+
+ @Test
+ fun testDecodeFloatingPoint() {
+ // Test data from CborParserTest.testParseDoubles
+ val doubleElement = decodeHexToCborElement("fb7e37e43c8800759c")
+ assertTrue(doubleElement is CborDouble)
+ assertEquals(1e+300, doubleElement.value)
+
+ val floatElement = decodeHexToCborElement("fa47c35000")
+ assertTrue(floatElement is CborDouble)
+ assertEquals(100000.0f, floatElement.value.toFloat())
+ }
+
+ @Test
+ fun testDecodeByteString() {
+ // Test data from CborParserTest.testRfc7049IndefiniteByteStringExample
+ val element = decodeHexToCborElement("5F44aabbccdd43eeff99FF")
+ assertTrue(element is CborByteString)
+ val byteString = element as CborByteString
+ val expectedBytes = HexConverter.parseHexBinary("aabbccddeeff99")
+ assertTrue(byteString.value.contentEquals(expectedBytes))
+ }
+
+ @Test
+ fun testDecodeArray() {
+ // Test data from CborParserTest.testSkipCollections
+ val element = decodeHexToCborElement("830118ff1a00010000")
+ assertTrue(element is CborList)
+ val list = element as CborList
+ assertEquals(3, list.size)
+ assertEquals(1u, (list[0] as CborPositiveInt).value)
+ assertEquals(255u, (list[1] as CborPositiveInt).value)
+ assertEquals(65536u, (list[2] as CborPositiveInt).value)
+ }
+
+ @Test
+ fun testDecodeMap() {
+ // Test data from CborParserTest.testSkipCollections
+ val element = decodeHexToCborElement("a26178676b6f746c696e7861796d73657269616c697a6174696f6e")
+ assertTrue(element is CborMap)
+ val map = element as CborMap
+ assertEquals(2, map.size)
+ assertEquals(CborString("kotlinx"), map[CborString("x")])
+ assertEquals(CborString("serialization"), map[CborString("y")])
+ }
+
+ @Test
+ fun testDecodeComplexStructure() {
+ // Test data from CborParserTest.testSkipIndefiniteLength
+ val element =
+ decodeHexToCborElement("a461615f42cafe43010203ff61627f6648656c6c6f2065776f726c64ff61639f676b6f746c696e786d73657269616c697a6174696f6eff6164bf613101613202613303ff")
+ assertTrue(element is CborMap)
+ val map = element as CborMap
+ assertEquals(4, map.size)
+
+ // Check the byte string
+ val byteString = map[CborString("a")] as CborByteString
+ val expectedBytes = HexConverter.parseHexBinary("cafe010203")
+ assertTrue(byteString.value.contentEquals(expectedBytes))
+
+ // Check the text string
+ assertEquals(CborString("Hello world"), map[CborString("b")])
+
+ // Check the array
+ val array = map[CborString("c")] as CborList
+ assertEquals(2, array.size)
+ assertEquals(CborString("kotlinx"), array[0])
+ assertEquals(CborString("serialization"), array[1])
+
+ // Check the nested map
+ val nestedMap = map[CborString("d")] as CborMap
+ assertEquals(3, nestedMap.size)
+ assertEquals(CborPositiveInt(1u), nestedMap[CborString("1")])
+ assertEquals(CborPositiveInt(2u), nestedMap[CborString("2")])
+ assertEquals(CborPositiveInt(3u), nestedMap[CborString("3")])
+ }
+
+ // Test removed due to incompatibility with the new tag implementation
+
+ @OptIn(ExperimentalStdlibApi::class)
+ @Test
+ fun testTagsRoundTrip() {
+ // Create a CborElement with tags
+ val originalElement = CborString("Hello, tagged world!", tags = ulongArrayOf(42u))
+
+ // Encode and decode
+ val bytes = cbor.encodeToByteArray(originalElement)
+ println(bytes.toHexString())
+ val decodedElement = cbor.decodeFromByteArray(bytes)
+
+ // Verify the value and tags
+ assertTrue(decodedElement is CborString)
+ assertEquals("Hello, tagged world!", decodedElement.value)
+ assertNotNull(decodedElement.tags)
+ assertEquals(1, decodedElement.tags.size)
+ assertEquals(42u, decodedElement.tags.first())
+ }
+
+
+
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt
index fa884c0ab4..30a9daf512 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt
@@ -571,6 +571,9 @@ class CborTaggedTest {
assertEquals(referenceHexString, cbor.encodeToHexString(ClassAsTagged.serializer(), reference))
assertEquals(reference, cbor.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString))
+ val struct = Cbor.CoseCompliant.encodeToCbor(reference)
+ assertEquals(Cbor.decodeFromByteArray(referenceHexString.hexToByteArray()), struct)
+
assertEquals(
reference,
Cbor { verifyObjectTags = false }.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString)
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
index 330d4ff0f0..f37624a1ff 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
@@ -5,6 +5,7 @@
package kotlinx.serialization.cbor
import kotlinx.serialization.*
+import kotlinx.serialization.cbor.CborSkipTagAndEmptyTest.DataClass
import kotlin.test.*
@@ -27,10 +28,14 @@ class CbrWriterTest {
HexConverter.parseHexBinary("cafe"),
HexConverter.parseHexBinary("cafe")
)
+ val encoded =
+ "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e6742cafe696279746541727261799f383521ffff"
assertEquals(
- "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e6742cafe696279746541727261799f383521ffff",
+ encoded,
Cbor.encodeToHexString(TypesUmbrella.serializer(), test)
)
+ val struct = Cbor.encodeToCbor(test)
+ assertEquals(Cbor.decodeFromByteArray(encoded.hexToByteArray()), struct)
}
@Test
@@ -46,10 +51,14 @@ class CbrWriterTest {
HexConverter.parseHexBinary("cafe"),
HexConverter.parseHexBinary("cafe")
)
+ val encoded =
+ "a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521"
assertEquals(
- "a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521",
+ encoded,
Cbor { useDefiniteLengthEncoding = true }.encodeToHexString(TypesUmbrella.serializer(), test)
)
+ val struct = Cbor.encodeToCbor(test)
+ assertEquals(Cbor.decodeFromByteArray(encoded.hexToByteArray()), struct)
}
@Test
@@ -62,10 +71,14 @@ class CbrWriterTest {
true,
'a'
)
+ val encoded =
+ "bf63696e741a00018894646c6f6e671b7fffffffffffffff65666c6f6174fa4228000066646f75626c65fb4271fb0c5a2b700067626f6f6c65616ef564636861721861ff"
assertEquals(
- "bf63696e741a00018894646c6f6e671b7fffffffffffffff65666c6f6174fa4228000066646f75626c65fb4271fb0c5a2b700067626f6f6c65616ef564636861721861ff",
+ encoded,
Cbor.encodeToHexString(NumberTypesUmbrella.serializer(), test)
)
+ val struct = Cbor.encodeToCbor(test)
+ assertEquals(Cbor.decodeFromByteArray(encoded.hexToByteArray()), struct)
}
@Test
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
index 8feb491191..cd5fea2891 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
@@ -73,6 +73,8 @@ data class NullableByteString(
@ByteString val byteString: ByteArray?
) {
override fun equals(other: Any?): Boolean {
+ return toString() == other.toString()
+ /*
if (this === other) return true
if (other == null || this::class != other::class) return false
@@ -83,7 +85,7 @@ data class NullableByteString(
if (!byteString.contentEquals(other.byteString)) return false
} else if (other.byteString != null) return false
- return true
+ return true*/
}
override fun hashCode(): Int {
@@ -99,7 +101,7 @@ data class NullableByteStringDefaultNull(
if (this === other) return true
if (other == null || this::class != other::class) return false
- other as NullableByteString
+ other as NullableByteStringDefaultNull
if (byteString != null) {
if (other.byteString == null) return false
diff --git a/formats/json-io/build.gradle.kts b/formats/json-io/build.gradle.kts
index 2effe4f2f5..334241fa09 100644
--- a/formats/json-io/build.gradle.kts
+++ b/formats/json-io/build.gradle.kts
@@ -33,12 +33,9 @@ kotlin {
project.configureJava9ModuleInfo()
-tasks.named("dokkaHtmlPartial") {
- dokkaSourceSets {
- configureEach {
- externalDocumentationLink {
- url.set(URI("https://kotlin.github.io/kotlinx-io/").toURL())
- }
- }
+dokka.dokkaSourceSets.configureEach {
+ externalDocumentationLinks.register("kotlinx-io") {
+ url("https://kotlinlang.org/api/kotlinx-io/")
+ packageListUrl = file("dokka/kotlinx-io.package-list").toURI()
}
}
diff --git a/formats/json-io/dokka/kotlinx-io.package-list b/formats/json-io/dokka/kotlinx-io.package-list
new file mode 100644
index 0000000000..867ab49bd0
--- /dev/null
+++ b/formats/json-io/dokka/kotlinx-io.package-list
@@ -0,0 +1,10 @@
+$dokka.format:html-v1
+$dokka.linkExtension:html
+
+module:kotlinx-io-bytestring
+kotlinx.io.bytestring
+module:kotlinx-io-core
+kotlinx.io
+kotlinx.io.files
+module:kotlinx-io-okio
+kotlinx.io.okio
diff --git a/formats/json-okio/build.gradle.kts b/formats/json-okio/build.gradle.kts
index 93513985c1..1d165755a9 100644
--- a/formats/json-okio/build.gradle.kts
+++ b/formats/json-okio/build.gradle.kts
@@ -34,15 +34,9 @@ kotlin {
project.configureJava9ModuleInfo()
-tasks.named("dokkaHtmlPartial") {
- dokkaSourceSets {
- configureEach {
- externalDocumentationLink {
- url.set(URI("https://square.github.io/okio/3.x/okio/").toURL())
- packageListUrl.set(
- file("dokka/okio.package.list").toURI().toURL()
- )
- }
- }
+dokka.dokkaSourceSets.configureEach {
+ externalDocumentationLinks.register("okio") {
+ url("https://square.github.io/okio/3.x/okio")
+ packageListUrl = file("dokka/okio.package-list").toURI()
}
}
diff --git a/formats/json-okio/dokka/okio.package.list b/formats/json-okio/dokka/okio.package-list
similarity index 89%
rename from formats/json-okio/dokka/okio.package.list
rename to formats/json-okio/dokka/okio.package-list
index 96713f52e9..2673ba6038 100644
--- a/formats/json-okio/dokka/okio.package.list
+++ b/formats/json-okio/dokka/okio.package-list
@@ -2,23 +2,27 @@ $dokka.format:html-v1
$dokka.linkExtension:html
$dokka.location:okio////PointingToDeclaration/okio/okio/index.html
$dokka.location:okio//Okio/#/PointingToDeclaration/okio/okio/-okio.html
+$dokka.location:okio//SYSTEM/okio.FileSystem.Companion#/PointingToDeclaration/okio/okio/-s-y-s-t-e-m.html
$dokka.location:okio//Utf8/#/PointingToDeclaration/okio/okio/-utf8.html
$dokka.location:okio//appendingSink/java.io.File#/PointingToDeclaration/okio/okio/appending-sink.html
+$dokka.location:okio//asOkioSocket/java.net.Socket#/PointingToDeclaration/okio/okio/as-okio-socket.html
$dokka.location:okio//asResourceFileSystem/java.lang.ClassLoader#/PointingToDeclaration/okio/okio/as-resource-file-system.html
$dokka.location:okio//blackholeSink/#/PointingToDeclaration/okio/okio/blackhole-sink.html
$dokka.location:okio//buffer/okio.Sink#/PointingToDeclaration/okio/okio/buffer.html
$dokka.location:okio//buffer/okio.Source#/PointingToDeclaration/okio/okio/buffer.html
$dokka.location:okio//cipherSink/okio.Sink#javax.crypto.Cipher/PointingToDeclaration/okio/okio/cipher-sink.html
$dokka.location:okio//cipherSource/okio.Source#javax.crypto.Cipher/PointingToDeclaration/okio/okio/cipher-source.html
-$dokka.location:okio//deflate/okio.Sink#java.util.zip.Deflater/PointingToDeclaration/okio/okio/deflate.html
+$dokka.location:okio//deflate/okio.Sink#okio.Deflater/PointingToDeclaration/okio/okio/deflate.html
$dokka.location:okio//gzip/okio.Sink#/PointingToDeclaration/okio/okio/gzip.html
$dokka.location:okio//gzip/okio.Source#/PointingToDeclaration/okio/okio/gzip.html
$dokka.location:okio//hashingSink/okio.Sink#java.security.MessageDigest/PointingToDeclaration/okio/okio/hashing-sink.html
$dokka.location:okio//hashingSink/okio.Sink#javax.crypto.Mac/PointingToDeclaration/okio/okio/hashing-sink.html
$dokka.location:okio//hashingSource/okio.Source#java.security.MessageDigest/PointingToDeclaration/okio/okio/hashing-source.html
$dokka.location:okio//hashingSource/okio.Source#javax.crypto.Mac/PointingToDeclaration/okio/okio/hashing-source.html
-$dokka.location:okio//inflate/okio.Source#java.util.zip.Inflater/PointingToDeclaration/okio/okio/inflate.html
+$dokka.location:okio//inMemorySocketPair/#kotlin.Long/PointingToDeclaration/okio/okio/in-memory-socket-pair.html
+$dokka.location:okio//inflate/okio.Source#okio.Inflater/PointingToDeclaration/okio/okio/inflate.html
$dokka.location:okio//openZip/okio.FileSystem#okio.Path/PointingToDeclaration/okio/okio/open-zip.html
+$dokka.location:okio//readByteString/kotlinx.cinterop.CPointer[kotlinx.cinterop.CPointed]#kotlin.Int/PointingToDeclaration/okio/okio/read-byte-string.html
$dokka.location:okio//sink/java.io.File#kotlin.Boolean/PointingToDeclaration/okio/okio/sink.html
$dokka.location:okio//sink/java.io.OutputStream#/PointingToDeclaration/okio/okio/sink.html
$dokka.location:okio//sink/java.net.Socket#/PointingToDeclaration/okio/okio/sink.html
@@ -29,14 +33,13 @@ $dokka.location:okio//source/java.net.Socket#/PointingToDeclaration/okio/okio/s
$dokka.location:okio//source/java.nio.file.Path#kotlin.Array[java.nio.file.OpenOption]/PointingToDeclaration/okio/okio/source.html
$dokka.location:okio//use/TypeParam(bounds=[okio.Closeable?])#kotlin.Function1[TypeParam(bounds=[okio.Closeable?]),TypeParam(bounds=[kotlin.Any?])]/PointingToDeclaration/okio/okio/use.html
$dokka.location:okio//utf8Size/kotlin.String#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/utf8-size.html
+$dokka.location:okio//withLock/java.util.concurrent.locks.ReentrantLock#kotlin.Function0[TypeParam(bounds=[kotlin.Any?])]/PointingToDeclaration/okio/okio/with-lock.html
$dokka.location:okio//withLock/okio.Lock#kotlin.Function0[TypeParam(bounds=[kotlin.Any?])]/PointingToDeclaration/okio/okio/with-lock.html
$dokka.location:okio/ArrayIndexOutOfBoundsException///PointingToDeclaration/okio/okio/-array-index-out-of-bounds-exception/index.html
$dokka.location:okio/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsException/#kotlin.String?/PointingToDeclaration/okio/okio/-array-index-out-of-bounds-exception/-array-index-out-of-bounds-exception.html
-$dokka.location:okio/AsyncTimeout.Companion///PointingToDeclaration/okio/okio/-async-timeout/-companion/index.html
-$dokka.location:okio/AsyncTimeout.Companion/condition/#/PointingToDeclaration/okio/okio/-async-timeout/-companion/condition.html
-$dokka.location:okio/AsyncTimeout.Companion/lock/#/PointingToDeclaration/okio/okio/-async-timeout/-companion/lock.html
$dokka.location:okio/AsyncTimeout///PointingToDeclaration/okio/okio/-async-timeout/index.html
$dokka.location:okio/AsyncTimeout/AsyncTimeout/#/PointingToDeclaration/okio/okio/-async-timeout/-async-timeout.html
+$dokka.location:okio/AsyncTimeout/cancel/#/PointingToDeclaration/okio/okio/-async-timeout/cancel.html
$dokka.location:okio/AsyncTimeout/enter/#/PointingToDeclaration/okio/okio/-async-timeout/enter.html
$dokka.location:okio/AsyncTimeout/exit/#/PointingToDeclaration/okio/okio/-async-timeout/exit.html
$dokka.location:okio/AsyncTimeout/sink/#okio.Sink/PointingToDeclaration/okio/okio/-async-timeout/sink.html
@@ -60,7 +63,7 @@ $dokka.location:okio/Buffer/Buffer/#/PointingToDeclaration/okio/okio/-buffer/-b
$dokka.location:okio/Buffer/buffer/#/PointingToDeclaration/okio/okio/-buffer/buffer.html
$dokka.location:okio/Buffer/clear/#/PointingToDeclaration/okio/okio/-buffer/clear.html
$dokka.location:okio/Buffer/clone/#/PointingToDeclaration/okio/okio/-buffer/clone.html
-$dokka.location:okio/Buffer/close/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]close.html
+$dokka.location:okio/Buffer/close/#/PointingToDeclaration/okio/okio/-buffer/close.html
$dokka.location:okio/Buffer/completeSegmentByteCount/#/PointingToDeclaration/okio/okio/-buffer/complete-segment-byte-count.html
$dokka.location:okio/Buffer/copy/#/PointingToDeclaration/okio/okio/-buffer/copy.html
$dokka.location:okio/Buffer/copyTo/#java.io.OutputStream#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/copy-to.html
@@ -69,62 +72,64 @@ $dokka.location:okio/Buffer/copyTo/#okio.Buffer#kotlin.Long/PointingToDeclaratio
$dokka.location:okio/Buffer/emit/#/PointingToDeclaration/okio/okio/-buffer/emit.html
$dokka.location:okio/Buffer/emitCompleteSegments/#/PointingToDeclaration/okio/okio/-buffer/emit-complete-segments.html
$dokka.location:okio/Buffer/equals/#kotlin.Any?/PointingToDeclaration/okio/okio/-buffer/[non-jvm]equals.html
-$dokka.location:okio/Buffer/exhausted/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]exhausted.html
-$dokka.location:okio/Buffer/flush/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]flush.html
+$dokka.location:okio/Buffer/exhausted/#/PointingToDeclaration/okio/okio/-buffer/exhausted.html
+$dokka.location:okio/Buffer/flush/#/PointingToDeclaration/okio/okio/-buffer/flush.html
$dokka.location:okio/Buffer/get/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/get.html
$dokka.location:okio/Buffer/hashCode/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]hash-code.html
$dokka.location:okio/Buffer/hmacSha1/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/hmac-sha1.html
$dokka.location:okio/Buffer/hmacSha256/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/hmac-sha256.html
$dokka.location:okio/Buffer/hmacSha512/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/hmac-sha512.html
-$dokka.location:okio/Buffer/indexOf/#kotlin.Byte#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of.html
-$dokka.location:okio/Buffer/indexOf/#kotlin.Byte#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of.html
-$dokka.location:okio/Buffer/indexOf/#kotlin.Byte/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of.html
-$dokka.location:okio/Buffer/indexOf/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of.html
-$dokka.location:okio/Buffer/indexOf/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of.html
-$dokka.location:okio/Buffer/indexOfElement/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of-element.html
-$dokka.location:okio/Buffer/indexOfElement/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/[non-jvm]index-of-element.html
+$dokka.location:okio/Buffer/indexOf/#kotlin.Byte#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOf/#kotlin.Byte#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOf/#kotlin.Byte/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOf/#okio.ByteString#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOf/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOf/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/index-of.html
+$dokka.location:okio/Buffer/indexOfElement/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/index-of-element.html
+$dokka.location:okio/Buffer/indexOfElement/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/index-of-element.html
$dokka.location:okio/Buffer/inputStream/#/PointingToDeclaration/okio/okio/-buffer/input-stream.html
$dokka.location:okio/Buffer/isOpen/#/PointingToDeclaration/okio/okio/-buffer/is-open.html
$dokka.location:okio/Buffer/md5/#/PointingToDeclaration/okio/okio/-buffer/md5.html
$dokka.location:okio/Buffer/outputStream/#/PointingToDeclaration/okio/okio/-buffer/output-stream.html
-$dokka.location:okio/Buffer/peek/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]peek.html
-$dokka.location:okio/Buffer/rangeEquals/#kotlin.Long#okio.ByteString#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/[non-jvm]range-equals.html
-$dokka.location:okio/Buffer/rangeEquals/#kotlin.Long#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/[non-jvm]range-equals.html
+$dokka.location:okio/Buffer/peek/#/PointingToDeclaration/okio/okio/-buffer/peek.html
+$dokka.location:okio/Buffer/rangeEquals/#kotlin.Long#okio.ByteString#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/range-equals.html
+$dokka.location:okio/Buffer/rangeEquals/#kotlin.Long#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/range-equals.html
$dokka.location:okio/Buffer/read/#java.nio.ByteBuffer/PointingToDeclaration/okio/okio/-buffer/read.html
-$dokka.location:okio/Buffer/read/#kotlin.ByteArray#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read.html
-$dokka.location:okio/Buffer/read/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read.html
-$dokka.location:okio/Buffer/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read.html
-$dokka.location:okio/Buffer/readAll/#okio.Sink/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-all.html
+$dokka.location:okio/Buffer/read/#kotlin.ByteArray#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/read.html
+$dokka.location:okio/Buffer/read/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-buffer/read.html
+$dokka.location:okio/Buffer/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read.html
+$dokka.location:okio/Buffer/readAll/#okio.Sink/PointingToDeclaration/okio/okio/-buffer/read-all.html
$dokka.location:okio/Buffer/readAndWriteUnsafe/#okio.Buffer.UnsafeCursor/PointingToDeclaration/okio/okio/-buffer/read-and-write-unsafe.html
-$dokka.location:okio/Buffer/readByte/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-byte.html
-$dokka.location:okio/Buffer/readByteArray/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-byte-array.html
-$dokka.location:okio/Buffer/readByteArray/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-byte-array.html
-$dokka.location:okio/Buffer/readByteString/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-byte-string.html
-$dokka.location:okio/Buffer/readByteString/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-byte-string.html
-$dokka.location:okio/Buffer/readDecimalLong/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-decimal-long.html
+$dokka.location:okio/Buffer/readByte/#/PointingToDeclaration/okio/okio/-buffer/read-byte.html
+$dokka.location:okio/Buffer/readByteArray/#/PointingToDeclaration/okio/okio/-buffer/read-byte-array.html
+$dokka.location:okio/Buffer/readByteArray/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-byte-array.html
+$dokka.location:okio/Buffer/readByteString/#/PointingToDeclaration/okio/okio/-buffer/read-byte-string.html
+$dokka.location:okio/Buffer/readByteString/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-byte-string.html
+$dokka.location:okio/Buffer/readDecimalLong/#/PointingToDeclaration/okio/okio/-buffer/read-decimal-long.html
$dokka.location:okio/Buffer/readFrom/#java.io.InputStream#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-from.html
$dokka.location:okio/Buffer/readFrom/#java.io.InputStream/PointingToDeclaration/okio/okio/-buffer/read-from.html
-$dokka.location:okio/Buffer/readFully/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-fully.html
-$dokka.location:okio/Buffer/readFully/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-fully.html
-$dokka.location:okio/Buffer/readHexadecimalUnsignedLong/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-hexadecimal-unsigned-long.html
-$dokka.location:okio/Buffer/readInt/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-int.html
-$dokka.location:okio/Buffer/readIntLe/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-int-le.html
-$dokka.location:okio/Buffer/readLong/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-long.html
-$dokka.location:okio/Buffer/readLongLe/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-long-le.html
-$dokka.location:okio/Buffer/readShort/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-short.html
-$dokka.location:okio/Buffer/readShortLe/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-short-le.html
+$dokka.location:okio/Buffer/readFully/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-buffer/read-fully.html
+$dokka.location:okio/Buffer/readFully/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-fully.html
+$dokka.location:okio/Buffer/readHexadecimalUnsignedLong/#/PointingToDeclaration/okio/okio/-buffer/read-hexadecimal-unsigned-long.html
+$dokka.location:okio/Buffer/readInt/#/PointingToDeclaration/okio/okio/-buffer/read-int.html
+$dokka.location:okio/Buffer/readIntLe/#/PointingToDeclaration/okio/okio/-buffer/read-int-le.html
+$dokka.location:okio/Buffer/readLong/#/PointingToDeclaration/okio/okio/-buffer/read-long.html
+$dokka.location:okio/Buffer/readLongLe/#/PointingToDeclaration/okio/okio/-buffer/read-long-le.html
+$dokka.location:okio/Buffer/readShort/#/PointingToDeclaration/okio/okio/-buffer/read-short.html
+$dokka.location:okio/Buffer/readShortLe/#/PointingToDeclaration/okio/okio/-buffer/read-short-le.html
$dokka.location:okio/Buffer/readString/#java.nio.charset.Charset/PointingToDeclaration/okio/okio/-buffer/read-string.html
$dokka.location:okio/Buffer/readString/#kotlin.Long#java.nio.charset.Charset/PointingToDeclaration/okio/okio/-buffer/read-string.html
$dokka.location:okio/Buffer/readUnsafe/#okio.Buffer.UnsafeCursor/PointingToDeclaration/okio/okio/-buffer/read-unsafe.html
-$dokka.location:okio/Buffer/readUtf8/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8.html
-$dokka.location:okio/Buffer/readUtf8/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8.html
-$dokka.location:okio/Buffer/readUtf8CodePoint/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8-code-point.html
-$dokka.location:okio/Buffer/readUtf8Line/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8-line.html
-$dokka.location:okio/Buffer/readUtf8LineStrict/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8-line-strict.html
-$dokka.location:okio/Buffer/readUtf8LineStrict/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]read-utf8-line-strict.html
-$dokka.location:okio/Buffer/request/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]request.html
-$dokka.location:okio/Buffer/require/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]require.html
-$dokka.location:okio/Buffer/select/#okio.Options/PointingToDeclaration/okio/okio/-buffer/[non-jvm]select.html
+$dokka.location:okio/Buffer/readUtf8/#/PointingToDeclaration/okio/okio/-buffer/read-utf8.html
+$dokka.location:okio/Buffer/readUtf8/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-utf8.html
+$dokka.location:okio/Buffer/readUtf8CodePoint/#/PointingToDeclaration/okio/okio/-buffer/read-utf8-code-point.html
+$dokka.location:okio/Buffer/readUtf8Line/#/PointingToDeclaration/okio/okio/-buffer/read-utf8-line.html
+$dokka.location:okio/Buffer/readUtf8LineStrict/#/PointingToDeclaration/okio/okio/-buffer/read-utf8-line-strict.html
+$dokka.location:okio/Buffer/readUtf8LineStrict/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/read-utf8-line-strict.html
+$dokka.location:okio/Buffer/request/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/request.html
+$dokka.location:okio/Buffer/require/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/require.html
+$dokka.location:okio/Buffer/select/#okio.Options/PointingToDeclaration/okio/okio/-buffer/select.html
+$dokka.location:okio/Buffer/select/#okio.TypedOptions[TypeParam(bounds=[kotlin.Any])]/PointingToDeclaration/okio/okio/-buffer/select.html
$dokka.location:okio/Buffer/sha1/#/PointingToDeclaration/okio/okio/-buffer/sha1.html
$dokka.location:okio/Buffer/sha256/#/PointingToDeclaration/okio/okio/-buffer/sha256.html
$dokka.location:okio/Buffer/sha512/#/PointingToDeclaration/okio/okio/-buffer/sha512.html
@@ -132,16 +137,16 @@ $dokka.location:okio/Buffer/size/#/PointingToDeclaration/okio/okio/-buffer/size
$dokka.location:okio/Buffer/skip/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/skip.html
$dokka.location:okio/Buffer/snapshot/#/PointingToDeclaration/okio/okio/-buffer/snapshot.html
$dokka.location:okio/Buffer/snapshot/#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/snapshot.html
-$dokka.location:okio/Buffer/timeout/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]timeout.html
+$dokka.location:okio/Buffer/timeout/#/PointingToDeclaration/okio/okio/-buffer/timeout.html
$dokka.location:okio/Buffer/toString/#/PointingToDeclaration/okio/okio/-buffer/[non-jvm]to-string.html
$dokka.location:okio/Buffer/write/#java.nio.ByteBuffer/PointingToDeclaration/okio/okio/-buffer/write.html
$dokka.location:okio/Buffer/write/#kotlin.ByteArray#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/write.html
$dokka.location:okio/Buffer/write/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-buffer/write.html
-$dokka.location:okio/Buffer/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/[non-jvm]write.html
+$dokka.location:okio/Buffer/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/write.html
$dokka.location:okio/Buffer/write/#okio.ByteString#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/write.html
$dokka.location:okio/Buffer/write/#okio.ByteString/PointingToDeclaration/okio/okio/-buffer/write.html
$dokka.location:okio/Buffer/write/#okio.Source#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/write.html
-$dokka.location:okio/Buffer/writeAll/#okio.Source/PointingToDeclaration/okio/okio/-buffer/[non-jvm]write-all.html
+$dokka.location:okio/Buffer/writeAll/#okio.Source/PointingToDeclaration/okio/okio/-buffer/write-all.html
$dokka.location:okio/Buffer/writeByte/#kotlin.Int/PointingToDeclaration/okio/okio/-buffer/write-byte.html
$dokka.location:okio/Buffer/writeDecimalLong/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/write-decimal-long.html
$dokka.location:okio/Buffer/writeHexadecimalUnsignedLong/#kotlin.Long/PointingToDeclaration/okio/okio/-buffer/write-hexadecimal-unsigned-long.html
@@ -189,6 +194,7 @@ $dokka.location:okio/BufferedSource/exhausted/#/PointingToDeclaration/okio/okio
$dokka.location:okio/BufferedSource/indexOf/#kotlin.Byte#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
$dokka.location:okio/BufferedSource/indexOf/#kotlin.Byte#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
$dokka.location:okio/BufferedSource/indexOf/#kotlin.Byte/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
+$dokka.location:okio/BufferedSource/indexOf/#okio.ByteString#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
$dokka.location:okio/BufferedSource/indexOf/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
$dokka.location:okio/BufferedSource/indexOf/#okio.ByteString/PointingToDeclaration/okio/okio/-buffered-source/index-of.html
$dokka.location:okio/BufferedSource/indexOfElement/#okio.ByteString#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/index-of-element.html
@@ -226,6 +232,7 @@ $dokka.location:okio/BufferedSource/readUtf8LineStrict/#kotlin.Long/PointingToDe
$dokka.location:okio/BufferedSource/request/#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/request.html
$dokka.location:okio/BufferedSource/require/#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/require.html
$dokka.location:okio/BufferedSource/select/#okio.Options/PointingToDeclaration/okio/okio/-buffered-source/select.html
+$dokka.location:okio/BufferedSource/select/#okio.TypedOptions[TypeParam(bounds=[kotlin.Any])]/PointingToDeclaration/okio/okio/-buffered-source/select.html
$dokka.location:okio/BufferedSource/skip/#kotlin.Long/PointingToDeclaration/okio/okio/-buffered-source/skip.html
$dokka.location:okio/ByteString.Companion///PointingToDeclaration/okio/okio/-byte-string/-companion/index.html
$dokka.location:okio/ByteString.Companion/EMPTY/#/PointingToDeclaration/okio/okio/-byte-string/-companion/-e-m-p-t-y.html
@@ -235,9 +242,9 @@ $dokka.location:okio/ByteString.Companion/encode/kotlin.String#java.nio.charset.
$dokka.location:okio/ByteString.Companion/encodeUtf8/kotlin.String#/PointingToDeclaration/okio/okio/-byte-string/-companion/encode-utf8.html
$dokka.location:okio/ByteString.Companion/of/#kotlin.ByteArray/PointingToDeclaration/okio/okio/-byte-string/-companion/of.html
$dokka.location:okio/ByteString.Companion/readByteString/java.io.InputStream#kotlin.Int/PointingToDeclaration/okio/okio/-byte-string/-companion/read-byte-string.html
-$dokka.location:okio/ByteString.Companion/toByteString/[Error type: Unresolved type for NSData]#/PointingToDeclaration/okio/okio/-byte-string/-companion/to-byte-string.html
$dokka.location:okio/ByteString.Companion/toByteString/java.nio.ByteBuffer#/PointingToDeclaration/okio/okio/-byte-string/-companion/to-byte-string.html
$dokka.location:okio/ByteString.Companion/toByteString/kotlin.ByteArray#kotlin.Int#kotlin.Int/PointingToDeclaration/okio/okio/-byte-string/-companion/to-byte-string.html
+$dokka.location:okio/ByteString.Companion/toByteString/platform.Foundation.NSData#/PointingToDeclaration/okio/okio/-byte-string/-companion/to-byte-string.html
$dokka.location:okio/ByteString///PointingToDeclaration/okio/okio/-byte-string/index.html
$dokka.location:okio/ByteString/asByteBuffer/#/PointingToDeclaration/okio/okio/-byte-string/as-byte-buffer.html
$dokka.location:okio/ByteString/base64/#/PointingToDeclaration/okio/okio/-byte-string/base64.html
@@ -289,14 +296,22 @@ $dokka.location:okio/CipherSource/read/#okio.Buffer#kotlin.Long/PointingToDeclar
$dokka.location:okio/CipherSource/timeout/#/PointingToDeclaration/okio/okio/-cipher-source/timeout.html
$dokka.location:okio/Closeable///PointingToDeclaration/okio/okio/-closeable/index.html
$dokka.location:okio/Closeable/close/#/PointingToDeclaration/okio/okio/-closeable/close.html
+$dokka.location:okio/Deflater///PointingToDeclaration/okio/okio/-deflater/index.html
+$dokka.location:okio/Deflater/Deflater/#/PointingToDeclaration/okio/okio/-deflater/-deflater.html
+$dokka.location:okio/Deflater/Deflater/#kotlin.Int#kotlin.Boolean/PointingToDeclaration/okio/okio/-deflater/-deflater.html
+$dokka.location:okio/Deflater/end/#/PointingToDeclaration/okio/okio/-deflater/end.html
+$dokka.location:okio/Deflater/flush/#/PointingToDeclaration/okio/okio/-deflater/flush.html
+$dokka.location:okio/Deflater/getBytesRead/#/PointingToDeclaration/okio/okio/-deflater/get-bytes-read.html
$dokka.location:okio/DeflaterSink///PointingToDeclaration/okio/okio/-deflater-sink/index.html
$dokka.location:okio/DeflaterSink/DeflaterSink/#okio.Sink#java.util.zip.Deflater/PointingToDeclaration/okio/okio/-deflater-sink/-deflater-sink.html
+$dokka.location:okio/DeflaterSink/DeflaterSink/#okio.Sink#okio.Deflater/PointingToDeclaration/okio/okio/-deflater-sink/-deflater-sink.html
$dokka.location:okio/DeflaterSink/close/#/PointingToDeclaration/okio/okio/-deflater-sink/close.html
$dokka.location:okio/DeflaterSink/flush/#/PointingToDeclaration/okio/okio/-deflater-sink/flush.html
$dokka.location:okio/DeflaterSink/timeout/#/PointingToDeclaration/okio/okio/-deflater-sink/timeout.html
$dokka.location:okio/DeflaterSink/toString/#/PointingToDeclaration/okio/okio/-deflater-sink/to-string.html
$dokka.location:okio/DeflaterSink/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-deflater-sink/write.html
$dokka.location:okio/EOFException///PointingToDeclaration/okio/okio/-e-o-f-exception/index.html
+$dokka.location:okio/EOFException/EOFException/#/PointingToDeclaration/okio/okio/-e-o-f-exception/-e-o-f-exception.html
$dokka.location:okio/EOFException/EOFException/#kotlin.String?/PointingToDeclaration/okio/okio/-e-o-f-exception/-e-o-f-exception.html
$dokka.location:okio/FileHandle///PointingToDeclaration/okio/okio/-file-handle/index.html
$dokka.location:okio/FileHandle/FileHandle/#kotlin.Boolean/PointingToDeclaration/okio/okio/-file-handle/-file-handle.html
@@ -343,6 +358,7 @@ $dokka.location:okio/FileSystem/appendingSink/#okio.Path#kotlin.Boolean/Pointing
$dokka.location:okio/FileSystem/appendingSink/#okio.Path/PointingToDeclaration/okio/okio/-file-system/appending-sink.html
$dokka.location:okio/FileSystem/atomicMove/#okio.Path#okio.Path/PointingToDeclaration/okio/okio/-file-system/atomic-move.html
$dokka.location:okio/FileSystem/canonicalize/#okio.Path/PointingToDeclaration/okio/okio/-file-system/canonicalize.html
+$dokka.location:okio/FileSystem/close/#/PointingToDeclaration/okio/okio/-file-system/close.html
$dokka.location:okio/FileSystem/copy/#okio.Path#okio.Path/PointingToDeclaration/okio/okio/-file-system/copy.html
$dokka.location:okio/FileSystem/createDirectories/#okio.Path#kotlin.Boolean/PointingToDeclaration/okio/okio/-file-system/create-directories.html
$dokka.location:okio/FileSystem/createDirectories/#okio.Path/PointingToDeclaration/okio/okio/-file-system/create-directories.html
@@ -373,6 +389,7 @@ $dokka.location:okio/ForwardingFileSystem/ForwardingFileSystem/#okio.FileSystem/
$dokka.location:okio/ForwardingFileSystem/appendingSink/#okio.Path#kotlin.Boolean/PointingToDeclaration/okio/okio/-forwarding-file-system/appending-sink.html
$dokka.location:okio/ForwardingFileSystem/atomicMove/#okio.Path#okio.Path/PointingToDeclaration/okio/okio/-forwarding-file-system/atomic-move.html
$dokka.location:okio/ForwardingFileSystem/canonicalize/#okio.Path/PointingToDeclaration/okio/okio/-forwarding-file-system/canonicalize.html
+$dokka.location:okio/ForwardingFileSystem/close/#/PointingToDeclaration/okio/okio/-forwarding-file-system/close.html
$dokka.location:okio/ForwardingFileSystem/createDirectory/#okio.Path#kotlin.Boolean/PointingToDeclaration/okio/okio/-forwarding-file-system/create-directory.html
$dokka.location:okio/ForwardingFileSystem/createSymlink/#okio.Path#okio.Path/PointingToDeclaration/okio/okio/-forwarding-file-system/create-symlink.html
$dokka.location:okio/ForwardingFileSystem/delegate/#/PointingToDeclaration/okio/okio/-forwarding-file-system/delegate.html
@@ -405,6 +422,8 @@ $dokka.location:okio/ForwardingSource/timeout/#/PointingToDeclaration/okio/okio
$dokka.location:okio/ForwardingSource/toString/#/PointingToDeclaration/okio/okio/-forwarding-source/to-string.html
$dokka.location:okio/ForwardingTimeout///PointingToDeclaration/okio/okio/-forwarding-timeout/index.html
$dokka.location:okio/ForwardingTimeout/ForwardingTimeout/#okio.Timeout/PointingToDeclaration/okio/okio/-forwarding-timeout/-forwarding-timeout.html
+$dokka.location:okio/ForwardingTimeout/awaitSignal/#java.util.concurrent.locks.Condition/PointingToDeclaration/okio/okio/-forwarding-timeout/await-signal.html
+$dokka.location:okio/ForwardingTimeout/cancel/#/PointingToDeclaration/okio/okio/-forwarding-timeout/cancel.html
$dokka.location:okio/ForwardingTimeout/clearDeadline/#/PointingToDeclaration/okio/okio/-forwarding-timeout/clear-deadline.html
$dokka.location:okio/ForwardingTimeout/clearTimeout/#/PointingToDeclaration/okio/okio/-forwarding-timeout/clear-timeout.html
$dokka.location:okio/ForwardingTimeout/deadlineNanoTime/#/PointingToDeclaration/okio/okio/-forwarding-timeout/deadline-nano-time.html
@@ -415,6 +434,7 @@ $dokka.location:okio/ForwardingTimeout/setDelegate/#okio.Timeout/PointingToDecla
$dokka.location:okio/ForwardingTimeout/throwIfReached/#/PointingToDeclaration/okio/okio/-forwarding-timeout/throw-if-reached.html
$dokka.location:okio/ForwardingTimeout/timeout/#kotlin.Long#java.util.concurrent.TimeUnit/PointingToDeclaration/okio/okio/-forwarding-timeout/timeout.html
$dokka.location:okio/ForwardingTimeout/timeoutNanos/#/PointingToDeclaration/okio/okio/-forwarding-timeout/timeout-nanos.html
+$dokka.location:okio/ForwardingTimeout/waitUntilNotified/#kotlin.Any/PointingToDeclaration/okio/okio/-forwarding-timeout/wait-until-notified.html
$dokka.location:okio/GzipSink///PointingToDeclaration/okio/okio/-gzip-sink/index.html
$dokka.location:okio/GzipSink/GzipSink/#okio.Sink/PointingToDeclaration/okio/okio/-gzip-sink/-gzip-sink.html
$dokka.location:okio/GzipSink/close/#/PointingToDeclaration/okio/okio/-gzip-sink/close.html
@@ -440,7 +460,7 @@ $dokka.location:okio/HashingSink/close/#/PointingToDeclaration/okio/okio/-hashi
$dokka.location:okio/HashingSink/flush/#/PointingToDeclaration/okio/okio/-hashing-sink/flush.html
$dokka.location:okio/HashingSink/hash/#/PointingToDeclaration/okio/okio/-hashing-sink/hash.html
$dokka.location:okio/HashingSink/timeout/#/PointingToDeclaration/okio/okio/-hashing-sink/timeout.html
-$dokka.location:okio/HashingSink/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-hashing-sink/[non-jvm]write.html
+$dokka.location:okio/HashingSink/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-hashing-sink/write.html
$dokka.location:okio/HashingSource.Companion///PointingToDeclaration/okio/okio/-hashing-source/-companion/index.html
$dokka.location:okio/HashingSource.Companion/hmacSha1/#okio.Source#okio.ByteString/PointingToDeclaration/okio/okio/-hashing-source/-companion/hmac-sha1.html
$dokka.location:okio/HashingSource.Companion/hmacSha256/#okio.Source#okio.ByteString/PointingToDeclaration/okio/okio/-hashing-source/-companion/hmac-sha256.html
@@ -452,13 +472,20 @@ $dokka.location:okio/HashingSource.Companion/sha512/#okio.Source/PointingToDecla
$dokka.location:okio/HashingSource///PointingToDeclaration/okio/okio/-hashing-source/index.html
$dokka.location:okio/HashingSource/close/#/PointingToDeclaration/okio/okio/-hashing-source/close.html
$dokka.location:okio/HashingSource/hash/#/PointingToDeclaration/okio/okio/-hashing-source/hash.html
-$dokka.location:okio/HashingSource/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-hashing-source/[non-jvm]read.html
+$dokka.location:okio/HashingSource/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-hashing-source/read.html
$dokka.location:okio/HashingSource/timeout/#/PointingToDeclaration/okio/okio/-hashing-source/timeout.html
$dokka.location:okio/IOException///PointingToDeclaration/okio/okio/-i-o-exception/index.html
+$dokka.location:okio/IOException/IOException/#/PointingToDeclaration/okio/okio/-i-o-exception/-i-o-exception.html
$dokka.location:okio/IOException/IOException/#kotlin.String?#kotlin.Throwable?/PointingToDeclaration/okio/okio/-i-o-exception/-i-o-exception.html
$dokka.location:okio/IOException/IOException/#kotlin.String?/PointingToDeclaration/okio/okio/-i-o-exception/-i-o-exception.html
+$dokka.location:okio/Inflater///PointingToDeclaration/okio/okio/-inflater/index.html
+$dokka.location:okio/Inflater/Inflater/#/PointingToDeclaration/okio/okio/-inflater/-inflater.html
+$dokka.location:okio/Inflater/Inflater/#kotlin.Boolean/PointingToDeclaration/okio/okio/-inflater/-inflater.html
+$dokka.location:okio/Inflater/end/#/PointingToDeclaration/okio/okio/-inflater/end.html
+$dokka.location:okio/Inflater/getBytesWritten/#/PointingToDeclaration/okio/okio/-inflater/get-bytes-written.html
$dokka.location:okio/InflaterSource///PointingToDeclaration/okio/okio/-inflater-source/index.html
$dokka.location:okio/InflaterSource/InflaterSource/#okio.Source#java.util.zip.Inflater/PointingToDeclaration/okio/okio/-inflater-source/-inflater-source.html
+$dokka.location:okio/InflaterSource/InflaterSource/#okio.Source#okio.Inflater/PointingToDeclaration/okio/okio/-inflater-source/-inflater-source.html
$dokka.location:okio/InflaterSource/close/#/PointingToDeclaration/okio/okio/-inflater-source/close.html
$dokka.location:okio/InflaterSource/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-inflater-source/read.html
$dokka.location:okio/InflaterSource/readOrInflate/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-inflater-source/read-or-inflate.html
@@ -518,6 +545,10 @@ $dokka.location:okio/Sink/close/#/PointingToDeclaration/okio/okio/-sink/close.h
$dokka.location:okio/Sink/flush/#/PointingToDeclaration/okio/okio/-sink/flush.html
$dokka.location:okio/Sink/timeout/#/PointingToDeclaration/okio/okio/-sink/timeout.html
$dokka.location:okio/Sink/write/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-sink/write.html
+$dokka.location:okio/Socket///PointingToDeclaration/okio/okio/-socket/index.html
+$dokka.location:okio/Socket/cancel/#/PointingToDeclaration/okio/okio/-socket/cancel.html
+$dokka.location:okio/Socket/sink/#/PointingToDeclaration/okio/okio/-socket/sink.html
+$dokka.location:okio/Socket/source/#/PointingToDeclaration/okio/okio/-socket/source.html
$dokka.location:okio/Source///PointingToDeclaration/okio/okio/-source/index.html
$dokka.location:okio/Source/close/#/PointingToDeclaration/okio/okio/-source/close.html
$dokka.location:okio/Source/read/#okio.Buffer#kotlin.Long/PointingToDeclaration/okio/okio/-source/read.html
@@ -532,9 +563,12 @@ $dokka.location:okio/Throttler/source/#okio.Source/PointingToDeclaration/okio/o
$dokka.location:okio/Timeout.Companion///PointingToDeclaration/okio/okio/-timeout/-companion/index.html
$dokka.location:okio/Timeout.Companion/NONE/#/PointingToDeclaration/okio/okio/-timeout/-companion/-n-o-n-e.html
$dokka.location:okio/Timeout.Companion/minTimeout/#kotlin.Long#kotlin.Long/PointingToDeclaration/okio/okio/-timeout/-companion/min-timeout.html
+$dokka.location:okio/Timeout.Companion/timeout/okio.Timeout#kotlin.Long#kotlin.time.DurationUnit/PointingToDeclaration/okio/okio/-timeout/-companion/timeout.html
+$dokka.location:okio/Timeout.Companion/timeout/okio.Timeout#kotlin.time.Duration/PointingToDeclaration/okio/okio/-timeout/-companion/timeout.html
$dokka.location:okio/Timeout///PointingToDeclaration/okio/okio/-timeout/index.html
$dokka.location:okio/Timeout/Timeout/#/PointingToDeclaration/okio/okio/-timeout/-timeout.html
$dokka.location:okio/Timeout/awaitSignal/#java.util.concurrent.locks.Condition/PointingToDeclaration/okio/okio/-timeout/await-signal.html
+$dokka.location:okio/Timeout/cancel/#/PointingToDeclaration/okio/okio/-timeout/cancel.html
$dokka.location:okio/Timeout/clearDeadline/#/PointingToDeclaration/okio/okio/-timeout/clear-deadline.html
$dokka.location:okio/Timeout/clearTimeout/#/PointingToDeclaration/okio/okio/-timeout/clear-timeout.html
$dokka.location:okio/Timeout/deadline/#kotlin.Long#java.util.concurrent.TimeUnit/PointingToDeclaration/okio/okio/-timeout/deadline.html
@@ -546,5 +580,10 @@ $dokka.location:okio/Timeout/throwIfReached/#/PointingToDeclaration/okio/okio/-
$dokka.location:okio/Timeout/timeout/#kotlin.Long#java.util.concurrent.TimeUnit/PointingToDeclaration/okio/okio/-timeout/timeout.html
$dokka.location:okio/Timeout/timeoutNanos/#/PointingToDeclaration/okio/okio/-timeout/timeout-nanos.html
$dokka.location:okio/Timeout/waitUntilNotified/#kotlin.Any/PointingToDeclaration/okio/okio/-timeout/wait-until-notified.html
+$dokka.location:okio/TypedOptions.Companion///PointingToDeclaration/okio/okio/-typed-options/-companion/index.html
+$dokka.location:okio/TypedOptions.Companion/of/#kotlin.collections.Iterable[TypeParam(bounds=[kotlin.Any])]#kotlin.Function1[TypeParam(bounds=[kotlin.Any]),okio.ByteString]/PointingToDeclaration/okio/okio/-typed-options/-companion/of.html
+$dokka.location:okio/TypedOptions///PointingToDeclaration/okio/okio/-typed-options/index.html
+$dokka.location:okio/TypedOptions/TypedOptions/#kotlin.collections.List[TypeParam(bounds=[kotlin.Any])]#okio.Options/PointingToDeclaration/okio/okio/-typed-options/-typed-options.html
+$dokka.location:okio/TypedOptions/get/#kotlin.Int/PointingToDeclaration/okio/okio/-typed-options/get.html
+$dokka.location:okio/TypedOptions/size/#/PointingToDeclaration/okio/okio/-typed-options/size.html
okio
-
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/InstantSerializationTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/InstantSerializationTest.kt
new file mode 100644
index 0000000000..2db0929db0
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/InstantSerializationTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2025-2025 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.builtins.*
+import kotlin.time.*
+import kotlin.test.*
+import kotlin.reflect.typeOf
+
+@OptIn(ExperimentalTime::class)
+class InstantSerializationTest: JsonTestBase() {
+ private fun iso8601Serialization(serializer: KSerializer) {
+ for ((instant, json) in listOf(
+ Pair(Instant.fromEpochSeconds(1607505416, 124000),
+ "\"2020-12-09T09:16:56.000124Z\""),
+ Pair(Instant.fromEpochSeconds(-1607505416, -124000),
+ "\"1919-01-23T14:43:03.999876Z\""),
+ Pair(Instant.fromEpochSeconds(987654321, 123456789),
+ "\"2001-04-19T04:25:21.123456789Z\""),
+ Pair(Instant.fromEpochSeconds(987654321, 0),
+ "\"2001-04-19T04:25:21Z\""),
+ )) {
+ assertJsonFormAndRestored(serializer, instant, json)
+ }
+ for ((instant, json) in listOf(
+ Pair(Instant.fromEpochSeconds(987654321, 123456789),
+ "\"2001-04-19T07:55:21.123456789+03:30\""),
+ Pair(Instant.fromEpochSeconds(987654321, 123456789),
+ "\"2001-04-19T00:55:21.123456789-03:30\""),
+ )) {
+ assertRestoredFromJsonForm(serializer, json, instant)
+ }
+ }
+
+ private fun componentSerialization(serializer: KSerializer) {
+ for ((instant, json) in listOf(
+ Pair(Instant.fromEpochSeconds(1607505416, 124000),
+ "{\"epochSeconds\":1607505416,\"nanosecondsOfSecond\":124000}"),
+ Pair(Instant.fromEpochSeconds(-1607505416, -124000),
+ "{\"epochSeconds\":-1607505417,\"nanosecondsOfSecond\":999876000}"),
+ Pair(Instant.fromEpochSeconds(987654321, 123456789),
+ "{\"epochSeconds\":987654321,\"nanosecondsOfSecond\":123456789}"),
+ Pair(Instant.fromEpochSeconds(987654321, 0),
+ "{\"epochSeconds\":987654321,\"nanosecondsOfSecond\":0}"),
+ )) {
+ assertJsonFormAndRestored(serializer, instant, json)
+ }
+ // by default, `nanosecondsOfSecond` is optional
+ assertJsonFormAndRestored(serializer, Instant.fromEpochSeconds(987654321, 0),
+ "{\"epochSeconds\":987654321}", Json { })
+ // having a `"nanosecondsOfSecond": 0` field doesn't break deserialization
+ assertEquals(Instant.fromEpochSeconds(987654321, 0),
+ Json.decodeFromString(serializer,
+ "{\"epochSeconds\":987654321,\"nanosecondsOfSecond\":0}"))
+ // with `default`, `nanosecondsOfSecond` is non-optional
+ assertJsonFormAndRestored(serializer, Instant.fromEpochSeconds(987654321, 0),
+ "{\"epochSeconds\":987654321,\"nanosecondsOfSecond\":0}")
+ // as does not having a `"nanosecondsOfSecond"` field if `encodeDefaults` is true
+ assertEquals(Instant.fromEpochSeconds(987654321, 0),
+ default.decodeFromString(serializer,
+ "{\"epochSeconds\":987654321}"))
+ // "epochSeconds" should always be present
+ assertFailsWith { Json.decodeFromString(serializer, "{}") }
+ assertFailsWith { Json.decodeFromString(serializer, "{\"nanosecondsOfSecond\":3}") }
+ }
+
+ @Test
+ fun testIso8601Serialization() {
+ iso8601Serialization(Instant.serializer())
+ }
+
+ @Test
+ fun testComponentSerialization() {
+ componentSerialization(InstantComponentSerializer)
+ }
+
+ @Test
+ fun testDefaultSerializers() {
+ // should be the same as the ISO 8601
+ @Suppress("UNCHECKED_CAST")
+ iso8601Serialization(serializer(typeOf()) as KSerializer)
+ // iso8601Serialization(serializer()) TODO: uncomment when the compiler adds KT-75759
+ }
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt
index 8d31ba2273..10f943877a 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonPathTest.kt
@@ -156,6 +156,27 @@ class JsonPathTest : JsonTestBase() {
expectPath(expectedPath) { json.decodeFromString(Sealed.serializer(), malformed) }
}
+ @Serializable
+ data class SimpleNested(val n: SimpleNested? = null, val t: DataObject? = null)
+
+ @Serializable
+ data object DataObject
+
+ @Test
+ fun testMalformedDataObjectInDeeplyNestedStructure() {
+ var outer = SimpleNested(t = DataObject)
+ repeat(20) {
+ outer = SimpleNested(n = outer)
+ }
+ val str = Json.encodeToString(SimpleNested.serializer(), outer)
+ // throw-away data
+ Json.decodeFromString(SimpleNested.serializer(), str)
+
+ val malformed = str.replace("{}", "42")
+ val expectedPath = "$" + ".n".repeat(20) + ".t\n"
+ expectPath(expectedPath) { Json.decodeFromString(SimpleNested.serializer(), malformed) }
+ }
+
private inline fun expectPath(path: String, block: () -> Unit) {
val message = runCatching { block() }
.exceptionOrNull()!!.message!!
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
index de8cfb38b0..66f00177aa 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
@@ -190,4 +190,15 @@ abstract class JsonTestBase {
assertTrue("Failed with streaming = $jsonTestingMode\n\tsource value =$data\n\tdeserialized value=$deserialized") { check(data, deserialized) }
}
}
+
+ internal fun assertRestoredFromJsonForm(
+ serializer: KSerializer,
+ jsonForm: String,
+ expected: T,
+ ) {
+ parametrizedTest { jsonTestingMode ->
+ val deserialized: T = Json.decodeFromString(serializer, jsonForm, jsonTestingMode)
+ assertEquals(expected, deserialized, "Failed with streaming = $jsonTestingMode")
+ }
+ }
}
diff --git a/formats/json/api/kotlinx-serialization-json.api b/formats/json/api/kotlinx-serialization-json.api
index d46439e0c3..48a747da2b 100644
--- a/formats/json/api/kotlinx-serialization-json.api
+++ b/formats/json/api/kotlinx-serialization-json.api
@@ -136,7 +136,7 @@ public abstract interface annotation class kotlinx/serialization/json/JsonClassD
public abstract fun discriminator ()Ljava/lang/String;
}
-public synthetic class kotlinx/serialization/json/JsonClassDiscriminator$Impl : kotlinx/serialization/json/JsonClassDiscriminator {
+public final synthetic class kotlinx/serialization/json/JsonClassDiscriminator$Impl : kotlinx/serialization/json/JsonClassDiscriminator {
public fun (Ljava/lang/String;)V
public final synthetic fun discriminator ()Ljava/lang/String;
}
@@ -265,7 +265,7 @@ public final class kotlinx/serialization/json/JsonEncoder$DefaultImpls {
public abstract interface annotation class kotlinx/serialization/json/JsonIgnoreUnknownKeys : java/lang/annotation/Annotation {
}
-public synthetic class kotlinx/serialization/json/JsonIgnoreUnknownKeys$Impl : kotlinx/serialization/json/JsonIgnoreUnknownKeys {
+public final synthetic class kotlinx/serialization/json/JsonIgnoreUnknownKeys$Impl : kotlinx/serialization/json/JsonIgnoreUnknownKeys {
public fun ()V
}
@@ -278,7 +278,7 @@ public abstract interface annotation class kotlinx/serialization/json/JsonNames
public abstract fun names ()[Ljava/lang/String;
}
-public synthetic class kotlinx/serialization/json/JsonNames$Impl : kotlinx/serialization/json/JsonNames {
+public final synthetic class kotlinx/serialization/json/JsonNames$Impl : kotlinx/serialization/json/JsonNames {
public fun ([Ljava/lang/String;)V
public final synthetic fun names ()[Ljava/lang/String;
}
diff --git a/formats/json/build.gradle.kts b/formats/json/build.gradle.kts
index db48d95564..2ef589ac19 100644
--- a/formats/json/build.gradle.kts
+++ b/formats/json/build.gradle.kts
@@ -52,11 +52,4 @@ kotlin {
}
}
-// This task should be disabled because of no need to build and publish intermediate JsWasm sourceset
-tasks.whenTaskAdded {
- if (name == "compileJsWasmMainKotlinMetadata") {
- enabled = false
- }
-}
-
configureJava9ModuleInfo()
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt
index 14e70a4259..d6e3de1516 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonPath.kt
@@ -134,7 +134,9 @@ internal class JsonPath {
private fun resize() {
val newSize = currentDepth * 2
currentObjectPath = currentObjectPath.copyOf(newSize)
- indicies = indicies.copyOf(newSize)
+ val newIndices = IntArray(newSize) { -1 }
+ indicies.copyInto(newIndices)
+ indicies = newIndices
}
override fun toString(): String = getPath()
diff --git a/formats/protobuf/api/kotlinx-serialization-protobuf.api b/formats/protobuf/api/kotlinx-serialization-protobuf.api
index 682e4bf16b..59a1073056 100644
--- a/formats/protobuf/api/kotlinx-serialization-protobuf.api
+++ b/formats/protobuf/api/kotlinx-serialization-protobuf.api
@@ -34,7 +34,7 @@ public abstract interface annotation class kotlinx/serialization/protobuf/ProtoN
public abstract fun number ()I
}
-public synthetic class kotlinx/serialization/protobuf/ProtoNumber$Impl : kotlinx/serialization/protobuf/ProtoNumber {
+public final synthetic class kotlinx/serialization/protobuf/ProtoNumber$Impl : kotlinx/serialization/protobuf/ProtoNumber {
public fun (I)V
public final synthetic fun number ()I
}
@@ -42,14 +42,14 @@ public synthetic class kotlinx/serialization/protobuf/ProtoNumber$Impl : kotlinx
public abstract interface annotation class kotlinx/serialization/protobuf/ProtoOneOf : java/lang/annotation/Annotation {
}
-public synthetic class kotlinx/serialization/protobuf/ProtoOneOf$Impl : kotlinx/serialization/protobuf/ProtoOneOf {
+public final synthetic class kotlinx/serialization/protobuf/ProtoOneOf$Impl : kotlinx/serialization/protobuf/ProtoOneOf {
public fun ()V
}
public abstract interface annotation class kotlinx/serialization/protobuf/ProtoPacked : java/lang/annotation/Annotation {
}
-public synthetic class kotlinx/serialization/protobuf/ProtoPacked$Impl : kotlinx/serialization/protobuf/ProtoPacked {
+public final synthetic class kotlinx/serialization/protobuf/ProtoPacked$Impl : kotlinx/serialization/protobuf/ProtoPacked {
public fun ()V
}
@@ -57,7 +57,7 @@ public abstract interface annotation class kotlinx/serialization/protobuf/ProtoT
public abstract fun type ()Lkotlinx/serialization/protobuf/ProtoIntegerType;
}
-public synthetic class kotlinx/serialization/protobuf/ProtoType$Impl : kotlinx/serialization/protobuf/ProtoType {
+public final synthetic class kotlinx/serialization/protobuf/ProtoType$Impl : kotlinx/serialization/protobuf/ProtoType {
public fun (Lkotlinx/serialization/protobuf/ProtoIntegerType;)V
public final synthetic fun type ()Lkotlinx/serialization/protobuf/ProtoIntegerType;
}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
index 18be4e0a69..6ba93e9e7d 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
@@ -36,6 +36,7 @@ internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, C
public final override fun encodeNull() {
if (nullableMode != NullableMode.ACCEPTABLE) {
+ @Suppress("REDUNDANT_ELSE_IN_WHEN")
val message = when (nullableMode) {
NullableMode.OPTIONAL -> "'null' is not supported for optional properties in ProtoBuf"
NullableMode.COLLECTION -> "'null' is not supported as the value of collection types in ProtoBuf"
diff --git a/gradle.properties b/gradle.properties
index deb1585fa6..092c559a8f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,11 +3,11 @@
#
group=org.jetbrains.kotlinx
-version=1.8.2-SNAPSHOT
+version=1.9.1-SNAPSHOT
jdk_toolchain_version=11
# This version takes precedence if 'bootstrap' property passed to project
-kotlin.version.snapshot=2.1.255-SNAPSHOT
+kotlin.version.snapshot=2.2.255-SNAPSHOT
# Also set kotlin.native.home to your $kotlin_project$/kotlin-native/dist if you want to use snapshot Native
#kotlin.native.home=
@@ -23,3 +23,6 @@ org.gradle.kotlin.dsl.allWarningsAsErrors=true
kotlin.native.distribution.type=prebuilt
org.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"
+
+org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
+org.jetbrains.dokka.experimental.gradle.pluginMode.nowarn=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 132a67b614..c9d8fd26a1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-kotlin = "2.1.20"
+kotlin = "2.2.0"
kover = "0.8.2"
dokka = "2.0.0"
knit = "0.5.0"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 381baa9cef..d7c3e63751 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/integration-test/build.gradle.kts b/integration-test/build.gradle.kts
index 64b44e6d9f..6ce87104e8 100644
--- a/integration-test/build.gradle.kts
+++ b/integration-test/build.gradle.kts
@@ -20,7 +20,7 @@ plugins {
repositories {
mavenCentral()
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+ maven("https://redirector.kotlinlang.org/maven/dev")
mavenLocal {
mavenContent {
snapshotsOnly()
diff --git a/integration-test/gradle.properties b/integration-test/gradle.properties
index 3b4f9014ab..2d2f12686e 100644
--- a/integration-test/gradle.properties
+++ b/integration-test/gradle.properties
@@ -2,8 +2,8 @@
# Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
#
-mainKotlinVersion=2.1.20
-mainLibVersion=1.8.2-SNAPSHOT
+mainKotlinVersion=2.2.0
+mainLibVersion=1.9.1-SNAPSHOT
kotlin.code.style=official
diff --git a/integration-test/gradle/wrapper/gradle-wrapper.properties b/integration-test/gradle/wrapper/gradle-wrapper.properties
index b82aa23a4f..371de143b6 100644
--- a/integration-test/gradle/wrapper/gradle-wrapper.properties
+++ b/integration-test/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/integration-test/kotlin-js-store/yarn.lock b/integration-test/kotlin-js-store/yarn.lock
index c44683dfd5..02b202d90e 100644
--- a/integration-test/kotlin-js-store/yarn.lock
+++ b/integration-test/kotlin-js-store/yarn.lock
@@ -461,11 +461,6 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
-typescript@5.5.4:
- version "5.5.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
- integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
-
workerpool@^6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
diff --git a/integration-test/settings.gradle.kts b/integration-test/settings.gradle.kts
index c2cb0c46f7..3d37c444fc 100644
--- a/integration-test/settings.gradle.kts
+++ b/integration-test/settings.gradle.kts
@@ -17,7 +17,7 @@ pluginManagement {
repositories {
mavenCentral()
maven("https://plugins.gradle.org/m2/")
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+ maven("https://redirector.kotlinlang.org/maven/dev")
mavenLocal()
}
}
diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock
index 2608a773ca..c90cd890a2 100644
--- a/kotlin-js-store/yarn.lock
+++ b/kotlin-js-store/yarn.lock
@@ -456,11 +456,6 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
-typescript@5.5.4:
- version "5.5.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
- integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
-
workerpool@^6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 5d8194dd35..6b2166dadf 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -25,11 +25,7 @@ pluginManagement {
}
// kotlin-dev with space redirector
- maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
-
- maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
- // For Dokka that depends on kotlinx-html
- maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
+ maven("https://redirector.kotlinlang.org/maven/dev")
gradlePluginPortal()
mavenCentral()