Skip to content

Commit

Permalink
#774 Do not re-use serializers for CompatibleFieldSerializer and Tagg…
Browse files Browse the repository at this point in the history
…edFieldSerializer (#788)
  • Loading branch information
theigl authored Nov 16, 2020
1 parent 04e0e90 commit 94103dd
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
public class CompatibleFieldSerializer<T> extends FieldSerializer<T> {
private static final int binarySearchThreshold = 32;

private CompatibleFieldSerializerConfig config;
private final CompatibleFieldSerializerConfig config;

public CompatibleFieldSerializer (Kryo kryo, Class type) {
this(kryo, type, new CompatibleFieldSerializerConfig());
Expand Down Expand Up @@ -102,6 +102,7 @@ public void write (Kryo kryo, Output output, T object) {
}
cachedField.setCanBeNull(false);
cachedField.setValueClass(valueClass);
cachedField.setReuseSerializer(false);
}

cachedField.write(fieldOutput, object);
Expand Down Expand Up @@ -174,6 +175,7 @@ public T read (Kryo kryo, Input input, Class<? extends T> type) {

cachedField.setCanBeNull(false);
cachedField.setValueClass(valueClass);
cachedField.setReuseSerializer(false);
} else if (cachedField == null) {
if (!chunked) throw new KryoException("Unknown field. (" + getType().getName() + ")");
if (TRACE) trace("kryo", "Skip unknown field.");
Expand Down
15 changes: 14 additions & 1 deletion src/com/esotericsoftware/kryo/serializers/FieldSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public abstract static class CachedField {
String name;
Class valueClass;
Serializer serializer;
boolean canBeNull, varEncoding = true, optimizePositive;
boolean canBeNull, varEncoding = true, optimizePositive, reuseSerializer = true;

// For AsmField.
FieldAccess access;
Expand Down Expand Up @@ -309,6 +309,18 @@ public boolean getOptimizePositive () {
return optimizePositive;
}

/** When true, serializers are re-used for all instances of the field if the {@link #valueClass} is known. Re-using
* serializers is significantly faster than looking them up for every read/write. However, this only works reliably
* when the {@link #valueClass} of the field never changes. Serializers that do not guarantee this must set the flag to
* false. */
void setReuseSerializer(boolean reuseSerializer) {
this.reuseSerializer = reuseSerializer;
}

boolean getReuseSerializer() {
return reuseSerializer;
}

public String getName () {
return name;
}
Expand All @@ -326,6 +338,7 @@ public String toString () {
public abstract void read (Input input, Object object);

public abstract void copy (Object original, Object copy);

}

/** Indicates a field should be ignored when its declaring class is registered unless the {@link Kryo#getContext() context} has
Expand Down
4 changes: 2 additions & 2 deletions src/com/esotericsoftware/kryo/serializers/ReflectField.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void write (Output output, Object object) {
if (serializer == null) {
serializer = kryo.getSerializer(concreteType);
// The concrete type of the field is known, always use the same serializer.
if (valueClass != null) this.serializer = serializer;
if (valueClass != null && reuseSerializer) this.serializer = serializer;
}
kryo.getGenerics().pushGenericType(genericType);
if (canBeNull) {
Expand Down Expand Up @@ -127,7 +127,7 @@ public void read (Input input, Object object) {
if (serializer == null) {
serializer = kryo.getSerializer(concreteType);
// The concrete type of the field is known, always use the same serializer.
if (valueClass != null) this.serializer = serializer;
if (valueClass != null && reuseSerializer) this.serializer = serializer;
}
kryo.getGenerics().pushGenericType(genericType);
if (canBeNull)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public void write (Kryo kryo, Output output, T object) {
}
cachedField.setCanBeNull(false);
cachedField.setValueClass(valueClass);
cachedField.setReuseSerializer(false);
}

cachedField.write(fieldOutput, object);
Expand Down Expand Up @@ -226,6 +227,7 @@ public T read (Kryo kryo, Input input, Class<? extends T> type) {
}
cachedField.setCanBeNull(false);
cachedField.setValueClass(valueClass);
cachedField.setReuseSerializer(false);
} else if (cachedField == null) {
if (!chunked) throw new KryoException("Unknown field tag: " + tag + " (" + getType().getName() + ")");
if (TRACE) trace("kryo", "Skip unknown field tag: " + tag);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,38 @@ public void testClassWithSuperTypeFields() {
roundTrip(71, new ClassWithSuperTypeFields("foo", Arrays.asList("bar"), "baz"));
}

// https://github.com/EsotericSoftware/kryo/issues/774
@Test
public void testClassWithObjectField() {
CompatibleFieldSerializer<ClassWithSuperTypeFields> serializer = new CompatibleFieldSerializer<>(kryo,
ClassWithObjectField.class);
CompatibleFieldSerializer.CompatibleFieldSerializerConfig config = serializer.getCompatibleFieldSerializerConfig();
config.setChunkedEncoding(true);
config.setReadUnknownFieldData(true);
kryo.register(ClassWithObjectField.class, serializer);

roundTrip(12, new ClassWithObjectField(123));
roundTrip(13, new ClassWithObjectField("foo"));
}

public static class ClassWithObjectField {
Object value;

public ClassWithObjectField() { }

public ClassWithObjectField(Object value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassWithObjectField wrapper = (ClassWithObjectField) o;
return Objects.equals(value, wrapper.value);
}
}

public static class TestClass {
public String text = "something";
public int moo = 120;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Objects;

import org.junit.Test;

Expand Down Expand Up @@ -156,6 +157,38 @@ public void testInvalidTagValue () {
assertTrue(receivedIAE);
}

// https://github.com/EsotericSoftware/kryo/issues/774
@Test
public void testClassWithObjectField() {
TaggedFieldSerializer<ClassWithObjectField> serializer = new TaggedFieldSerializer<>(kryo, ClassWithObjectField.class);
final TaggedFieldSerializer.TaggedFieldSerializerConfig config = serializer.getTaggedFieldSerializerConfig();
config.setChunkedEncoding(true);
config.setReadUnknownTagData(true);
kryo.register(ClassWithObjectField.class, serializer);

roundTrip(8, new ClassWithObjectField(123));
roundTrip(9, new ClassWithObjectField("foo"));
}

public static class ClassWithObjectField {
@Tag(0)
Object value;

public ClassWithObjectField() { }

public ClassWithObjectField(Object value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassWithObjectField wrapper = (ClassWithObjectField) o;
return Objects.equals(value, wrapper.value);
}
}

public static class TestClass {
@Tag(0) public String text = "something";
@Tag(1) public int moo = 120;
Expand Down

0 comments on commit 94103dd

Please sign in to comment.