From 59f8d2b1fef5dc566c2e8a7a02970c8617c03333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Tue, 26 Oct 2021 11:41:20 +0200 Subject: [PATCH 1/4] chore: update working version to 0.5.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ba26c09..154c354 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,4 +11,4 @@ org.gradle.parallel=true org.gradle.caching=true org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g -version=0.4.4 +version=0.5.0 From 26f5acd2a1bcdf91ff2f0c00a43a2bf2f0b13fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Tue, 26 Oct 2021 11:47:42 +0200 Subject: [PATCH 2/4] fix: fix TODO in MsgPackDatetimeSerializer --- .../datetime/MsgPackDatetimeSerializer.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/serialization-msgpack-timestamp-extension/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/datetime/MsgPackDatetimeSerializer.kt b/serialization-msgpack-timestamp-extension/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/datetime/MsgPackDatetimeSerializer.kt index 96475c1..b39b92a 100644 --- a/serialization-msgpack-timestamp-extension/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/datetime/MsgPackDatetimeSerializer.kt +++ b/serialization-msgpack-timestamp-extension/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/datetime/MsgPackDatetimeSerializer.kt @@ -7,6 +7,11 @@ import com.ensarsarajcic.kotlinx.serialization.msgpack.extensions.MsgPackTimesta import kotlinx.datetime.Instant sealed class BaseMsgPackDatetimeSerializer(private val outputType: Byte) : BaseMsgPackExtensionSerializer() { + + init { + require(outputType in 0..2) { "Output type can only take values from 0 to 2 inclusive" } + } + private val timestampSerializer = MsgPackTimestampExtensionSerializer override fun deserialize(extension: MsgPackExtension): Instant { return when (val timestamp = timestampSerializer.deserialize(extension)) { @@ -27,7 +32,7 @@ sealed class BaseMsgPackDatetimeSerializer(private val outputType: Byte) : BaseM 2.toByte() -> { MsgPackTimestamp.T92(extension.epochSeconds, extension.nanosecondsOfSecond.toLong()) } - else -> TODO("Needs more info") + else -> throw AssertionError() } return timestampSerializer.serialize(timestamp) } From 02c850a807405b98e5854c40b012b2e59491afe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Tue, 26 Oct 2021 20:24:47 +0200 Subject: [PATCH 3/4] feat: add more descriptive errors and exceptions This closes #21 --- CHANGELOG.md | 3 + .../msgpack/unsigned/UnsignedCoders.kt | 9 ++- .../MsgPackDynamicSerializer.kt | 5 +- .../MsgPackSerializationException.kt | 80 +++++++++++++++++++ .../BaseMsgPackExtensionSerializer.kt | 5 +- .../DynamicMsgPackExtensionSerializer.kt | 3 +- .../extensions/MsgPackTimestampExtension.kt | 5 +- .../internal/MsgPackDecoder.kt | 25 +++--- .../internal/MsgPackEncoder.kt | 15 ++-- .../internal/MsgPacker.kt | 5 +- .../internal/MsgUnpacker.kt | 35 ++++---- .../stream/MsgPackDataBuffer.kt | 3 +- .../serialization/msgpack/MsgPackTest.kt | 4 +- 13 files changed, 145 insertions(+), 52 deletions(-) create mode 100644 serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/exceptions/MsgPackSerializationException.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f209b5..4e8d2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). ## [Unreleased] +### Added +- More descriptive errors ([#21][i21]) ## [0.4.4] - 2021-12-20 - Added support for ignoring unknown keys ([#69][i69]) @@ -102,6 +104,7 @@ MsgPack.default.encodeToByteArray(...) [i18]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/18 [i19]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/19 [i20]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/20 +[i21]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/21 [i55]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/55 [i57]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/57 [i60]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/60 diff --git a/serialization-msgpack-unsigned-support/src/commonMain/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/unsigned/UnsignedCoders.kt b/serialization-msgpack-unsigned-support/src/commonMain/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/unsigned/UnsignedCoders.kt index 185274e..0a5ebb3 100644 --- a/serialization-msgpack-unsigned-support/src/commonMain/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/unsigned/UnsignedCoders.kt +++ b/serialization-msgpack-unsigned-support/src/commonMain/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/unsigned/UnsignedCoders.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.unsigned +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.internal.InlineDecoderHelper import com.ensarsarajcic.kotlinx.serialization.msgpack.internal.InlineEncoderHelper import com.ensarsarajcic.kotlinx.serialization.msgpack.unsigned.utils.joinToNumber @@ -58,7 +59,7 @@ class UByteDecoder(private val decoderHelper: InlineDecoderHelper) : AbstractDec override fun decodeByte(): Byte { val type = decoderHelper.inputBuffer.requireNextByte() - if (type != UnsignedTypes.UINT8) throw TODO("BAD TYPE") + if (type != UnsignedTypes.UINT8) throw MsgPackSerializationException.deserialization(decoderHelper.inputBuffer, "Expected UINT8 type, but found $type") return decoderHelper.inputBuffer.requireNextByte() } } @@ -70,7 +71,7 @@ class UShortDecoder(private val decoderHelper: InlineDecoderHelper) : AbstractDe override fun decodeShort(): Short { val type = decoderHelper.inputBuffer.requireNextByte() - if (type != UnsignedTypes.UINT16) throw TODO("BAD TYPE") + if (type != UnsignedTypes.UINT16) throw MsgPackSerializationException.deserialization(decoderHelper.inputBuffer, "Expected UINT16 type, but found $type") return decoderHelper.inputBuffer.takeNext(2).joinToNumber() } } @@ -82,7 +83,7 @@ class UIntDecoder(private val decoderHelper: InlineDecoderHelper) : AbstractDeco override fun decodeInt(): Int { val type = decoderHelper.inputBuffer.requireNextByte() - if (type != UnsignedTypes.UINT32) throw TODO("BAD TYPE") + if (type != UnsignedTypes.UINT32) throw MsgPackSerializationException.deserialization(decoderHelper.inputBuffer, "Expected UINT32 type, but found $type") return decoderHelper.inputBuffer.takeNext(4).joinToNumber() } } @@ -94,7 +95,7 @@ class ULongDecoder(private val decoderHelper: InlineDecoderHelper) : AbstractDec override fun decodeLong(): Long { val type = decoderHelper.inputBuffer.requireNextByte() - if (type != UnsignedTypes.UINT64) throw TODO("BAD TYPE") + if (type != UnsignedTypes.UINT64) throw MsgPackSerializationException.deserialization(decoderHelper.inputBuffer, "Expected UINT64 type, but found $type") return decoderHelper.inputBuffer.takeNext(8).joinToNumber() } } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/MsgPackDynamicSerializer.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/MsgPackDynamicSerializer.kt index c4954b5..545fe15 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/MsgPackDynamicSerializer.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/MsgPackDynamicSerializer.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.extensions.DynamicMsgPackExtensionSerializer import com.ensarsarajcic.kotlinx.serialization.msgpack.internal.MsgPackTypeDecoder import com.ensarsarajcic.kotlinx.serialization.msgpack.types.MsgPackType @@ -39,7 +40,7 @@ open class MsgPackNullableDynamicSerializer( ) : KSerializer { companion object Default : MsgPackNullableDynamicSerializer(DynamicMsgPackExtensionSerializer) final override fun deserialize(decoder: Decoder): Any? { - if (decoder !is MsgPackTypeDecoder) TODO("Unsupported decoder!") + if (decoder !is MsgPackTypeDecoder) throw MsgPackSerializationException.dynamicSerializationError("Unsupported decoder: $decoder") val type = decoder.peekNextType() return when { type == MsgPackType.NULL -> decoder.decodeNull() @@ -78,7 +79,7 @@ open class MsgPackNullableDynamicSerializer( MsgPackType.Array.isArray(type) -> ListSerializer(this).deserialize(decoder) MsgPackType.Map.isMap(type) -> MapSerializer(this, this).deserialize(decoder) MsgPackType.Ext.isExt(type) -> dynamicMsgPackExtensionSerializer.deserialize(decoder) - else -> TODO("Missing decoder for type") + else -> throw MsgPackSerializationException.dynamicSerializationError("Missing decoder for type: $type") } } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/exceptions/MsgPackSerializationException.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/exceptions/MsgPackSerializationException.kt new file mode 100644 index 0000000..f9a1682 --- /dev/null +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/exceptions/MsgPackSerializationException.kt @@ -0,0 +1,80 @@ +package com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions + +import com.ensarsarajcic.kotlinx.serialization.msgpack.extensions.MsgPackExtension +import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataBuffer +import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataInputBuffer +import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataOutputBuffer +import kotlinx.serialization.SerializationException + +private fun ByteArray.toHex() = this.joinToString(separator = "") { it.toHex() } +private fun Byte.toHex() = toInt().and(0xff).toString(16).padStart(2, '0') +private fun MsgPackExtension.toInfoString() = "{type = $type, extTypeId = $extTypeId, data = ${data.toHex()}}" + +class MsgPackSerializationException private constructor( + override val message: String +) : SerializationException() { + companion object { + private fun MsgPackDataInputBuffer.locationInfo() = + toByteArray().toList().let { + """ + ${it.subList(0, index).toByteArray().toHex()}[${peekSafely()}]${it.subList((index + 1).coerceAtMost(it.size), it.size)} + ${(0 until index).joinToString(separator = "") { " " }}} ^^ ${((index + 1) until it.size).joinToString(separator = "") { " " }} + """.trimIndent() + } + + private fun MsgPackDataOutputBuffer.locationInfo() = + "Written so far: ${toByteArray().toHex()}\nSize: ${toByteArray().size} bytes" + + private fun coreSerialization(buffer: MsgPackDataBuffer, locationInfo: String, reason: String? = null): MsgPackSerializationException { + return MsgPackSerializationException( + "MsgPack Serialization failure while serializing: ${buffer.toByteArray().toHex()}\nReason: $reason\nCurrent position:\n\n$locationInfo" + ) + } + + private fun extensionSerialization(extension: MsgPackExtension, reason: String? = null): MsgPackSerializationException { + return MsgPackSerializationException( + "MsgPack Serialization failure while serializing: ${extension.toInfoString()}\nReason: $reason" + ) + } + + fun deserialization(buffer: MsgPackDataInputBuffer, reason: String? = null): MsgPackSerializationException { + return coreSerialization(buffer, buffer.locationInfo(), reason) + } + + fun serialization(buffer: MsgPackDataOutputBuffer, reason: String? = null): MsgPackSerializationException { + return coreSerialization(buffer, buffer.locationInfo(), reason) + } + + fun extensionSerializationWrongType(extension: MsgPackExtension, expectedType: Byte, foundType: Byte): MsgPackSerializationException { + return extensionSerialization(extension, "Expected extension type ${expectedType.toHex()} but found ${foundType.toHex()}. Deserialized extension: $extension") + } + + fun extensionDeserializationWrongType(extension: MsgPackExtension, expectedType: Byte, foundType: Byte): MsgPackSerializationException { + return extensionSerialization(extension, "Expected extension type ${expectedType.toHex()} but found ${foundType.toHex()}. Serialized extension: $extension") + } + + fun genericExtensionError(extension: MsgPackExtension, reason: String? = null): MsgPackSerializationException { + return extensionSerialization(extension, reason) + } + + fun packingError(reason: String? = null): MsgPackSerializationException { + return MsgPackSerializationException("MsgPack Serialization failure while packing value! Reason: $reason") + } + + fun unpackingError(reason: String? = null): MsgPackSerializationException { + return MsgPackSerializationException("MsgPack Serialization failure while unpacking value! Reason: $reason") + } + + fun dynamicSerializationError(reason: String? = null): MsgPackSerializationException { + return MsgPackSerializationException("MsgPack Dynamic Serialization failure! Reason: $reason") + } + + fun strictTypeError(buffer: MsgPackDataInputBuffer, expectedType: String, foundType: String): MsgPackSerializationException { + return deserialization(buffer, "Strict type error! Expected type $expectedType, but found $foundType") + } + + fun overflowError(buffer: MsgPackDataInputBuffer): MsgPackSerializationException { + return deserialization(buffer, "Overflow error!") + } + } +} diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/BaseMsgPackExtensionSerializer.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/BaseMsgPackExtensionSerializer.kt index a079ef7..42e2775 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/BaseMsgPackExtensionSerializer.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/BaseMsgPackExtensionSerializer.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.extensions +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder @@ -11,7 +12,7 @@ abstract class BaseMsgPackExtensionSerializer : KSerializer { override fun deserialize(decoder: Decoder): T { val extension = decoder.decodeSerializableValue(serializer) if (checkTypeId && extension.extTypeId != extTypeId) { - throw TODO("Add more info") + throw MsgPackSerializationException.extensionDeserializationWrongType(extension, extTypeId, extension.extTypeId) } return deserialize(extension) } @@ -21,7 +22,7 @@ abstract class BaseMsgPackExtensionSerializer : KSerializer { override fun serialize(encoder: Encoder, value: T) { val extension = serialize(value) if (checkTypeId && extension.extTypeId != extTypeId) { - throw TODO("Add more info") + throw MsgPackSerializationException.extensionSerializationWrongType(extension, extTypeId, extension.extTypeId) } encoder.encodeSerializableValue(serializer, serialize(value)) } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/DynamicMsgPackExtensionSerializer.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/DynamicMsgPackExtensionSerializer.kt index 45fc42f..6403fe7 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/DynamicMsgPackExtensionSerializer.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/extensions/DynamicMsgPackExtensionSerializer.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.extensions +import kotlinx.serialization.SerializationException import kotlin.reflect.KClass open class DynamicMsgPackExtensionSerializer : BaseMsgPackExtensionSerializer() { @@ -52,7 +53,7 @@ open class DynamicMsgPackExtensionSerializer : BaseMsgPackExtensionSerializer { // Working with Timestamp 96 if (extension.data.size != TIMESTAMP_96_DATA_SIZE) { - throw TODO("Needs more info") + throw MsgPackSerializationException.genericExtensionError(extension, "Error when parsing datetime. Expected data size of $TIMESTAMP_96_DATA_SIZE, but found ${extension.data.size}") } val nanoseconds = extension.data .take(4) @@ -71,7 +72,7 @@ open class MsgPackTimestampExtensionSerializer : .joinToNumber() return MsgPackTimestamp.T92(seconds, nanoseconds) } - else -> TODO("Needs more info") + else -> throw MsgPackSerializationException.genericExtensionError(extension, "Unsupported extension type for timestamp: ${extension.type}") } } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackDecoder.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackDecoder.kt index 69766c0..b97f20f 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackDecoder.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackDecoder.kt @@ -2,6 +2,7 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.internal import com.ensarsarajcic.kotlinx.serialization.msgpack.MsgPackConfiguration import com.ensarsarajcic.kotlinx.serialization.msgpack.MsgPackNullableDynamicSerializer +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataInputBuffer import com.ensarsarajcic.kotlinx.serialization.msgpack.types.MsgPackType import com.ensarsarajcic.kotlinx.serialization.msgpack.utils.joinToNumber @@ -127,7 +128,7 @@ internal class BasicMsgPackDecoder( if (configuration.preventOverflows) { val number = dataBuffer.takeNext(4).joinToNumber() if (number !in Int.MIN_VALUE..Int.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toInt() } @@ -136,7 +137,7 @@ internal class BasicMsgPackDecoder( } } else -> { - throw TODO("Add a more descriptive error when wrong type is found!") + throw MsgPackSerializationException.deserialization(dataBuffer, "Unknown array type: $next") } } @@ -148,7 +149,7 @@ internal class BasicMsgPackDecoder( if (configuration.preventOverflows) { val number = dataBuffer.takeNext(4).joinToNumber() if (number !in Int.MIN_VALUE..Int.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toInt() } @@ -157,12 +158,12 @@ internal class BasicMsgPackDecoder( } } else -> { - throw TODO("Add a more descriptive error when wrong type is found!") + throw MsgPackSerializationException.deserialization(dataBuffer, "Unknown map type: $next") } } else -> { - TODO("Unsupported collection") + throw MsgPackSerializationException.deserialization(dataBuffer, "Unsupported collection: ${descriptor.kind}") } } } @@ -181,9 +182,8 @@ internal class BasicMsgPackDecoder( return ExtensionTypeDecoder(this) } // Handle extension types as arrays - decodeCollectionSize(descriptor) - return ClassMsgPackDecoder(this) - // TODO compare with descriptor.elementsCount + val size = decodeCollectionSize(descriptor) + return ClassMsgPackDecoder(this, size) } return this } @@ -200,14 +200,15 @@ internal class MsgPackDecoder( } internal class ClassMsgPackDecoder( - private val basicMsgPackDecoder: BasicMsgPackDecoder + private val basicMsgPackDecoder: BasicMsgPackDecoder, + private val size: Int ) : Decoder by basicMsgPackDecoder, CompositeDecoder by basicMsgPackDecoder, MsgPackTypeDecoder by basicMsgPackDecoder { override val serializersModule: SerializersModule = basicMsgPackDecoder.serializersModule private var decodedElements = 0 override fun decodeElementIndex(descriptor: SerialDescriptor): Int { - if (decodedElements >= descriptor.elementsCount) return CompositeDecoder.DECODE_DONE + if (decodedElements >= size) return CompositeDecoder.DECODE_DONE val result = basicMsgPackDecoder.decodeElementIndex(descriptor) if (result != CompositeDecoder.DECODE_DONE) decodedElements++ return result @@ -234,7 +235,7 @@ internal class ExtensionTypeDecoder( val byte = dataBuffer.requireNextByte() bytesRead++ if (!MsgPackType.Ext.isExt(byte)) { - throw TODO("Unexpected byte") + throw MsgPackSerializationException.deserialization(dataBuffer, "Unexpected byte: $byte. Expected extension type byte!") } type = byte if (MsgPackType.Ext.SIZES.containsKey(type)) { @@ -254,7 +255,7 @@ internal class ExtensionTypeDecoder( typeId = byte typeId!! } else { - throw TODO("Handle?") + throw AssertionError() } } override fun decodeElementIndex(descriptor: SerialDescriptor): Int { diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackEncoder.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackEncoder.kt index a7fafab..c59f321 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackEncoder.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPackEncoder.kt @@ -1,6 +1,7 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.internal import com.ensarsarajcic.kotlinx.serialization.msgpack.MsgPackConfiguration +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataOutputBuffer import com.ensarsarajcic.kotlinx.serialization.msgpack.types.MsgPackType import com.ensarsarajcic.kotlinx.serialization.msgpack.utils.splitToByteArray @@ -105,7 +106,7 @@ internal class BasicMsgPackEncoder( result.add(MsgPackType.Array.ARRAY32) result.addAll(collectionSize.toInt().splitToByteArray().toList()) } - else -> TODO("TOO LONG COLLECTION") + else -> throw MsgPackSerializationException.serialization(result, "Collection too long (max size = ${MsgPackType.Array.MAX_ARRAY32_LENGTH}, size = $collectionSize)!") } StructureKind.CLASS, StructureKind.OBJECT, StructureKind.MAP -> @@ -121,10 +122,10 @@ internal class BasicMsgPackEncoder( result.add(MsgPackType.Map.MAP32) result.addAll(collectionSize.toInt().splitToByteArray().toList()) } - else -> TODO("TOO LONG COLLECTION") + else -> throw MsgPackSerializationException.serialization(result, "Object too long (max size = ${MsgPackType.Map.MAX_MAP32_LENGTH}, size = $collectionSize)!") } - else -> TODO("UNSUPPORTED COLLECTION!") + else -> throw MsgPackSerializationException.serialization(result, "Unsupported collection type: ${descriptor.kind}") } return this } @@ -183,20 +184,20 @@ internal class ExtensionTypeEncoder( MsgPackType.Ext.EXT8 -> MsgPackType.Ext.MAX_EXT8_LENGTH MsgPackType.Ext.EXT16 -> MsgPackType.Ext.MAX_EXT16_LENGTH MsgPackType.Ext.EXT32 -> MsgPackType.Ext.MAX_EXT32_LENGTH - else -> TODO("HANDLE") + else -> throw MsgPackSerializationException.serialization(result, "Unexpected extension type: $type") }.toLong() - if (size!!.toLong() > maxSize) throw TODO("Size exceeded") + if (size!!.toLong() > maxSize) throw MsgPackSerializationException.serialization(result, "Size ($size) too long for extension type ($maxSize)!") result.addAll( when (type) { MsgPackType.Ext.EXT8 -> size!!.toByte().splitToByteArray() MsgPackType.Ext.EXT16 -> size!!.toShort().splitToByteArray() MsgPackType.Ext.EXT32 -> size!!.toInt().splitToByteArray() - else -> TODO("HANDLE") + else -> throw MsgPackSerializationException.serialization(result, "Unexpected extension type: $type") } ) result.add(typeId!!) } else { - if (value.size != size) throw TODO("Invalid size") + if (value.size != size) throw MsgPackSerializationException.serialization(result, "Invalid size for fixed size extension type! Expected $size but found ${value.size}") } result.addAll(value) } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPacker.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPacker.kt index 1b44c5e..cea8500 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPacker.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgPacker.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.internal +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.types.MsgPackType import com.ensarsarajcic.kotlinx.serialization.msgpack.utils.splitToByteArray @@ -113,7 +114,7 @@ internal class BasicMsgPacker : MsgPacker { bytes.size <= MsgPackType.String.MAX_STR32_LENGTH -> { byteArrayOf(MsgPackType.String.STR32) + bytes.size.toInt().splitToByteArray() } - else -> TODO("TOO LONG STRING") + else -> throw MsgPackSerializationException.packingError("String too long. Byte size: ${bytes.size}. Max size: ${MsgPackType.String.MAX_STR32_LENGTH}") } return prefix + bytes } @@ -129,7 +130,7 @@ internal class BasicMsgPacker : MsgPacker { value.size <= MsgPackType.Bin.MAX_BIN32_LENGTH -> { byteArrayOf(MsgPackType.Bin.BIN32) + value.size.toInt().splitToByteArray() } - else -> TODO("TOO LONG STRING") + else -> throw MsgPackSerializationException.packingError("Byte array too long. Byte size: ${value.size}. Max size: ${MsgPackType.Bin.MAX_BIN32_LENGTH}") } return prefix + value } diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgUnpacker.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgUnpacker.kt index 4b0e228..14ef448 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgUnpacker.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/internal/MsgUnpacker.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack.internal +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.stream.MsgPackDataInputBuffer import com.ensarsarajcic.kotlinx.serialization.msgpack.types.MsgPackType import com.ensarsarajcic.kotlinx.serialization.msgpack.utils.joinToNumber @@ -40,7 +41,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (next == MsgPackType.Int.UINT8 && preventOverflow) { val number = (dataBuffer.requireNextByte().toInt() and 0xff).toShort() if (number !in Byte.MIN_VALUE..Byte.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toByte() } @@ -48,7 +49,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) dataBuffer.requireNextByte() } } - else -> throw TODO("Add a more descriptive error when wrong type is found!") + else -> throw MsgPackSerializationException.deserialization(dataBuffer, "Expected byte type, but found $next") } } @@ -60,7 +61,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (next == MsgPackType.Int.UINT16 && preventOverflow) { val number = dataBuffer.takeNext(2).joinToNumber() if (number !in Short.MIN_VALUE..Short.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toShort() } @@ -72,7 +73,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) dataBuffer.skip(1) (dataBuffer.requireNextByte().toInt() and 0xff).toShort() } - else -> if (strict) TODO("Strict type error") else unpackByte(strict).toShort() + else -> if (strict) throw MsgPackSerializationException.strictTypeError(dataBuffer, "short", "byte") else unpackByte(strict).toShort() } } @@ -84,7 +85,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (next == MsgPackType.Int.UINT32 && preventOverflow) { val number = dataBuffer.takeNext(4).joinToNumber() if (number !in Int.MIN_VALUE..Int.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toInt() } @@ -96,7 +97,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) dataBuffer.skip(1) dataBuffer.takeNext(2).joinToNumber() } - else -> if (strict) TODO("Strict type error") else unpackShort(strict).toInt() + else -> if (strict) throw MsgPackSerializationException.strictTypeError(dataBuffer, "int", "short") else unpackShort(strict).toInt() } } @@ -108,7 +109,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (next == MsgPackType.Int.UINT64 && preventOverflow) { val number = dataBuffer.takeNext(8).joinToNumber() if (number < 0) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number } @@ -120,28 +121,28 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) dataBuffer.skip(1) dataBuffer.takeNext(4).joinToNumber() } - else -> if (strict) TODO("Strict type error") else unpackInt(strict).toLong() + else -> if (strict) throw MsgPackSerializationException.strictTypeError(dataBuffer, "long", "int") else unpackInt(strict).toLong() } } override fun unpackFloat(strict: Boolean): Float { - return when (dataBuffer.peek()) { + return when (val type = dataBuffer.peek()) { MsgPackType.Float.FLOAT -> { dataBuffer.skip(1) Float.fromBits(dataBuffer.takeNext(4).joinToNumber()) } - else -> TODO("Add a more descriptive error when wrong type is found!") + else -> throw MsgPackSerializationException.deserialization(dataBuffer, "Expected float type, but found $type") } } override fun unpackDouble(strict: Boolean): Double { - return when (dataBuffer.peek()) { + return when (val type = dataBuffer.peek()) { MsgPackType.Float.DOUBLE -> { dataBuffer.skip(1) Double.fromBits(dataBuffer.takeNext(8).joinToNumber()) } - MsgPackType.Float.FLOAT -> if (strict) TODO("Strict type error") else unpackFloat(strict).toDouble() - else -> TODO("Add a more descriptive error when wrong type is found!") + MsgPackType.Float.FLOAT -> if (strict) throw MsgPackSerializationException.strictTypeError(dataBuffer, "double", "float") else unpackFloat(strict).toDouble() + else -> throw MsgPackSerializationException.deserialization(dataBuffer, "Expected double type, but found $type") } } @@ -155,7 +156,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (preventOverflow) { val number = dataBuffer.takeNext(4).joinToNumber() if (number !in Int.MIN_VALUE..Int.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toInt() } @@ -164,7 +165,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) } } else -> { - throw TODO("Add a more descriptive error when wrong type is found!") + throw MsgPackSerializationException.deserialization(dataBuffer, "Expected string type, but found $next") } } if (length == 0) return "" @@ -180,7 +181,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) if (preventOverflow) { val number = dataBuffer.takeNext(4).joinToNumber() if (number !in Int.MIN_VALUE..Int.MAX_VALUE) { - throw TODO("Overflow error") + throw MsgPackSerializationException.overflowError(dataBuffer) } else { number.toInt() } @@ -189,7 +190,7 @@ internal class BasicMsgUnpacker(private val dataBuffer: MsgPackDataInputBuffer) } } else -> { - throw TODO("Add a more descriptive error when wrong type is found!") + throw MsgPackSerializationException.deserialization(dataBuffer, "Expected binary type, but found $next") } } if (length == 0) return byteArrayOf() diff --git a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/stream/MsgPackDataBuffer.kt b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/stream/MsgPackDataBuffer.kt index ac036e6..e420ebb 100644 --- a/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/stream/MsgPackDataBuffer.kt +++ b/serialization-msgpack/src/commonMain/kotlin/com.ensarsarajcic.kotlinx.serialization.msgpack/stream/MsgPackDataBuffer.kt @@ -15,7 +15,8 @@ class MsgPackDataOutputBuffer() : MsgPackDataBuffer { } class MsgPackDataInputBuffer(private val byteArray: ByteArray) : MsgPackDataBuffer { - private var index = 0 + var index = 0 + private set fun skip(bytes: Int) { index += bytes diff --git a/serialization-msgpack/src/commonTest/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/MsgPackTest.kt b/serialization-msgpack/src/commonTest/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/MsgPackTest.kt index 1c9dc5e..5f19712 100644 --- a/serialization-msgpack/src/commonTest/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/MsgPackTest.kt +++ b/serialization-msgpack/src/commonTest/kotlin/com/ensarsarajcic/kotlinx/serialization/msgpack/MsgPackTest.kt @@ -1,5 +1,6 @@ package com.ensarsarajcic.kotlinx.serialization.msgpack +import com.ensarsarajcic.kotlinx.serialization.msgpack.exceptions.MsgPackSerializationException import com.ensarsarajcic.kotlinx.serialization.msgpack.extensions.MsgPackTimestamp import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.ArraySerializer @@ -317,8 +318,7 @@ internal class MsgPackTest { configuration = MsgPackConfiguration(preventOverflows = true) ).decodeFromByteArray(serializer, it.hexStringToByteArray()) fail("Overflow should have occurred") - // TODO change to proper exception once it is made - } catch (ex: Throwable) {} + } catch (ex: MsgPackSerializationException) {} } } testPairs(TestData.uByteTestPairs.map { it.first }, Byte.serializer()) From 86c6ef71f8e2ebb74ecb26bdf70c48be06f85c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 22 Dec 2021 16:13:02 +0100 Subject: [PATCH 4/4] chore: update changelog for 0.5.0 release --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8d2fb..8862379 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). ## [Unreleased] -### Added + +## [0.5.0] - 2021-12-22 - More descriptive errors ([#21][i21]) ## [0.4.4] - 2021-12-20 @@ -84,7 +85,7 @@ MsgPack.default.encodeToByteArray(...) - `MsgPackDynamicSerializer` as placeholder for future [contextual serializer](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization) - Full implementation of msgpack spec excluding extension types and bin format family -[Unreleased]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.4.4...main +[Unreleased]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.5.0...main [0.2.0]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.1.0...0.2.0 [0.2.1]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.2.0...0.2.1 [0.3.0]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.2.1...0.3.0 @@ -93,6 +94,7 @@ MsgPack.default.encodeToByteArray(...) [0.4.2]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.4.1...0.4.2 [0.4.3]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.4.2...0.4.3 [0.4.4]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.4.3...0.4.4 +[0.5.0]: https://github.com/esensar/kotlinx-serialization-msgpack/compare/0.4.4...0.5.0 [i6]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/6 [i9]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/9 [i10]: https://github.com/esensar/kotlinx-serialization-msgpack/issues/10