From 06a8a4bada2de6522608fbb0a500ac675420fb04 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 27 Jul 2017 12:08:03 +0100 Subject: [PATCH 1/2] Serialising null chars was broken The recent changes that were put in to allow chars to be serialised at all failed to take into account nullability and thus if a char was null and that object was first serlialised then desierliased it would throw a NPE --- .../core/serialization/amqp/PropertySerializer.kt | 9 ++++++--- .../serialization/amqp/DeserializeSimpleTypesTests.kt | 11 +++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt b/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt index 0211af8541..5ac754af18 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt @@ -113,13 +113,16 @@ sealed class PropertySerializer(val name: String, val readMethod: Method, val re * casting back to a char otherwise it's treated as an Integer and a TypeMismatch occurs */ class AMQPCharPropertySerializer(name: String, readMethod: Method) : - PropertySerializer(name, readMethod, Char::class.java) { + PropertySerializer(name, readMethod, Character::class.java) { override fun writeClassInfo(output: SerializationOutput) {} - override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput) = (obj as Int).toChar() + override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? { + return if(obj == null) null else (obj as Int).toChar() + } override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) { - data.putChar((readMethod.invoke(obj) as Char).toInt()) + val input = readMethod.invoke(obj) + if (input != null) data.putChar((input as Char).toInt()) else data.putNull() } } } diff --git a/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt b/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt index 35e322fcdc..7ce9789d78 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt @@ -50,6 +50,17 @@ class DeserializeSimpleTypesTests { assertEquals(c.c, deserializedC.c) } + @Test + fun testNullCharacter() { + data class C(val c: Char?) + + val c = C(null) + val serialisedC = SerializationOutput().serialize(c) + val deserializedC = DeserializationInput().deserialize(serialisedC) + + assertEquals(c.c, deserializedC.c) + } + @Test fun testArrayOfInt() { class IA(val ia: Array) From 3fd4baa9b2b38419ca9c0b25dd7cfe6de83014f1 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 27 Jul 2017 14:34:36 +0100 Subject: [PATCH 2/2] Review comments: Swap ints to shorts for storing chars as there is no point wasting the 16 bits otherwise Add better char tests, i.e. Unicode points that aren't going to fit into the bottom 8 bits --- .../serialization/amqp/PropertySerializer.kt | 4 +-- .../amqp/DeserializeSimpleTypesTests.kt | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt b/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt index 5ac754af18..5d14e35c99 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/amqp/PropertySerializer.kt @@ -117,12 +117,12 @@ sealed class PropertySerializer(val name: String, val readMethod: Method, val re override fun writeClassInfo(output: SerializationOutput) {} override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? { - return if(obj == null) null else (obj as Int).toChar() + return if(obj == null) null else (obj as Short).toChar() } override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) { val input = readMethod.invoke(obj) - if (input != null) data.putChar((input as Char).toInt()) else data.putNull() + if (input != null) data.putShort((input as Char).toShort()) else data.putNull() } } } diff --git a/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt b/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt index 7ce9789d78..97361ca92b 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/amqp/DeserializeSimpleTypesTests.kt @@ -32,11 +32,24 @@ class DeserializeSimpleTypesTests { fun testChar() { data class C(val c: Char) - val c = C('c') - val serialisedC = SerializationOutput().serialize(c) - val deserializedC = DeserializationInput().deserialize(serialisedC) + var deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('c'))) + assertEquals('c', deserializedC.c) - assertEquals(c.c, deserializedC.c) + // CYRILLIC CAPITAL LETTER YU (U+042E) + deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('Ю'))) + assertEquals('Ю', deserializedC.c) + + // ARABIC LETTER FEH WITH DOT BELOW (U+06A3) + deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('ڣ'))) + assertEquals('ڣ', deserializedC.c) + + // ARABIC LETTER DAD WITH DOT BELOW (U+06FB) + deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('ۻ'))) + assertEquals('ۻ', deserializedC.c) + + // BENGALI LETTER AA (U+0986) + deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('আ'))) + assertEquals('আ', deserializedC.c) } @Test @@ -150,12 +163,23 @@ class DeserializeSimpleTypesTests { assertEquals(SerializerFactory.nameForType(c.c::class.java), "char[p]") val serialisedC = TestSerializationOutput(VERBOSE, sf).serialize(c) - val deserializedC = DeserializationInput(sf).deserialize(serialisedC) + var deserializedC = DeserializationInput(sf).deserialize(serialisedC) assertEquals(c.c.size, deserializedC.c.size) assertEquals(c.c[0], deserializedC.c[0]) assertEquals(c.c[1], deserializedC.c[1]) assertEquals(c.c[2], deserializedC.c[2]) + + // second test with more interesting characters + v[0] = 'ই'; v[1] = ' '; v[2] = 'ਔ' + val c2 = C(v) + + deserializedC = DeserializationInput(sf).deserialize(TestSerializationOutput(VERBOSE, sf).serialize(c2)) + + assertEquals(c2.c.size, deserializedC.c.size) + assertEquals(c2.c[0], deserializedC.c[0]) + assertEquals(c2.c[1], deserializedC.c[1]) + assertEquals(c2.c[2], deserializedC.c[2]) } @Test