fixe #2 effective Instant binary representation (truncates to millis though)
This commit is contained in:
parent
d0f29ce06f
commit
ee20bfdee7
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "1.8.10"
|
kotlin("multiplatform") version "1.8.20"
|
||||||
kotlin("plugin.serialization") version "1.8.10"
|
kotlin("plugin.serialization") version "1.8.20"
|
||||||
id("org.jetbrains.dokka") version "1.6.0"
|
id("org.jetbrains.dokka") version "1.6.0"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
@ -8,7 +8,7 @@ plugins {
|
|||||||
val serialization_version = "1.3.4"
|
val serialization_version = "1.3.4"
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.0.2-SNAPSHOT"
|
version = "0.0.3-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -68,6 +68,7 @@ kotlin {
|
|||||||
// this is actually a bug: we need only the core, but bare core causes strange errors
|
// this is actually a bug: we need only the core, but bare core causes strange errors
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||||
// api("net.sergeych:mp_stools:[1.3.3,)")
|
// api("net.sergeych:mp_stools:[1.3.3,)")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val commonTest by getting {
|
val commonTest by getting {
|
||||||
|
34
src/commonMain/kotlin/net.sergeych.bintools/MotherPack.kt
Normal file
34
src/commonMain/kotlin/net.sergeych.bintools/MotherPack.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package net.sergeych.bintools
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental interface for packing and unpacking binary formats.
|
||||||
|
* Initial support intended for BiPack and BOSS to make it fast replaceable.
|
||||||
|
* Also, JSON text version with binary converter is presented by default.
|
||||||
|
*/
|
||||||
|
interface MotherPacker {
|
||||||
|
fun <T>pack(type: KType, payload: T): ByteArray
|
||||||
|
fun <T>unpack(type: KType,packed: ByteArray): T
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T>MotherPacker.pack(payload: T) = pack(typeOf<T>(), payload)
|
||||||
|
inline fun <reified T>MotherPacker.unpack(packed: ByteArray) = unpack<T>(typeOf<T>(), packed)
|
||||||
|
|
||||||
|
class JsonPacker : MotherPacker {
|
||||||
|
override fun <T> pack(type: KType, payload: T): ByteArray {
|
||||||
|
return Json.encodeToString(serializer(type), payload).encodeToByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> unpack(type: KType, packed: ByteArray): T {
|
||||||
|
return Json.decodeFromString<T>(
|
||||||
|
serializer(type) as KSerializer<T>,
|
||||||
|
packed.decodeToString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
package net.sergeych.bipack
|
package net.sergeych.bipack
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
@ -15,6 +16,7 @@ import net.sergeych.bintools.*
|
|||||||
* Decode BiPack format. Note that it relies on [DataSource] so can throw [DataSource.EndOfData]
|
* Decode BiPack format. Note that it relies on [DataSource] so can throw [DataSource.EndOfData]
|
||||||
* excpetion. Specific frames when used can throw [InvalidFrameException] and its derivatives.e
|
* excpetion. Specific frames when used can throw [InvalidFrameException] and its derivatives.e
|
||||||
*/
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
class BipackDecoder(
|
class BipackDecoder(
|
||||||
val input: DataSource, var elementsCount: Int = 0, val isCollection: Boolean = false,
|
val input: DataSource, var elementsCount: Int = 0, val isCollection: Boolean = false,
|
||||||
val hasFixedSize: Boolean = false,
|
val hasFixedSize: Boolean = false,
|
||||||
@ -64,12 +66,18 @@ class BipackDecoder(
|
|||||||
return elementIndex++
|
return elementIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
|
||||||
|
return if( deserializer == Instant.serializer() )
|
||||||
|
Instant.fromEpochMilliseconds(decodeLong()) as T
|
||||||
|
else
|
||||||
|
super.decodeSerializableValue(deserializer)
|
||||||
|
}
|
||||||
override fun decodeSequentially(): Boolean = isCollection
|
override fun decodeSequentially(): Boolean = isCollection
|
||||||
|
|
||||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
||||||
val isCollection = descriptor.kind == StructureKind.LIST || descriptor.kind == StructureKind.MAP
|
val isCollection = descriptor.kind == StructureKind.LIST || descriptor.kind == StructureKind.MAP
|
||||||
|
|
||||||
var source = if (descriptor.annotations.any { it is CrcProtected })
|
val source = if (descriptor.annotations.any { it is CrcProtected })
|
||||||
CRC32Source(input)
|
CRC32Source(input)
|
||||||
else
|
else
|
||||||
input
|
input
|
||||||
@ -119,6 +127,7 @@ class BipackDecoder(
|
|||||||
fun <T> decode(source: DataSource, deserializer: DeserializationStrategy<T>): T =
|
fun <T> decode(source: DataSource, deserializer: DeserializationStrategy<T>): T =
|
||||||
BipackDecoder(source).decodeSerializableValue(deserializer)
|
BipackDecoder(source).decodeSerializableValue(deserializer)
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
inline fun <reified T> decode(source: DataSource): T = decode(source, serializer())
|
inline fun <reified T> decode(source: DataSource): T = decode(source, serializer())
|
||||||
inline fun <reified T> decode(source: ByteArray): T =
|
inline fun <reified T> decode(source: ByteArray): T =
|
||||||
decode(source.toDataSource(), serializer())
|
decode(source.toDataSource(), serializer())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.sergeych.bipack
|
package net.sergeych.bipack
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.serialization.SerializationStrategy
|
import kotlinx.serialization.SerializationStrategy
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.AbstractEncoder
|
import kotlinx.serialization.encoding.AbstractEncoder
|
||||||
@ -76,6 +77,11 @@ class BipackEncoder(val output: DataSink) : AbstractEncoder() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
|
||||||
|
if (value is Instant) encodeLong(value.toEpochMilliseconds())
|
||||||
|
else super.encodeSerializableValue(serializer, value)
|
||||||
|
}
|
||||||
|
|
||||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
|
||||||
// frame protection should start before anything else:
|
// frame protection should start before anything else:
|
||||||
val sink = if (descriptor.annotations.any { it is CrcProtected })
|
val sink = if (descriptor.annotations.any { it is CrcProtected })
|
||||||
@ -115,6 +121,7 @@ class BipackEncoder(val output: DataSink) : AbstractEncoder() {
|
|||||||
ArrayDataSink().also { encode(serializer, value, it) }.toByteArray()
|
ArrayDataSink().also { encode(serializer, value, it) }.toByteArray()
|
||||||
|
|
||||||
inline fun <reified T> encode(value: T) = encode(serializer(), value)
|
inline fun <reified T> encode(value: T) = encode(serializer(), value)
|
||||||
|
@Suppress("unused")
|
||||||
inline fun <reified T> encode(value: T, sink: DataSink) = encode(serializer(), value, sink)
|
inline fun <reified T> encode(value: T, sink: DataSink) = encode(serializer(), value, sink)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
18
src/commonMain/kotlin/net.sergeych.bipack/MotherBipack.kt
Normal file
18
src/commonMain/kotlin/net.sergeych.bipack/MotherBipack.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package net.sergeych.bipack
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import net.sergeych.bintools.MotherPacker
|
||||||
|
import net.sergeych.bintools.toDataSource
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
|
class MotherBipack : MotherPacker {
|
||||||
|
override fun <T> pack(type: KType, payload: T): ByteArray {
|
||||||
|
return BipackEncoder.encode(serializer(type), payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> unpack(type: KType, packed: ByteArray): T {
|
||||||
|
return BipackDecoder.decode<T>(packed.toDataSource(),
|
||||||
|
serializer(type) as KSerializer<T>)
|
||||||
|
}
|
||||||
|
}
|
28
src/commonTest/kotlin/bintools/JsonPackerTest.kt
Normal file
28
src/commonTest/kotlin/bintools/JsonPackerTest.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package bintools
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.sergeych.bintools.JsonPacker
|
||||||
|
import net.sergeych.bintools.pack
|
||||||
|
import net.sergeych.bintools.unpack
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
class JsonPackerTest {
|
||||||
|
@Serializable
|
||||||
|
data class FB1(val foo: Int,val bar: String)
|
||||||
|
@Test
|
||||||
|
fun testPackUnpack() {
|
||||||
|
val mp = JsonPacker()
|
||||||
|
println(mp.pack(mapOf("foo" to 42)).decodeToString())
|
||||||
|
assertEquals("""{"foo":42}""", mp.pack(mapOf("foo" to 42)).decodeToString())
|
||||||
|
val x = mp.unpack<FB1>("""{"foo":42, "bar": "foo"}""".encodeToByteArray())
|
||||||
|
println(x)
|
||||||
|
assertEquals(42, x.foo)
|
||||||
|
assertEquals("foo", x.bar)
|
||||||
|
|
||||||
|
var nx: FB1? = null
|
||||||
|
println(mp.pack(nx).decodeToString())
|
||||||
|
assertNull(mp.unpack<FB1?>("null".encodeToByteArray()))
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package bipack
|
package bipack
|
||||||
|
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.sergeych.bintools.encodeToHex
|
import net.sergeych.bintools.encodeToHex
|
||||||
@ -341,4 +343,12 @@ class BipackEncoderTest {
|
|||||||
assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(Foo(0x100000002)).encodeToHex())
|
assertEquals("00 00 00 01 00 00 00 02", BipackEncoder.encode(Foo(0x100000002)).encodeToHex())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInstant() {
|
||||||
|
val x = Clock.System.now()
|
||||||
|
// println( BipackEncoder.encode(x).toDump() )
|
||||||
|
val y = BipackDecoder.decode<Instant>(BipackEncoder.encode(x))
|
||||||
|
assertEquals(x.toEpochMilliseconds(), y.toEpochMilliseconds())
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user