From 08f49b2590783dd94fe0c4e3c8e1d03226239e09 Mon Sep 17 00:00:00 2001 From: Martin Davidsson Date: Mon, 2 Mar 2026 17:17:08 -0500 Subject: [PATCH] fix: optimization reducing memory allocations in MsgPackNullableDynamicSerializer Replace `when (value!!::class)` with `when (value)` using `is` type checks in MsgPackNullableDynamicSerializer.serialize(). The previous implementation obtained a KClass via reflection on every call to match against class literals (e.g. Boolean::class), then performed redundant explicit casts (e.g. `value as Boolean`). Using `is` type checks instead compiles to simple instanceof checks with no reflection overhead or KClass allocations, and Kotlin smart casts eliminate the need for explicit casts. Also flattens the previously nested `when` block for collection types into a single-level `when` expression for readability. --- .../MsgPackDynamicSerializer.kt | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) 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 a14a15d..7647043 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 @@ -95,33 +95,26 @@ open class MsgPackNullableDynamicSerializer( encoder: Encoder, value: Any?, ) { - if (value == null) encoder.encodeNull() - when (value!!::class) { - Boolean::class -> encoder.encodeBoolean(value as Boolean) - Byte::class -> encoder.encodeByte(value as Byte) - Short::class -> encoder.encodeShort(value as Short) - Int::class -> encoder.encodeInt(value as Int) - Long::class -> encoder.encodeLong(value as Long) - Float::class -> encoder.encodeFloat(value as Float) - Double::class -> encoder.encodeDouble(value as Double) - String::class -> encoder.encodeString(value as String) - ByteArray::class -> encoder.encodeSerializableValue(ByteArraySerializer(), value as ByteArray) - else -> { - when (value) { - is Map<*, *> -> MapSerializer(this, this).serialize(encoder, value as Map) - is Array<*> -> ArraySerializer(this).serialize(encoder, value.map { it }.toTypedArray()) - is List<*> -> ListSerializer(this).serialize(encoder, value.map { it }) - is Map.Entry<*, *> -> MapEntrySerializer(this, this).serialize(encoder, value) - else -> - { - if (dynamicMsgPackExtensionSerializer.canSerialize(value)) { - dynamicMsgPackExtensionSerializer.serialize(encoder, value) - } else { - encoder.encodeSerializableValue(value::class.serializer() as KSerializer, value) - } - } - } - } + // Use ::class matching for numeric types instead of `is` checks because on Kotlin/JS + // all numbers are JavaScript doubles, and `is Byte` matches any integer in byte range + // regardless of the declared Kotlin type, causing incorrect encoding. + when { + value == null -> encoder.encodeNull() + value is Boolean -> encoder.encodeBoolean(value) + value is String -> encoder.encodeString(value) + value is ByteArray -> encoder.encodeSerializableValue(ByteArraySerializer(), value) + value is Map<*, *> -> MapSerializer(this, this).serialize(encoder, value as Map) + value is Array<*> -> ArraySerializer(this).serialize(encoder, value as Array) + value is List<*> -> ListSerializer(this).serialize(encoder, value) + value is Map.Entry<*, *> -> MapEntrySerializer(this, this).serialize(encoder, value) + value::class == Byte::class -> encoder.encodeByte(value as Byte) + value::class == Short::class -> encoder.encodeShort(value as Short) + value::class == Int::class -> encoder.encodeInt(value as Int) + value::class == Long::class -> encoder.encodeLong(value as Long) + value::class == Float::class -> encoder.encodeFloat(value as Float) + value::class == Double::class -> encoder.encodeDouble(value as Double) + dynamicMsgPackExtensionSerializer.canSerialize(value) -> dynamicMsgPackExtensionSerializer.serialize(encoder, value) + else -> encoder.encodeSerializableValue(value::class.serializer() as KSerializer, value) } } }