Skip to content

Commit

Permalink
CompatibleFieldSerializerConfig setReadUnknownTagData -> setReadUnkno…
Browse files Browse the repository at this point in the history
…wnFieldData. Added BitSet default serializer test. Clean up.
  • Loading branch information
NathanSweet committed Feb 12, 2019
1 parent 87a85aa commit d73e935
Show file tree
Hide file tree
Showing 14 changed files with 47 additions and 39 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ When `readUnknownTagData` and `chunkedEncoding` are false, fields must not be re

Setting | Description | Default value
--- | --- | ---
`readUnknownTagData` | When false and an unknown tag is encountered, an exception is thrown or, if `chunkedEncoding` is true, the data is skipped. If the data is skipped and references are enabled, then any references in the skipped data are not read and further deserialization receive the wrong references and fail.<br><br>When true, the class for each field value is written before the value. When an unknown tag is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.
`readUnknownTagData` | When false and an unknown tag is encountered, an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.<br><br>When true, the class for each field value is written before the value. When an unknown tag is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.<br><br>In either case, if the data is skipped and references are enabled, then any references in the skipped data are not read and further deserialization may receive the wrong references and fail.
`chunkedEncoding` | When true, fields are written with chunked encoding to allow unknown field data to be skipped. This impacts performance. | false
`chunkSize` | The maximum size of each chunk for chunked encoding. | 1024

Expand All @@ -1069,13 +1069,13 @@ TaggedFieldSerializer also inherits all the settings of FieldSerializer.

CompatibleFieldSerializer extends FieldSerializer to provided both forward and backward compatibility. This means fields can be added or removed without invalidating previously serialized bytes. Renaming or changing the type of a field is not supported. Like FieldSerializer, it can serialize most classes without needing annotations.

The forward and backward compatibility and serialization [performance](https://raw.github.com/wiki/EsotericSoftware/kryo/images/benchmarks/fieldSerializer.png) depends on the `readUnknownTagData` and `chunkedEncoding` settings. Additionally, the first time the class is encountered in the serialized bytes, a simple schema is written containing the field name strings. Because field data is identified by name, if a super class has a field with the same name as a subclass, `extendedFieldNames` must be true.
The forward and backward compatibility and serialization [performance](https://raw.github.com/wiki/EsotericSoftware/kryo/images/benchmarks/fieldSerializer.png) depends on the `readUnknownFieldData` and `chunkedEncoding` settings. Additionally, the first time the class is encountered in the serialized bytes, a simple schema is written containing the field name strings. Because field data is identified by name, if a super class has a field with the same name as a subclass, `extendedFieldNames` must be true.

#### CompatibleFieldSerializer settings

Setting | Description | Default value
--- | --- | ---
`readUnknownTagData` | When false and an unknown tag is encountered, an exception is thrown or, if `chunkedEncoding` is true, the data is skipped. If the data is skipped and references are enabled, then any references in the data are not read and deserializing further data will fail.<br><br>When true, the class for each field value is written before the value. When an unknown field is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.
`readUnknownFieldData` | When false and an unknown field is encountered, an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.<br><br>When true, the class for each field value is written before the value. When an unknown field is encountered, an attempt to read the data is made. This is used to skip the data and, if references are enabled, any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, if `chunkedEncoding` is true, the data is skipped.<br><br>In either case, if the data is skipped and references are enabled, then any references in the skipped data are not read and further deserialization may receive the wrong references and fail.
`chunkedEncoding` | When true, fields are written with chunked encoding to allow unknown field data to be skipped. This impacts performance. | false
`chunkSize` | The maximum size of each chunk for chunked encoding. | 1024

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ static public class CompatibleState extends BenchmarkState {
public void setup () {
CompatibleFieldSerializerFactory factory = new CompatibleFieldSerializerFactory();
factory.getConfig().setChunkedEncoding(chunked);
factory.getConfig().setReadUnknownTagData(true); // Typical to always use.
factory.getConfig().setReadUnknownFieldData(true); // Typical to always use.
kryo.setDefaultSerializer(factory);
super.setup();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* Like {@link FieldSerializer}, it can serialize most classes without needing annotations.
* <p>
* The forward and backward compatibility and serialization performance depend on
* {@link CompatibleFieldSerializerConfig#setReadUnknownTagData(boolean)} and
* {@link CompatibleFieldSerializerConfig#setReadUnknownFieldData(boolean)} and
* {@link CompatibleFieldSerializerConfig#setChunkedEncoding(boolean)}. Additionally, the first time the class is encountered in
* the serialized bytes, a simple schema is written containing the field name strings.
* <p>
Expand Down Expand Up @@ -72,7 +72,7 @@ public void write (Kryo kryo, Output output, T object) {
}
}

boolean chunked = config.chunked, readUnknownTagData = config.readUnknownTagData;
boolean chunked = config.chunked, readUnknownTagData = config.readUnknownFieldData;
Output fieldOutput;
OutputChunked outputChunked = null;
if (chunked)
Expand Down Expand Up @@ -118,7 +118,7 @@ public T read (Kryo kryo, Input input, Class<? extends T> type) {
CachedField[] fields = (CachedField[])kryo.getGraphContext().get(this);
if (fields == null) fields = readFields(kryo, input);

boolean chunked = config.chunked, readUnknownTagData = config.readUnknownTagData;
boolean chunked = config.chunked, readUnknownTagData = config.readUnknownFieldData;
Input fieldInput;
InputChunked inputChunked = null;
if (chunked)
Expand Down Expand Up @@ -233,32 +233,35 @@ public CompatibleFieldSerializerConfig getCompatibleFieldSerializerConfig () {

/** Configuration for CompatibleFieldSerializer instances. */
static public class CompatibleFieldSerializerConfig extends FieldSerializerConfig {
boolean readUnknownTagData = true, chunked;
boolean readUnknownFieldData = true, chunked;
int chunkSize = 1024;

public CompatibleFieldSerializerConfig clone () {
return (CompatibleFieldSerializerConfig)super.clone(); // Clone is ok as we have only primitive fields.
}

/** When false and encountering an unknown tag, an exception is thrown or, if {@link #setChunkedEncoding(boolean) chunked
* encoding} is enabled, the data is skipped. If the data is skipped and {@link Kryo#setReferences(boolean) references} are
* enabled, then any references in the skipped data are not read and further deserialization receive the wrong references
* and fail.
/** When false and encountering an unknown field, an exception is thrown or, if {@link #setChunkedEncoding(boolean) chunked
* encoding} is enabled, the data is skipped.
* <p>
* When true, the type of each field value is written before the value. When an unknown tag is encountered, an attempt to
* When true, the type of each field value is written before the value. When an unknown field is encountered, an attempt to
* read the data is made so if it is a reference then any other values in the object graph referencing that data can be
* deserialized. If reading the data fails (eg the class is unknown or has been removed) then an exception is thrown or, if
* {@link #setChunkedEncoding(boolean) chunked encoding} is enabled, the data is skipped. Default is true. */
public void setReadUnknownTagData (boolean readUnknownTagData) {
this.readUnknownTagData = readUnknownTagData;
* {@link #setChunkedEncoding(boolean) chunked encoding} is enabled, the data is skipped.
* <p>
* In either case, if the data is skipped and {@link Kryo#setReferences(boolean) references} are enabled, then any
* references in the skipped data are not read and further deserialization receive the wrong references and fail.
* <p>
* Default is true. */
public void setReadUnknownFieldData (boolean readUnknownTagData) {
this.readUnknownFieldData = readUnknownTagData;
}

public boolean getReadUnknownTagData () {
return readUnknownTagData;
return readUnknownFieldData;
}

/** When true, fields are written with chunked encoding to allow unknown field data to be skipped. Default is false.
* @see #setReadUnknownTagData(boolean) */
* @see #setReadUnknownFieldData(boolean) */
public void setChunkedEncoding (boolean chunked) {
this.chunked = chunked;
if (TRACE) trace("kryo", "CompatibleFieldSerializerConfig setChunked: " + chunked);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,14 +833,12 @@ public void write (Kryo kryo, Output output, BitSet set) {
long[] values = set.toLongArray();
output.writeVarInt(values.length, true);
output.writeLongs(values, 0, values.length);
System.out.println("write " + Arrays.toString(values));
}

public BitSet read (Kryo kryo, Input input, Class type) {
int length = input.readVarInt(true);
long[] values = input.readLongs(length);
BitSet set = BitSet.valueOf(values);
System.out.println("read " + Arrays.toString(values));
return set;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,18 @@ public TaggedFieldSerializerConfig clone () {
}

/** When false and encountering an unknown tag, an exception is thrown or, if {@link #setChunkedEncoding(boolean) chunked
* encoding} is enabled, the data is skipped. If the data is skipped and {@link Kryo#setReferences(boolean) references} are
* enabled, then any references in the skipped data are not read and further deserialization receive the wrong references
* and fail. Default is false.
* encoding} is enabled, the data is skipped.
* <p>
* When true, the type of each field value is written before the value. When an unknown tag is encountered, an attempt to
* read the data is made. This is used to skip the data and, if {@link Kryo#setReferences(boolean) references} are enabled,
* then any other values in the object graph referencing that data can still be deserialized. If reading the data fails (eg
* the class is unknown or has been removed) then an exception is thrown or, if {@link #setChunkedEncoding(boolean) chunked
* encoding} is enabled, the data is skipped. */
* encoding} is enabled, the data is skipped.
* <p>
* In either case, if the data is skipped and {@link Kryo#setReferences(boolean) references} are enabled, then any
* references in the skipped data are not read and further deserialization receive the wrong references and fail.
* <p>
* Default is false. */
public void setReadUnknownTagData (boolean readUnknownTagData) {
this.readUnknownTagData = readUnknownTagData;
}
Expand Down
8 changes: 4 additions & 4 deletions test/com/esotericsoftware/kryo/SerializationCompatTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ public class SerializationCompatTest extends KryoTestCase {
static {
// java.version is e.g. 1.8.0 or 9.0.4
String[] strVersions = System.getProperty("java.version").split("\\.");
int[] versions = new int[] { parseInt(strVersions[0]), parseInt(strVersions[1]) };
int[] versions = new int[] {parseInt(strVersions[0]), parseInt(strVersions[1])};
JAVA_VERSION = versions[0] > 1 ? versions[0] : versions[1];
}
static private final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 8 ? 38 : 56; // Also change
static private final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 8 ? 39 : 57; // Also change
// Kryo#defaultSerializers.
static private final List<TestDataDescription> TEST_DATAS = new ArrayList<>();

static {
TEST_DATAS.add(new TestDataDescription<TestData>("5.0.0", new TestData(), 1889));
if (JAVA_VERSION >= 8) TEST_DATAS.add(new TestDataDescription<TestDataJava8>("5.0.0", new TestDataJava8(), 2047));
TEST_DATAS.add(new TestDataDescription<TestData>("5.0.0", new TestData(), 1941));
if (JAVA_VERSION >= 8) TEST_DATAS.add(new TestDataDescription<TestDataJava8>("5.0.0", new TestDataJava8(), 2099));
};

@Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -176,6 +177,7 @@ static public class TestData implements Serializable {
private char[] _charArray;
private String[] _stringArray;
private Person[] _personArray;
private BitSet _bitSet;

private Generic<String> _generic;
private GenericList<String> _genericList;
Expand Down Expand Up @@ -266,11 +268,12 @@ public TestData () {
_personArray[0].addFriend(_personArray[1]);
_personArray[1].addFriend(_personArray[0]);

_bitSet = BitSet.valueOf(new long[] {1, 2, 99999, 2345678987654l});

_generic = new Generic("foo");
_genericList = new GenericList(new ArrayList(Arrays.asList(new Generic("foo"), new Generic("bar"))));
_genericArray = new GenericArray(new Generic("foo"), new Generic("bar"));
_public = new PublicClass(new PrivateClass("foo"));

}

public int hashCode () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,4 @@ public void testStringArray () {
kryo.setReferences(true);
roundTrip(28, array);
}

@Test
public void testBitSet () {
kryo.register(BitSet.class);
BitSet set = BitSet.valueOf(new long[] {1L, 2L, 99999L, 2345678987654L});
roundTrip(34, set);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private void testAddedFieldToClassWithManyFields (int length, boolean references
kryo.setReferences(references);

CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithManyFields.class);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownTagData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownFieldData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setChunkedEncoding(chunked);
kryo.register(ClassWithManyFields.class, serializer);

Expand Down Expand Up @@ -225,7 +225,7 @@ private void testRemovedFieldFromClassWithManyFields (int length, boolean refere
kryo.setReferences(references);

CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithManyFields.class);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownTagData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownFieldData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setChunkedEncoding(chunked);
kryo.register(ClassWithManyFields.class, serializer);

Expand Down Expand Up @@ -302,7 +302,7 @@ private void testRemovedMultipleFieldsFromClassWithManyFields (int length, boole
kryo.setReferences(references);

CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithManyFields.class);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownTagData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setReadUnknownFieldData(readUnknownTagData);
serializer.getCompatibleFieldSerializerConfig().setChunkedEncoding(chunked);
kryo.register(ClassWithManyFields.class, serializer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -348,6 +349,13 @@ public void testCalendar () {
roundTrip(64, calendar);
}

@Test
public void testBitSet () {
kryo.register(BitSet.class);
BitSet set = BitSet.valueOf(new long[] {1L, 2L, 99999L, 2345678987654L});
roundTrip(34, set);
}

@Test
public void testClassSerializer () {
kryo.register(Class.class);
Expand Down
Binary file modified test/resources/TestData-bytebuffer.ser
Binary file not shown.
Binary file modified test/resources/TestData-standard.ser
Binary file not shown.
Binary file modified test/resources/TestDataJava8-bytebuffer.ser
Binary file not shown.
Binary file modified test/resources/TestDataJava8-standard.ser
Binary file not shown.

0 comments on commit d73e935

Please sign in to comment.