diff --git a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/protocol/ZBytes.kt b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/protocol/ZBytes.kt index a55414022..1cb2dc75e 100644 --- a/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/protocol/ZBytes.kt +++ b/zenoh-kotlin/src/commonMain/kotlin/io/zenoh/protocol/ZBytes.kt @@ -17,6 +17,7 @@ package io.zenoh.protocol import io.zenoh.jni.JNIZBytes import java.nio.ByteBuffer import java.nio.ByteOrder +import kotlin.reflect.KClass import kotlin.reflect.KFunction1 import kotlin.reflect.KType import kotlin.reflect.full.* @@ -294,25 +295,34 @@ class ZBytes internal constructor(internal val bytes: ByteArray) : Serializable * @see ZBytes * @return a [Result] with the serialized [ZBytes]. */ - inline fun serialize(t: T): Result = runCatching { + inline fun serialize(t: T): Result = runCatching { + return serialize(t, T::class) + } + + fun serialize(t: T, clazz: KClass): Result = runCatching { + val type: KType = when (clazz) { + List::class -> typeOf>() + Map::class -> typeOf>() + else -> clazz.createType() + } when { - typeOf>().isSupertypeOf(typeOf()) -> { + typeOf>().isSupertypeOf(type) -> { val list = t as List<*> val zbytesList = list.map { it.into() } return Result.success(JNIZBytes.serializeIntoList(zbytesList)) } - typeOf>().isSupertypeOf(typeOf()) -> { + typeOf>().isSupertypeOf(type) -> { val map = t as Map<*, *> val zbytesMap = map.map { (k, v) -> k.into() to v.into() }.toMap() return Result.success(JNIZBytes.serializeIntoMap(zbytesMap)) } - typeOf().isSupertypeOf(typeOf()) -> { + typeOf().isSupertypeOf(type) -> { return Result.success((t as Any).into()) } - else -> throw IllegalArgumentException("Unsupported type '${typeOf()}' for serialization.") + else -> throw IllegalArgumentException("Unsupported type '$type' for serialization.") } } } @@ -348,40 +358,101 @@ class ZBytes internal constructor(internal val bytes: ByteArray) : Serializable * @see Deserializable * @return a [Result] with the deserialization. */ - inline fun deserialize( + inline fun deserialize( deserializers: Map> = emptyMap() ): Result { - val deserializer = deserializers[typeOf()] + val type = typeOf() + val deserializer = deserializers[type] if (deserializer != null) { return Result.success(deserializer(this) as T) } + when { + typeOf>().isSupertypeOf(type) -> { + val itemsClass = type.arguments.firstOrNull()?.type?.jvmErasure + return deserialize(T::class, arg1clazz = itemsClass) + } + typeOf>().isSupertypeOf(type) -> { + val keyClass = type.arguments.getOrNull(0)?.type?.jvmErasure + val valueClass = type.arguments.getOrNull(1)?.type?.jvmErasure + return deserialize(T::class, arg1clazz = keyClass, arg2clazz = valueClass) + } + typeOf().isSupertypeOf(type) -> { + return deserialize(T::class) + } + } + throw IllegalArgumentException("Unsupported type for deserialization: '$type'.") + } + + /** + * Deserialize the [ZBytes] into an element of class [clazz]. + * + * It's generally preferable to use the [ZBytes.deserialize] function with reification, however + * this function is exposed for cases when reification needs to be avoided. + * + * Example: + * ```kotlin + * val list = listOf("value1", "value2", "value3") + * val zbytes = ZBytes.serialize(list).getOrThrow() + * val deserializedList = zbytes.deserialize(clazz = List::class, arg1clazz = String::class).getOrThrow() + * check(list == deserializedList) + * ``` + * + * Supported types: + * - [Number]: Byte, Short, Int, Long, Float, Double + * - [String] + * - [ByteArray] + * - [Deserializable] + * - Lists and Maps of the above-mentioned types. + * + * @see [ZBytes.deserialize] + * + * + * @param clazz: the [KClass] of the type to be serialized. + * @param arg1clazz Optional first nested parameter of the provided clazz, for instance when trying to deserialize + * into a `List`, arg1clazz should be set to `String::class`, when trying to deserialize into a + * `Map`, arg1clazz should be set to `Int::class`. Can be null if providing a basic type. + * @param arg2clazz Optional second nested parameter of the provided clazz, to be used for the cases of maps. + * For instance, when trying to deserialize into a `Map`, arg2clazz should be set to `String::class`. + * Can be null if providing a basic type. + */ + @Suppress("UNCHECKED_CAST") + fun deserialize( + clazz: KClass, + arg1clazz: KClass<*>? = null, + arg2clazz: KClass<*>? = null, + ): Result { + val type: KType = when (clazz) { + List::class -> typeOf>() + Map::class -> typeOf>() + else -> clazz.createType() + } return when { - typeOf>().isSupertypeOf(typeOf()) -> { - val type = typeOf().arguments.firstOrNull()?.type - if (type != null) { - Result.success(JNIZBytes.deserializeIntoList(this).map { it.intoAny(type) } as T) + typeOf>().isSupertypeOf(type) -> { + val typeElement = arg1clazz?.createType() + if (typeElement != null) { + Result.success(JNIZBytes.deserializeIntoList(this).map { it.intoAny(typeElement) } as T) } else { - Result.failure(IllegalArgumentException("Unsupported list type for deserialization: ${typeOf()}")) + Result.failure(IllegalArgumentException("Unsupported list type for deserialization: $type")) } } - typeOf>().isSupertypeOf(typeOf()) -> { - val keyType = typeOf().arguments.getOrNull(0)?.type - val valueType = typeOf().arguments.getOrNull(1)?.type + typeOf>().isSupertypeOf(type) -> { + val keyType = arg1clazz?.createType() + val valueType = arg2clazz?.createType() if (keyType != null && valueType != null) { Result.success(JNIZBytes.deserializeIntoMap(this) .map { (k, v) -> k.intoAny(keyType) to v.intoAny(valueType) }.toMap() as T ) } else { - Result.failure(IllegalArgumentException("Unsupported map type for deserialization: ${typeOf()}")) + Result.failure(IllegalArgumentException("Unsupported map type for deserialization: $type")) } } - typeOf().isSupertypeOf(typeOf()) -> { - Result.success(this.intoAny(typeOf()) as T) + typeOf().isSupertypeOf(type) -> { + Result.success(this.intoAny(type) as T) } - else -> Result.failure(IllegalArgumentException("Unsupported type for deserialization: ${typeOf()}")) + else -> Result.failure(IllegalArgumentException("Unsupported type for deserialization: $type")) } } @@ -433,8 +504,7 @@ fun ByteArray.into(): ZBytes { return ZBytes(this) } -@Throws -@PublishedApi +@Throws(Exception::class) internal fun Any?.into(): ZBytes { return when (this) { is String -> this.into() @@ -445,8 +515,7 @@ internal fun Any?.into(): ZBytes { } } -@Throws -@PublishedApi +@Throws(Exception::class) internal fun ZBytes.intoAny(type: KType): Any { return when (type) { typeOf() -> this.toString() @@ -475,8 +544,7 @@ internal fun ZBytes.intoAny(type: KType): Any { } } - else -> throw IllegalArgumentException("Unsupported type '$type' for deserialization. " + - "If you are providing a generic, try using reification.") + else -> throw IllegalArgumentException("Unsupported type '$type' for deserialization.") } } diff --git a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/ZBytesTest.kt b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/ZBytesTest.kt index c12f0d78c..6320cd5ce 100644 --- a/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/ZBytesTest.kt +++ b/zenoh-kotlin/src/commonTest/kotlin/io/zenoh/ZBytesTest.kt @@ -19,25 +19,31 @@ import io.zenoh.protocol.Serializable import io.zenoh.protocol.ZBytes import io.zenoh.protocol.into import org.junit.jupiter.api.Assertions.assertArrayEquals -import java.nio.ByteBuffer -import java.nio.ByteOrder -import kotlin.reflect.typeOf -import kotlin.test.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import java.nio.ByteBuffer +import java.nio.ByteOrder +import kotlin.reflect.KClass +import kotlin.reflect.typeOf +import kotlin.test.Test +import kotlin.test.assertTrue -data class SimpleTestCase( - val originalItem: T +data class SimpleTestCase( + val originalItem: T, + val clazz: KClass ) -data class ListTestCase( - val originalList: List +data class ListTestCase( + val originalList: List, + val itemclazz: KClass ) -data class MapTestCase( +data class MapTestCase( val originalMap: Map, + val keyclazz: KClass, + val valueclazz: KClass, ) class ZBytesTests { @@ -46,15 +52,15 @@ class ZBytesTests { @JvmStatic fun simpleTestCases(): List> { return listOf( - SimpleTestCase(1.toByte()), - SimpleTestCase(1.toShort()), - SimpleTestCase(1), - SimpleTestCase(1L), - SimpleTestCase(1.0f), - SimpleTestCase(1.0), - SimpleTestCase("value1"), - SimpleTestCase(byteArrayOf(1, 2, 3)), - SimpleTestCase(MyZBytes("foo")) + SimpleTestCase(1.toByte(), Byte::class), + SimpleTestCase(1.toShort(), Short::class), + SimpleTestCase(1, Int::class), + SimpleTestCase(1L, Long::class), + SimpleTestCase(1.0f, Float::class), + SimpleTestCase(1.0, Double::class), + SimpleTestCase("value1", String::class), + SimpleTestCase(byteArrayOf(1, 2, 3), ByteArray::class), + SimpleTestCase(MyZBytes("foo"), MyZBytes::class) ) } @@ -62,23 +68,23 @@ class ZBytesTests { fun listTestCases(): List> { return listOf( // Byte Lists - ListTestCase(listOf(1.toByte(), 2.toByte(), 3.toByte())), + ListTestCase(listOf(1.toByte(), 2.toByte(), 3.toByte()), Byte::class), // Short Lists - ListTestCase(listOf(1.toShort(), 2.toShort(), 3.toShort())), + ListTestCase(listOf(1.toShort(), 2.toShort(), 3.toShort()), Short::class), // Int Lists - ListTestCase(listOf(1, 2, 3)), + ListTestCase(listOf(1, 2, 3), Int::class), // Long Lists - ListTestCase(listOf(1L, 2L, 3L)), + ListTestCase(listOf(1L, 2L, 3L), Long::class), // Float Lists - ListTestCase(listOf(1.0f, 2.0f, 3.0f)), + ListTestCase(listOf(1.0f, 2.0f, 3.0f), Float::class), // Double Lists - ListTestCase(listOf(1.0, 2.0, 3.0)), + ListTestCase(listOf(1.0, 2.0, 3.0), Double::class), // String Lists - ListTestCase(listOf("value1", "value2", "value3")), + ListTestCase(listOf("value1", "value2", "value3"), String::class), // ByteArray Lists - ListTestCase(listOf(byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6))), + ListTestCase(listOf(byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6)), ByteArray::class), // MyZBytes Lists - ListTestCase(listOf(MyZBytes("foo"), MyZBytes("bar"))) + ListTestCase(listOf(MyZBytes("foo"), MyZBytes("bar")), MyZBytes::class) ) } @@ -86,115 +92,115 @@ class ZBytesTests { fun mapTestCases(): List> { return listOf( // Byte Keys - MapTestCase(mapOf(1.toByte() to "value1", 2.toByte() to "value2")), - MapTestCase(mapOf(1.toByte() to 1.toByte(), 2.toByte() to 2.toByte())), - MapTestCase(mapOf(1.toByte() to 1.toShort(), 2.toByte() to 2.toShort())), - MapTestCase(mapOf(1.toByte() to 1, 2.toByte() to 2)), - MapTestCase(mapOf(1.toByte() to 1L, 2.toByte() to 2L)), - MapTestCase(mapOf(1.toByte() to 1.0f, 2.toByte() to 2.0f)), - MapTestCase(mapOf(1.toByte() to 1.0, 2.toByte() to 2.0)), - MapTestCase(mapOf(1.toByte() to byteArrayOf(1, 2, 3), 2.toByte() to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1.toByte() to MyZBytes("foo"), 2.toByte() to MyZBytes("bar"))), + MapTestCase(mapOf(1.toByte() to "value1", 2.toByte() to "value2"), Byte::class, String::class), + MapTestCase(mapOf(1.toByte() to 1.toByte(), 2.toByte() to 2.toByte()), Byte::class, Byte::class), + MapTestCase(mapOf(1.toByte() to 1.toShort(), 2.toByte() to 2.toShort()), Byte::class, Short::class), + MapTestCase(mapOf(1.toByte() to 1, 2.toByte() to 2), Byte::class, Int::class), + MapTestCase(mapOf(1.toByte() to 1L, 2.toByte() to 2L), Byte::class, Long::class), + MapTestCase(mapOf(1.toByte() to 1.0f, 2.toByte() to 2.0f), Byte::class, Float::class), + MapTestCase(mapOf(1.toByte() to 1.0, 2.toByte() to 2.0), Byte::class, Double::class), + MapTestCase(mapOf(1.toByte() to byteArrayOf(1, 2, 3), 2.toByte() to byteArrayOf(4, 5, 6)), Byte::class, ByteArray::class), + MapTestCase(mapOf(1.toByte() to MyZBytes("foo"), 2.toByte() to MyZBytes("bar")), Byte::class, MyZBytes::class), // Short Keys - MapTestCase(mapOf(1.toShort() to "value1", 2.toShort() to "value2")), - MapTestCase(mapOf(1.toShort() to 1.toByte(), 2.toShort() to 2.toByte())), - MapTestCase(mapOf(1.toShort() to 1.toShort(), 2.toShort() to 2.toShort())), - MapTestCase(mapOf(1.toShort() to 1, 2.toShort() to 2)), - MapTestCase(mapOf(1.toShort() to 1L, 2.toShort() to 2L)), - MapTestCase(mapOf(1.toShort() to 1.0f, 2.toShort() to 2.0f)), - MapTestCase(mapOf(1.toShort() to 1.0, 2.toShort() to 2.0)), - MapTestCase(mapOf(1.toShort() to byteArrayOf(1, 2, 3), 2.toShort() to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1.toShort() to MyZBytes("foo"), 2.toShort() to MyZBytes("bar"))), + MapTestCase(mapOf(1.toShort() to "value1", 2.toShort() to "value2"), Short::class, String::class), + MapTestCase(mapOf(1.toShort() to 1.toByte(), 2.toShort() to 2.toByte()), Short::class, Byte::class), + MapTestCase(mapOf(1.toShort() to 1.toShort(), 2.toShort() to 2.toShort()), Short::class, Short::class), + MapTestCase(mapOf(1.toShort() to 1, 2.toShort() to 2), Short::class, Int::class), + MapTestCase(mapOf(1.toShort() to 1L, 2.toShort() to 2L), Short::class, Long::class), + MapTestCase(mapOf(1.toShort() to 1.0f, 2.toShort() to 2.0f), Short::class, Float::class), + MapTestCase(mapOf(1.toShort() to 1.0, 2.toShort() to 2.0), Short::class, Double::class), + MapTestCase(mapOf(1.toShort() to byteArrayOf(1, 2, 3), 2.toShort() to byteArrayOf(4, 5, 6)), Short::class, ByteArray::class), + MapTestCase(mapOf(1.toShort() to MyZBytes("foo"), 2.toShort() to MyZBytes("bar")), Short::class, MyZBytes::class), // Int Keys - MapTestCase(mapOf(1 to "value1", 2 to "value2")), - MapTestCase(mapOf(1 to 1.toByte(), 2 to 2.toByte())), - MapTestCase(mapOf(1 to 1.toShort(), 2 to 2.toShort())), - MapTestCase(mapOf(1 to 1, 2 to 2)), - MapTestCase(mapOf(1 to 1L, 2 to 2L)), - MapTestCase(mapOf(1 to 1.0f, 2 to 2.0f)), - MapTestCase(mapOf(1 to 1.0, 2 to 2.0)), - MapTestCase(mapOf(1 to byteArrayOf(1, 2, 3), 2 to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1 to MyZBytes("foo"), 2 to MyZBytes("bar"))), + MapTestCase(mapOf(1 to "value1", 2 to "value2"), Int::class, String::class), + MapTestCase(mapOf(1 to 1.toByte(), 2 to 2.toByte()), Int::class, Byte::class), + MapTestCase(mapOf(1 to 1.toShort(), 2 to 2.toShort()), Int::class, Short::class), + MapTestCase(mapOf(1 to 1, 2 to 2), Int::class, Int::class), + MapTestCase(mapOf(1 to 1L, 2 to 2L), Int::class, Long::class), + MapTestCase(mapOf(1 to 1.0f, 2 to 2.0f), Int::class, Float::class), + MapTestCase(mapOf(1 to 1.0, 2 to 2.0), Int::class, Double::class), + MapTestCase(mapOf(1 to byteArrayOf(1, 2, 3), 2 to byteArrayOf(4, 5, 6)), Int::class, ByteArray::class), + MapTestCase(mapOf(1 to MyZBytes("foo"), 2 to MyZBytes("bar")), Int::class, MyZBytes::class), // Long Keys - MapTestCase(mapOf(1L to "value1", 2L to "value2")), - MapTestCase(mapOf(1L to 1.toByte(), 2L to 2.toByte())), - MapTestCase(mapOf(1L to 1.toShort(), 2L to 2.toShort())), - MapTestCase(mapOf(1L to 1, 2L to 2)), - MapTestCase(mapOf(1L to 1L, 2L to 2L)), - MapTestCase(mapOf(1L to 1.0f, 2L to 2.0f)), - MapTestCase(mapOf(1L to 1.0, 2L to 2.0)), - MapTestCase(mapOf(1L to byteArrayOf(1, 2, 3), 2L to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1L to MyZBytes("foo"), 2L to MyZBytes("bar"))), + MapTestCase(mapOf(1L to "value1", 2L to "value2"), Long::class, String::class), + MapTestCase(mapOf(1L to 1.toByte(), 2L to 2.toByte()), Long::class, Byte::class), + MapTestCase(mapOf(1L to 1.toShort(), 2L to 2.toShort()), Long::class, Short::class), + MapTestCase(mapOf(1L to 1, 2L to 2), Long::class, Int::class), + MapTestCase(mapOf(1L to 1L, 2L to 2L), Long::class, Long::class), + MapTestCase(mapOf(1L to 1.0f, 2L to 2.0f), Long::class, Float::class), + MapTestCase(mapOf(1L to 1.0, 2L to 2.0), Long::class, Double::class), + MapTestCase(mapOf(1L to byteArrayOf(1, 2, 3), 2L to byteArrayOf(4, 5, 6)), Long::class, ByteArray::class), + MapTestCase(mapOf(1L to MyZBytes("foo"), 2L to MyZBytes("bar")), Long::class, MyZBytes::class), // Float Keys - MapTestCase(mapOf(1.0f to "value1", 2.0f to "value2")), - MapTestCase(mapOf(1.0f to 1.toByte(), 2.0f to 2.toByte())), - MapTestCase(mapOf(1.0f to 1.toShort(), 2.0f to 2.toShort())), - MapTestCase(mapOf(1.0f to 1, 2.0f to 2)), - MapTestCase(mapOf(1.0f to 1L, 2.0f to 2L)), - MapTestCase(mapOf(1.0f to 1.0f, 2.0f to 2.0f)), - MapTestCase(mapOf(1.0f to 1.0, 2.0f to 2.0)), - MapTestCase(mapOf(1.0f to byteArrayOf(1, 2, 3), 2.0f to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1.0f to MyZBytes("foo"), 2.0f to MyZBytes("bar"))), + MapTestCase(mapOf(1.0f to "value1", 2.0f to "value2"), Float::class, String::class), + MapTestCase(mapOf(1.0f to 1.toByte(), 2.0f to 2.toByte()), Float::class, Byte::class), + MapTestCase(mapOf(1.0f to 1.toShort(), 2.0f to 2.toShort()), Float::class, Short::class), + MapTestCase(mapOf(1.0f to 1, 2.0f to 2), Float::class, Int::class), + MapTestCase(mapOf(1.0f to 1L, 2.0f to 2L), Float::class, Long::class), + MapTestCase(mapOf(1.0f to 1.0f, 2.0f to 2.0f), Float::class, Float::class), + MapTestCase(mapOf(1.0f to 1.0, 2.0f to 2.0), Float::class, Double::class), + MapTestCase(mapOf(1.0f to byteArrayOf(1, 2, 3), 2.0f to byteArrayOf(4, 5, 6)), Float::class, ByteArray::class), + MapTestCase(mapOf(1.0f to MyZBytes("foo"), 2.0f to MyZBytes("bar")), Float::class, MyZBytes::class), // Double Keys - MapTestCase(mapOf(1.0 to "value1", 2.0 to "value2")), - MapTestCase(mapOf(1.0 to 1.toByte(), 2.0 to 2.toByte())), - MapTestCase(mapOf(1.0 to 1.toShort(), 2.0 to 2.toShort())), - MapTestCase(mapOf(1.0 to 1, 2.0 to 2)), - MapTestCase(mapOf(1.0 to 1L, 2.0 to 2L)), - MapTestCase(mapOf(1.0 to 1.0f, 2.0 to 2.0f)), - MapTestCase(mapOf(1.0 to 1.0, 2.0 to 2.0)), - MapTestCase(mapOf(1.0 to byteArrayOf(1, 2, 3), 2.0 to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(1.0 to MyZBytes("foo"), 2.0 to MyZBytes("bar"))), + MapTestCase(mapOf(1.0 to "value1", 2.0 to "value2"), Double::class, String::class), + MapTestCase(mapOf(1.0 to 1.toByte(), 2.0 to 2.toByte()), Double::class, Byte::class), + MapTestCase(mapOf(1.0 to 1.toShort(), 2.0 to 2.toShort()), Double::class, Short::class), + MapTestCase(mapOf(1.0 to 1, 2.0 to 2), Double::class, Int::class), + MapTestCase(mapOf(1.0 to 1L, 2.0 to 2L), Double::class, Long::class), + MapTestCase(mapOf(1.0 to 1.0f, 2.0 to 2.0f), Double::class, Float::class), + MapTestCase(mapOf(1.0 to 1.0, 2.0 to 2.0), Double::class, Double::class), + MapTestCase(mapOf(1.0 to byteArrayOf(1, 2, 3), 2.0 to byteArrayOf(4, 5, 6)), Double::class, ByteArray::class), + MapTestCase(mapOf(1.0 to MyZBytes("foo"), 2.0 to MyZBytes("bar")), Double::class, MyZBytes::class), // String Keys - MapTestCase(mapOf("key1" to "value1", "key2" to "value2")), - MapTestCase(mapOf("key1" to 1.toByte(), "key2" to 2.toByte())), - MapTestCase(mapOf("key1" to 1.toShort(), "key2" to 2.toShort())), - MapTestCase(mapOf("key1" to 1, "key2" to 2)), - MapTestCase(mapOf("key1" to 1L, "key2" to 2L)), - MapTestCase(mapOf("key1" to 1.0f, "key2" to 2.0f)), - MapTestCase(mapOf("key1" to 1.0, "key2" to 2.0)), - MapTestCase(mapOf("key1" to byteArrayOf(1, 2, 3), "key2" to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf("key1" to MyZBytes("foo"), "key2" to MyZBytes("bar"))), + MapTestCase(mapOf("key1" to "value1", "key2" to "value2"), String::class, String::class), + MapTestCase(mapOf("key1" to 1.toByte(), "key2" to 2.toByte()), String::class, Byte::class), + MapTestCase(mapOf("key1" to 1.toShort(), "key2" to 2.toShort()), String::class, Short::class), + MapTestCase(mapOf("key1" to 1, "key2" to 2), String::class, Int::class), + MapTestCase(mapOf("key1" to 1L, "key2" to 2L), String::class, Long::class), + MapTestCase(mapOf("key1" to 1.0f, "key2" to 2.0f), String::class, Float::class), + MapTestCase(mapOf("key1" to 1.0, "key2" to 2.0), String::class, Double::class), + MapTestCase(mapOf("key1" to byteArrayOf(1, 2, 3), "key2" to byteArrayOf(4, 5, 6)), String::class, ByteArray::class), + MapTestCase(mapOf("key1" to MyZBytes("foo"), "key2" to MyZBytes("bar")), String::class, MyZBytes::class), // ByteArray Keys - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to "value1", byteArrayOf(4, 5, 6) to "value2")), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.toByte(), byteArrayOf(4, 5, 6) to 2.toByte())), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.toShort(), byteArrayOf(4, 5, 6) to 2.toShort())), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1, byteArrayOf(4, 5, 6) to 2)), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1L, byteArrayOf(4, 5, 6) to 2L)), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.0f, byteArrayOf(4, 5, 6) to 2.0f)), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.0, byteArrayOf(4, 5, 6) to 2.0)), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6) to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(byteArrayOf(1, 2, 3) to MyZBytes("foo"), byteArrayOf(4, 5, 6) to MyZBytes("bar"))), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to "value1", byteArrayOf(4, 5, 6) to "value2"), ByteArray::class, String::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.toByte(), byteArrayOf(4, 5, 6) to 2.toByte()), ByteArray::class, Byte::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.toShort(), byteArrayOf(4, 5, 6) to 2.toShort()), ByteArray::class, Short::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1, byteArrayOf(4, 5, 6) to 2), ByteArray::class, Int::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1L, byteArrayOf(4, 5, 6) to 2L), ByteArray::class, Long::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.0f, byteArrayOf(4, 5, 6) to 2.0f), ByteArray::class, Float::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to 1.0, byteArrayOf(4, 5, 6) to 2.0), ByteArray::class, Double::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6) to byteArrayOf(4, 5, 6)), ByteArray::class, ByteArray::class), + MapTestCase(mapOf(byteArrayOf(1, 2, 3) to MyZBytes("foo"), byteArrayOf(4, 5, 6) to MyZBytes("bar")), ByteArray::class, MyZBytes::class), // MyZBytes (Serializable and Deserializable) Keys - MapTestCase(mapOf(MyZBytes("foo") to "value1", MyZBytes("bar") to "value2")), - MapTestCase(mapOf(MyZBytes("foo") to 1.toByte(), MyZBytes("bar") to 2.toByte())), - MapTestCase(mapOf(MyZBytes("foo") to 1.toShort(), MyZBytes("bar") to 2.toShort())), - MapTestCase(mapOf(MyZBytes("foo") to 1, MyZBytes("bar") to 2)), - MapTestCase(mapOf(MyZBytes("foo") to 1L, MyZBytes("bar") to 2L)), - MapTestCase(mapOf(MyZBytes("foo") to 1.0f, MyZBytes("bar") to 2.0f)), - MapTestCase(mapOf(MyZBytes("foo") to 1.0, MyZBytes("bar") to 2.0)), - MapTestCase(mapOf(MyZBytes("foo") to byteArrayOf(1, 2, 3), MyZBytes("bar") to byteArrayOf(4, 5, 6))), - MapTestCase(mapOf(MyZBytes("foo") to MyZBytes("foo"), MyZBytes("bar") to MyZBytes("bar"))) + MapTestCase(mapOf(MyZBytes("foo") to "value1", MyZBytes("bar") to "value2"), MyZBytes::class, String::class), + MapTestCase(mapOf(MyZBytes("foo") to 1.toByte(), MyZBytes("bar") to 2.toByte()), MyZBytes::class, Byte::class), + MapTestCase(mapOf(MyZBytes("foo") to 1.toShort(), MyZBytes("bar") to 2.toShort()), MyZBytes::class, Short::class), + MapTestCase(mapOf(MyZBytes("foo") to 1, MyZBytes("bar") to 2), MyZBytes::class, Int::class), + MapTestCase(mapOf(MyZBytes("foo") to 1L, MyZBytes("bar") to 2L), MyZBytes::class, Long::class), + MapTestCase(mapOf(MyZBytes("foo") to 1.0f, MyZBytes("bar") to 2.0f), MyZBytes::class, Float::class), + MapTestCase(mapOf(MyZBytes("foo") to 1.0, MyZBytes("bar") to 2.0), MyZBytes::class, Double::class), + MapTestCase(mapOf(MyZBytes("foo") to byteArrayOf(1, 2, 3), MyZBytes("bar") to byteArrayOf(4, 5, 6)), MyZBytes::class, ByteArray::class), + MapTestCase(mapOf(MyZBytes("foo") to MyZBytes("foo"), MyZBytes("bar") to MyZBytes("bar")), MyZBytes::class, MyZBytes::class) ) } } @ParameterizedTest @MethodSource("simpleTestCases") - inline fun serializationAndDeserialization_simpleTest(testCase: SimpleTestCase) { + fun serializationAndDeserialization_simpleTest(testCase: SimpleTestCase) { val originalItem = testCase.originalItem + val clazz = testCase.clazz - val bytes = ZBytes.serialize(originalItem).getOrThrow() - - val deserializedItem = bytes.deserialize().getOrThrow() + val bytes = ZBytes.serialize(originalItem, clazz = clazz).getOrThrow() + val deserializedItem = bytes.deserialize(clazz = clazz).getOrThrow() if (originalItem is ByteArray) { assertArrayEquals(originalItem, deserializedItem as ByteArray) @@ -205,12 +211,13 @@ class ZBytesTests { @ParameterizedTest @MethodSource("listTestCases") - inline fun serializationAndDeserialization_listTest(testCase: ListTestCase) { + fun serializationAndDeserialization_listTest(testCase: ListTestCase) { val originalList = testCase.originalList + val itemClass = testCase.itemclazz val bytes = ZBytes.serialize(originalList).getOrThrow() - val deserializedList = bytes.deserialize>().getOrThrow() + val deserializedList = bytes.deserialize(clazz = List::class, arg1clazz = itemClass).getOrThrow() if (originalList.isNotEmpty() && originalList[0] is ByteArray) { originalList.forEachIndexed { index, value -> @@ -223,163 +230,362 @@ class ZBytesTests { @ParameterizedTest @MethodSource("mapTestCases") - inline fun serializationAndDeserialization_mapTest(testCase: MapTestCase) { + fun serializationAndDeserialization_mapTest(testCase: MapTestCase) { val originalMap = testCase.originalMap + val keyClass = testCase.keyclazz + val valueClass = testCase.valueclazz val bytes = ZBytes.serialize(originalMap).getOrThrow() - val deserializedMap = bytes.deserialize>().getOrThrow() + + val deserializedMap = bytes.deserialize( + clazz = Map::class, + arg1clazz = keyClass, + arg2clazz = valueClass + ).getOrThrow() + + if (keyClass == ByteArray::class && valueClass != ByteArray::class) { + val map1 = originalMap.map { (k, v) -> (k as ByteArray).toList() to v }.toMap() + val map2 = originalMap.map { (k, v) -> (k as ByteArray).toList() to v }.toMap() + assertEquals(map1, map2) + return + } + + if (keyClass != ByteArray::class && valueClass == ByteArray::class) { + val map1 = originalMap.map { (k, v) -> k to (v as ByteArray).toList() }.toMap() + val map2 = originalMap.map { (k, v) -> k to (v as ByteArray).toList() }.toMap() + assertEquals(map1, map2) + return + } + + if (keyClass == ByteArray::class && valueClass == ByteArray::class) { + val map1 = originalMap.map { (k, v) -> (k as ByteArray).toList() to (v as ByteArray).toList() }.toMap() + val map2 = originalMap.map { (k, v) -> (k as ByteArray).toList() to (v as ByteArray).toList() }.toMap() + assertEquals(map1, map2) + return + } assertEquals(originalMap, deserializedMap) } -// @Test -// fun customDeserializerTest() { -// val stringMap = mapOf("key1" to "value1", "key2" to "value2") -// val zbytesMap = stringMap.map { (k, v) -> k.into() to v.into() }.toMap() -// val zbytesListOfPairs = stringMap.map { (k, v) -> k.into() to v.into() } -// val intMap = mapOf(1 to 10, 2 to 20, 3 to 30) -// val zbytesList = listOf(1.into(), 2.into(), 3.into()) -// -// val serializedBytes = serializeZBytesMap(zbytesMap) -// -// val customDeserializers = mapOf( -// typeOf>() to ::deserializeIntoZBytesMap, -// typeOf>() to ::deserializeIntoStringMap, -// typeOf>() to ::deserializeIntoIntMap, -// typeOf>() to ::deserializeIntoZBytesList, -// typeOf>>() to ::deserializeIntoListOfPairs, -// ) -// -// val deserializedMap = serializedBytes.deserialize>(customDeserializers).getOrThrow() -// assertEquals(zbytesMap, deserializedMap) -// -// val deserializedMap2 = serializedBytes.deserialize>(customDeserializers).getOrThrow() -// assertEquals(stringMap, deserializedMap2) -// -// val intMapBytes = serializeIntoIntMap(intMap) -// val deserializedMap3 = intMapBytes.deserialize>(customDeserializers).getOrThrow() -// assertEquals(intMap, deserializedMap3) -// -// val serializedZBytesList = serializeZBytesList(zbytesList) -// val deserializedList = serializedZBytesList.deserialize>(customDeserializers).getOrThrow() -// assertEquals(zbytesList, deserializedList) -// -// val serializedZBytesPairList = serializeZBytesMap(zbytesListOfPairs.toMap()) -// val deserializedZBytesPairList = -// serializedZBytesPairList.deserialize>>(customDeserializers).getOrThrow() -// assertEquals(zbytesListOfPairs, deserializedZBytesPairList) -// } -// -// -// /*--------- Serializers and deserializers for testing purposes. ----------*/ -// -// private fun serializeZBytesMap(testMap: Map): ZBytes { -// return testMap.map { -// val key = it.key.bytes -// val keyLength = ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(key.size).array() -// val value = it.value.bytes -// val valueLength = -// ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(value.size).array() -// keyLength + key + valueLength + value -// }.reduce { acc, bytes -> acc + bytes }.into() -// } -// -// private fun deserializeIntoZBytesMap(serializedMap: ZBytes): Map { -// var idx = 0 -// var sliceSize: Int -// val decodedMap = mutableMapOf() -// while (idx < serializedMap.bytes.size) { -// sliceSize = ByteBuffer.wrap(serializedMap.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))) -// .order(ByteOrder.LITTLE_ENDIAN).int -// idx += Int.SIZE_BYTES -// -// val key = serializedMap.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) -// idx += sliceSize -// -// sliceSize = ByteBuffer.wrap(serializedMap.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))).order( -// ByteOrder.LITTLE_ENDIAN -// ).int -// idx += Int.SIZE_BYTES -// -// val value = serializedMap.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) -// idx += sliceSize -// -// decodedMap[key.into()] = value.into() -// } -// return decodedMap -// } -// -// private fun serializeIntoIntMap(intMap: Map): ZBytes { -// val zBytesMap = intMap.map { (k, v) -> k.into() to v.into() }.toMap() -// return serializeZBytesMap(zBytesMap) -// } -// -// private fun deserializeIntoStringMap(serializerMap: ZBytes): Map { -// return deserializeIntoZBytesMap(serializerMap).map { (k, v) -> k.toString() to v.toString() }.toMap() -// } -// -// private fun deserializeIntoIntMap(serializerMap: ZBytes): Map { -// return deserializeIntoZBytesMap(serializerMap).map { (k, v) -> -// k.deserialize().getOrThrow() to v.deserialize().getOrThrow() -// }.toMap() -// } -// -// private fun serializeZBytesList(list: List): ZBytes { -// return list.map { -// val item = it.bytes -// val itemLength = -// ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(item.size).array() -// itemLength + item -// }.reduce { acc, bytes -> acc + bytes }.into() -// } -// -// private fun deserializeIntoZBytesList(serializedList: ZBytes): List { -// var idx = 0 -// var sliceSize: Int -// val decodedList = mutableListOf() -// while (idx < serializedList.bytes.size) { -// sliceSize = ByteBuffer.wrap(serializedList.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))) -// .order(ByteOrder.LITTLE_ENDIAN).int -// idx += Int.SIZE_BYTES -// -// val item = serializedList.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) -// idx += sliceSize -// -// decodedList.add(item.into()) -// } -// return decodedList -// } -// -// private fun deserializeIntoListOfPairs(serializedList: ZBytes): List> { -// return deserializeIntoZBytesMap(serializedList).map { (k, v) -> k to v } -// } -} - -/** - * Custom class for the tests. The purpose of this class is to test - * the proper functioning of the serialization and deserialization for - * a class implementing the [Serializable] and the [Deserializable] interface. - */ -class MyZBytes(val content: String) : Serializable, Deserializable { - - override fun into(): ZBytes = content.into() - - companion object : Deserializable.From { - override fun from(zbytes: ZBytes): MyZBytes { - return MyZBytes(zbytes.toString()) + @Test + fun deserializationWithMapOfDeserializationFunctionsTest() { + val stringMap = mapOf("key1" to "value1", "key2" to "value2") + val zbytesMap = stringMap.map { (k, v) -> k.into() to v.into() }.toMap() + val zbytesListOfPairs = stringMap.map { (k, v) -> k.into() to v.into() } + val intMap = mapOf(1 to 10, 2 to 20, 3 to 30) + val zbytesList = listOf(1.into(), 2.into(), 3.into()) + + val serializedBytes = serializeZBytesMap(zbytesMap) + + val customDeserializers = mapOf( + typeOf>() to ::deserializeIntoZBytesMap, + typeOf>() to ::deserializeIntoStringMap, + typeOf>() to ::deserializeIntoIntMap, + typeOf>() to ::deserializeIntoZBytesList, + typeOf>>() to ::deserializeIntoListOfPairs, + ) + + val deserializedMap = serializedBytes.deserialize>(customDeserializers).getOrThrow() + assertEquals(zbytesMap, deserializedMap) + + val deserializedMap2 = serializedBytes.deserialize>(customDeserializers).getOrThrow() + assertEquals(stringMap, deserializedMap2) + + val intMapBytes = serializeIntoIntMap(intMap) + val deserializedMap3 = intMapBytes.deserialize>(customDeserializers).getOrThrow() + assertEquals(intMap, deserializedMap3) + + val serializedZBytesList = serializeZBytesList(zbytesList) + val deserializedList = serializedZBytesList.deserialize>(customDeserializers).getOrThrow() + assertEquals(zbytesList, deserializedList) + + val serializedZBytesPairList = serializeZBytesMap(zbytesListOfPairs.toMap()) + val deserializedZBytesPairList = + serializedZBytesPairList.deserialize>>(customDeserializers).getOrThrow() + assertEquals(zbytesListOfPairs, deserializedZBytesPairList) + } + + /** + * A series of tests to verify the correct functioning of the [ZBytes.deserialize] function. + * + * The [ZBytes.deserialize] function with reification can not be tested in a parametrized fashion because + * it uses reified parameters which causes the testing framework (designed for Java) to fail to properly + * set up the tests. + */ + @Test + fun serializationAndDeserializationWithReification() { + /*********************************************** + * Standard serialization and deserialization. * + ***********************************************/ + + /** Numeric: byte, short, int, float, double */ + val intInput = 1234 + var payload = ZBytes.from(intInput) + val intOutput = payload.deserialize().getOrThrow() + assertEquals(intInput, intOutput) + + // Another example with float + val floatInput = 3.1415f + payload = ZBytes.from(floatInput) + val floatOutput = payload.deserialize().getOrThrow() + assertEquals(floatInput, floatOutput) + + /** String serialization and deserialization. */ + val stringInput = "example" + payload = ZBytes.from(stringInput) + val stringOutput = payload.deserialize().getOrThrow() + assertEquals(stringInput, stringOutput) + + /** ByteArray serialization and deserialization. */ + val byteArrayInput = "example".toByteArray() + payload = ZBytes.from(byteArrayInput) // Equivalent to `byteArrayInput.into()` + val byteArrayOutput = payload.deserialize().getOrThrow() + assertTrue(byteArrayInput.contentEquals(byteArrayOutput)) + + val inputList = listOf("sample1", "sample2", "sample3") + payload = ZBytes.serialize(inputList).getOrThrow() + val outputList = payload.deserialize>().getOrThrow() + assertEquals(inputList, outputList) + + val inputListZBytes = inputList.map { value -> value.into() } + payload = ZBytes.serialize(inputListZBytes).getOrThrow() + val outputListZBytes = payload.deserialize>().getOrThrow() + assertEquals(inputListZBytes, outputListZBytes) + + val inputListByteArray = inputList.map { value -> value.toByteArray() } + payload = ZBytes.serialize(inputListByteArray).getOrThrow() + val outputListByteArray = payload.deserialize>().getOrThrow() + assertTrue(compareByteArrayLists(inputListByteArray, outputListByteArray)) + + val inputMap = mapOf("key1" to "value1", "key2" to "value2", "key3" to "value3") + payload = ZBytes.serialize(inputMap).getOrThrow() + val outputMap = payload.deserialize>().getOrThrow() + assertEquals(inputMap, outputMap) + + val combinedInputMap = mapOf("key1" to ZBytes.from("zbytes1"), "key2" to ZBytes.from("zbytes2")) + payload = ZBytes.serialize(combinedInputMap).getOrThrow() + val combinedOutputMap = payload.deserialize>().getOrThrow() + assertEquals(combinedInputMap, combinedOutputMap) + + /********************************************* + * Custom serialization and deserialization. * + *********************************************/ + + val inputMyZBytes = MyZBytes("example") + payload = ZBytes.serialize(inputMyZBytes).getOrThrow() + val outputMyZBytes = payload.deserialize().getOrThrow() + assertEquals(inputMyZBytes, outputMyZBytes) + + /** List of MyZBytes. */ + val inputListMyZBytes = inputList.map { value -> MyZBytes(value) } + payload = ZBytes.serialize>(inputListMyZBytes).getOrThrow() + val outputListMyZBytes = payload.deserialize>().getOrThrow() + assertEquals(inputListMyZBytes, outputListMyZBytes) + + /** Map of MyZBytes. */ + val inputMapMyZBytes = inputMap.map { (k, v) -> MyZBytes(k) to MyZBytes(v)}.toMap() + payload = ZBytes.serialize>(inputMapMyZBytes).getOrThrow() + val outputMapMyZBytes = payload.deserialize>().getOrThrow() + assertEquals(inputMapMyZBytes, outputMapMyZBytes) + + val combinedMap = mapOf(MyZBytes("foo") to 1, MyZBytes("bar") to 2) + payload = ZBytes.serialize>(combinedMap).getOrThrow() + val combinedOutput = payload.deserialize>().getOrThrow() + assertEquals(combinedMap, combinedOutput) + + /** + * Providing a map of deserializers. + */ + val fooMap = mapOf(Foo("foo1") to Foo("bar1"), Foo("foo2") to Foo("bar2")) + val fooMapSerialized = ZBytes.from(serializeFooMap(fooMap)) + val deserializersMap = mapOf(typeOf>() to ::deserializeFooMap) + val deserializedFooMap = fooMapSerialized.deserialize>(deserializersMap).getOrThrow() + assertEquals(fooMap, deserializedFooMap) + } + + /***************** + * Testing utils * + *****************/ + + /** + * Custom class for the tests. The purpose of this class is to test + * the proper functioning of the serialization and deserialization for + * a class implementing the [Serializable] and the [Deserializable] interface. + */ + class MyZBytes(val content: String) : Serializable, Deserializable { + + override fun into(): ZBytes = content.into() + + companion object : Deserializable.From { + override fun from(zbytes: ZBytes): MyZBytes { + return MyZBytes(zbytes.toString()) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MyZBytes + + return content == other.content + } + + override fun hashCode(): Int { + return content.hashCode() } } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false + /** Example class for the deserialization map examples. */ + class Foo(val content: String) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Foo - other as MyZBytes + return content == other.content + } + + override fun hashCode(): Int { + return content.hashCode() + } + } - return content == other.content + private fun compareByteArrayLists(list1: List, list2: List): Boolean { + if (list1.size != list2.size) { + return false + } + for (i in list1.indices) { + if (!list1[i].contentEquals(list2[i])) { + return false + } + } + return true + } + + + /********************************************************************************** + * Serializers and deserializers for testing the functionality of deserialization * + * with deserializer functions. * + **********************************************************************************/ + + private fun serializeFooMap(testMap: Map): ByteArray { + return testMap.map { + val key = it.key.content.toByteArray() + val keyLength = ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(key.size).array() + val value = it.value.content.toByteArray() + val valueLength = + ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(value.size).array() + keyLength + key + valueLength + value + }.reduce { acc, bytes -> acc + bytes } + } + + private fun deserializeFooMap(serializedMap: ZBytes): Map { + var idx = 0 + var sliceSize: Int + val bytes = serializedMap.toByteArray() + val decodedMap = mutableMapOf() + while (idx < bytes.size) { + sliceSize = ByteBuffer.wrap(bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))) + .order(ByteOrder.LITTLE_ENDIAN).int + idx += Int.SIZE_BYTES + + val key = bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) + idx += sliceSize + + sliceSize = ByteBuffer.wrap(bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))).order( + ByteOrder.LITTLE_ENDIAN + ).int + idx += Int.SIZE_BYTES + + val value = bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) + idx += sliceSize + + decodedMap[Foo(key.decodeToString())] = Foo(value.decodeToString()) + } + return decodedMap + } + + private fun serializeZBytesMap(testMap: Map): ZBytes { + return testMap.map { + val key = it.key.bytes + val keyLength = ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(key.size).array() + val value = it.value.bytes + val valueLength = + ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(value.size).array() + keyLength + key + valueLength + value + }.reduce { acc, bytes -> acc + bytes }.into() + } + + private fun deserializeIntoZBytesMap(serializedMap: ZBytes): Map { + var idx = 0 + var sliceSize: Int + val decodedMap = mutableMapOf() + while (idx < serializedMap.bytes.size) { + sliceSize = ByteBuffer.wrap(serializedMap.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))) + .order(ByteOrder.LITTLE_ENDIAN).int + idx += Int.SIZE_BYTES + + val key = serializedMap.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) + idx += sliceSize + + sliceSize = ByteBuffer.wrap(serializedMap.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))).order( + ByteOrder.LITTLE_ENDIAN + ).int + idx += Int.SIZE_BYTES + + val value = serializedMap.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) + idx += sliceSize + + decodedMap[key.into()] = value.into() + } + return decodedMap + } + + private fun serializeIntoIntMap(intMap: Map): ZBytes { + val zBytesMap = intMap.map { (k, v) -> k.into() to v.into() }.toMap() + return serializeZBytesMap(zBytesMap) + } + + private fun deserializeIntoStringMap(serializerMap: ZBytes): Map { + return deserializeIntoZBytesMap(serializerMap).map { (k, v) -> k.toString() to v.toString() }.toMap() + } + + private fun deserializeIntoIntMap(serializerMap: ZBytes): Map { + return deserializeIntoZBytesMap(serializerMap).map { (k, v) -> + k.deserialize().getOrThrow() to v.deserialize().getOrThrow() + }.toMap() + } + + private fun serializeZBytesList(list: List): ZBytes { + return list.map { + val item = it.bytes + val itemLength = + ByteBuffer.allocate(Int.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(item.size).array() + itemLength + item + }.reduce { acc, bytes -> acc + bytes }.into() + } + + private fun deserializeIntoZBytesList(serializedList: ZBytes): List { + var idx = 0 + var sliceSize: Int + val decodedList = mutableListOf() + while (idx < serializedList.bytes.size) { + sliceSize = ByteBuffer.wrap(serializedList.bytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1))) + .order(ByteOrder.LITTLE_ENDIAN).int + idx += Int.SIZE_BYTES + + val item = serializedList.bytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) + idx += sliceSize + + decodedList.add(item.into()) + } + return decodedList } - override fun hashCode(): Int { - return content.hashCode() + private fun deserializeIntoListOfPairs(serializedList: ZBytes): List> { + return deserializeIntoZBytesMap(serializedList).map { (k, v) -> k to v } } -} +} \ No newline at end of file