Skip to content

Commit

Permalink
Improved okio support (#1982)
Browse files Browse the repository at this point in the history
  • Loading branch information
shanshin committed Jul 12, 2022
1 parent 4524b65 commit df3a161
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 71 deletions.
Expand Up @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.module.kotlin.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.json.okio.encodeToSink
import kotlinx.serialization.json.okio.encodeToBufferedSink
import okio.blackholeSink
import okio.buffer
import org.openjdk.jmh.annotations.*
Expand Down Expand Up @@ -97,7 +97,7 @@ open class JacksonComparisonBenchmark {
fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), data, devNullStream)

@Benchmark
fun kotlinToOkio() = Json.encodeToSink(DefaultPixelEvent.serializer(), data, devNullSink)
fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), data, devNullSink)

@Benchmark
fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), dataWithEscapes)
Expand All @@ -109,7 +109,7 @@ open class JacksonComparisonBenchmark {
fun kotlinSmallToStream() = Json.encodeToStream(SmallDataClass.serializer(), smallData, devNullStream)

@Benchmark
fun kotlinSmallToOkio() = Json.encodeToSink(SmallDataClass.serializer(), smallData, devNullSink)
fun kotlinSmallToOkio() = Json.encodeToBufferedSink(SmallDataClass.serializer(), smallData, devNullSink)

@Benchmark
fun jacksonFromString(): DefaultPixelEvent = objectMapper.readValue(stringData, DefaultPixelEvent::class.java)
Expand Down
8 changes: 4 additions & 4 deletions formats/json-okio/api/kotlinx-serialization-json-okio.api
@@ -1,7 +1,7 @@
public final class kotlinx/serialization/json/okio/OkioStreamsKt {
public static final fun decodeFromSource (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Lokio/Source;)Ljava/lang/Object;
public static final fun decodeSourceToSequence (Lkotlinx/serialization/json/Json;Lokio/Source;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;)Lkotlin/sequences/Sequence;
public static synthetic fun decodeSourceToSequence$default (Lkotlinx/serialization/json/Json;Lokio/Source;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun encodeToSink (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lokio/Sink;)V
public static final fun decodeBufferedSourceToSequence (Lkotlinx/serialization/json/Json;Lokio/BufferedSource;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;)Lkotlin/sequences/Sequence;
public static synthetic fun decodeBufferedSourceToSequence$default (Lkotlinx/serialization/json/Json;Lokio/BufferedSource;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun decodeFromBufferedSource (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Lokio/BufferedSource;)Ljava/lang/Object;
public static final fun encodeToBufferedSink (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lokio/BufferedSink;)V
}

Expand Up @@ -2,6 +2,8 @@
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")

package kotlinx.serialization.json.okio

import kotlinx.serialization.*
Expand All @@ -16,19 +18,16 @@ import okio.*
/**
* Serializes the [value] with [serializer] into a [target] using JSON format and UTF-8 encoding.
*
* If [target] is not a [BufferedSink] then called [Sink.buffer] for it to create buffered wrapper.
*
* @throws [SerializationException] if the given value cannot be serialized to JSON.
* @throws [okio.IOException] If an I/O error occurs and sink can't be written to.
*/
@ExperimentalSerializationApi
public fun <T> Json.encodeToSink(
public fun <T> Json.encodeToBufferedSink(
serializer: SerializationStrategy<T>,
value: T,
target: Sink
target: BufferedSink
) {
val buffered = if (target is BufferedSink) target else target.buffer()
val writer = JsonToOkioStreamWriter(buffered)
val writer = JsonToOkioStreamWriter(target)
try {
encodeByWriter(writer, serializer, value)
} finally {
Expand All @@ -45,95 +44,85 @@ public fun <T> Json.encodeToSink(
* @throws [okio.IOException] If an I/O error occurs and sink can't be written to.
*/
@ExperimentalSerializationApi
public inline fun <reified T> Json.encodeToSink(
public inline fun <reified T> Json.encodeToBufferedSink(
value: T,
target: Sink
): Unit = encodeToSink(serializersModule.serializer(), value, target)
target: BufferedSink
): Unit = encodeToBufferedSink(serializersModule.serializer(), value, target)


/**
* Deserializes JSON from [source] using UTF-8 encoding to a value of type [T] using [deserializer].
*
* If [source] is not a [BufferedSource] then called [Source.buffer] for it to create buffered wrapper.
*
* Note that this functions expects that exactly one object would be present in the source
* and throws an exception if there are any dangling bytes after an object.
*
* @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
* @throws [okio.IOException] If an I/O error occurs and source can't be read from.
*/
@ExperimentalSerializationApi
public fun <T> Json.decodeFromSource(
public fun <T> Json.decodeFromBufferedSource(
deserializer: DeserializationStrategy<T>,
source: Source
source: BufferedSource
): T {
val buffered = if (source is BufferedSource) source else source.buffer()
return decodeByReader(deserializer, OkioSerialReader(buffered))
return decodeByReader(deserializer, OkioSerialReader(source))
}

/**
* Deserializes the contents of given [source] to the value of type [T] using UTF-8 encoding and
* deserializer retrieved from the reified type parameter.
*
* If [source] is not a [BufferedSource] then called [Source.buffer] for it to create buffered wrapper.
*
* Note that this functions expects that exactly one object would be present in the stream
* and throws an exception if there are any dangling bytes after an object.
*
* @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
* @throws [okio.IOException] If an I/O error occurs and source can't be read from.
*/
@ExperimentalSerializationApi
public inline fun <reified T> Json.decodeFromSource(source: Source): T =
decodeFromSource(serializersModule.serializer(), source)
public inline fun <reified T> Json.decodeFromBufferedSource(source: BufferedSource): T =
decodeFromBufferedSource(serializersModule.serializer(), source)


/**
* Transforms the given [source] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and [deserializer].
* Unlike [decodeFromSource], [source] is allowed to have more than one element, separated as [format] declares.
*
* If [source] is not a [BufferedSource] then called [Source.buffer] for it to create buffered wrapper.
* Unlike [decodeFromBufferedSource], [source] is allowed to have more than one element, separated as [format] declares.
*
* Elements must all be of type [T].
* Elements are parsed lazily when resulting [Sequence] is evaluated.
* Resulting sequence is tied to the stream and can be evaluated only once.
*
* **Resource caution:** this method neither closes the [source] when the parsing is finished nor provides a method to close it manually.
* It is a caller responsibility to hold a reference to a stream and close it. Moreover, because stream is parsed lazily,
* It is a caller responsibility to hold a reference to a source and close it. Moreover, because source is parsed lazily,
* closing it before returned sequence is evaluated completely will result in [Exception] from decoder.
*
* @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
* @throws [okio.IOException] If an I/O error occurs and source can't be read from.
*/
@ExperimentalSerializationApi
public fun <T> Json.decodeSourceToSequence(
source: Source,
public fun <T> Json.decodeBufferedSourceToSequence(
source: BufferedSource,
deserializer: DeserializationStrategy<T>,
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
): Sequence<T> {
val buffered = if (source is BufferedSource) source else source.buffer()
return decodeToSequenceByReader(OkioSerialReader(buffered), deserializer, format)
return decodeToSequenceByReader(OkioSerialReader(source), deserializer, format)
}

/**
* Transforms the given [source] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and deserializer retrieved from the reified type parameter.
* Unlike [decodeFromSource], [source] is allowed to have more than one element, separated as [format] declares.
*
* If [source] is not a [BufferedSource] then called [Source.buffer] for it to create buffered wrapper.
* Unlike [decodeFromBufferedSource], [source] is allowed to have more than one element, separated as [format] declares.
*
* Elements must all be of type [T].
* Elements are parsed lazily when resulting [Sequence] is evaluated.
* Resulting sequence is tied to the stream and constrained to be evaluated only once.
*
* **Resource caution:** this method does not close [source] when the parsing is finished neither provides method to close it manually.
* It is a caller responsibility to hold a reference to a stream and close it. Moreover, because stream is parsed lazily,
* It is a caller responsibility to hold a reference to a source and close it. Moreover, because source is parsed lazily,
* closing it before returned sequence is evaluated fully would result in [Exception] from decoder.
*
* @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
* @throws [okio.IOException] If an I/O error occurs and source can't be read from.
*/
@ExperimentalSerializationApi
public inline fun <reified T> Json.decodeSourceToSequence(
source: Source,
public inline fun <reified T> Json.decodeBufferedSourceToSequence(
source: BufferedSource,
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
): Sequence<T> = decodeSourceToSequence(source, serializersModule.serializer(), format)
): Sequence<T> = decodeBufferedSourceToSequence(source, serializersModule.serializer(), format)
Expand Up @@ -2,7 +2,7 @@
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")

package kotlinx.serialization.json.okio.internal

Expand Down Expand Up @@ -42,7 +42,7 @@ internal class JsonToOkioStreamWriter(private val target: BufferedSink) : JsonWr
}

override fun release() {
target.flush()
// no-op, see https://github.com/Kotlin/kotlinx.serialization/pull/1982#discussion_r915043700
}
}

Expand Down
Expand Up @@ -6,8 +6,8 @@ package kotlinx.serialization.json

import kotlinx.serialization.*
import kotlinx.serialization.json.internal.*
import kotlinx.serialization.json.okio.decodeFromSource
import kotlinx.serialization.json.okio.encodeToSink
import kotlinx.serialization.json.okio.decodeFromBufferedSource
import kotlinx.serialization.json.okio.encodeToBufferedSink
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.test.*
Expand Down Expand Up @@ -53,7 +53,7 @@ abstract class JsonTestBase {
}
JsonTestingMode.OKIO_STREAMS -> {
val buffer = Buffer()
encodeToSink(serializer, value, buffer)
encodeToBufferedSink(serializer, value, buffer)
buffer.readUtf8()
}
}
Expand Down Expand Up @@ -82,7 +82,7 @@ abstract class JsonTestBase {
JsonTestingMode.OKIO_STREAMS -> {
val buffer = Buffer()
buffer.writeUtf8(source)
decodeFromSource(deserializer, buffer)
decodeFromBufferedSource(deserializer, buffer)
}
}

Expand Down
Expand Up @@ -4,25 +4,22 @@ import kotlinx.serialization.*
import kotlinx.serialization.json.DecodeSequenceMode
import kotlinx.serialization.json.Json

/** @suppress */
@InternalSerializationApi
public interface JsonWriter {
public fun writeLong(value: Long)
public fun writeChar(char: Char)
public fun write(text: String)
public fun writeQuoted(text: String)
public fun release()
@PublishedApi
internal interface JsonWriter {
fun writeLong(value: Long)
fun writeChar(char: Char)
fun write(text: String)
fun writeQuoted(text: String)
fun release()
}

/** @suppress */
@InternalSerializationApi
public interface SerialReader {
public fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int
@PublishedApi
internal interface SerialReader {
fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int
}

/** @suppress */
@InternalSerializationApi
public fun <T> Json.encodeByWriter(writer: JsonWriter, serializer: SerializationStrategy<T>, value: T) {
@PublishedApi
internal fun <T> Json.encodeByWriter(writer: JsonWriter, serializer: SerializationStrategy<T>, value: T) {
val encoder = StreamingJsonEncoder(
writer, this,
WriteMode.OBJ,
Expand All @@ -31,9 +28,8 @@ public fun <T> Json.encodeByWriter(writer: JsonWriter, serializer: Serialization
encoder.encodeSerializableValue(serializer, value)
}

/** @suppress */
@InternalSerializationApi
public fun <T> Json.decodeByReader(
@PublishedApi
internal fun <T> Json.decodeByReader(
deserializer: DeserializationStrategy<T>,
reader: SerialReader
): T {
Expand All @@ -44,10 +40,9 @@ public fun <T> Json.decodeByReader(
return result
}

/** @suppress */
@InternalSerializationApi
@PublishedApi
@ExperimentalSerializationApi
public fun <T> Json.decodeToSequenceByReader(
internal fun <T> Json.decodeToSequenceByReader(
reader: SerialReader,
deserializer: DeserializationStrategy<T>,
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
Expand All @@ -57,10 +52,9 @@ public fun <T> Json.decodeToSequenceByReader(
return Sequence { iter }.constrainOnce()
}

/** @suppress */
@InternalSerializationApi
@PublishedApi
@ExperimentalSerializationApi
public inline fun <reified T> Json.decodeToSequenceByReader(
internal inline fun <reified T> Json.decodeToSequenceByReader(
reader: SerialReader,
format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
): Sequence<T> = decodeToSequenceByReader(reader, serializersModule.serializer(), format)

0 comments on commit df3a161

Please sign in to comment.