From fc7f0cc7037ff1384b4cdac5b7ada287c64f0a00 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 15 Jul 2017 21:20:43 +0200 Subject: [PATCH] More refactoring, major FieldSerializer clean up, registration required by default. closes #398 closes #522 --- .classpath | 2 +- .settings/org.eclipse.jdt.core.prefs | 3 + build/jarjar-command-1.0.0.jar | Bin 0 -> 6183 bytes lib/reflectasm-1.10.1-shaded.jar | Bin 74282 -> 0 bytes lib/reflectasm-1.11.5-all.jar | Bin 0 -> 71350 bytes .../esotericsoftware/kryo/ClassResolver.java | 2 +- .../kryo/DefaultSerializer.java | 2 +- src/com/esotericsoftware/kryo/Kryo.java | 131 +--- .../esotericsoftware/kryo/KryoCopyable.java | 2 +- .../esotericsoftware/kryo/KryoException.java | 2 +- .../kryo/KryoSerializable.java | 2 +- src/com/esotericsoftware/kryo/NotNull.java | 2 +- .../kryo/ReferenceResolver.java | 2 +- .../esotericsoftware/kryo/Registration.java | 2 +- src/com/esotericsoftware/kryo/Serializer.java | 2 +- .../kryo/SerializerFactory.java | 18 +- .../kryo/io/ByteBufferInput.java | 31 - .../kryo/io/ByteBufferInputStream.java | 2 +- .../kryo/io/ByteBufferOutput.java | 38 +- .../kryo/io/ByteBufferOutputStream.java | 2 +- src/com/esotericsoftware/kryo/io/Input.java | 24 +- .../kryo/io/InputChunked.java | 6 +- src/com/esotericsoftware/kryo/io/Output.java | 83 +-- .../kryo/io/OutputChunked.java | 6 +- .../esotericsoftware/kryo/pool/KryoPool.java | 3 +- .../kryo/pool/SoftReferenceQueue.java | 9 +- .../serializers/AsmCachedFieldFactory.java | 69 --- .../{AsmCacheFields.java => AsmField.java} | 93 ++- .../kryo/serializers/BeanSerializer.java | 2 +- .../kryo/serializers/BlowfishSerializer.java | 2 +- .../kryo/serializers/CachedFields.java | 339 ++++++++++ .../serializers/CollectionSerializer.java | 8 +- .../CompatibleFieldSerializer.java | 25 +- .../serializers/DefaultArraySerializers.java | 89 +-- .../kryo/serializers/DefaultSerializers.java | 155 ++--- .../kryo/serializers/EnumNameSerializer.java | 3 +- .../serializers/ExternalizableSerializer.java | 6 +- .../kryo/serializers/FieldSerializer.java | 582 +++--------------- .../FieldSerializerAnnotationsUtil.java | 128 ---- .../serializers/FieldSerializerConfig.java | 121 ++-- .../FieldSerializerGenericsUtil.java | 177 +++--- .../kryo/serializers/Generics.java | 57 -- .../kryo/serializers/GenericsResolver.java | 29 +- .../kryo/serializers/ImmutableSerializer.java | 13 + .../kryo/serializers/JavaSerializer.java | 5 +- .../kryo/serializers/MapSerializer.java | 6 +- .../serializers/ObjectCachedFieldFactory.java | 62 -- .../kryo/serializers/OptionalSerializers.java | 31 +- .../{ObjectField.java => ReflectField.java} | 98 ++- .../serializers/TaggedFieldSerializer.java | 55 +- .../TaggedFieldSerializerConfig.java | 10 +- .../kryo/serializers/TimeSerializers.java | 112 +--- .../serializers/VersionFieldSerializer.java | 15 +- .../kryo/util/DefaultClassResolver.java | 18 +- .../util/DefaultInstantiatorStrategy.java | 97 +++ .../kryo/util/IdentityMap.java | 27 +- .../esotericsoftware/kryo/util/IntMap.java | 27 +- .../kryo/util/ListReferenceResolver.java | 2 +- .../kryo/util/MapReferenceResolver.java | 2 +- src/com/esotericsoftware/kryo/util/Util.java | 2 +- .../kryo/ArraySerializerTest.java | 5 +- .../kryo/BeanSerializerTest.java | 2 +- .../kryo/BlowfishSerializerTest.java | 2 +- .../kryo/ByteBufferInputOutputTest.java | 6 +- .../esotericsoftware/kryo/ChunkedTest.java | 2 +- .../kryo/CollectionSerializerTest.java | 4 +- .../kryo/CompatibleFieldSerializerTest.java | 73 +-- .../kryo/DefaultSerializersTest.java | 9 +- .../kryo/DeflateSerializerTest.java | 6 +- .../kryo/FieldSerializerGenericsTest.java | 18 +- .../kryo/FieldSerializerInheritanceTest.java | 10 +- .../kryo/FieldSerializerTest.java | 67 +- .../kryo/GarbageCollectionTest.java | 2 +- .../esotericsoftware/kryo/GenericsTest.java | 14 +- .../kryo/InputOutputTest.java | 6 +- .../kryo/JavaSerializerTest.java | 2 +- .../esotericsoftware/kryo/KryoStringTest.java | 13 +- .../esotericsoftware/kryo/KryoTestCase.java | 4 +- .../kryo/MapSerializerTest.java | 21 +- .../esotericsoftware/kryo/ReferenceTest.java | 1 + .../kryo/ReflectionAssert.java | 21 +- .../kryo/SerializationBenchmarkTest.java | 19 +- .../kryo/SerializationCompatTest.java | 16 +- .../kryo/SerializationCompatTestData.java | 80 +-- .../kryo/TaggedFieldSerializerTest.java | 18 +- .../kryo/WarnUnregisteredClassesTest.java | 4 +- .../kryo/pool/KryoPoolBenchmarkTest.java | 18 +- .../kryo/pool/KryoPoolTest.java | 10 +- .../Java8ClosureSerializerTest.java | 12 +- .../Java8OptionalSerializersTest.java | 6 +- .../serializers/Java8TimeSerializersTest.java | 5 +- 91 files changed, 1335 insertions(+), 1984 deletions(-) create mode 100644 build/jarjar-command-1.0.0.jar delete mode 100644 lib/reflectasm-1.10.1-shaded.jar create mode 100644 lib/reflectasm-1.11.5-all.jar delete mode 100644 src/com/esotericsoftware/kryo/serializers/AsmCachedFieldFactory.java rename src/com/esotericsoftware/kryo/serializers/{AsmCacheFields.java => AsmField.java} (74%) create mode 100644 src/com/esotericsoftware/kryo/serializers/CachedFields.java delete mode 100644 src/com/esotericsoftware/kryo/serializers/FieldSerializerAnnotationsUtil.java delete mode 100644 src/com/esotericsoftware/kryo/serializers/Generics.java create mode 100644 src/com/esotericsoftware/kryo/serializers/ImmutableSerializer.java delete mode 100644 src/com/esotericsoftware/kryo/serializers/ObjectCachedFieldFactory.java rename src/com/esotericsoftware/kryo/serializers/{ObjectField.java => ReflectField.java} (85%) create mode 100644 src/com/esotericsoftware/kryo/util/DefaultInstantiatorStrategy.java diff --git a/.classpath b/.classpath index 16d46c0fc..316a03b49 100644 --- a/.classpath +++ b/.classpath @@ -3,9 +3,9 @@ + - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index bdd0264e4..de969ce6a 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -96,6 +96,9 @@ org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/build/jarjar-command-1.0.0.jar b/build/jarjar-command-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..a8e006cb7e104abecb0366553decadecdbd54bb1 GIT binary patch literal 6183 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≷Xr(DSqzj5mY5Z_#f)?d9$?UpE}QJ5V@kD^ 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 */ + * @author Nathan Sweet */ public interface ClassResolver { /** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */ public void setKryo (Kryo kryo); diff --git a/src/com/esotericsoftware/kryo/DefaultSerializer.java b/src/com/esotericsoftware/kryo/DefaultSerializer.java index 1c61be682..21b22310b 100644 --- a/src/com/esotericsoftware/kryo/DefaultSerializer.java +++ b/src/com/esotericsoftware/kryo/DefaultSerializer.java @@ -27,7 +27,7 @@ /** Sets the default serializer to use for the annotated class. The specified Serializer class must have a constructor taking a * Kryo instance and a class, a Kryo instance, a class, or no arguments. * @see Kryo#register(Class) - * @author Nathan Sweet */ + * @author Nathan Sweet */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DefaultSerializer { diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index 54c16f568..6587e7846 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -22,7 +22,6 @@ import static com.esotericsoftware.kryo.util.Util.*; import static com.esotericsoftware.minlog.Log.*; -import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; @@ -104,15 +103,15 @@ import com.esotericsoftware.kryo.serializers.OptionalSerializers; import com.esotericsoftware.kryo.serializers.TimeSerializers; import com.esotericsoftware.kryo.util.DefaultClassResolver; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; import com.esotericsoftware.kryo.util.IdentityMap; import com.esotericsoftware.kryo.util.IntArray; import com.esotericsoftware.kryo.util.MapReferenceResolver; import com.esotericsoftware.kryo.util.ObjectMap; import com.esotericsoftware.kryo.util.Util; -import com.esotericsoftware.reflectasm.ConstructorAccess; /** Maps classes to serializers so object graphs can be serialized automatically. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class Kryo { static public final byte NULL = 0; static public final byte NOT_NULL = 1; @@ -121,14 +120,14 @@ public class Kryo { static private final int NO_REF = -2; private SerializerFactory defaultSerializer = new ReflectionSerializerFactory(FieldSerializer.class); - private final ArrayList defaultSerializers = new ArrayList(33); + private final ArrayList defaultSerializers = new ArrayList(53); private final int lowPriorityDefaultSerializerCount; private final ClassResolver classResolver; private int nextRegisterID; private ClassLoader classLoader = getClass().getClassLoader(); private InstantiatorStrategy strategy = new DefaultInstantiatorStrategy(); - private boolean registrationRequired; + private boolean registrationRequired = true; private boolean warnUnregisteredClasses; private int depth, maxDepth = Integer.MAX_VALUE; @@ -224,6 +223,7 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { } // --- Default serializers --- + /** Sets the serializer factory to use when no {@link #addDefaultSerializer(Class, Class) default serializers} match an * object's type. Default is {@link ReflectionSerializerFactory} with {@link FieldSerializer}. * @see #newDefaultSerializer(Class) */ @@ -641,11 +641,11 @@ public void writeClassAndObject (Output output, Object object) { boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) { if (object == null) { if (TRACE || (DEBUG && depth == 1)) log("Write", null); - output.writeVarInt(Kryo.NULL, true); + output.writeInt(Kryo.NULL, true); return true; } if (!referenceResolver.useReferences(object.getClass())) { - if (mayBeNull) output.writeVarInt(Kryo.NOT_NULL, true); + if (mayBeNull) output.writeInt(Kryo.NOT_NULL, true); return false; } @@ -655,13 +655,13 @@ boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) { // If not the first time encountered, only write reference ID. if (id != -1) { if (DEBUG) debug("kryo", "Write object reference " + id + ": " + string(object)); - output.writeVarInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL. + output.writeInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL. return true; } // Otherwise write NOT_NULL and then the object bytes. id = referenceResolver.addWrittenObject(object); - output.writeVarInt(NOT_NULL, true); + output.writeInt(NOT_NULL, true); if (TRACE) trace("kryo", "Write initial object reference " + id + ": " + string(object)); return false; } @@ -810,7 +810,7 @@ int readReferenceOrNull (Input input, Class type, boolean mayBeNull) { boolean referencesSupported = referenceResolver.useReferences(type); int id; if (mayBeNull) { - id = input.readVarInt(true); + id = input.readInt(true); if (id == Kryo.NULL) { if (TRACE || (DEBUG && depth == 1)) log("Read", null); readObject = null; @@ -825,7 +825,7 @@ int readReferenceOrNull (Input input, Class type, boolean mayBeNull) { readReferenceIds.add(NO_REF); return readReferenceIds.size; } - id = input.readVarInt(true); + id = input.readInt(true); } if (id == NOT_NULL) { // First time object has been encountered. @@ -1015,14 +1015,18 @@ public ClassLoader getClassLoader () { return classLoader; } - /** If true, an exception is thrown when an unregistered class is encountered. Default is false. + /** If true, an exception is thrown when an unregistered class is encountered. Default is true. *

* If false, when an unregistered class is encountered, its fully qualified class name will be serialized and the * {@link #addDefaultSerializer(Class, Class) default serializer} for the class used to serialize the object. Subsequent * appearances of the class within the same object graph are serialized as an int id. *

* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the drawback - * of needing to know the classes to be serialized up front. */ + * of needing to know the classes to be serialized up front. + *

+ * Requiring class registeration controls which classes Kryo will instantiate. When false, during deserialization Kryo will + * invoke the constructor for whatever class name is found in the data. It can be a security problem to allow arbitrary classes + * to be instantiated (and later finalized). */ public void setRegistrationRequired (boolean registrationRequired) { this.registrationRequired = registrationRequired; if (TRACE) trace("kryo", "Registration required: " + registrationRequired); @@ -1032,15 +1036,13 @@ public boolean isRegistrationRequired () { return registrationRequired; } - /** If true, kryo writes a warn log telling about the classes unregistered. Default is false. - *

- * If false, no log are written when unregistered classes are encountered. */ + /** If true, kryo writes a warn log entry when an unregistered class is encountered. Default is false. */ public void setWarnUnregisteredClasses (boolean warnUnregisteredClasses) { this.warnUnregisteredClasses = warnUnregisteredClasses; if (TRACE) trace("kryo", "Warn unregistered classes: " + warnUnregisteredClasses); } - public boolean isWarnUnregisteredClasses () { + public boolean getWarnUnregisteredClasses () { return warnUnregisteredClasses; } @@ -1166,6 +1168,10 @@ protected boolean isClosure (Class type) { return type.getName().indexOf('/') >= 0; } + public GenericsResolver getGenericsResolver () { + return genericsResolver; + } + static final class DefaultSerializerEntry { final Class type; final SerializerFactory serializerFactory; @@ -1175,95 +1181,4 @@ static final class DefaultSerializerEntry { this.serializerFactory = serializerFactory; } } - - public GenericsResolver getGenericsResolver () { - return genericsResolver; - } - - static public class DefaultInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy { - private InstantiatorStrategy fallbackStrategy; - - public DefaultInstantiatorStrategy () { - } - - public DefaultInstantiatorStrategy (InstantiatorStrategy fallbackStrategy) { - this.fallbackStrategy = fallbackStrategy; - } - - public void setFallbackInstantiatorStrategy (final InstantiatorStrategy fallbackStrategy) { - this.fallbackStrategy = fallbackStrategy; - } - - public InstantiatorStrategy getFallbackInstantiatorStrategy () { - return fallbackStrategy; - } - - public ObjectInstantiator newInstantiatorOf (final Class type) { - if (!Util.isAndroid) { - // Use ReflectASM if the class is not a non-static member class. - Class enclosingType = type.getEnclosingClass(); - boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass() - && !Modifier.isStatic(type.getModifiers()); - if (!isNonStaticMemberClass) { - try { - final ConstructorAccess access = ConstructorAccess.get(type); - return new ObjectInstantiator() { - public Object newInstance () { - try { - return access.newInstance(); - } catch (Exception ex) { - throw new KryoException("Error constructing instance of class: " + className(type), ex); - } - } - }; - } catch (Exception ignored) { - } - } - } - // Reflection. - try { - Constructor ctor; - try { - ctor = type.getConstructor((Class[])null); - } catch (Exception ex) { - ctor = type.getDeclaredConstructor((Class[])null); - ctor.setAccessible(true); - } - final Constructor constructor = ctor; - return new ObjectInstantiator() { - public Object newInstance () { - try { - return constructor.newInstance(); - } catch (Exception ex) { - throw new KryoException("Error constructing instance of class: " + className(type), ex); - } - } - }; - } catch (Exception ignored) { - } - if (fallbackStrategy == null) { - if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) - throw new KryoException("Class cannot be created (non-static member class): " + className(type)); - else { - StringBuilder errorMessageSb = new StringBuilder( - "Class cannot be created (missing no-arg constructor): " + className(type)); - if (type.getSimpleName().equals("")) { - errorMessageSb - .append("\n\tThis is an anonymous class, which is not serializable by default in Kryo. Possible solutions: ") - .append("1. Remove uses of anonymous classes, including double brace initialization, from the containing ") - .append( - "class. This is the safest solution, as anonymous classes don't have predictable names for serialization.") - .append("\n\t2. Register a FieldSerializer for the containing class and call ") - .append( - "FieldSerializer#setIgnoreSyntheticFields(false) on it. This is not safe but may be sufficient temporarily. ") - .append("Use at your own risk."); - } - throw new KryoException(errorMessageSb.toString()); - } - } - // InstantiatorStrategy. - return fallbackStrategy.newInstantiatorOf(type); - } - } - } diff --git a/src/com/esotericsoftware/kryo/KryoCopyable.java b/src/com/esotericsoftware/kryo/KryoCopyable.java index 21ac0673c..e5816c525 100644 --- a/src/com/esotericsoftware/kryo/KryoCopyable.java +++ b/src/com/esotericsoftware/kryo/KryoCopyable.java @@ -22,7 +22,7 @@ /** Allows implementing classes to perform their own copying. Hand written copying can be more efficient in some cases. *

* This method is used instead of the registered serializer {@link Serializer#copy(Kryo, Object)} method. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public interface KryoCopyable { /** Returns a copy that has the same values as this object. Before Kryo can be used to copy child objects, * {@link Kryo#reference(Object)} must be called with the copy to ensure it can be referenced by the child objects. diff --git a/src/com/esotericsoftware/kryo/KryoException.java b/src/com/esotericsoftware/kryo/KryoException.java index c654c514b..00002586e 100644 --- a/src/com/esotericsoftware/kryo/KryoException.java +++ b/src/com/esotericsoftware/kryo/KryoException.java @@ -20,7 +20,7 @@ package com.esotericsoftware.kryo; /** General Kryo RuntimeException. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class KryoException extends RuntimeException { private StringBuffer trace; diff --git a/src/com/esotericsoftware/kryo/KryoSerializable.java b/src/com/esotericsoftware/kryo/KryoSerializable.java index 00cff225e..2693747b2 100644 --- a/src/com/esotericsoftware/kryo/KryoSerializable.java +++ b/src/com/esotericsoftware/kryo/KryoSerializable.java @@ -28,7 +28,7 @@ *

* The default serializer for KryoSerializable is {@link KryoSerializableSerializer}, which uses {@link Kryo#newInstance(Class)} * to construct the class. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public interface KryoSerializable { public void write (Kryo kryo, Output output); diff --git a/src/com/esotericsoftware/kryo/NotNull.java b/src/com/esotericsoftware/kryo/NotNull.java index 3c589be89..906a6a86d 100644 --- a/src/com/esotericsoftware/kryo/NotNull.java +++ b/src/com/esotericsoftware/kryo/NotNull.java @@ -28,7 +28,7 @@ /** Indicates a field can never be null when it is being serialized and deserialized. Some serializers use this to save space. Eg, * {@link FieldSerializer} may save 1 byte per field. - * @author Nathan Sweet */ + * @author Nathan Sweet */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NotNull { diff --git a/src/com/esotericsoftware/kryo/ReferenceResolver.java b/src/com/esotericsoftware/kryo/ReferenceResolver.java index 24d4c57a2..d76127bff 100644 --- a/src/com/esotericsoftware/kryo/ReferenceResolver.java +++ b/src/com/esotericsoftware/kryo/ReferenceResolver.java @@ -21,7 +21,7 @@ /** When references are enabled, this tracks objects that have already been read or written, provides an ID for objects that are * written, and looks up by ID objects that have been read. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public interface ReferenceResolver { /** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */ public void setKryo (Kryo kryo); diff --git a/src/com/esotericsoftware/kryo/Registration.java b/src/com/esotericsoftware/kryo/Registration.java index ff6e28378..ae5a36981 100644 --- a/src/com/esotericsoftware/kryo/Registration.java +++ b/src/com/esotericsoftware/kryo/Registration.java @@ -25,7 +25,7 @@ import org.objenesis.instantiator.ObjectInstantiator; /** Describes the {@link Serializer} and class ID to use for a class. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class Registration { private final Class type; private final int id; diff --git a/src/com/esotericsoftware/kryo/Serializer.java b/src/com/esotericsoftware/kryo/Serializer.java index a07feda7d..dd1084c67 100644 --- a/src/com/esotericsoftware/kryo/Serializer.java +++ b/src/com/esotericsoftware/kryo/Serializer.java @@ -23,7 +23,7 @@ import com.esotericsoftware.kryo.io.Output; /** Reads and writes objects to and from bytes. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public abstract class Serializer { private boolean acceptsNull, immutable; diff --git a/src/com/esotericsoftware/kryo/SerializerFactory.java b/src/com/esotericsoftware/kryo/SerializerFactory.java index 4aba64019..781b59ea1 100644 --- a/src/com/esotericsoftware/kryo/SerializerFactory.java +++ b/src/com/esotericsoftware/kryo/SerializerFactory.java @@ -35,7 +35,7 @@ public interface SerializerFactory { * @param kryo The serializer instance requesting the new serializer. * @param type The type of the object that is to be serialized. * @return An implementation of a serializer that is able to serialize an object of type {@code type}. */ - Serializer newSerializer (Kryo kryo, Class type); + Serializer newSerializer (Kryo kryo, Class type); /** This factory instantiates new serializers of a given class via reflection. The constructors of the given * {@code serializerClass} must either take an instance of {@link Kryo} and an instance of {@link Class} as its parameter, take @@ -49,14 +49,13 @@ public ReflectionSerializerFactory (Class serializerClass) this.serializerClass = serializerClass; } - @Override - public Serializer newSerializer (Kryo kryo, Class type) { + public Serializer newSerializer (Kryo kryo, Class type) { return newSerializer(kryo, serializerClass, type); } /** Creates a new instance of the specified serializer for serializing the specified class. Serializers must have a zero * argument constructor or one that takes (Kryo), (Class), or (Kryo, Class). */ - static public Serializer newSerializer (Kryo kryo, Class serializerClass, Class type) { + static public Serializer newSerializer (Kryo kryo, Class serializerClass, Class type) { try { try { return serializerClass.getConstructor(Kryo.class, Class.class).newInstance(kryo, type); @@ -83,14 +82,13 @@ static public Serializer newSerializer (Kryo kryo, Class s * different {@link Kryo} instances. * @author Rafael Winterhalter */ static public class SingletonSerializerFactory implements SerializerFactory { - private final Serializer serializer; + private final Serializer serializer; - public SingletonSerializerFactory (Serializer serializer) { + public SingletonSerializerFactory (Serializer serializer) { this.serializer = serializer; } - @Override - public Serializer newSerializer (Kryo kryo, Class type) { + public Serializer newSerializer (Kryo kryo, Class type) { return serializer; } } @@ -112,7 +110,7 @@ public FieldSerializerConfig getConfig () { return config; } - public Serializer newSerializer (Kryo kryo, Class type) { + public Serializer newSerializer (Kryo kryo, Class type) { return new FieldSerializer(kryo, type, null, config.clone()); } } @@ -134,7 +132,7 @@ public TaggedFieldSerializerConfig getConfig () { return config; } - public Serializer newSerializer (Kryo kryo, Class type) { + public Serializer newSerializer (Kryo kryo, Class type) { return new TaggedFieldSerializer(kryo, type, config.clone()); } } diff --git a/src/com/esotericsoftware/kryo/io/ByteBufferInput.java b/src/com/esotericsoftware/kryo/io/ByteBufferInput.java index 49dba5101..b160dda0d 100644 --- a/src/com/esotericsoftware/kryo/io/ByteBufferInput.java +++ b/src/com/esotericsoftware/kryo/io/ByteBufferInput.java @@ -40,8 +40,6 @@ public class ByteBufferInput extends Input { protected ByteBuffer niobuffer; - protected boolean varIntsEnabled = true; - /* Default byte order is BIG_ENDIAN to be compatible to the base class */ ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; @@ -354,13 +352,6 @@ public int readInt () throws KryoException { } public int readInt (boolean optimizePositive) throws KryoException { - if (varIntsEnabled) - return readVarInt(optimizePositive); - else - return readInt(); - } - - public int readVarInt (boolean optimizePositive) throws KryoException { niobuffer.position(position); if (require(1) < 5) return readInt_slow(optimizePositive); position++; @@ -705,16 +696,7 @@ public long readLong () throws KryoException { return niobuffer.getLong(); } - /** {@inheritDoc} */ public long readLong (boolean optimizePositive) throws KryoException { - if (varIntsEnabled) - return readVarLong(optimizePositive); - else - return readLong(); - } - - /** {@inheritDoc} */ - public long readVarLong (boolean optimizePositive) throws KryoException { niobuffer.position(position); if (require(1) < 9) return readLong_slow(optimizePositive); position++; @@ -930,17 +912,4 @@ public double[] readDoubles (int length) throws KryoException { private boolean isNativeOrder () { return byteOrder == nativeOrder; } - - /** Return current setting for variable length encoding of integers - * @return current setting for variable length encoding of integers */ - public boolean getVarIntsEnabled () { - return varIntsEnabled; - } - - /** Controls if a variable length encoding for integer types should be used when serializers suggest it. - * - * @param varIntsEnabled */ - public void setVarIntsEnabled (boolean varIntsEnabled) { - this.varIntsEnabled = varIntsEnabled; - } } diff --git a/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java b/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java index 067216f48..cf73174c7 100644 --- a/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java +++ b/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java @@ -24,7 +24,7 @@ import java.nio.ByteBuffer; /** An InputStream whose source is a {@link ByteBuffer}. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class ByteBufferInputStream extends InputStream { private ByteBuffer byteBuffer; diff --git a/src/com/esotericsoftware/kryo/io/ByteBufferOutput.java b/src/com/esotericsoftware/kryo/io/ByteBufferOutput.java index b91495062..d6f189ee0 100644 --- a/src/com/esotericsoftware/kryo/io/ByteBufferOutput.java +++ b/src/com/esotericsoftware/kryo/io/ByteBufferOutput.java @@ -40,8 +40,6 @@ public class ByteBufferOutput extends Output { protected ByteBuffer niobuffer; - protected boolean varIntsEnabled = true; - // Default byte order is BIG_ENDIAN to be compatible to the base class ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; @@ -180,7 +178,7 @@ protected boolean require (int required) throws KryoException { if (capacity == 0) capacity = 1; capacity = Math.min(capacity * 2, maxCapacity); if (capacity < 0) capacity = maxCapacity; - ByteBuffer newBuffer = (niobuffer != null && !niobuffer.isDirect()) ? ByteBuffer.allocate(capacity) + ByteBuffer newBuffer = niobuffer != null && !niobuffer.isDirect() ? ByteBuffer.allocate(capacity) : ByteBuffer.allocateDirect(capacity); // Copy the whole buffer niobuffer.position(0); @@ -188,7 +186,7 @@ protected boolean require (int required) throws KryoException { newBuffer.put(niobuffer); newBuffer.order(niobuffer.order()); - // writeVarInt & writeVarLong mess with the byte order. need to keep track of the current byte order when growing + // writeInt & writeLong mess with the byte order, need to keep track of the current byte order when growing. final ByteOrder currentByteOrder = byteOrder; setBuffer(newBuffer, maxCapacity); byteOrder = currentByteOrder; @@ -287,15 +285,7 @@ public void writeInt (int value) throws KryoException { position += 4; } - public int writeInt (int value, boolean optimizePositive) throws KryoException { - if (!varIntsEnabled) { - writeInt(value); - return 4; - } else - return writeVarInt(value, optimizePositive); - } - - public int writeVarInt (int val, boolean optimizePositive) throws KryoException { + public int writeInt (int val, boolean optimizePositive) throws KryoException { niobuffer.position(position); int value = val; @@ -539,7 +529,6 @@ private void writeAscii_slow (String value, int charCount) throws KryoException byte[] tmp = new byte[charCount]; value.getBytes(charIndex, charIndex + charsToWrite, tmp, 0); buffer.put(tmp, 0, charsToWrite); -// value.getBytes(charIndex, charIndex + charsToWrite, buffer, position); charIndex += charsToWrite; position += charsToWrite; charsToWrite = Math.min(charCount - charIndex, capacity); @@ -582,14 +571,6 @@ public void writeLong (long value) throws KryoException { } public int writeLong (long value, boolean optimizePositive) throws KryoException { - if (!varIntsEnabled) { - writeLong(value); - return 8; - } else - return writeVarLong(value, optimizePositive); - } - - public int writeVarLong (long value, boolean optimizePositive) throws KryoException { if (!optimizePositive) value = (value << 1) ^ (value >> 63); int varInt = 0; @@ -900,17 +881,4 @@ public void writeDoubles (double[] object) throws KryoException { private boolean isNativeOrder () { return byteOrder == nativeOrder; } - - /** Return current setting for variable length encoding of integers - * @return current setting for variable length encoding of integers */ - public boolean getVarIntsEnabled () { - return varIntsEnabled; - } - - /** Controls if a variable length encoding for integer types should be used when serializers suggest it. - * - * @param varIntsEnabled */ - public void setVarIntsEnabled (boolean varIntsEnabled) { - this.varIntsEnabled = varIntsEnabled; - } } diff --git a/src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java b/src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java index c3bf02ddb..4c4828d86 100644 --- a/src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java +++ b/src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java @@ -25,7 +25,7 @@ /** An OutputStream whose target is a {@link ByteBuffer}. If bytes would be written that would overflow the buffer, * {@link #flush()} is called. Subclasses can override flush to empty the buffer. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class ByteBufferOutputStream extends OutputStream { private ByteBuffer byteBuffer; diff --git a/src/com/esotericsoftware/kryo/io/Input.java b/src/com/esotericsoftware/kryo/io/Input.java index 4299b1a31..0009e75ac 100644 --- a/src/com/esotericsoftware/kryo/io/Input.java +++ b/src/com/esotericsoftware/kryo/io/Input.java @@ -30,7 +30,7 @@ *

* The byte[] buffer may be modified and then returned to its original state during some read operations, so the same byte[] * should not be used concurrently in separate threads. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class Input extends InputStream { protected byte[] buffer; protected int position; @@ -40,7 +40,7 @@ public class Input extends InputStream { protected char[] chars = new char[32]; protected InputStream inputStream; - /** Creates an uninitialized Input. {@link #setBuffer(byte[])} must be called before the Input is used. */ + /** Creates an uninitialized Input, {@link #setBuffer(byte[])} must be called before the Input is used. */ public Input () { } @@ -248,7 +248,7 @@ public boolean eof () { // InputStream public int available () throws IOException { - return limit - position + ((null != inputStream) ? inputStream.available() : 0); + return limit - position + (inputStream != null ? inputStream.available() : 0); } /** Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available. */ @@ -362,15 +362,8 @@ public int readInt () throws KryoException { | buffer[position + 3] & 0xFF; } - /** Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not guaranteed that - * a variable length encoding will be really used. The stream may decide to use native-sized integer representation for - * efficiency reasons. **/ + /** Reads a 1-5 byte int. */ public int readInt (boolean optimizePositive) throws KryoException { - return readVarInt(optimizePositive); - } - - /** Reads a 1-5 byte int. It is guaranteed that a varible length encoding will be used. */ - public int readVarInt (boolean optimizePositive) throws KryoException { if (require(1) < 5) return readInt_slow(optimizePositive); int b = buffer[position++]; int result = b & 0x7F; @@ -697,15 +690,8 @@ public long readLong () throws KryoException { } - /** Reads a 1-9 byte long. This stream may consider such a variable length encoding request as a hint. It is not guaranteed - * that a variable length encoding will be really used. The stream may decide to use native-sized integer representation for - * efficiency reasons. */ + /** Reads a 1-9 byte long. */ public long readLong (boolean optimizePositive) throws KryoException { - return readVarLong(optimizePositive); - } - - /** Reads a 1-9 byte long. It is guaranteed that a varible length encoding will be used. */ - public long readVarLong (boolean optimizePositive) throws KryoException { if (require(1) < 9) return readLong_slow(optimizePositive); int b = buffer[position++]; long result = b & 0x7F; diff --git a/src/com/esotericsoftware/kryo/io/InputChunked.java b/src/com/esotericsoftware/kryo/io/InputChunked.java index 517c1b54f..728d31448 100644 --- a/src/com/esotericsoftware/kryo/io/InputChunked.java +++ b/src/com/esotericsoftware/kryo/io/InputChunked.java @@ -27,16 +27,16 @@ import com.esotericsoftware.kryo.KryoException; /** An InputStream that reads lengths and chunks of data from another OutputStream, allowing chunks to be skipped. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class InputChunked extends Input { private int chunkSize = -1; - /** Creates an uninitialized InputChunked with a buffer size of 2048. The InputStream must be set before it can be used. */ + /** Creates an uninitialized InputChunked with a buffer size of 2048, the InputStream must be set before it can be used. */ public InputChunked () { super(2048); } - /** Creates an uninitialized InputChunked. The InputStream must be set before it can be used. */ + /** Creates an uninitialized InputChunked, the InputStream must be set before it can be used. */ public InputChunked (int bufferSize) { super(bufferSize); } diff --git a/src/com/esotericsoftware/kryo/io/Output.java b/src/com/esotericsoftware/kryo/io/Output.java index a5bb3d3b8..2e59ed596 100644 --- a/src/com/esotericsoftware/kryo/io/Output.java +++ b/src/com/esotericsoftware/kryo/io/Output.java @@ -27,11 +27,7 @@ /** An OutputStream that buffers data in a byte array and optionally flushes to another OutputStream. Utility methods are provided * for efficiently writing primitive types and strings. - * - * Encoding of integers: BIG_ENDIAN is used for storing fixed native size integer values LITTLE_ENDIAN is used for a variable - * length encoding of integer values - * - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class Output extends OutputStream { protected int maxCapacity; protected long total; @@ -40,7 +36,7 @@ public class Output extends OutputStream { protected byte[] buffer; protected OutputStream outputStream; - /** Creates an uninitialized Output. {@link #setBuffer(byte[], int)} must be called before the Output is used. */ + /** Creates an uninitialized Output, {@link #setBuffer(byte[], int)} must be called before the Output is used. */ public Output () { } @@ -265,21 +261,10 @@ public void writeInt (int value) throws KryoException { buffer[position++] = (byte)value; } - /** Writes a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not guaranteed - * that a variable length encoding will be really used. The stream may decide to use native-sized integer representation for - * efficiency reasons. - * + /** Writes a 1-5 byte int. * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (5 bytes). */ public int writeInt (int value, boolean optimizePositive) throws KryoException { - return writeVarInt(value, optimizePositive); - } - - /** Writes a 1-5 byte int. It is guaranteed that a varible length encoding will be used. - * - * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be - * inefficient (5 bytes). */ - public int writeVarInt (int value, boolean optimizePositive) throws KryoException { if (!optimizePositive) value = (value << 1) ^ (value >> 31); if (value >>> 7 == 0) { require(1); @@ -539,20 +524,10 @@ public void writeLong (long value) throws KryoException { buffer[position++] = (byte)value; } - /** Writes a 1-9 byte long. This stream may consider such a variable length encoding request as a hint. It is not guaranteed - * that a variable length encoding will be really used. The stream may decide to use native-sized integer representation for - * efficiency reasons. - * + /** Writes a 1-9 byte long. * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (9 bytes). */ public int writeLong (long value, boolean optimizePositive) throws KryoException { - return writeVarLong(value, optimizePositive); - } - - /** Writes a 1-9 byte long. It is guaranteed that a varible length encoding will be used. - * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be - * inefficient (9 bytes). */ - public int writeVarLong (long value, boolean optimizePositive) throws KryoException { if (!optimizePositive) value = (value << 1) ^ (value >> 63); if (value >>> 7 == 0) { require(1); @@ -666,31 +641,7 @@ public int writeDouble (double value, double precision, boolean optimizePositive return writeLong((long)(value * precision), optimizePositive); } - /** Returns the number of bytes that would be written with {@link #writeInt(int, boolean)}. */ - static public int intLength (int value, boolean optimizePositive) { - if (!optimizePositive) value = (value << 1) ^ (value >> 31); - if (value >>> 7 == 0) return 1; - if (value >>> 14 == 0) return 2; - if (value >>> 21 == 0) return 3; - if (value >>> 28 == 0) return 4; - return 5; - } - - /** Returns the number of bytes that would be written with {@link #writeLong(long, boolean)}. */ - static public int longLength (long value, boolean optimizePositive) { - if (!optimizePositive) value = (value << 1) ^ (value >> 63); - if (value >>> 7 == 0) return 1; - if (value >>> 14 == 0) return 2; - if (value >>> 21 == 0) return 3; - if (value >>> 28 == 0) return 4; - if (value >>> 35 == 0) return 5; - if (value >>> 42 == 0) return 6; - if (value >>> 49 == 0) return 7; - if (value >>> 56 == 0) return 8; - return 9; - } - - // Methods implementing bulk operations on arrays of primitive types + // Methods implementing bulk operations on arrays of primitive types: /** Bulk output of an int array. */ public void writeInts (int[] object, boolean optimizePositive) throws KryoException { @@ -739,4 +690,28 @@ public void writeDoubles (double[] object) throws KryoException { for (int i = 0, n = object.length; i < n; i++) writeDouble(object[i]); } + + /** Returns the number of bytes that would be written with {@link #writeInt(int, boolean)}. */ + static public int intLength (int value, boolean optimizePositive) { + if (!optimizePositive) value = (value << 1) ^ (value >> 31); + if (value >>> 7 == 0) return 1; + if (value >>> 14 == 0) return 2; + if (value >>> 21 == 0) return 3; + if (value >>> 28 == 0) return 4; + return 5; + } + + /** Returns the number of bytes that would be written with {@link #writeLong(long, boolean)}. */ + static public int longLength (long value, boolean optimizePositive) { + if (!optimizePositive) value = (value << 1) ^ (value >> 63); + if (value >>> 7 == 0) return 1; + if (value >>> 14 == 0) return 2; + if (value >>> 21 == 0) return 3; + if (value >>> 28 == 0) return 4; + if (value >>> 35 == 0) return 5; + if (value >>> 42 == 0) return 6; + if (value >>> 49 == 0) return 7; + if (value >>> 56 == 0) return 8; + return 9; + } } diff --git a/src/com/esotericsoftware/kryo/io/OutputChunked.java b/src/com/esotericsoftware/kryo/io/OutputChunked.java index 547e91f3e..0141393de 100644 --- a/src/com/esotericsoftware/kryo/io/OutputChunked.java +++ b/src/com/esotericsoftware/kryo/io/OutputChunked.java @@ -28,15 +28,15 @@ /** An OutputStream that buffers data in a byte array and flushes to another OutputStream, writing the length before each flush. * The length allows the chunks to be skipped when reading. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class OutputChunked extends Output { - /** Creates an uninitialized OutputChunked with a maximum chunk size of 2048. The OutputStream must be set before it can be + /** Creates an uninitialized OutputChunked with a maximum chunk size of 2048, the OutputStream must be set before it can be * used. */ public OutputChunked () { super(2048); } - /** Creates an uninitialized OutputChunked. The OutputStream must be set before it can be used. + /** Creates an uninitialized OutputChunked, the OutputStream must be set before it can be used. * @param bufferSize The maximum size of a chunk. */ public OutputChunked (int bufferSize) { super(bufferSize); diff --git a/src/com/esotericsoftware/kryo/pool/KryoPool.java b/src/com/esotericsoftware/kryo/pool/KryoPool.java index 3d38d6869..004c7aa9d 100644 --- a/src/com/esotericsoftware/kryo/pool/KryoPool.java +++ b/src/com/esotericsoftware/kryo/pool/KryoPool.java @@ -69,7 +69,7 @@ public interface KryoPool { /** Builder for a {@link KryoPool} instance, constructs a {@link KryoPoolQueueImpl} instance. */ static public class Builder { private final KryoFactory factory; - private Queue queue = new ConcurrentLinkedQueue(); + private Queue queue = new ConcurrentLinkedQueue(); private boolean softReferences; public Builder (KryoFactory factory) { @@ -101,7 +101,6 @@ public KryoPool build () { return new KryoPoolQueueImpl(factory, q); } - @Override public String toString () { return getClass().getName() + "[queue.class=" + queue.getClass() + ", softReferences=" + softReferences + "]"; } diff --git a/src/com/esotericsoftware/kryo/pool/SoftReferenceQueue.java b/src/com/esotericsoftware/kryo/pool/SoftReferenceQueue.java index b821a3ef9..d098e8509 100644 --- a/src/com/esotericsoftware/kryo/pool/SoftReferenceQueue.java +++ b/src/com/esotericsoftware/kryo/pool/SoftReferenceQueue.java @@ -32,7 +32,7 @@ class SoftReferenceQueue implements Queue { private Queue> delegate; - public SoftReferenceQueue (Queue delegate) { + public SoftReferenceQueue (Queue delegate) { this.delegate = (Queue>)delegate; } @@ -79,7 +79,6 @@ public int hashCode () { return delegate.hashCode(); } - @Override public String toString () { return getClass().getSimpleName() + super.toString(); } @@ -112,7 +111,7 @@ public boolean remove (Object o) { throw new UnsupportedOperationException(); } - public boolean containsAll (Collection c) { + public boolean containsAll (Collection c) { throw new UnsupportedOperationException(); } @@ -120,11 +119,11 @@ public boolean addAll (Collection c) { throw new UnsupportedOperationException(); } - public boolean removeAll (Collection c) { + public boolean removeAll (Collection c) { throw new UnsupportedOperationException(); } - public boolean retainAll (Collection c) { + public boolean retainAll (Collection c) { throw new UnsupportedOperationException(); } } diff --git a/src/com/esotericsoftware/kryo/serializers/AsmCachedFieldFactory.java b/src/com/esotericsoftware/kryo/serializers/AsmCachedFieldFactory.java deleted file mode 100644 index 45e9c90fc..000000000 --- a/src/com/esotericsoftware/kryo/serializers/AsmCachedFieldFactory.java +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (c) 2008-2017, Nathan Sweet - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the distribution. - * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package com.esotericsoftware.kryo.serializers; - -import java.lang.reflect.Field; - -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmBooleanField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmByteField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmCharField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmDoubleField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmFloatField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmIntField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmLongField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmObjectField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmShortField; -import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmStringField; -import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; -import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedFieldFactory; - -class AsmCachedFieldFactory implements CachedFieldFactory { - public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser) { - CachedField cachedField; - // Use ASM-based serializers - if (fieldClass.isPrimitive()) { - if (fieldClass == boolean.class) - cachedField = new AsmBooleanField(); - else if (fieldClass == byte.class) - cachedField = new AsmByteField(); - else if (fieldClass == char.class) - cachedField = new AsmCharField(); - else if (fieldClass == short.class) - cachedField = new AsmShortField(); - else if (fieldClass == int.class) - cachedField = new AsmIntField(); - else if (fieldClass == long.class) - cachedField = new AsmLongField(); - else if (fieldClass == float.class) - cachedField = new AsmFloatField(); - else if (fieldClass == double.class) - cachedField = new AsmDoubleField(); - else { - cachedField = new AsmObjectField(ser); - } - } else if (fieldClass == String.class - && (!ser.kryo.getReferences() || !ser.kryo.getReferenceResolver().useReferences(String.class))) { - cachedField = new AsmStringField(); - } else { - cachedField = new AsmObjectField(ser); - } - return cachedField; - } -} diff --git a/src/com/esotericsoftware/kryo/serializers/AsmCacheFields.java b/src/com/esotericsoftware/kryo/serializers/AsmField.java similarity index 74% rename from src/com/esotericsoftware/kryo/serializers/AsmCacheFields.java rename to src/com/esotericsoftware/kryo/serializers/AsmField.java index 15db7a04d..8f2c35615 100644 --- a/src/com/esotericsoftware/kryo/serializers/AsmCacheFields.java +++ b/src/com/esotericsoftware/kryo/serializers/AsmField.java @@ -19,6 +19,7 @@ package com.esotericsoftware.kryo.serializers; +import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; @@ -26,23 +27,46 @@ import com.esotericsoftware.reflectasm.FieldAccess; /*** Implementations of ASM-based serializers for fields. - * - * @author Nathan Sweet */ -class AsmCacheFields { + * @author Nathan Sweet */ +class AsmField extends ReflectField { + public AsmField (Kryo kryo, Class type) { + super(kryo, type); + } + + public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { + if (accessIndex == -1) throw new KryoException("Unknown access index"); + return access.get(object, accessIndex); + } + + public void setField (Object object, Object value) throws IllegalArgumentException, IllegalAccessException { + if (accessIndex == -1) throw new KryoException("Unknown access index"); + ((FieldAccess)access).set(object, accessIndex, value); + } - abstract static class AsmCachedField extends CachedField { + public void copy (Object original, Object copy) { + try { + if (accessIndex == -1) throw new KryoException("Unknown access index"); + access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); + } catch (KryoException ex) { + ex.addTrace(this + " (" + type.getName() + ")"); + throw ex; + } catch (RuntimeException runtimeEx) { + KryoException ex = new KryoException(runtimeEx); + ex.addTrace(this + " (" + type.getName() + ")"); + throw ex; + } } - final static class AsmIntField extends AsmCachedField { + final static class IntAsmField extends CachedField { public void write (Output output, Object object) { - if (varIntsEnabled) + if (varInt) output.writeInt(access.getInt(object, accessIndex), false); else output.writeInt(access.getInt(object, accessIndex)); } public void read (Input input, Object object) { - if (varIntsEnabled) + if (varInt) access.setInt(object, accessIndex, input.readInt(false)); else access.setInt(object, accessIndex, input.readInt()); @@ -53,7 +77,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmFloatField extends AsmCachedField { + final static class FloatAsmField extends CachedField { public void write (Output output, Object object) { output.writeFloat(access.getFloat(object, accessIndex)); } @@ -67,7 +91,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmShortField extends AsmCachedField { + final static class ShortAsmField extends CachedField { public void write (Output output, Object object) { output.writeShort(access.getShort(object, accessIndex)); } @@ -81,7 +105,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmByteField extends AsmCachedField { + final static class ByteAsmField extends CachedField { public void write (Output output, Object object) { output.writeByte(access.getByte(object, accessIndex)); } @@ -95,7 +119,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmBooleanField extends AsmCachedField { + final static class BooleanAsmField extends CachedField { public void write (Output output, Object object) { output.writeBoolean(access.getBoolean(object, accessIndex)); } @@ -109,7 +133,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmCharField extends AsmCachedField { + final static class CharAsmField extends CachedField { public void write (Output output, Object object) { output.writeChar(access.getChar(object, accessIndex)); } @@ -123,16 +147,16 @@ public void copy (Object original, Object copy) { } } - final static class AsmLongField extends AsmCachedField { + final static class LongAsmField extends CachedField { public void write (Output output, Object object) { - if (varIntsEnabled) + if (varInt) output.writeLong(access.getLong(object, accessIndex), false); else output.writeLong(access.getLong(object, accessIndex)); } public void read (Input input, Object object) { - if (varIntsEnabled) + if (varInt) access.setLong(object, accessIndex, input.readLong(false)); else access.setLong(object, accessIndex, input.readLong()); @@ -143,7 +167,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmDoubleField extends AsmCachedField { + final static class DoubleAsmField extends CachedField { public void write (Output output, Object object) { output.writeDouble(access.getDouble(object, accessIndex)); } @@ -157,7 +181,7 @@ public void copy (Object original, Object copy) { } } - final static class AsmStringField extends AsmCachedField { + final static class StringAsmField extends CachedField { public void write (Output output, Object object) { output.writeString(access.getString(object, accessIndex)); } @@ -170,39 +194,4 @@ public void copy (Object original, Object copy) { access.set(copy, accessIndex, access.getString(original, accessIndex)); } } - - final static class AsmObjectField extends ObjectField { - - public AsmObjectField (FieldSerializer fieldSerializer) { - super(fieldSerializer); - } - - public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { - if (accessIndex != -1) return ((FieldAccess)access).get(object, accessIndex); - throw new KryoException("Unknown acess index"); - } - - public void setField (Object object, Object value) throws IllegalArgumentException, IllegalAccessException { - if (accessIndex != -1) - ((FieldAccess)access).set(object, accessIndex, value); - else - throw new KryoException("Unknown acess index"); - } - - public void copy (Object original, Object copy) { - try { - if (accessIndex != -1) { - access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); - } else - throw new KryoException("Unknown acess index"); - } catch (KryoException ex) { - ex.addTrace(this + " (" + type.getName() + ")"); - throw ex; - } catch (RuntimeException runtimeEx) { - KryoException ex = new KryoException(runtimeEx); - ex.addTrace(this + " (" + type.getName() + ")"); - throw ex; - } - } - } } diff --git a/src/com/esotericsoftware/kryo/serializers/BeanSerializer.java b/src/com/esotericsoftware/kryo/serializers/BeanSerializer.java index 7a2f8013e..29f498a8a 100644 --- a/src/com/esotericsoftware/kryo/serializers/BeanSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/BeanSerializer.java @@ -46,7 +46,7 @@ * primitives are final) then an extra byte is written for that property. * @see Serializer * @see Kryo#register(Class, Serializer) - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class BeanSerializer extends Serializer { static final Object[] noArgs = {}; private CachedProperty[] properties; diff --git a/src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java b/src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java index f52c7e836..99ec929c2 100644 --- a/src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java @@ -33,7 +33,7 @@ import com.esotericsoftware.kryo.io.Output; /** Encrypts data using the blowfish cipher. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class BlowfishSerializer extends Serializer { private final Serializer serializer; static private SecretKeySpec keySpec; diff --git a/src/com/esotericsoftware/kryo/serializers/CachedFields.java b/src/com/esotericsoftware/kryo/serializers/CachedFields.java new file mode 100644 index 000000000..c23bbb29d --- /dev/null +++ b/src/com/esotericsoftware/kryo/serializers/CachedFields.java @@ -0,0 +1,339 @@ + +package com.esotericsoftware.kryo.serializers; + +import static com.esotericsoftware.minlog.Log.*; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.NotNull; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.SerializerFactory.ReflectionSerializerFactory; +import com.esotericsoftware.kryo.serializers.AsmField.BooleanAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.ByteAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.CharAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.DoubleAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.FloatAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.IntAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.LongAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.ShortAsmField; +import com.esotericsoftware.kryo.serializers.AsmField.StringAsmField; +import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; +import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional; +import com.esotericsoftware.kryo.serializers.FieldSerializerGenericsUtil.Generics; +import com.esotericsoftware.kryo.serializers.ReflectField.BooleanReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.ByteReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.CharReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.DoubleReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.FloatReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.IntReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.LongReflectField; +import com.esotericsoftware.kryo.serializers.ReflectField.ShortReflectField; +import com.esotericsoftware.kryo.util.Util; +import com.esotericsoftware.reflectasm.FieldAccess; + +class CachedFields implements Comparator { + static final CachedField[] emptyCachedFields = new CachedField[0]; + + private final FieldSerializer serializer; + private final Kryo kryo; + private final Class type; + private final FieldSerializerConfig config; + + CachedField[] fields = new CachedField[0]; + CachedField[] transientFields = new CachedField[0]; + protected final ArrayList removedFields = new ArrayList(); + Object access; + + private final FieldSerializerGenericsUtil genericsUtil; + Class[] generics; + Generics genericsScope; + + public CachedFields (FieldSerializer serializer, Class[] generics) { + this.serializer = serializer; + kryo = serializer.kryo; + type = serializer.type; + config = serializer.config; + this.generics = generics; + + genericsUtil = new FieldSerializerGenericsUtil(serializer); + } + + /** Called when the list of cached fields must be rebuilt. This is done any time settings are changed that affect which fields + * will be used. It is called from the constructor for FieldSerializer. Subclasses may need to call this from their + * constructor. + * @param genericsOnly When true, the generic types from the current scope are used to set the field types. */ + public void update (boolean genericsOnly) { + if (type.isInterface()) { + fields = emptyCachedFields; // No fields to serialize. + return; + } + + if (TRACE && generics != null) trace("kryo", "Generic type parameters: " + Arrays.toString(generics)); + if (config.optimizedGenerics) { + // For generic classes, generate a mapping from type variable names to the concrete types + // This mapping is the same for the whole class. + genericsScope = genericsUtil.buildGenericsScope(type, generics); + + // Push proper scopes at serializer construction time + if (genericsScope != null) kryo.getGenericsResolver().pushScope(type, genericsScope); + } + + if (genericsOnly) { + for (CachedField cachedField : fields) + genericsUtil.updateGenericCachedField(cachedField); + for (CachedField cachedField : transientFields) + genericsUtil.updateGenericCachedField(cachedField); + } else { + ArrayList newFields = new ArrayList(), newTransientFields = new ArrayList(); + boolean asm = !Util.isAndroid && Modifier.isPublic(type.getModifiers()); + Class nextClass = type; + while (nextClass != Object.class) { + for (Field field : nextClass.getDeclaredFields()) + addField(field, asm, newFields, newTransientFields); + nextClass = nextClass.getSuperclass(); + } + + if (fields.length != newFields.size()) fields = new CachedField[newFields.size()]; + newFields.toArray(fields); + Arrays.sort(this.fields, this); + + if (transientFields.length != newTransientFields.size()) transientFields = new CachedField[newTransientFields.size()]; + newTransientFields.toArray(transientFields); + Arrays.sort(transientFields, this); + } + + serializer.initializeCachedFields(); + + if (genericsScope != null) kryo.getGenericsResolver().popScope(); + } + + private void addField (Field field, boolean asm, ArrayList fields, ArrayList transientFields) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers)) return; + if (field.isSynthetic() && config.ignoreSyntheticFields) return; + + if (!field.isAccessible()) { + if (!config.setFieldsAsAccessible) return; + try { + field.setAccessible(true); + } catch (AccessControlException ex) { + if (DEBUG) debug("kryo", "Unable to set field as accessible: " + field); + return; + } + } + + Optional optional = field.getAnnotation(Optional.class); + if (optional != null && !kryo.getContext().containsKey(optional.value())) return; + + if (removedFields.contains(field)) return; + + Class fieldClass = field.getType(); + int accessIndex = -1; + if (asm // + && !Modifier.isFinal(modifiers) // + && Modifier.isPublic(modifiers) // + && Modifier.isPublic(fieldClass.getModifiers())) { + if (access == null) { + try { + access = FieldAccess.get(type); + } catch (RuntimeException ignored) { + asm = false; + } + } + if (asm) accessIndex = ((FieldAccess)access).getIndex(field); + } + + CachedField cachedField = createCachedField(fieldClass, accessIndex != -1); + cachedField.field = field; + cachedField.name = field.getName(); + cachedField.varInt = config.varInts; + cachedField.access = (FieldAccess)access; + cachedField.accessIndex = accessIndex; + if (config.extendedFieldNames) + cachedField.name = cachedField.field.getDeclaringClass().getSimpleName() + "." + field.getName(); + else + cachedField.name = field.getName(); + + if (!config.optimizedGenerics) { + if (TRACE) trace("kryo", "Field " + field.getName() + ": " + fieldClass); + } else + fieldClass = genericsUtil.updateGenericCachedField(cachedField); + + cachedField.canBeNull = config.fieldsCanBeNull && !fieldClass.isPrimitive() && !field.isAnnotationPresent(NotNull.class); + + // Always use the same serializer for this field if the field's class is final. + if (kryo.isFinal(fieldClass) || config.fixedFieldTypes) cachedField.valueClass = fieldClass; + + applyAnnotations(cachedField); + + if (Modifier.isTransient(modifiers)) + transientFields.add(cachedField); + else + fields.add(cachedField); + } + + private CachedField createCachedField (Class fieldClass, boolean asm) { + if (asm) { + if (fieldClass.isPrimitive()) { + if (fieldClass == boolean.class) return new BooleanAsmField(); + if (fieldClass == byte.class) return new ByteAsmField(); + if (fieldClass == char.class) return new CharAsmField(); + if (fieldClass == short.class) return new ShortAsmField(); + if (fieldClass == int.class) return new IntAsmField(); + if (fieldClass == long.class) return new LongAsmField(); + if (fieldClass == float.class) return new FloatAsmField(); + if (fieldClass == double.class) return new DoubleAsmField(); + } + if (fieldClass == String.class && (!kryo.getReferences() || !kryo.getReferenceResolver().useReferences(String.class))) + return new StringAsmField(); + return new AsmField(kryo, type); + } + if (fieldClass.isPrimitive()) { + if (fieldClass == boolean.class) return new BooleanReflectField(kryo, type); + if (fieldClass == byte.class) return new ByteReflectField(kryo, type); + if (fieldClass == char.class) return new CharReflectField(kryo, type); + if (fieldClass == short.class) return new ShortReflectField(kryo, type); + if (fieldClass == int.class) return new IntReflectField(kryo, type); + if (fieldClass == long.class) return new LongReflectField(kryo, type); + if (fieldClass == float.class) return new FloatReflectField(kryo, type); + if (fieldClass == double.class) return new DoubleReflectField(kryo, type); + } + return new ReflectField(kryo, type); + } + + public int compare (CachedField o1, CachedField o2) { + // Fields are sorted by name so the order of the data is known. + return o1.name.compareTo(o2.name); + } + + /** Removes a field so that it won't be serialized. */ + public void removeField (String fieldName) { + for (int i = 0; i < fields.length; i++) { + CachedField cachedField = fields[i]; + if (cachedField.name.equals(fieldName)) { + CachedField[] newFields = new CachedField[fields.length - 1]; + System.arraycopy(fields, 0, newFields, 0, i); + System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); + fields = newFields; + removedFields.add(cachedField.field); + return; + } + } + + for (int i = 0; i < transientFields.length; i++) { + CachedField cachedField = transientFields[i]; + if (cachedField.name.equals(fieldName)) { + CachedField[] newFields = new CachedField[transientFields.length - 1]; + System.arraycopy(transientFields, 0, newFields, 0, i); + System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); + transientFields = newFields; + removedFields.add(cachedField.field); + return; + } + } + throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); + } + + /** Removes a field so that it won't be serialized. */ + public void removeField (CachedField removeField) { + for (int i = 0; i < fields.length; i++) { + CachedField cachedField = fields[i]; + if (cachedField == removeField) { + CachedField[] newFields = new CachedField[fields.length - 1]; + System.arraycopy(fields, 0, newFields, 0, i); + System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); + fields = newFields; + removedFields.add(cachedField.field); + return; + } + } + + for (int i = 0; i < transientFields.length; i++) { + CachedField cachedField = transientFields[i]; + if (cachedField == removeField) { + CachedField[] newFields = new CachedField[transientFields.length - 1]; + System.arraycopy(transientFields, 0, newFields, 0, i); + System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); + transientFields = newFields; + removedFields.add(cachedField.field); + return; + } + } + throw new IllegalArgumentException("Field \"" + removeField + "\" not found on class: " + type.getName()); + } + + /** Sets serializers using annotations. + * @see FieldSerializer.Bind + * @see CollectionSerializer.BindCollection + * @see MapSerializer.BindMap */ + private void applyAnnotations (CachedField cachedField) { + Field field = cachedField.field; + + // Set a specific serializer for a particular field. + if (field.isAnnotationPresent(FieldSerializer.Bind.class)) { + Class serializerClass = field.getAnnotation(FieldSerializer.Bind.class).value(); + cachedField.setSerializer(ReflectionSerializerFactory.newSerializer(kryo, serializerClass, field.getClass())); + } + + // Set a specific collection serializer for a particular field + if (field.isAnnotationPresent(CollectionSerializer.BindCollection.class)) { + if (cachedField.serializer != null) { + throw new RuntimeException("CollectionSerialier.Bind cannot be used with field " + field.getDeclaringClass().getName() + + "." + field.getName() + ", because it has a serializer already."); + } + if (Collection.class.isAssignableFrom(field.getType())) { + CollectionSerializer.BindCollection annotation = field.getAnnotation(CollectionSerializer.BindCollection.class); + Serializer elementSerializer = newSerializer(annotation.elementSerializer(), field); + Class elementClass = annotation.elementClass(); + + CollectionSerializer serializer = new CollectionSerializer(); + serializer.setElementsCanBeNull(annotation.elementsCanBeNull()); + serializer.setElementClass(elementClass == Object.class ? null : elementClass, elementSerializer); + cachedField.setSerializer(serializer); + } else { + throw new RuntimeException( + "CollectionSerialier.Bind should be used only with fields implementing java.util.Collection, but field " + + field.getDeclaringClass().getName() + "." + field.getName() + " does not implement it."); + } + } + + // Set a specific map serializer for a particular field + if (field.isAnnotationPresent(MapSerializer.BindMap.class)) { + if (cachedField.serializer != null) { + throw new RuntimeException("MapSerialier.Bind cannot be used with field " + field.getDeclaringClass().getName() + "." + + field.getName() + ", it has a serializer already."); + } + if (Map.class.isAssignableFrom(field.getType())) { + MapSerializer.BindMap annotation = field.getAnnotation(MapSerializer.BindMap.class); + Serializer valueSerializer = newSerializer(annotation.valueSerializer(), field); + Serializer keySerializer = newSerializer(annotation.keySerializer(), field); + + Class keyClass = annotation.keyClass(); + Class valueClass = annotation.valueClass(); + + MapSerializer serializer = new MapSerializer(); + serializer.setKeysCanBeNull(annotation.keysCanBeNull()); + serializer.setValuesCanBeNull(annotation.valuesCanBeNull()); + serializer.setKeyClass(keyClass == Object.class ? null : keyClass, keySerializer); + serializer.setValueClass(valueClass == Object.class ? null : valueClass, valueSerializer); + cachedField.setSerializer(serializer); + } else { + throw new RuntimeException("MapSerialier.Bind should be used only with fields implementing java.util.Map, but field " + + field.getDeclaringClass().getName() + "." + field.getName() + " does not implement it."); + } + } + } + + private Serializer newSerializer (Class serializerClass, Field field) { + if (serializerClass == Serializer.class) return null; + return ReflectionSerializerFactory.newSerializer(kryo, serializerClass, field.getClass()); + } +} diff --git a/src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java b/src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java index 04a472f26..6252bf1fd 100644 --- a/src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java @@ -36,7 +36,7 @@ * With the default constructor, a collection requires a 1-3 byte header and an extra 2-3 bytes is written for each element in the * collection. The alternate constructor can be used to improve efficiency to match that of using an array instead of a * collection. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class CollectionSerializer extends Serializer { private boolean elementsCanBeNull = true; private Serializer serializer; @@ -81,7 +81,7 @@ public void setGenerics (Kryo kryo, Class[] generics) { public void write (Kryo kryo, Output output, Collection collection) { int length = collection.size(); - output.writeVarInt(length, true); + output.writeInt(length, true); Serializer serializer = this.serializer; if (genericType != null) { if (serializer == null) serializer = kryo.getSerializer(genericType); @@ -110,7 +110,7 @@ protected Collection create (Kryo kryo, Input input, Class type) { public Collection read (Kryo kryo, Input input, Class type) { Collection collection = create(kryo, input, type); kryo.reference(collection); - int length = input.readVarInt(true); + int length = input.readInt(true); if (collection instanceof ArrayList) ((ArrayList)collection).ensureCapacity(length); Class elementClass = this.elementClass; Serializer serializer = this.serializer; @@ -160,7 +160,7 @@ public Collection copy (Kryo kryo, Collection original) { /** Class used for elements * @return the class used for elements */ - Class elementClass() default Object.class; + Class elementClass() default Object.class; /** Indicates if elements can be null * @return true, if elements can be null */ diff --git a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java index 1d9be6909..d2e8e895d 100644 --- a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java @@ -27,6 +27,7 @@ import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.io.OutputChunked; import com.esotericsoftware.kryo.util.ObjectMap; +import com.esotericsoftware.kryo.util.Util; /** Serializes objects using direct field assignment, providing both forward and backward compatibility. This means fields can be * added or removed without invalidating previously serialized bytes. Changing the type of a field is not supported. Like @@ -40,8 +41,8 @@ *

- * Important: This setting changes the serialized representation, so that data can be deserialized only with - * if this setting is the same as it was for serialization. - *

- * @param setOptimizedGenerics If true, the serialization of generics will be optimize for smaller size (default: false) */ - public void setOptimizedGenerics (boolean setOptimizedGenerics) { - config.setOptimizedGenerics(setOptimizedGenerics); - rebuildCachedFields(); - } - /** This method can be called for different fields having the same type. Even though the raw type is the same, if the type is * generic, it could happen that different concrete classes are used to instantiate it. Therefore, in case of different * instantiation parameters, the fields analysis should be repeated. @@ -419,69 +115,47 @@ public void setOptimizedGenerics (boolean setOptimizedGenerics) { public void write (Kryo kryo, Output output, T object) { if (TRACE) trace("kryo", "FieldSerializer.write fields of class: " + object.getClass().getName()); - if (config.getOptimizedGenerics()) { - if (typeParameters != null && generics != null) { - // Rebuild fields info. It may result in rebuilding the genericScope - rebuildCachedFields(); - } - - if (genericsScope != null) { - // Push proper scopes at serializer usage time - kryo.getGenericsResolver().pushScope(type, genericsScope); - } + if (config.optimizedGenerics) { + // Rebuild cached fields, may result in rebuilding the genericScope. + if (typeParameters != null && cachedFields.generics != null) cachedFields.update(false); + if (cachedFields.genericsScope != null) kryo.getGenericsResolver().pushScope(type, cachedFields.genericsScope); } - CachedField[] fields = this.fields; + CachedField[] fields = cachedFields.fields; for (int i = 0, n = fields.length; i < n; i++) fields[i].write(output, object); - // Serialize transient fields - if (config.getSerializeTransient()) { - for (int i = 0, n = transientFields.length; i < n; i++) - transientFields[i].write(output, object); + if (config.serializeTransient) { + for (int i = 0, n = cachedFields.transientFields.length; i < n; i++) + cachedFields.transientFields[i].write(output, object); } - if (config.getOptimizedGenerics() && genericsScope != null) { - // Pop the scope for generics - kryo.getGenericsResolver().popScope(); - } + if (config.optimizedGenerics && cachedFields.genericsScope != null) kryo.getGenericsResolver().popScope(); } public T read (Kryo kryo, Input input, Class type) { - try { - - if (config.getOptimizedGenerics()) { - if (typeParameters != null && generics != null) { - // Rebuild fields info. It may result in rebuilding the - // genericScope - rebuildCachedFields(); - } - - if (genericsScope != null) { - // Push a new scope for generics - kryo.getGenericsResolver().pushScope(type, genericsScope); - } - } - - T object = create(kryo, input, type); - kryo.reference(object); - - CachedField[] fields = this.fields; - for (int i = 0, n = fields.length; i < n; i++) - fields[i].read(input, object); - - // De-serialize transient fields - if (config.getSerializeTransient()) { - for (int i = 0, n = transientFields.length; i < n; i++) - transientFields[i].read(input, object); - } - return object; - } finally { - if (config.getOptimizedGenerics() && genericsScope != null && kryo.getGenericsResolver() != null) { - // Pop the scope for generics - kryo.getGenericsResolver().popScope(); - } + if (config.optimizedGenerics) { + // Rebuild cached fields, may result in rebuilding the genericScope. + if (typeParameters != null && cachedFields.generics != null) cachedFields.update(false); + if (cachedFields.genericsScope != null) kryo.getGenericsResolver().pushScope(type, cachedFields.genericsScope); } + + T object = create(kryo, input, type); + kryo.reference(object); + + CachedField[] fields = cachedFields.fields; + for (int i = 0, n = fields.length; i < n; i++) + fields[i].read(input, object); + + if (config.serializeTransient) { + for (int i = 0, n = cachedFields.transientFields.length; i < n; i++) + cachedFields.transientFields[i].read(input, object); + } + + if (config.optimizedGenerics && cachedFields.genericsScope != null && kryo.getGenericsResolver() != null) + kryo.getGenericsResolver().popScope(); + + return object; } /** Used by {@link #read(Kryo, Input, Class)} to create the new object. This can be overridden to customize object creation, eg @@ -492,81 +166,29 @@ protected T create (Kryo kryo, Input input, Class type) { /** Allows specific fields to be optimized. */ public CachedField getField (String fieldName) { - for (CachedField cachedField : fields) - if (getCachedFieldName(cachedField).equals(fieldName)) return cachedField; + for (CachedField cachedField : cachedFields.fields) + if (cachedField.name.equals(fieldName)) return cachedField; throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); } - protected String getCachedFieldName (CachedField cachedField) { - return config.getCachedFieldNameStrategy().getName(cachedField); - } - - /** Removes a field so that it won't be serialized. */ public void removeField (String fieldName) { - for (int i = 0; i < fields.length; i++) { - CachedField cachedField = fields[i]; - if (getCachedFieldName(cachedField).equals(fieldName)) { - CachedField[] newFields = new CachedField[fields.length - 1]; - System.arraycopy(fields, 0, newFields, 0, i); - System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); - fields = newFields; - removedFields.add(cachedField); - return; - } - } - - for (int i = 0; i < transientFields.length; i++) { - CachedField cachedField = transientFields[i]; - if (getCachedFieldName(cachedField).equals(fieldName)) { - CachedField[] newFields = new CachedField[transientFields.length - 1]; - System.arraycopy(transientFields, 0, newFields, 0, i); - System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); - transientFields = newFields; - removedFields.add(cachedField); - return; - } - } - throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); + cachedFields.removeField(fieldName); } - /** Removes a field so that it won't be serialized. */ - public void removeField (CachedField removeField) { - for (int i = 0; i < fields.length; i++) { - CachedField cachedField = fields[i]; - if (cachedField == removeField) { - CachedField[] newFields = new CachedField[fields.length - 1]; - System.arraycopy(fields, 0, newFields, 0, i); - System.arraycopy(fields, i + 1, newFields, i, newFields.length - i); - fields = newFields; - removedFields.add(cachedField); - return; - } - } - - for (int i = 0; i < transientFields.length; i++) { - CachedField cachedField = transientFields[i]; - if (cachedField == removeField) { - CachedField[] newFields = new CachedField[transientFields.length - 1]; - System.arraycopy(transientFields, 0, newFields, 0, i); - System.arraycopy(transientFields, i + 1, newFields, i, newFields.length - i); - transientFields = newFields; - removedFields.add(cachedField); - return; - } - } - throw new IllegalArgumentException("Field \"" + removeField + "\" not found on class: " + type.getName()); + public void removeField (CachedField field) { + cachedFields.removeField(field); } /** Get all fields controlled by this FieldSerializer * @return all fields controlled by this FieldSerializer */ public CachedField[] getFields () { - return fields; + return cachedFields.fields; } /** Get all transient fields controlled by this FieldSerializer * @return all transient fields controlled by this FieldSerializer */ public CachedField[] getTransientFields () { - return transientFields; + return cachedFields.transientFields; } public Class getType () { @@ -577,14 +199,6 @@ public Kryo getKryo () { return kryo; } - public boolean getCopyTransient () { - return config.getCopyTransient(); - } - - public boolean getSerializeTransient () { - return config.getSerializeTransient(); - } - /** Used by {@link #copy(Kryo, Object)} to create the new object. This can be overridden to customize object creation, eg to * call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ protected T createCopy (Kryo kryo, T original) { @@ -595,35 +209,31 @@ public T copy (Kryo kryo, T original) { T copy = createCopy(kryo, original); kryo.reference(copy); - // Copy transient fields - if (config.getCopyTransient()) { - for (int i = 0, n = transientFields.length; i < n; i++) - transientFields[i].copy(original, copy); + if (config.copyTransient) { + for (int i = 0, n = cachedFields.transientFields.length; i < n; i++) + cachedFields.transientFields[i].copy(original, copy); } - for (int i = 0, n = fields.length; i < n; i++) - fields[i].copy(original, copy); + for (int i = 0, n = cachedFields.fields.length; i < n; i++) + cachedFields.fields[i].copy(original, copy); return copy; } - final Generics getGenericsScope () { - return genericsScope; - } - - public C getConfig () { + public FieldSerializerConfig getFieldSerializerConfig () { return config; } /** Controls how a field will be serialized. */ - static public abstract class CachedField { + static public abstract class CachedField { Field field; FieldAccess access; + String name; Class valueClass; Serializer serializer; boolean canBeNull; int accessIndex = -1; - boolean varIntsEnabled = true; + boolean varInt = true, optimizePositive; /** @param valueClass The concrete class of the values for this field. This saves 1-2 bytes. The serializer registered for * the specified class will be used. Only set to a non-null value if the field type in the class definition is @@ -652,12 +262,39 @@ public void setCanBeNull (boolean canBeNull) { this.canBeNull = canBeNull; } + public boolean getCanBeNull () { + return canBeNull; + } + + /** When true, variable length values are used for int and long fields. Default is true. */ + public void setVarInt (boolean varInt) { + this.varInt = varInt; + } + + public boolean getVarInt () { + return varInt; + } + + /** When true, variable length int and long values are written with fewer bytes when the values are positive. Default is + * false. */ + public void setOptimizePositive (boolean optimizePositive) { + this.optimizePositive = optimizePositive; + } + + public boolean getOptimizePositive () { + return optimizePositive; + } + + public String getName () { + return name; + } + public Field getField () { return field; } public String toString () { - return field.getName(); + return name; } abstract public void write (Output output, Object object); @@ -667,33 +304,11 @@ public String toString () { abstract public void copy (Object original, Object copy); } - static public interface CachedFieldFactory { - public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser); - } - - public interface CachedFieldNameStrategy { - CachedFieldNameStrategy DEFAULT = new CachedFieldNameStrategy() { - @Override - public String getName (CachedField cachedField) { - return cachedField.field.getName(); - } - }; - - CachedFieldNameStrategy EXTENDED = new CachedFieldNameStrategy() { - @Override - public String getName (CachedField cachedField) { - return cachedField.field.getDeclaringClass().getSimpleName() + "." + cachedField.field.getName(); - } - }; - - String getName (CachedField cachedField); - } - /** Indicates a field should be ignored when its declaring class is registered unless the {@link Kryo#getContext() context} has * a value set for the specified key. This can be useful when a field must be serialized for one purpose, but not for another. * Eg, a class for a networked application could have a field that should not be serialized and sent to clients, but should be * serialized when stored on the server. - * @author Nathan Sweet */ + * @author Nathan Sweet */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) static public @interface Optional { @@ -704,8 +319,7 @@ public String getName (CachedField cachedField) { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Bind { - /** Value. - * @return the class used for this field */ + /** The serializer class to use for this field. */ Class value(); } } diff --git a/src/com/esotericsoftware/kryo/serializers/FieldSerializerAnnotationsUtil.java b/src/com/esotericsoftware/kryo/serializers/FieldSerializerAnnotationsUtil.java deleted file mode 100644 index c5abb778b..000000000 --- a/src/com/esotericsoftware/kryo/serializers/FieldSerializerAnnotationsUtil.java +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright (c) 2008-2017, Nathan Sweet - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the distribution. - * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package com.esotericsoftware.kryo.serializers; - -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Map; - -import com.esotericsoftware.kryo.Serializer; -import com.esotericsoftware.kryo.SerializerFactory.ReflectionSerializerFactory; -import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; - -/** A few utility methods for processing field annotations. - * - * @author Roman Levenstein */ -final class FieldSerializerAnnotationsUtil { - public FieldSerializerAnnotationsUtil (FieldSerializer serializer) { - } - - /** Process annotated fields and set serializers according to the provided annotation. - * - * @see FieldSerializer.Bind - * @see CollectionSerializer.BindCollection - * @see MapSerializer.BindMap */ - public void processAnnotatedFields (FieldSerializer fieldSerializer) { - CachedField[] fields = fieldSerializer.getFields(); - for (int i = 0, n = fields.length; i < n; i++) { - Field field = fields[i].getField(); - - // Set a specific serializer for a particular field - if (field.isAnnotationPresent(FieldSerializer.Bind.class)) { - Class serializerClass = field.getAnnotation(FieldSerializer.Bind.class).value(); - Serializer s = ReflectionSerializerFactory.newSerializer(fieldSerializer.getKryo(), serializerClass, - field.getClass()); - fields[i].setSerializer(s); - } - - if (field.isAnnotationPresent(CollectionSerializer.BindCollection.class) - && field.isAnnotationPresent(MapSerializer.BindMap.class)) { - - } - // Set a specific collection serializer for a particular field - if (field.isAnnotationPresent(CollectionSerializer.BindCollection.class)) { - if (fields[i].serializer != null) throw new RuntimeException( - "CollectionSerialier.Bind cannot be used with field " + fields[i].getField().getDeclaringClass().getName() + "." - + fields[i].getField().getName() + ", because it has a serializer already."); - CollectionSerializer.BindCollection annotation = field.getAnnotation(CollectionSerializer.BindCollection.class); - if (Collection.class.isAssignableFrom(fields[i].field.getType())) { - Class elementSerializerClass = annotation.elementSerializer(); - if (elementSerializerClass == Serializer.class) elementSerializerClass = null; - Serializer elementSerializer = (elementSerializerClass == null) ? null - : ReflectionSerializerFactory.newSerializer(fieldSerializer.getKryo(), elementSerializerClass, - field.getClass()); - boolean elementsCanBeNull = annotation.elementsCanBeNull(); - Class elementClass = annotation.elementClass(); - if (elementClass == Object.class) elementClass = null; - CollectionSerializer serializer = new CollectionSerializer(); - serializer.setElementsCanBeNull(elementsCanBeNull); - serializer.setElementClass(elementClass, elementSerializer); - fields[i].setSerializer(serializer); - } else { - throw new RuntimeException( - "CollectionSerialier.Bind should be used only with fields implementing java.util.Collection, but field " - + fields[i].getField().getDeclaringClass().getName() + "." + fields[i].getField().getName() - + " does not implement it."); - } - } - - // Set a specific map serializer for a particular field - if (field.isAnnotationPresent(MapSerializer.BindMap.class)) { - if (fields[i].serializer != null) throw new RuntimeException( - "MapSerialier.Bind cannot be used with field " + fields[i].getField().getDeclaringClass().getName() + "." - + fields[i].getField().getName() + ", because it has a serializer already."); - MapSerializer.BindMap annotation = field.getAnnotation(MapSerializer.BindMap.class); - if (Map.class.isAssignableFrom(fields[i].field.getType())) { - Class valueSerializerClass = annotation.valueSerializer(); - Class keySerializerClass = annotation.keySerializer(); - - if (valueSerializerClass == Serializer.class) valueSerializerClass = null; - if (keySerializerClass == Serializer.class) keySerializerClass = null; - - Serializer valueSerializer = (valueSerializerClass == null) ? null - : ReflectionSerializerFactory.newSerializer(fieldSerializer.getKryo(), valueSerializerClass, field.getClass()); - Serializer keySerializer = (keySerializerClass == null) ? null - : ReflectionSerializerFactory.newSerializer(fieldSerializer.getKryo(), keySerializerClass, field.getClass()); - boolean valuesCanBeNull = annotation.valuesCanBeNull(); - boolean keysCanBeNull = annotation.keysCanBeNull(); - Class keyClass = annotation.keyClass(); - Class valueClass = annotation.valueClass(); - - if (keyClass == Object.class) keyClass = null; - if (valueClass == Object.class) valueClass = null; - - MapSerializer serializer = new MapSerializer(); - serializer.setKeysCanBeNull(keysCanBeNull); - serializer.setValuesCanBeNull(valuesCanBeNull); - serializer.setKeyClass(keyClass, keySerializer); - serializer.setValueClass(valueClass, valueSerializer); - fields[i].setSerializer(serializer); - } else { - throw new RuntimeException( - "MapSerialier.Bind should be used only with fields implementing java.util.Map, but field " - + fields[i].getField().getDeclaringClass().getName() + "." + fields[i].getField().getName() - + " does not implement it."); - } - } - - } - } - -} diff --git a/src/com/esotericsoftware/kryo/serializers/FieldSerializerConfig.java b/src/com/esotericsoftware/kryo/serializers/FieldSerializerConfig.java index f6f08bf8a..2934f050a 100644 --- a/src/com/esotericsoftware/kryo/serializers/FieldSerializerConfig.java +++ b/src/com/esotericsoftware/kryo/serializers/FieldSerializerConfig.java @@ -22,26 +22,23 @@ import static com.esotericsoftware.minlog.Log.*; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; /** Configuration for FieldSerializer instances. */ public class FieldSerializerConfig implements Cloneable { - private boolean fieldsCanBeNull = true, setFieldsAsAccessible = true; - private boolean ignoreSyntheticFields = true; - private boolean fixedFieldTypes; - /** If set, transient fields will be copied */ - private boolean copyTransient = true; - /** If set, transient fields will be serialized */ - private boolean serializeTransient = false; - /** Try to optimize handling of generics for smaller size */ - private boolean optimizedGenerics = false; - - private FieldSerializer.CachedFieldNameStrategy cachedFieldNameStrategy = FieldSerializer.CachedFieldNameStrategy.DEFAULT; - - @Override + boolean fieldsCanBeNull = true; + boolean setFieldsAsAccessible = true; + boolean ignoreSyntheticFields = true; + boolean fixedFieldTypes; + boolean copyTransient = true; + boolean serializeTransient; + boolean optimizedGenerics; + boolean varInts = true; + boolean extendedFieldNames; + public FieldSerializerConfig clone () { - // clone is ok here as we have only primitive fields try { - return (FieldSerializerConfig)super.clone(); + return (FieldSerializerConfig)super.clone(); // Clone is ok as we have only primitive fields. } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } @@ -51,7 +48,11 @@ public FieldSerializerConfig clone () { * @param fieldsCanBeNull False if none of the fields are null. Saves 0-1 byte per field. True if it is not known (default). */ public void setFieldsCanBeNull (boolean fieldsCanBeNull) { this.fieldsCanBeNull = fieldsCanBeNull; - if (TRACE) trace("kryo.FieldSerializerConfig", "setFieldsCanBeNull: " + fieldsCanBeNull); + if (TRACE) trace("kryo", "FieldSerializerConfig fieldsCanBeNull: " + fieldsCanBeNull); + } + + public boolean getFieldsCanBeNull () { + return fieldsCanBeNull; } /** Controls which fields are serialized. @@ -60,14 +61,22 @@ public void setFieldsCanBeNull (boolean fieldsCanBeNull) { * fields in the public API will be serialized. */ public void setFieldsAsAccessible (boolean setFieldsAsAccessible) { this.setFieldsAsAccessible = setFieldsAsAccessible; - if (TRACE) trace("kryo.FieldSerializerConfig", "setFieldsAsAccessible: " + setFieldsAsAccessible); + if (TRACE) trace("kryo", "FieldSerializerConfig setFieldsAsAccessible: " + setFieldsAsAccessible); + } + + public boolean getSetFieldsAsAccessible () { + return setFieldsAsAccessible; } /** Controls if synthetic fields are serialized. Default is true. * @param ignoreSyntheticFields If true, only non-synthetic fields will be serialized. */ public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) { this.ignoreSyntheticFields = ignoreSyntheticFields; - if (TRACE) trace("kryo.FieldSerializerConfig", "setIgnoreSyntheticFields: " + ignoreSyntheticFields); + if (TRACE) trace("kryo", "FieldSerializerConfig ignoreSyntheticFields: " + ignoreSyntheticFields); + } + + public boolean getIgnoreSyntheticFields () { + return ignoreSyntheticFields; } /** Sets the default value for {@link FieldSerializer.CachedField#setClass(Class)} to the field's declared type. This allows @@ -75,67 +84,63 @@ public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) { * false. */ public void setFixedFieldTypes (boolean fixedFieldTypes) { this.fixedFieldTypes = fixedFieldTypes; - if (TRACE) trace("kryo.FieldSerializerConfig", "setFixedFieldTypes: " + fixedFieldTypes); - } - - /** Controls if the serialization of generics should be optimized for smaller size. - *

- * Important: This setting changes the serialized representation, so that data can be deserialized only with - * if this setting is the same as it was for serialization. - *

- * @param setOptimizedGenerics If true, the serialization of generics will be optimize for smaller size (default: false) */ - public void setOptimizedGenerics (boolean setOptimizedGenerics) { - optimizedGenerics = setOptimizedGenerics; - if (TRACE) trace("kryo.FieldSerializerConfig", "setOptimizedGenerics: " + setOptimizedGenerics); + if (TRACE) trace("kryo", "FieldSerializerConfig fixedFieldTypes: " + fixedFieldTypes); } - /** If false, when {@link Kryo#copy(Object)} is called all transient fields that are accessible will be ignored from being - * copied. This has to be set before registering classes with kryo for it to be used by all field serializers. If transient - * fields has to be copied for specific classes then use {@link FieldSerializer#setCopyTransient(boolean)}. Default is true. */ - public void setCopyTransient (boolean setCopyTransient) { - copyTransient = setCopyTransient; + public boolean getFixedFieldTypes () { + return fixedFieldTypes; } - /** If set, transient fields will be serialized Default is false - * @param serializeTransient */ - public void setSerializeTransient (boolean serializeTransient) { - this.serializeTransient = serializeTransient; + /** If true, serialization of generic types are optimized for smaller size. Default is false. */ + public void setOptimizedGenerics (boolean optimizedGenerics) { + this.optimizedGenerics = optimizedGenerics; + if (TRACE) trace("kryo", "FieldSerializerConfig optimizedGenerics: " + optimizedGenerics); } - public boolean getFieldsCanBeNull () { - return fieldsCanBeNull; + public boolean getOptimizedGenerics () { + return optimizedGenerics; } - public boolean getSetFieldsAsAccessible () { - return setFieldsAsAccessible; + /** If false, when {@link Kryo#copy(Object)} is called all transient fields that are accessible will be ignored from being + * copied. Default is true. */ + public void setCopyTransient (boolean copyTransient) { + this.copyTransient = copyTransient; + if (TRACE) trace("kryo", "FieldSerializerConfig copyTransient: " + copyTransient); } - public boolean getIgnoreSyntheticFields () { - return ignoreSyntheticFields; + public boolean getCopyTransient () { + return copyTransient; } - public boolean getFixedFieldTypes () { - return fixedFieldTypes; + /** If set, transient fields will be serialized. Default is false. */ + public void setSerializeTransient (boolean serializeTransient) { + this.serializeTransient = serializeTransient; + if (TRACE) trace("kryo", "FieldSerializerConfig serializeTransient: " + serializeTransient); } - public boolean getOptimizedGenerics () { - return optimizedGenerics; + public boolean getSerializeTransient () { + return serializeTransient; } - public boolean getCopyTransient () { - return copyTransient; + /** When true, int fields are written as varints by default. Default is true. + * @see CachedField#setVarInt(boolean) */ + public void setVarInts (boolean varInts) { + this.varInts = varInts; + if (TRACE) trace("kryo", "FieldSerializerConfig varInts: " + varInts); } - public boolean getSerializeTransient () { - return serializeTransient; + public boolean getVarInts () { + return varInts; } - public FieldSerializer.CachedFieldNameStrategy getCachedFieldNameStrategy () { - return cachedFieldNameStrategy; + /** When true, field names are prefixed by their declaring class. This can avoid conflicts when a subclass has a field with the + * same name as a super class. */ + public void setExtendedFieldNames (boolean extendedFieldNames) { + this.extendedFieldNames = extendedFieldNames; + if (TRACE) trace("kryo", "FieldSerializerConfig extendedFieldNames: " + extendedFieldNames); } - public void setCachedFieldNameStrategy (FieldSerializer.CachedFieldNameStrategy cachedFieldNameStrategy) { - this.cachedFieldNameStrategy = cachedFieldNameStrategy; - if (TRACE) trace("kryo.FieldSerializerConfig", "CachedFieldNameStrategy: " + cachedFieldNameStrategy); + public boolean getExtendedFieldNames () { + return extendedFieldNames; } } diff --git a/src/com/esotericsoftware/kryo/serializers/FieldSerializerGenericsUtil.java b/src/com/esotericsoftware/kryo/serializers/FieldSerializerGenericsUtil.java index d9334b7b3..e8c9f711d 100644 --- a/src/com/esotericsoftware/kryo/serializers/FieldSerializerGenericsUtil.java +++ b/src/com/esotericsoftware/kryo/serializers/FieldSerializerGenericsUtil.java @@ -35,8 +35,7 @@ import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; -/** A few utility methods for using generic type parameters, mostly by FieldSerializer - * +/** Utility methods used by FieldSerializer for generic type parameters. * @author Roman Levenstein */ final class FieldSerializerGenericsUtil { private Kryo kryo; @@ -49,42 +48,41 @@ public FieldSerializerGenericsUtil (FieldSerializer serializer) { /*** Create a mapping from type variable names (which are declared as type parameters of a generic class) to the concrete * classes used for type instantiation. - * * @param clazz class with generic type arguments * @param generics concrete types used to instantiate the class * @return new scope for type parameters */ Generics buildGenericsScope (Class clazz, Class[] generics) { - Class typ = clazz; + Class type = clazz; TypeVariable[] typeParams = null; - while (typ != null) { - if (typ == this.serializer.type) + while (type != null) { + if (type == this.serializer.type) typeParams = this.serializer.typeParameters; else - typeParams = typ.getTypeParameters(); + typeParams = type.getTypeParameters(); if (typeParams == null || typeParams.length == 0) { - if (typ == this.serializer.type) { - typ = this.serializer.componentType; - if (typ != null) continue; + if (type == this.serializer.type) { + type = this.serializer.componentType; + if (type != null) continue; // This is not a generic type. // Check if its superclass is generic. - typ = this.serializer.type; + type = this.serializer.type; Type superclass = null; do { - superclass = typ.getGenericSuperclass(); - typ = typ.getSuperclass(); + superclass = type.getGenericSuperclass(); + type = type.getSuperclass(); } while (superclass != null && !(superclass instanceof ParameterizedType)); if (superclass == null) break; + ParameterizedType pt = (ParameterizedType)superclass; Type[] typeArgs = pt.getActualTypeArguments(); - typeParams = typ.getTypeParameters(); + typeParams = type.getTypeParameters(); generics = new Class[typeArgs.length]; - for (int i = 0; i < typeArgs.length; i++) { - generics[i] = (typeArgs[i] instanceof Class) ? (Class)typeArgs[i] : Object.class; - } + for (int i = 0; i < typeArgs.length; i++) + generics[i] = typeArgs[i] instanceof Class ? (Class)typeArgs[i] : Object.class; break; } else - typ = typ.getComponentType(); + type = type.getComponentType(); } else break; } @@ -93,30 +91,28 @@ Generics buildGenericsScope (Class clazz, Class[] generics) { Generics genScope; if (TRACE) trace("kryo", "Class " + clazz.getName() + " has generic type parameters"); int typeVarNum = 0; - Map typeVar2concreteClass; - typeVar2concreteClass = new HashMap(); + Map typeVarToConcreteClass; + typeVarToConcreteClass = new HashMap(); for (TypeVariable typeVar : typeParams) { String typeVarName = typeVar.getName(); - if (TRACE) { - trace("kryo", - "Type parameter variable: name=" + typeVarName + " type bounds=" + Arrays.toString(typeVar.getBounds())); - } + if (TRACE) trace("kryo", + "Type parameter variable: name=" + typeVarName + " type bounds=" + Arrays.toString(typeVar.getBounds())); - final Class concreteClass = getTypeVarConcreteClass(generics, typeVarNum, typeVarName); + Class concreteClass = getTypeVarConcreteClass(generics, typeVarNum, typeVarName); if (concreteClass != null) { - typeVar2concreteClass.put(typeVarName, concreteClass); + typeVarToConcreteClass.put(typeVarName, concreteClass); if (TRACE) trace("kryo", "Concrete type used for " + typeVarName + " is: " + concreteClass.getName()); } typeVarNum++; } - genScope = new Generics(typeVar2concreteClass); + genScope = new Generics(typeVarToConcreteClass); return genScope; } else return null; } - private Class getTypeVarConcreteClass (Class[] generics, int typeVarNum, String typeVarName) { + private Class getTypeVarConcreteClass (Class[] generics, int typeVarNum, String typeVarName) { if (generics != null && generics.length > typeVarNum) { // If passed concrete classes are known explicitly, use this information return generics[typeVarNum]; @@ -134,10 +130,10 @@ private Class getTypeVarConcreteClass (Class[] generics, int typeVarNum, Stri Class[] computeFieldGenerics (Type fieldGenericType, Field field, Class[] fieldClass) { Class[] fieldGenerics = null; if (fieldGenericType != null) { - if (fieldGenericType instanceof TypeVariable && serializer.getGenericsScope() != null) { + if (fieldGenericType instanceof TypeVariable && serializer.cachedFields.genericsScope != null) { TypeVariable typeVar = (TypeVariable)fieldGenericType; // Obtain information about a concrete type of a given variable from the environment - Class concreteClass = serializer.getGenericsScope().getConcreteClass(typeVar.getName()); + Class concreteClass = serializer.cachedFields.genericsScope.getConcreteClass(typeVar.getName()); if (concreteClass != null) { fieldClass[0] = concreteClass; fieldGenerics = new Class[] {fieldClass[0]}; @@ -157,8 +153,8 @@ Class[] computeFieldGenerics (Type fieldGenericType, Field field, Class[] fieldC fieldGenerics[i] = (Class)t; else if (t instanceof ParameterizedType) fieldGenerics[i] = (Class)((ParameterizedType)t).getRawType(); - else if (t instanceof TypeVariable && serializer.getGenericsScope() != null) { - fieldGenerics[i] = serializer.getGenericsScope().getConcreteClass(((TypeVariable)t).getName()); + else if (t instanceof TypeVariable && serializer.cachedFields.genericsScope != null) { + fieldGenerics[i] = serializer.cachedFields.genericsScope.getConcreteClass(((TypeVariable)t).getName()); if (fieldGenerics[i] == null) fieldGenerics[i] = Object.class; } else if (t instanceof WildcardType) fieldGenerics[i] = Object.class; @@ -167,12 +163,10 @@ else if (t instanceof GenericArrayType) { if (componentType instanceof Class) fieldGenerics[i] = Array.newInstance((Class)componentType, 0).getClass(); else if (componentType instanceof TypeVariable) { - Generics scope = serializer.getGenericsScope(); + Generics scope = serializer.cachedFields.genericsScope; if (scope != null) { Class clazz = scope.getConcreteClass(((TypeVariable)componentType).getName()); - if (clazz != null) { - fieldGenerics[i] = Array.newInstance(clazz, 0).getClass(); - } + if (clazz != null) fieldGenerics[i] = Array.newInstance(clazz, 0).getClass(); } } } else @@ -200,53 +194,53 @@ else if (componentType instanceof TypeVariable) { return fieldGenerics; } - /** Special processing for fiels of generic types */ - CachedField newCachedFieldOfGenericType (Field field, int accessIndex, Class[] fieldClass, Type fieldGenericType) { - Class[] fieldGenerics; - CachedField cachedField; - // This is a field with generic type parameters - if (TRACE) { - trace("kryo", "Field '" + field.getName() + "' of type " + fieldClass[0] + " of generic type " + fieldGenericType); - } - - if (TRACE && fieldGenericType != null) - trace("kryo", "Field generic type is of class " + fieldGenericType.getClass().getName()); - - // Get set of provided type parameters + /** Special processing for fields of generic types. */ + Class updateGenericCachedField (CachedField cachedField) { + // This is a field with generic type parameters. + Field field = cachedField.field; + Type fieldGenericType = field.getGenericType(); + Class fieldClass = field.getType(); + if (TRACE) + trace("kryo", "Field " + field.getName() + ": " + fieldClass + " <" + fieldGenericType.getClass().getName() + ">"); // Get list of field specific concrete classes passed as generic parameters Class[] cachedFieldGenerics = FieldSerializerGenericsUtil.getGenerics(fieldGenericType, kryo); - // Build a generics scope for this field - Generics scope = buildGenericsScope(fieldClass[0], cachedFieldGenerics); + Generics scope = buildGenericsScope(fieldClass, cachedFieldGenerics); // Is it a field of a generic parameter type, i.e. "T field"? - if (fieldClass[0] == Object.class && fieldGenericType instanceof TypeVariable && serializer.getGenericsScope() != null) { + if (fieldClass == Object.class && fieldGenericType instanceof TypeVariable + && serializer.cachedFields.genericsScope != null) { TypeVariable typeVar = (TypeVariable)fieldGenericType; // Obtain information about a concrete type of a given variable from the environment - Class concreteClass = serializer.getGenericsScope().getConcreteClass(typeVar.getName()); + Class concreteClass = serializer.cachedFields.genericsScope.getConcreteClass(typeVar.getName()); if (concreteClass != null) { scope = new Generics(); scope.add(typeVar.getName(), concreteClass); } } - if (TRACE) { - trace("kryo", "Generics scope of field '" + field.getName() + "' of class " + fieldGenericType + " is " + scope); - } + if (TRACE) trace("kryo", "Generics scope of field '" + cachedField + "' of class " + fieldGenericType + " is " + scope); - fieldGenerics = computeFieldGenerics(fieldGenericType, field, fieldClass); - cachedField = serializer.newMatchingCachedField(field, accessIndex, fieldClass[0], fieldGenericType, fieldGenerics); + Class[] c = {fieldClass}; // BOZO - Using an array is nasty! + Class[] fieldGenerics = computeFieldGenerics(fieldGenericType, field, c); + fieldClass = c[0]; - if (fieldGenerics != null && cachedField instanceof ObjectField) { + if (fieldGenerics != null) + ((ReflectField)cachedField).generics = fieldGenerics; + else if (fieldGenericType != null) { + ((ReflectField)cachedField).generics = cachedFieldGenerics; + if (TRACE) trace("kryo", "Field generics: " + Arrays.toString(cachedFieldGenerics)); + } + + if (fieldGenerics != null && cachedField instanceof ReflectField) { if (fieldGenerics.length > 0 && fieldGenerics[0] != null) { - // If any information about concrete types for generic arguments of current field's type - // was deriver, remember it. - ((ObjectField)cachedField).generics = fieldGenerics; + // If any information about concrete types for generic arguments of current field's type was derived, remember it. + ((ReflectField)cachedField).generics = fieldGenerics; if (TRACE) trace("kryo", "Field generics: " + Arrays.toString(fieldGenerics)); } } - return cachedField; + return fieldClass; } /** Returns the first level of classes or interfaces for a generic type. @@ -254,10 +248,8 @@ CachedField newCachedFieldOfGenericType (Field field, int accessIndex, Class[] f static public Class[] getGenerics (Type genericType, Kryo kryo) { if (genericType instanceof GenericArrayType) { Type componentType = ((GenericArrayType)genericType).getGenericComponentType(); - if (componentType instanceof Class) - return new Class[] {(Class)componentType}; - else - return getGenerics(componentType, kryo); + if (componentType instanceof Class) return new Class[] {(Class)componentType}; + return getGenerics(componentType, kryo); } if (!(genericType instanceof ParameterizedType)) return null; if (TRACE) trace("kryo", "Processing generic type " + genericType); @@ -267,21 +259,21 @@ static public Class[] getGenerics (Type genericType, Kryo kryo) { for (int i = 0, n = actualTypes.length; i < n; i++) { Type actualType = actualTypes[i]; if (TRACE) trace("kryo", "Processing actual type " + actualType + " (" + actualType.getClass().getName() + ")"); + generics[i] = Object.class; if (actualType instanceof Class) generics[i] = (Class)actualType; + else if (actualType instanceof ParameterizedType) generics[i] = (Class)((ParameterizedType)actualType).getRawType(); + else if (actualType instanceof TypeVariable) { GenericsResolver scope = kryo.getGenericsResolver(); - if (scope.isSet()) { - Class clazz = scope.getConcreteClass(((TypeVariable)actualType).getName()); - if (clazz != null) { - generics[i] = clazz; - } else - continue; - } else - continue; + if (!scope.isSet()) continue; + Class clazz = scope.getConcreteClass(((TypeVariable)actualType).getName()); + if (clazz == null) continue; + generics[i] = clazz; + } else if (actualType instanceof GenericArrayType) { Type componentType = ((GenericArrayType)actualType).getGenericComponentType(); if (componentType instanceof Class) @@ -290,14 +282,13 @@ else if (componentType instanceof TypeVariable) { GenericsResolver scope = kryo.getGenericsResolver(); if (scope.isSet()) { Class clazz = scope.getConcreteClass(((TypeVariable)componentType).getName()); - if (clazz != null) { - generics[i] = Array.newInstance(clazz, 0).getClass(); - } + if (clazz != null) generics[i] = Array.newInstance(clazz, 0).getClass(); } } else { Class[] componentGenerics = getGenerics(componentType, kryo); if (componentGenerics != null) generics[i] = componentGenerics[0]; } + } else continue; count++; @@ -305,4 +296,34 @@ else if (componentType instanceof TypeVariable) { if (count == 0) return null; return generics; } + + /** Maps type name variables to concrete classes that are used during instantiation. + * @author Roman Levenstein */ + static final class Generics { + private Map typeVarToClass; + + public Generics () { + typeVarToClass = new HashMap(); + } + + public Generics (Map mappings) { + typeVarToClass = new HashMap(mappings); + } + + public void add (String typeVar, Class clazz) { + typeVarToClass.put(typeVar, clazz); + } + + public Class getConcreteClass (String typeVar) { + return typeVarToClass.get(typeVar); + } + + public Map getMappings () { + return typeVarToClass; + } + + public String toString () { + return typeVarToClass.toString(); + } + } } diff --git a/src/com/esotericsoftware/kryo/serializers/Generics.java b/src/com/esotericsoftware/kryo/serializers/Generics.java deleted file mode 100644 index d844a8cea..000000000 --- a/src/com/esotericsoftware/kryo/serializers/Generics.java +++ /dev/null @@ -1,57 +0,0 @@ -/* Copyright (c) 2008-2017, Nathan Sweet - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the distribution. - * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package com.esotericsoftware.kryo.serializers; - -import java.util.HashMap; -import java.util.Map; - -/** INTERNAL API - * - * Helper class to map type name variables to concrete classes that are used during instantiation - * - * @author Roman Levenstein */ -final class Generics { - private Map typeVar2class; - - public Generics () { - typeVar2class = new HashMap(); - } - - public Generics (Map mappings) { - typeVar2class = new HashMap(mappings); - } - - public void add (String typeVar, Class clazz) { - typeVar2class.put(typeVar, clazz); - } - - public Class getConcreteClass (String typeVar) { - return typeVar2class.get(typeVar); - } - - public Map getMappings () { - return typeVar2class; - } - - public String toString () { - return typeVar2class.toString(); - } - -} diff --git a/src/com/esotericsoftware/kryo/serializers/GenericsResolver.java b/src/com/esotericsoftware/kryo/serializers/GenericsResolver.java index d20987486..ccff14cb0 100644 --- a/src/com/esotericsoftware/kryo/serializers/GenericsResolver.java +++ b/src/com/esotericsoftware/kryo/serializers/GenericsResolver.java @@ -23,21 +23,22 @@ import java.util.LinkedList; -/** INTERNAL API - * - * Helper class that resolves a type name variable to a concrete class using the current class serialization stack - * +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.FieldSerializerGenericsUtil.Generics; + +/** Resolves a type name variable to a concrete class using the current class serialization stack * @author Jeroen van Erp */ public final class GenericsResolver { - private LinkedList stack = new LinkedList(); + private LinkedList stack = new LinkedList(); public GenericsResolver () { } Class getConcreteClass (String typeVar) { for (Generics generics : stack) { - Class clazz = generics.getConcreteClass(typeVar); - if (clazz != null) return clazz; + Class concreteClass = generics.getConcreteClass(typeVar); + if (concreteClass != null) return concreteClass; } return null; } @@ -54,4 +55,18 @@ void pushScope (Class type, Generics scope) { void popScope () { stack.removeFirst(); } + + static public class Test { + String a; + int b; + } + + static public void main (String[] args) throws Exception { + Test test = new Test(); + test.a = "moo"; + +// Kryo kryo = new Kryo(); +// Output output = new Output(); +// kryo.writeObjectOrNull(output, object, type); + } } diff --git a/src/com/esotericsoftware/kryo/serializers/ImmutableSerializer.java b/src/com/esotericsoftware/kryo/serializers/ImmutableSerializer.java new file mode 100644 index 000000000..65d279b5b --- /dev/null +++ b/src/com/esotericsoftware/kryo/serializers/ImmutableSerializer.java @@ -0,0 +1,13 @@ + +package com.esotericsoftware.kryo.serializers; + +import com.esotericsoftware.kryo.Serializer; + +/** A serializer which has {@link #setImmutable(boolean)} set to true. This convenience class exists only to reduce the typing + * needed to define a serializer which is immutable. + * @author Nathan Sweet */ +abstract public class ImmutableSerializer extends Serializer { + { + setImmutable(true); + } +} diff --git a/src/com/esotericsoftware/kryo/serializers/JavaSerializer.java b/src/com/esotericsoftware/kryo/serializers/JavaSerializer.java index 2ac75984d..edbb1d89d 100644 --- a/src/com/esotericsoftware/kryo/serializers/JavaSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/JavaSerializer.java @@ -38,7 +38,7 @@ * @see Serializer * @see FieldSerializer * @see KryoSerializable - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class JavaSerializer extends Serializer { public void write (Kryo kryo, Output output, Object object) { try { @@ -81,8 +81,7 @@ static private class ObjectInputStreamWithKryoClassLoader extends ObjectInputStr this.loader = kryo.getClassLoader(); } - @Override - protected Class resolveClass (ObjectStreamClass desc) { + protected Class resolveClass (ObjectStreamClass desc) { try { return Class.forName(desc.getName(), false, loader); } catch (ClassNotFoundException e) { diff --git a/src/com/esotericsoftware/kryo/serializers/MapSerializer.java b/src/com/esotericsoftware/kryo/serializers/MapSerializer.java index d2d995fe1..8429834c0 100644 --- a/src/com/esotericsoftware/kryo/serializers/MapSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/MapSerializer.java @@ -35,7 +35,7 @@ /** Serializes objects that implement the {@link Map} interface. *

* With the default constructor, a map requires a 1-3 byte header and an extra 4 bytes is written for each key/value pair. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class MapSerializer extends Serializer { private Class keyClass, valueClass; private Serializer keySerializer, valueSerializer; @@ -191,11 +191,11 @@ public Map copy (Kryo kryo, Map original) { /** Class used for keys * @return the class used for keys */ - Class keyClass() default Object.class; + Class keyClass() default Object.class; /** Class used for values * @return the class used for values */ - Class valueClass() default Object.class; + Class valueClass() default Object.class; /** Indicates if keys can be null * @return true, if keys can be null */ diff --git a/src/com/esotericsoftware/kryo/serializers/ObjectCachedFieldFactory.java b/src/com/esotericsoftware/kryo/serializers/ObjectCachedFieldFactory.java deleted file mode 100644 index c07b00b86..000000000 --- a/src/com/esotericsoftware/kryo/serializers/ObjectCachedFieldFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (c) 2008-2017, Nathan Sweet - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the distribution. - * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package com.esotericsoftware.kryo.serializers; - -import java.lang.reflect.Field; - -import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; -import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedFieldFactory; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectBooleanField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectByteField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectCharField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectDoubleField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectFloatField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectIntField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectLongField; -import com.esotericsoftware.kryo.serializers.ObjectField.ObjectShortField; - -class ObjectCachedFieldFactory implements CachedFieldFactory { - public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser) { - CachedField cachedField; - if (fieldClass.isPrimitive()) { - if (fieldClass == boolean.class) - cachedField = new ObjectBooleanField(ser); - else if (fieldClass == byte.class) - cachedField = new ObjectByteField(ser); - else if (fieldClass == char.class) - cachedField = new ObjectCharField(ser); - else if (fieldClass == short.class) - cachedField = new ObjectShortField(ser); - else if (fieldClass == int.class) - cachedField = new ObjectIntField(ser); - else if (fieldClass == long.class) - cachedField = new ObjectLongField(ser); - else if (fieldClass == float.class) - cachedField = new ObjectFloatField(ser); - else if (fieldClass == double.class) - cachedField = new ObjectDoubleField(ser); - else { - cachedField = new ObjectField(ser); - } - } else - cachedField = new ObjectField(ser); - return cachedField; - } -} diff --git a/src/com/esotericsoftware/kryo/serializers/OptionalSerializers.java b/src/com/esotericsoftware/kryo/serializers/OptionalSerializers.java index 5872e231f..975425487 100644 --- a/src/com/esotericsoftware/kryo/serializers/OptionalSerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/OptionalSerializers.java @@ -35,31 +35,28 @@ * serializers for java >= 1.8. */ public final class OptionalSerializers { static public void addDefaultSerializers (Kryo kryo) { - if (isClassAvailable("java.util.Optional")) kryo.addDefaultSerializer(Optional.class, new OptionalSerializer()); - if (isClassAvailable("java.util.OptionalInt")) kryo.addDefaultSerializer(OptionalInt.class, new OptionalIntSerializer()); - if (isClassAvailable("java.util.OptionalLong")) kryo.addDefaultSerializer(OptionalLong.class, new OptionalLongSerializer()); + if (isClassAvailable("java.util.Optional")) kryo.addDefaultSerializer(Optional.class, OptionalSerializer.class); + if (isClassAvailable("java.util.OptionalInt")) kryo.addDefaultSerializer(OptionalInt.class, OptionalIntSerializer.class); + if (isClassAvailable("java.util.OptionalLong")) kryo.addDefaultSerializer(OptionalLong.class, OptionalLongSerializer.class); if (isClassAvailable("java.util.OptionalDouble")) - kryo.addDefaultSerializer(OptionalDouble.class, new OptionalDoubleSerializer()); + kryo.addDefaultSerializer(OptionalDouble.class, OptionalDoubleSerializer.class); } - static class OptionalSerializer extends Serializer { + static public class OptionalSerializer extends Serializer { { setAcceptsNull(false); } - @Override @SuppressWarnings("unchecked") public void write (Kryo kryo, Output output, Optional object) { Object nullable = object.isPresent() ? object.get() : null; kryo.writeClassAndObject(output, nullable); } - @Override public Optional read (Kryo kryo, Input input, Class type) { return Optional.ofNullable(kryo.readClassAndObject(input)); } - @Override public Optional copy (Kryo kryo, Optional original) { if (original.isPresent()) { return Optional.of(kryo.copy(original.get())); @@ -68,11 +65,7 @@ public Optional copy (Kryo kryo, Optional original) { } } - static class OptionalIntSerializer extends Serializer { - { - setImmutable(true); - } - + static public class OptionalIntSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, OptionalInt object) { output.writeBoolean(object.isPresent()); if (object.isPresent()) output.writeInt(object.getAsInt()); @@ -84,11 +77,7 @@ public OptionalInt read (Kryo kryo, Input input, Class type) { } } - static class OptionalLongSerializer extends Serializer { - { - setImmutable(true); - } - + static public class OptionalLongSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, OptionalLong object) { output.writeBoolean(object.isPresent()); if (object.isPresent()) output.writeLong(object.getAsLong()); @@ -100,11 +89,7 @@ public OptionalLong read (Kryo kryo, Input input, Class type) { } } - static class OptionalDoubleSerializer extends Serializer { - { - setImmutable(true); - } - + static public class OptionalDoubleSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, OptionalDouble object) { output.writeBoolean(object.isPresent()); if (object.isPresent()) output.writeDouble(object.getAsDouble()); diff --git a/src/com/esotericsoftware/kryo/serializers/ObjectField.java b/src/com/esotericsoftware/kryo/serializers/ReflectField.java similarity index 85% rename from src/com/esotericsoftware/kryo/serializers/ObjectField.java rename to src/com/esotericsoftware/kryo/serializers/ReflectField.java index 9e28283f0..4ff761896 100644 --- a/src/com/esotericsoftware/kryo/serializers/ObjectField.java +++ b/src/com/esotericsoftware/kryo/serializers/ReflectField.java @@ -28,21 +28,18 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; -import com.esotericsoftware.reflectasm.FieldAccess; /*** Defer generation of serializers until it is really required at run-time. By default, use reflection-based approach. - * @author Nathan Sweet + * @author Nathan Sweet * @author Roman Levenstein */ -class ObjectField extends CachedField { - public Class[] generics; - final FieldSerializer fieldSerializer; - final Class type; +class ReflectField extends CachedField { final Kryo kryo; + final Class type; + public Class[] generics; - ObjectField (FieldSerializer fieldSerializer) { - this.fieldSerializer = fieldSerializer; - this.kryo = fieldSerializer.kryo; - this.type = fieldSerializer.type; + ReflectField (Kryo kryo, Class type) { + this.kryo = kryo; + this.type = type; } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -55,11 +52,6 @@ public void setField (Object object, Object value) throws IllegalArgumentExcepti public void write (Output output, Object object) { try { - // if(typeVar2concreteClass != null) { - // // Push a new scope for generics - // kryo.pushGenericsScope(type, new Generics(typeVar2concreteClass)); - // } - if (TRACE) trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")" + " pos=" + output.position()); @@ -74,17 +66,15 @@ public void write (Output output, Object object) { } Registration registration = kryo.writeClass(output, value.getClass()); if (serializer == null) serializer = registration.getSerializer(); - // if (generics != null) serializer.setGenerics(kryo, generics); kryo.writeObject(output, value, serializer); } else { // The concrete type of the field is known, always use the same serializer. if (serializer == null) this.serializer = serializer = kryo.getSerializer(valueClass); - // if (generics != null) serializer.setGenerics(kryo, generics); - if (canBeNull) { + if (canBeNull) kryo.writeObjectOrNull(output, value, serializer); - } else { + else { if (value == null) { throw new KryoException( "Field value is null but canBeNull is false: " + this + " (" + object.getClass().getName() + ")"); @@ -101,9 +91,6 @@ public void write (Output output, Object object) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(this + " (" + object.getClass().getName() + ")"); throw ex; - } finally { - // if(typeVar2concreteClass != null) - // kryo.popGenericsScope(); } } @@ -120,13 +107,11 @@ public void read (Input input, Object object) { value = null; else { if (serializer == null) serializer = registration.getSerializer(); - // if (generics != null) serializer.setGenerics(kryo, generics); value = kryo.readObject(input, registration.getType(), serializer); } } else { if (serializer == null) this.serializer = serializer = kryo.getSerializer(valueClass); - // if (generics != null) serializer.setGenerics(kryo, generics); if (canBeNull) value = kryo.readObjectOrNull(input, concreteType, serializer); @@ -144,19 +129,12 @@ public void read (Input input, Object object) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(this + " (" + type.getName() + ")"); throw ex; - } finally { - // if(typeVar2concreteClass != null) - // kryo.popGenericsScope(); } } public void copy (Object original, Object copy) { try { - if (accessIndex != -1) { - FieldAccess access = (FieldAccess)fieldSerializer.access; - access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); - } else - setField(copy, kryo.copy(getField(original))); + setField(copy, kryo.copy(getField(original))); } catch (IllegalAccessException ex) { throw new KryoException("Error accessing field: " + this + " (" + type.getName() + ")", ex); } catch (KryoException ex) { @@ -169,9 +147,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectIntField extends ObjectField { - public ObjectIntField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class IntReflectField extends ReflectField { + public IntReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -180,7 +158,7 @@ public Object getField (Object object) throws IllegalArgumentException, IllegalA public void write (Output output, Object object) { try { - if (varIntsEnabled) + if (varInt) output.writeInt(field.getInt(object), false); else output.writeInt(field.getInt(object)); @@ -193,7 +171,7 @@ public void write (Output output, Object object) { public void read (Input input, Object object) { try { - if (varIntsEnabled) + if (varInt) field.setInt(object, input.readInt(false)); else field.setInt(object, input.readInt()); @@ -215,9 +193,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectFloatField extends ObjectField { - public ObjectFloatField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class FloatReflectField extends ReflectField { + public FloatReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -255,9 +233,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectShortField extends ObjectField { - public ObjectShortField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class ShortReflectField extends ReflectField { + public ShortReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -295,9 +273,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectByteField extends ObjectField { - public ObjectByteField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class ByteReflectField extends ReflectField { + public ByteReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -335,9 +313,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectBooleanField extends ObjectField { - public ObjectBooleanField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class BooleanReflectField extends ReflectField { + public BooleanReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -375,9 +353,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectCharField extends ObjectField { - public ObjectCharField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class CharReflectField extends ReflectField { + public CharReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -415,9 +393,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectLongField extends ObjectField { - public ObjectLongField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class LongReflectField extends ReflectField { + public LongReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { @@ -426,7 +404,7 @@ public Object getField (Object object) throws IllegalArgumentException, IllegalA public void write (Output output, Object object) { try { - if (varIntsEnabled) + if (varInt) output.writeLong(field.getLong(object), false); else output.writeLong(field.getLong(object)); @@ -439,7 +417,7 @@ public void write (Output output, Object object) { public void read (Input input, Object object) { try { - if (varIntsEnabled) + if (varInt) field.setLong(object, input.readLong(false)); else field.setLong(object, input.readLong()); @@ -461,9 +439,9 @@ public void copy (Object original, Object copy) { } } - final static class ObjectDoubleField extends ObjectField { - public ObjectDoubleField (FieldSerializer fieldSerializer) { - super(fieldSerializer); + final static class DoubleReflectField extends ReflectField { + public DoubleReflectField (Kryo kryo, Class type) { + super(kryo, type); } public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { diff --git a/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java index 278c079f9..188ec4c3b 100644 --- a/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java @@ -38,8 +38,8 @@ /** Serializes objects using direct field assignment for fields that have a @Tag(int) annotation. This provides * backward compatibility so new fields can be added. TaggedFieldSerializer has two advantages over {@link VersionFieldSerializer} - * : 1) fields can be renamed and 2) fields marked with the @Deprecated annotation will be ignored when reading old - * bytes and won't be written to new bytes. Deprecation effectively removes the field from serialization, though the field and + * : 1) fields can be renamed and 2) fields marked with the @Deprecated annotation will be read when reading old + * bytes but won't be written to new bytes. Deprecation effectively removes the field from serialization, though the field and * @Tag annotation must remain in the class. Deprecated fields can optionally be made private and/or renamed so they * don't clutter the class (eg, ignored, ignored2). For these reasons, TaggedFieldSerializer generally * provides more flexibility for classes to evolve. The downside is that it has a small amount of additional overhead compared to @@ -47,26 +47,33 @@ *

* Forward compatibility is optionally supported by enabling {@link TaggedFieldSerializerConfig#setSkipUnknownTags(boolean)}, * which allows it to skip reading unknown tagged fields, which are presumably new fields added in future versions of an - * application. The data is only forward compatible if the newly added fields are tagged with - * {@link TaggedFieldSerializer.Tag#annexed()} set true, which comes with the cost of chunked encoding. When annexed fields are - * encountered during the read or write process of an object, a buffer is allocated to perform the chunked encoding. + * application. The data is only forward compatible for fields that have {@link TaggedFieldSerializer.Tag#annexed()} set to true, + * which comes with the cost of chunked encoding: when annexed fields are encountered during the read or write process of an + * object, a buffer is allocated to perform the chunked encoding. *

- * Tag values must be entirely unique, even among a class and its superclass(es). An IllegalArgumentException will be thrown by - * {@link Kryo#register(Class)} (and its overloads) if duplicate Tag values are encountered. + * Tag values must be entirely unique, both within a class and all its superclasses. An IllegalArgumentException will be thrown by + * {@link Kryo#register(Class)} (and its overloads) if duplicate tag values are encountered. * @see VersionFieldSerializer - * @author Nathan Sweet */ -public class TaggedFieldSerializer extends FieldSerializer { + * @author Nathan Sweet */ +public class TaggedFieldSerializer extends FieldSerializer { + static private final Comparator tagComparator = new Comparator() { + public int compare (CachedField o1, CachedField o2) { + return o1.getField().getAnnotation(Tag.class).value() - o2.getField().getAnnotation(Tag.class).value(); + } + }; + private int[] tags; private int writeFieldCount; - private boolean[] deprecated; - private boolean[] annexed; + private boolean[] deprecated, annexed; + private final TaggedFieldSerializerConfig config; public TaggedFieldSerializer (Kryo kryo, Class type) { - super(kryo, type, null, new TaggedFieldSerializerConfig()); + this(kryo, type, new TaggedFieldSerializerConfig()); } public TaggedFieldSerializer (Kryo kryo, Class type, TaggedFieldSerializerConfig config) { super(kryo, type, null, config); + this.config = config; } protected void initializeCachedFields () { @@ -86,7 +93,7 @@ protected void initializeCachedFields () { annexed = new boolean[fields.length]; writeFieldCount = fields.length; - Arrays.sort(fields, TAGGED_VALUE_COMPARATOR); // fields are sorted to easily check for reused tag values + Arrays.sort(fields, tagComparator); // fields are sorted to easily check for reused tag values for (int i = 0, n = fields.length; i < n; i++) { Field field = fields[i].getField(); tags[i] = field.getAnnotation(Tag.class).value(); @@ -100,7 +107,7 @@ protected void initializeCachedFields () { if (field.getAnnotation(Tag.class).annexed()) annexed[i] = true; } - this.removedFields.clear(); + cachedFields.removedFields.clear(); } public void removeField (String fieldName) { @@ -115,12 +122,12 @@ public void removeField (CachedField field) { public void write (Kryo kryo, Output output, T object) { CachedField[] fields = getFields(); - output.writeVarInt(writeFieldCount, true); // Can be used for null. + output.writeInt(writeFieldCount, true); // Can be used for null. OutputChunked outputChunked = null; // only instantiate if needed for (int i = 0, n = fields.length; i < n; i++) { if (deprecated[i]) continue; - output.writeVarInt(tags[i], true); + output.writeInt(tags[i], true); if (annexed[i]) { if (outputChunked == null) outputChunked = new OutputChunked(output, 1024); fields[i].write(outputChunked, object); @@ -134,12 +141,12 @@ public void write (Kryo kryo, Output output, T object) { public T read (Kryo kryo, Input input, Class type) { T object = create(kryo, input, type); kryo.reference(object); - int fieldCount = input.readVarInt(true); + int fieldCount = input.readInt(true); int[] tags = this.tags; InputChunked inputChunked = null; // only instantiate if needed CachedField[] fields = getFields(); for (int i = 0, n = fieldCount; i < n; i++) { - int tag = input.readVarInt(true); + int tag = input.readInt(true); CachedField cachedField = null; boolean isAnnexed = false; @@ -169,11 +176,9 @@ public T read (Kryo kryo, Input input, Class type) { return object; } - static private final Comparator TAGGED_VALUE_COMPARATOR = new Comparator() { - public int compare (CachedField o1, CachedField o2) { - return o1.getField().getAnnotation(Tag.class).value() - o2.getField().getAnnotation(Tag.class).value(); - } - }; + public TaggedFieldSerializerConfig getTaggedFieldSerializerConfig () { + return config; + } /** Marks a field for serialization. */ @Retention(RetentionPolicy.RUNTIME) @@ -181,8 +186,8 @@ public int compare (CachedField o1, CachedField o2) { public @interface Tag { int value(); - /** If true, the field is serialized with chunked encoding and is forward compatible, meaning safe to read in iterations of - * the class without it if {@link TaggedFieldSerializerConfig#getSkipUnknownTags()}. */ + /** If true, the field is serialized with chunked encoding and is forward compatible, meaning safe to read in versions of + * the class without the field if {@link TaggedFieldSerializerConfig#getSkipUnknownTags()} is true. */ boolean annexed() default false; } } diff --git a/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializerConfig.java b/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializerConfig.java index a78bff5d6..47dda40d4 100644 --- a/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializerConfig.java +++ b/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializerConfig.java @@ -25,13 +25,15 @@ public class TaggedFieldSerializerConfig extends FieldSerializerConfig { boolean skipUnknownTags; + public TaggedFieldSerializerConfig clone () { + return (TaggedFieldSerializerConfig)super.clone(); // Clone is ok as we have only primitive fields. + } + /** Set whether associated TaggedFieldSerializers should attempt to skip reading the data of unknown tags, rather than throwing * a KryoException. Data can be skipped if it was tagged with {@link TaggedFieldSerializer.Tag#annexed()} set true. This * enables forward compatibility. *

* This setting is false by default. - *

- * * @param skipUnknownTags If true, unknown field tags will be skipped, with the assumption that they are future tagged values * with {@link TaggedFieldSerializer.Tag#annexed()} set true. If false KryoException will be thrown whenever unknown * tags are encountered. */ @@ -47,8 +49,4 @@ public boolean getSkipUnknownTags () { return skipUnknownTags; } - @Override - public TaggedFieldSerializerConfig clone () { - return (TaggedFieldSerializerConfig)super.clone(); - } } diff --git a/src/com/esotericsoftware/kryo/serializers/TimeSerializers.java b/src/com/esotericsoftware/kryo/serializers/TimeSerializers.java index 7c6f27b03..cf8ba11ab 100644 --- a/src/com/esotericsoftware/kryo/serializers/TimeSerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/TimeSerializers.java @@ -49,30 +49,26 @@ * Implementation note: All serialization is inspired by oracles java.time.Ser. */ public final class TimeSerializers { static public void addDefaultSerializers (Kryo kryo) { - if (isClassAvailable("java.time.Duration")) kryo.addDefaultSerializer(Duration.class, new DurationSerializer()); - if (isClassAvailable("java.time.Instant")) kryo.addDefaultSerializer(Instant.class, new InstantSerializer()); - if (isClassAvailable("java.time.LocalDate")) kryo.addDefaultSerializer(LocalDate.class, new LocalDateSerializer()); - if (isClassAvailable("java.time.LocalTime")) kryo.addDefaultSerializer(LocalTime.class, new LocalTimeSerializer()); + if (isClassAvailable("java.time.Duration")) kryo.addDefaultSerializer(Duration.class, DurationSerializer.class); + if (isClassAvailable("java.time.Instant")) kryo.addDefaultSerializer(Instant.class, InstantSerializer.class); + if (isClassAvailable("java.time.LocalDate")) kryo.addDefaultSerializer(LocalDate.class, LocalDateSerializer.class); + if (isClassAvailable("java.time.LocalTime")) kryo.addDefaultSerializer(LocalTime.class, LocalTimeSerializer.class); if (isClassAvailable("java.time.LocalDateTime")) - kryo.addDefaultSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); - if (isClassAvailable("java.time.ZoneOffset")) kryo.addDefaultSerializer(ZoneOffset.class, new ZoneOffsetSerializer()); - if (isClassAvailable("java.time.ZoneId")) kryo.addDefaultSerializer(ZoneId.class, new ZoneIdSerializer()); - if (isClassAvailable("java.time.OffsetTime")) kryo.addDefaultSerializer(OffsetTime.class, new OffsetTimeSerializer()); + kryo.addDefaultSerializer(LocalDateTime.class, LocalDateTimeSerializer.class); + if (isClassAvailable("java.time.ZoneOffset")) kryo.addDefaultSerializer(ZoneOffset.class, ZoneOffsetSerializer.class); + if (isClassAvailable("java.time.ZoneId")) kryo.addDefaultSerializer(ZoneId.class, ZoneIdSerializer.class); + if (isClassAvailable("java.time.OffsetTime")) kryo.addDefaultSerializer(OffsetTime.class, OffsetTimeSerializer.class); if (isClassAvailable("java.time.OffsetDateTime")) - kryo.addDefaultSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer()); + kryo.addDefaultSerializer(OffsetDateTime.class, OffsetDateTimeSerializer.class); if (isClassAvailable("java.time.ZonedDateTime")) - kryo.addDefaultSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); - if (isClassAvailable("java.time.Year")) kryo.addDefaultSerializer(Year.class, new YearSerializer()); - if (isClassAvailable("java.time.YearMonth")) kryo.addDefaultSerializer(YearMonth.class, new YearMonthSerializer()); - if (isClassAvailable("java.time.MonthDay")) kryo.addDefaultSerializer(MonthDay.class, new MonthDaySerializer()); - if (isClassAvailable("java.time.Period")) kryo.addDefaultSerializer(Period.class, new PeriodSerializer()); + kryo.addDefaultSerializer(ZonedDateTime.class, ZonedDateTimeSerializer.class); + if (isClassAvailable("java.time.Year")) kryo.addDefaultSerializer(Year.class, YearSerializer.class); + if (isClassAvailable("java.time.YearMonth")) kryo.addDefaultSerializer(YearMonth.class, YearMonthSerializer.class); + if (isClassAvailable("java.time.MonthDay")) kryo.addDefaultSerializer(MonthDay.class, MonthDaySerializer.class); + if (isClassAvailable("java.time.Period")) kryo.addDefaultSerializer(Period.class, PeriodSerializer.class); } - static class DurationSerializer extends Serializer { - { - setImmutable(true); - } - + static public class DurationSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, Duration duration) { out.writeLong(duration.getSeconds()); out.writeInt(duration.getNano(), true); @@ -85,11 +81,7 @@ public Duration read (Kryo kryo, Input in, Class type) { } } - static class InstantSerializer extends Serializer { - { - setImmutable(true); - } - + static public class InstantSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, Instant instant) { out.writeLong(instant.getEpochSecond(), true); out.writeInt(instant.getNano(), true); @@ -102,11 +94,7 @@ public Instant read (Kryo kryo, Input in, Class type) { } } - static class LocalDateSerializer extends Serializer { - { - setImmutable(true); - } - + static public class LocalDateSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, LocalDate date) { write(out, date); } @@ -129,11 +117,7 @@ static LocalDate read (Input in) { } } - static class LocalDateTimeSerializer extends Serializer { - { - setImmutable(true); - } - + static public class LocalDateTimeSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, LocalDateTime dateTime) { LocalDateSerializer.write(out, dateTime.toLocalDate()); LocalTimeSerializer.write(out, dateTime.toLocalTime()); @@ -146,11 +130,7 @@ public LocalDateTime read (Kryo kryo, Input in, Class type) { } } - static class LocalTimeSerializer extends Serializer { - { - setImmutable(true); - } - + static public class LocalTimeSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, LocalTime time) { write(out, time); } @@ -205,11 +185,7 @@ static LocalTime read (Input in) { } } - static class ZoneOffsetSerializer extends Serializer { - { - setImmutable(true); - } - + static public class ZoneOffsetSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, ZoneOffset obj) { write(out, obj); } @@ -233,11 +209,7 @@ static ZoneOffset read (Input in) { } } - static class ZoneIdSerializer extends Serializer { - { - setImmutable(true); - } - + static public class ZoneIdSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, ZoneId obj) { write(out, obj); } @@ -256,11 +228,7 @@ static ZoneId read (Input in) { } } - static class OffsetTimeSerializer extends Serializer { - { - setImmutable(true); - } - + static public class OffsetTimeSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, OffsetTime obj) { LocalTimeSerializer.write(out, obj.toLocalTime()); ZoneOffsetSerializer.write(out, obj.getOffset()); @@ -273,11 +241,7 @@ public OffsetTime read (Kryo kryo, Input in, Class type) { } } - static class OffsetDateTimeSerializer extends Serializer { - { - setImmutable(true); - } - + static public class OffsetDateTimeSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, OffsetDateTime obj) { LocalDateSerializer.write(out, obj.toLocalDate()); LocalTimeSerializer.write(out, obj.toLocalTime()); @@ -292,11 +256,7 @@ public OffsetDateTime read (Kryo kryo, Input in, Class type) { } } - static class ZonedDateTimeSerializer extends Serializer { - { - setImmutable(true); - } - + static public class ZonedDateTimeSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, ZonedDateTime obj) { LocalDateSerializer.write(out, obj.toLocalDate()); LocalTimeSerializer.write(out, obj.toLocalTime()); @@ -311,11 +271,7 @@ public ZonedDateTime read (Kryo kryo, Input in, Class type) { } } - static class YearSerializer extends Serializer { - { - setImmutable(true); - } - + static public class YearSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, Year obj) { out.writeInt(obj.getValue(), true); } @@ -325,11 +281,7 @@ public Year read (Kryo kryo, Input in, Class type) { } } - static class YearMonthSerializer extends Serializer { - { - setImmutable(true); - } - + static public class YearMonthSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, YearMonth obj) { out.writeInt(obj.getYear(), true); out.writeByte(obj.getMonthValue()); @@ -342,11 +294,7 @@ public YearMonth read (Kryo kryo, Input in, Class type) { } } - static class MonthDaySerializer extends Serializer { - { - setImmutable(true); - } - + static public class MonthDaySerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, MonthDay obj) { out.writeByte(obj.getMonthValue()); out.writeByte(obj.getDayOfMonth()); @@ -359,11 +307,7 @@ public MonthDay read (Kryo kryo, Input in, Class type) { } } - static class PeriodSerializer extends Serializer { - { - setImmutable(true); - } - + static public class PeriodSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output out, Period obj) { out.writeInt(obj.getYears(), true); out.writeInt(obj.getMonths(), true); diff --git a/src/com/esotericsoftware/kryo/serializers/VersionFieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/VersionFieldSerializer.java index 89fd514a0..13bee0a8f 100644 --- a/src/com/esotericsoftware/kryo/serializers/VersionFieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/VersionFieldSerializer.java @@ -40,10 +40,10 @@ * overhead (a single additional varint) compared to FieldSerializer. Forward compatibility is not supported. * @see TaggedFieldSerializer * @author Tianyi HE */ -public class VersionFieldSerializer extends FieldSerializer { +public class VersionFieldSerializer extends FieldSerializer { private int typeVersion = 0; // Version of current type. private int[] fieldVersion; // Version of each field. - private boolean compatible = true; // Whether current type is compatible with serialized objects with different version. + private boolean compatible = true; // True if current type is compatible with serialized objects that have a different version. public VersionFieldSerializer (Kryo kryo, Class type) { super(kryo, type); @@ -56,7 +56,6 @@ public VersionFieldSerializer (Kryo kryo, Class type, boolean compatible) { this.compatible = compatible; } - @Override protected void initializeCachedFields () { CachedField[] fields = getFields(); fieldVersion = new int[fields.length]; @@ -71,40 +70,36 @@ protected void initializeCachedFields () { fieldVersion[i] = 0; } } - this.removedFields.clear(); + cachedFields.removedFields.clear(); if (DEBUG) debug("Version for type " + getType().getName() + " is " + typeVersion); } - @Override public void removeField (String fieldName) { super.removeField(fieldName); initializeCachedFields(); } - @Override public void removeField (CachedField field) { super.removeField(field); initializeCachedFields(); } - @Override public void write (Kryo kryo, Output output, T object) { CachedField[] fields = getFields(); // Write type version. - output.writeVarInt(typeVersion, true); + output.writeInt(typeVersion, true); // Write fields. for (int i = 0, n = fields.length; i < n; i++) { fields[i].write(output, object); } } - @Override public T read (Kryo kryo, Input input, Class type) { T object = create(kryo, input, type); kryo.reference(object); // Read input version. - int version = input.readVarInt(true); + int version = input.readInt(true); if (!compatible && version != typeVersion) { // Reject to read throw new KryoException("Version not compatible: " + version + " <-> " + typeVersion); diff --git a/src/com/esotericsoftware/kryo/util/DefaultClassResolver.java b/src/com/esotericsoftware/kryo/util/DefaultClassResolver.java index 9ec53385d..86fb611e4 100644 --- a/src/com/esotericsoftware/kryo/util/DefaultClassResolver.java +++ b/src/com/esotericsoftware/kryo/util/DefaultClassResolver.java @@ -30,7 +30,7 @@ import com.esotericsoftware.kryo.io.Output; /** Resolves classes by ID or by fully qualified class name. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class DefaultClassResolver implements ClassResolver { static public final byte NAME = -1; @@ -91,7 +91,7 @@ public Registration getRegistration (int classID) { public Registration writeClass (Output output, Class type) { if (type == null) { if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Write", null); - output.writeVarInt(Kryo.NULL, true); + output.writeInt(Kryo.NULL, true); return null; } Registration registration = kryo.getRegistration(type); @@ -99,18 +99,18 @@ public Registration writeClass (Output output, Class type) { writeName(output, type, registration); else { if (TRACE) trace("kryo", "Write class " + registration.getId() + ": " + className(type)); - output.writeVarInt(registration.getId() + 2, true); + output.writeInt(registration.getId() + 2, true); } return registration; } protected void writeName (Output output, Class type, Registration registration) { - output.writeVarInt(NAME + 2, true); + output.writeInt(NAME + 2, true); if (classToNameId != null) { int nameId = classToNameId.get(type, -1); if (nameId != -1) { if (TRACE) trace("kryo", "Write class name reference " + nameId + ": " + className(type)); - output.writeVarInt(nameId, true); + output.writeInt(nameId, true); return; } } @@ -119,12 +119,12 @@ protected void writeName (Output output, Class type, Registration registration) int nameId = nextNameId++; if (classToNameId == null) classToNameId = new IdentityObjectIntMap(); classToNameId.put(type, nameId); - output.writeVarInt(nameId, true); + output.writeInt(nameId, true); output.writeString(type.getName()); } public Registration readClass (Input input) { - int classID = input.readVarInt(true); + int classID = input.readInt(true); switch (classID) { case Kryo.NULL: if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Read", null); @@ -142,7 +142,7 @@ public Registration readClass (Input input) { } protected Registration readName (Input input) { - int nameId = input.readVarInt(true); + int nameId = input.readInt(true); if (nameIdToClass == null) nameIdToClass = new IntMap(); Class type = nameIdToClass.get(nameId); if (type == null) { @@ -171,7 +171,7 @@ protected Registration readName (Input input) { return kryo.getRegistration(type); } - protected Class getTypeByName (final String className) { + protected Class getTypeByName (final String className) { return nameToClass != null ? nameToClass.get(className) : null; } diff --git a/src/com/esotericsoftware/kryo/util/DefaultInstantiatorStrategy.java b/src/com/esotericsoftware/kryo/util/DefaultInstantiatorStrategy.java new file mode 100644 index 000000000..0cda947e7 --- /dev/null +++ b/src/com/esotericsoftware/kryo/util/DefaultInstantiatorStrategy.java @@ -0,0 +1,97 @@ + +package com.esotericsoftware.kryo.util; + +import static com.esotericsoftware.kryo.util.Util.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import org.objenesis.instantiator.ObjectInstantiator; +import org.objenesis.strategy.InstantiatorStrategy; + +import com.esotericsoftware.kryo.KryoException; +import com.esotericsoftware.reflectasm.ConstructorAccess; + +public class DefaultInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy { + private InstantiatorStrategy fallbackStrategy; + + public DefaultInstantiatorStrategy () { + } + + public DefaultInstantiatorStrategy (InstantiatorStrategy fallbackStrategy) { + this.fallbackStrategy = fallbackStrategy; + } + + public void setFallbackInstantiatorStrategy (final InstantiatorStrategy fallbackStrategy) { + this.fallbackStrategy = fallbackStrategy; + } + + public InstantiatorStrategy getFallbackInstantiatorStrategy () { + return fallbackStrategy; + } + + public ObjectInstantiator newInstantiatorOf (final Class type) { + if (!Util.isAndroid) { + // Use ReflectASM if the class is not a non-static member class. + Class enclosingType = type.getEnclosingClass(); + boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass() + && !Modifier.isStatic(type.getModifiers()); + if (!isNonStaticMemberClass) { + try { + final ConstructorAccess access = ConstructorAccess.get(type); + return new ObjectInstantiator() { + public Object newInstance () { + try { + return access.newInstance(); + } catch (Exception ex) { + throw new KryoException("Error constructing instance of class: " + className(type), ex); + } + } + }; + } catch (Exception ignored) { + } + } + } + + // Reflection. + try { + Constructor ctor; + try { + ctor = type.getConstructor((Class[])null); + } catch (Exception ex) { + ctor = type.getDeclaredConstructor((Class[])null); + ctor.setAccessible(true); + } + final Constructor constructor = ctor; + return new ObjectInstantiator() { + public Object newInstance () { + try { + return constructor.newInstance(); + } catch (Exception ex) { + throw new KryoException("Error constructing instance of class: " + className(type), ex); + } + } + }; + } catch (Exception ignored) { + } + + if (fallbackStrategy == null) { + if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) + throw new KryoException("Class cannot be created (non-static member class): " + className(type)); + else { + StringBuilder message = new StringBuilder("Class cannot be created (missing no-arg constructor): " + className(type)); + if (type.getSimpleName().equals("")) { + message.append("\nThis is an anonymous class, which is not serializable by default in Kryo. Possible solutions:\n") + .append("1. Remove uses of anonymous classes, including double brace initialization, from the containing\n") + .append( + "class. This is the safest solution, as anonymous classes don't have predictable names for serialization.\n") + .append("2. Register a FieldSerializer for the containing class and call FieldSerializer\n") + .append("setIgnoreSyntheticFields(false) on it. This is not safe but may be sufficient temporarily."); + } + throw new KryoException(message.toString()); + } + } + // InstantiatorStrategy. + return fallbackStrategy.newInstantiatorOf(type); + } +} diff --git a/src/com/esotericsoftware/kryo/util/IdentityMap.java b/src/com/esotericsoftware/kryo/util/IdentityMap.java index 2b45d0e9f..7a708ac9d 100644 --- a/src/com/esotericsoftware/kryo/util/IdentityMap.java +++ b/src/com/esotericsoftware/kryo/util/IdentityMap.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. +/* Copyright (c) 2008-2017, Nathan Sweet + * All rights reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: * - * http://www.apache.org/licenses/LICENSE-2.0 + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.esotericsoftware.kryo.util; diff --git a/src/com/esotericsoftware/kryo/util/IntMap.java b/src/com/esotericsoftware/kryo/util/IntMap.java index f23c6c747..72acf53fb 100644 --- a/src/com/esotericsoftware/kryo/util/IntMap.java +++ b/src/com/esotericsoftware/kryo/util/IntMap.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. +/* Copyright (c) 2008-2017, Nathan Sweet + * All rights reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: * - * http://www.apache.org/licenses/LICENSE-2.0 + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.esotericsoftware.kryo.util; diff --git a/src/com/esotericsoftware/kryo/util/ListReferenceResolver.java b/src/com/esotericsoftware/kryo/util/ListReferenceResolver.java index 619e5a136..d52461f6f 100644 --- a/src/com/esotericsoftware/kryo/util/ListReferenceResolver.java +++ b/src/com/esotericsoftware/kryo/util/ListReferenceResolver.java @@ -28,7 +28,7 @@ * {@link MapReferenceResolver} for graphs with few objects, providing an approximate 15% increase in deserialization speed. This * should not be used for graphs with many objects because it uses a linear look up to find objects that have already been * written. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class ListReferenceResolver implements ReferenceResolver { protected Kryo kryo; protected final ArrayList seenObjects = new ArrayList(); diff --git a/src/com/esotericsoftware/kryo/util/MapReferenceResolver.java b/src/com/esotericsoftware/kryo/util/MapReferenceResolver.java index a4231a369..d4daba127 100644 --- a/src/com/esotericsoftware/kryo/util/MapReferenceResolver.java +++ b/src/com/esotericsoftware/kryo/util/MapReferenceResolver.java @@ -26,7 +26,7 @@ /** Uses an {@link IdentityObjectIntMap} to track objects that have already been written. This can handle a graph with any number * of objects, but is slightly slower than {@link ListReferenceResolver} for graphs with few objects. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class MapReferenceResolver implements ReferenceResolver { protected Kryo kryo; protected final IdentityObjectIntMap writtenObjects = new IdentityObjectIntMap(); diff --git a/src/com/esotericsoftware/kryo/util/Util.java b/src/com/esotericsoftware/kryo/util/Util.java index c575b820b..bfff539c7 100644 --- a/src/com/esotericsoftware/kryo/util/Util.java +++ b/src/com/esotericsoftware/kryo/util/Util.java @@ -22,7 +22,7 @@ import static com.esotericsoftware.minlog.Log.*; /** A few utility methods, mostly for private use. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class Util { static public final boolean isAndroid = "Dalvik".equals(System.getProperty("java.vm.name")); diff --git a/test/com/esotericsoftware/kryo/ArraySerializerTest.java b/test/com/esotericsoftware/kryo/ArraySerializerTest.java index b49796deb..0c047fe26 100644 --- a/test/com/esotericsoftware/kryo/ArraySerializerTest.java +++ b/test/com/esotericsoftware/kryo/ArraySerializerTest.java @@ -21,7 +21,7 @@ import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ObjectArraySerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class ArraySerializerTest extends KryoTestCase { { supportsCopy = true; @@ -43,7 +43,8 @@ public void testArrays () { roundTrip(13, new int[][][] {{{1}, {2}}, {{100}, {4}}}); roundTrip(12, new String[] {"11", "2222", "3", "4"}); roundTrip(11, new String[] {"11", "2222", null, "4"}); - roundTrip(28, new Object[] {new String[] {"11", "2222", null, "4"}, new int[] {1, 2, 3, 4}, new int[][] {{1, 2}, {100, 4}}}); + roundTrip(28, + new Object[] {new String[] {"11", "2222", null, "4"}, new int[] {1, 2, 3, 4}, new int[][] {{1, 2}, {100, 4}}}); ObjectArraySerializer serializer = new ObjectArraySerializer(kryo, String[].class); kryo.register(String[].class, serializer); diff --git a/test/com/esotericsoftware/kryo/BeanSerializerTest.java b/test/com/esotericsoftware/kryo/BeanSerializerTest.java index 737b4bbdf..8ecfd2ae7 100644 --- a/test/com/esotericsoftware/kryo/BeanSerializerTest.java +++ b/test/com/esotericsoftware/kryo/BeanSerializerTest.java @@ -21,7 +21,7 @@ import com.esotericsoftware.kryo.serializers.BeanSerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class BeanSerializerTest extends KryoTestCase { { supportsCopy = true; diff --git a/test/com/esotericsoftware/kryo/BlowfishSerializerTest.java b/test/com/esotericsoftware/kryo/BlowfishSerializerTest.java index 11fadf3d0..abb5bf9ee 100644 --- a/test/com/esotericsoftware/kryo/BlowfishSerializerTest.java +++ b/test/com/esotericsoftware/kryo/BlowfishSerializerTest.java @@ -24,7 +24,7 @@ import com.esotericsoftware.kryo.serializers.BlowfishSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class BlowfishSerializerTest extends KryoTestCase { public void testZip () throws Exception { byte[] key = KeyGenerator.getInstance("Blowfish").generateKey().getEncoded(); diff --git a/test/com/esotericsoftware/kryo/ByteBufferInputOutputTest.java b/test/com/esotericsoftware/kryo/ByteBufferInputOutputTest.java index 16cf9c9d2..ab0239509 100644 --- a/test/com/esotericsoftware/kryo/ByteBufferInputOutputTest.java +++ b/test/com/esotericsoftware/kryo/ByteBufferInputOutputTest.java @@ -14,7 +14,7 @@ public void testByteBufferOutputResetEndiannessAfterException () { boolean exceptionTriggered = false; try { for (int i = 0; i < 10; i++) { - outputBuffer.writeVarInt(1234, true); + outputBuffer.writeInt(1234, true); } } catch (KryoException exception) { exceptionTriggered = true; @@ -26,7 +26,7 @@ public void testByteBufferOutputResetEndiannessAfterException () { exceptionTriggered = false; try { for (int i = 0; i < 10; i++) { - outputBuffer.writeVarLong(1234L, true); + outputBuffer.writeLong(1234L, true); } } catch (KryoException exception) { assertEquals(outputBuffer.order(), outputBuffer.getByteBuffer().order()); @@ -107,7 +107,7 @@ public void testByteBufferOutputSetOrder () { public void testByteBufferByteOrderTheSameAfterGrowingForVarInt () { final ByteBufferOutput outputBuffer = new ByteBufferOutput(1, -1); final ByteOrder byteOrder = outputBuffer.order(); - outputBuffer.writeVarInt(300, true); + outputBuffer.writeInt(300, true); assertEquals(byteOrder, outputBuffer.order()); } } diff --git a/test/com/esotericsoftware/kryo/ChunkedTest.java b/test/com/esotericsoftware/kryo/ChunkedTest.java index 9c106eb9d..4e754e56b 100644 --- a/test/com/esotericsoftware/kryo/ChunkedTest.java +++ b/test/com/esotericsoftware/kryo/ChunkedTest.java @@ -24,7 +24,7 @@ import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.io.OutputChunked; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class ChunkedTest extends KryoTestCase { public void testChunks () { Output output = new Output(512); diff --git a/test/com/esotericsoftware/kryo/CollectionSerializerTest.java b/test/com/esotericsoftware/kryo/CollectionSerializerTest.java index 4fcd14f77..077c30ba6 100644 --- a/test/com/esotericsoftware/kryo/CollectionSerializerTest.java +++ b/test/com/esotericsoftware/kryo/CollectionSerializerTest.java @@ -30,7 +30,7 @@ import com.esotericsoftware.kryo.serializers.CollectionSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class CollectionSerializerTest extends KryoTestCase { { supportsCopy = true; @@ -74,7 +74,7 @@ public void testCollections () { roundTrip(9, set); kryo.register(TreeSetSubclass.class); - set = new TreeSetSubclass(); + set = new TreeSetSubclass(); set.add(12); set.add(63); set.add(34); diff --git a/test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java b/test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java index 82597dc5d..4b9e49889 100644 --- a/test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java +++ b/test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java @@ -19,19 +19,15 @@ package com.esotericsoftware.kryo; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import com.esotericsoftware.kryo.io.Input; +import org.apache.commons.lang.builder.EqualsBuilder; + import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer; -import com.esotericsoftware.kryo.serializers.FieldSerializer; import com.esotericsoftware.kryo.serializers.FieldSerializerConfig; +import com.esotericsoftware.minlog.Log; -import org.apache.commons.lang.builder.EqualsBuilder; - -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class CompatibleFieldSerializerTest extends KryoTestCase { { supportsCopy = true; @@ -172,7 +168,6 @@ public void testRemovedFieldFromClassWithManyFields () throws FileNotFoundExcept object1.yy = "yy"; object1.zz = "zzaa"; - kryo.register(ClassWithManyFields.class, new CompatibleFieldSerializer(kryo, ClassWithManyFields.class)); roundTrip(236, object1); @@ -181,9 +176,9 @@ public void testRemovedFieldFromClassWithManyFields () throws FileNotFoundExcept kryo.register(ClassWithManyFields.class, serializer); Object object2 = kryo.readClassAndObject(input); assertTrue(object2 instanceof ClassWithManyFields); - assertNull("the bAdd field should be null", ((ClassWithManyFields) object2).bAdd); + assertNull("the bAdd field should be null", ((ClassWithManyFields)object2).bAdd); // update the field in order to verify the remainder of the object was deserialized correctly - ((ClassWithManyFields) object2).bAdd = object1.bAdd; + ((ClassWithManyFields)object2).bAdd = object1.bAdd; assertEquals(object1, object2); } @@ -192,8 +187,9 @@ public void testExtendedClass () throws FileNotFoundException { // this test would fail with DEFAULT field name strategy FieldSerializerConfig config = new FieldSerializerConfig(); - config.setCachedFieldNameStrategy(FieldSerializer.CachedFieldNameStrategy.EXTENDED); + config.setExtendedFieldNames(true); + Log.TRACE(); CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ExtendedTestClass.class, config); kryo.register(ExtendedTestClass.class, serializer); roundTrip(286, extendedObject); @@ -301,49 +297,18 @@ static public class ClassWithManyFields { public String j0; public String k0; - @Override - public boolean equals(Object obj) { + + public boolean equals (Object obj) { if (obj instanceof ClassWithManyFields) { - ClassWithManyFields other = (ClassWithManyFields) obj; - return new EqualsBuilder() - .append(aa, other.aa) - .append(a0, other.a0) - .append(bb, other.bb) - .append(b0, other.b0) - .append(cc, other.cc) - .append(c0, other.c0) - .append(dd, other.dd) - .append(d0, other.d0) - .append(ee, other.ee) - .append(e0, other.e0) - .append(ff, other.ff) - .append(f0, other.f0) - .append(gg, other.gg) - .append(g0, other.g0) - .append(hh, other.hh) - .append(h0, other.h0) - .append(ii, other.ii) - .append(i0, other.i0) - .append(jj, other.jj) - .append(j0, other.j0) - .append(kk, other.kk) - .append(k0, other.k0) - .append(ll, other.ll) - .append(mm, other.mm) - .append(nn, other.nn) - .append(oo, other.oo) - .append(pp, other.pp) - .append(qq, other.qq) - .append(rr, other.rr) - .append(ss, other.ss) - .append(tt, other.tt) - .append(uu, other.uu) - .append(vv, other.vv) - .append(xx, other.xx) - .append(yy, other.yy) - .append(zz, other.zz) - .append(bAdd, other.bAdd) - .isEquals(); + ClassWithManyFields other = (ClassWithManyFields)obj; + return new EqualsBuilder().append(aa, other.aa).append(a0, other.a0).append(bb, other.bb).append(b0, other.b0) + .append(cc, other.cc).append(c0, other.c0).append(dd, other.dd).append(d0, other.d0).append(ee, other.ee) + .append(e0, other.e0).append(ff, other.ff).append(f0, other.f0).append(gg, other.gg).append(g0, other.g0) + .append(hh, other.hh).append(h0, other.h0).append(ii, other.ii).append(i0, other.i0).append(jj, other.jj) + .append(j0, other.j0).append(kk, other.kk).append(k0, other.k0).append(ll, other.ll).append(mm, other.mm) + .append(nn, other.nn).append(oo, other.oo).append(pp, other.pp).append(qq, other.qq).append(rr, other.rr) + .append(ss, other.ss).append(tt, other.tt).append(uu, other.uu).append(vv, other.vv).append(xx, other.xx) + .append(yy, other.yy).append(zz, other.zz).append(bAdd, other.bAdd).isEquals(); } return false; } diff --git a/test/com/esotericsoftware/kryo/DefaultSerializersTest.java b/test/com/esotericsoftware/kryo/DefaultSerializersTest.java index 7d0adae8e..a89d757c2 100644 --- a/test/com/esotericsoftware/kryo/DefaultSerializersTest.java +++ b/test/com/esotericsoftware/kryo/DefaultSerializersTest.java @@ -37,8 +37,9 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class DefaultSerializersTest extends KryoTestCase { { supportsCopy = true; @@ -134,7 +135,6 @@ public void testShort () { public void testString () { kryo = new Kryo(); - kryo.setRegistrationRequired(true); roundTrip(6, "meow"); roundTrip(70, "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"); @@ -155,7 +155,6 @@ public void testVoid () throws InstantiationException, IllegalAccessException { public void testNull () { kryo = new Kryo(); - kryo.setRegistrationRequired(true); kryo.register(ArrayList.class); roundTrip(1, null); testNull(Long.class); @@ -338,7 +337,6 @@ public void testClassSerializer () { } public void testLocaleSerializer () { - kryo.setRegistrationRequired(true); kryo.register(Locale.class); roundTrip(5, Locale.ENGLISH); @@ -369,8 +367,7 @@ public void testCharset () { } public void testURLSerializer () throws Exception { - kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); - kryo.setRegistrationRequired(true); + kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); kryo.register(URL.class); roundTrip(41, new URL("https://github.com/EsotericSoftware/kryo")); diff --git a/test/com/esotericsoftware/kryo/DeflateSerializerTest.java b/test/com/esotericsoftware/kryo/DeflateSerializerTest.java index 033d043ae..88cbc5148 100644 --- a/test/com/esotericsoftware/kryo/DeflateSerializerTest.java +++ b/test/com/esotericsoftware/kryo/DeflateSerializerTest.java @@ -22,7 +22,7 @@ import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; import com.esotericsoftware.kryo.serializers.DeflateSerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class DeflateSerializerTest extends KryoTestCase { public void testString () { kryo.register(String.class, new DeflateSerializer(new StringSerializer())); @@ -80,8 +80,8 @@ public Message () { public int hashCode () { final int prime = 31; int result = 1; - result = prime * result + ((data == null) ? 0 : data.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + result = prime * result + (type == null ? 0 : type.hashCode()); return result; } diff --git a/test/com/esotericsoftware/kryo/FieldSerializerGenericsTest.java b/test/com/esotericsoftware/kryo/FieldSerializerGenericsTest.java index 141bc0047..bfc273037 100644 --- a/test/com/esotericsoftware/kryo/FieldSerializerGenericsTest.java +++ b/test/com/esotericsoftware/kryo/FieldSerializerGenericsTest.java @@ -20,7 +20,7 @@ public class FieldSerializerGenericsTest { @Parameters(name = "optimizedGenerics_{0}") - static public Iterable optimizedGenerics () { + static public Iterable optimizedGenerics () { return Arrays.asList(true, false); } @@ -33,13 +33,14 @@ public FieldSerializerGenericsTest (boolean optimizedGenerics) { @Test public void testNoStackOverflowForSimpleGenericsCase () { FooRef fooRef = new FooRef(); - GenericFoo genFoo1 = new GenericFoo(fooRef); - GenericFoo genFoo2 = new GenericFoo(fooRef); - List> foos = new ArrayList>(); + GenericFoo genFoo1 = new GenericFoo(fooRef); + GenericFoo genFoo2 = new GenericFoo(fooRef); + List> foos = new ArrayList(); foos.add(genFoo2); foos.add(genFoo1); new FooContainer(foos); Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(false); FieldSerializerFactory factory = new FieldSerializerFactory(); factory.getConfig().setOptimizedGenerics(optimizedGenerics); kryo.setDefaultSerializer(factory); @@ -51,13 +52,14 @@ public void testNoStackOverflowForSimpleGenericsCase () { @Test public void testNoStackOverflowForComplexGenericsCase () { BarRef barRef = new BarRef(); - GenericBar genBar1 = new GenericBar(barRef); - GenericBar genBar2 = new GenericBar(barRef); - List> bars = new ArrayList>(); + GenericBar genBar1 = new GenericBar(barRef); + GenericBar genBar2 = new GenericBar(barRef); + List> bars = new ArrayList(); bars.add(genBar2); bars.add(genBar1); new GenericBarContainer(new BarContainer(bars)); Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(false); FieldSerializerFactory factory = new FieldSerializerFactory(); factory.getConfig().setOptimizedGenerics(optimizedGenerics); kryo.setDefaultSerializer(factory); @@ -108,7 +110,7 @@ static class FooRef implements Foo { } static class FooContainer { - List> foos = new ArrayList>(); + List> foos = new ArrayList(); public FooContainer (List> foos) { this.foos = foos; diff --git a/test/com/esotericsoftware/kryo/FieldSerializerInheritanceTest.java b/test/com/esotericsoftware/kryo/FieldSerializerInheritanceTest.java index af1ff31c6..9470e3a73 100644 --- a/test/com/esotericsoftware/kryo/FieldSerializerInheritanceTest.java +++ b/test/com/esotericsoftware/kryo/FieldSerializerInheritanceTest.java @@ -65,7 +65,7 @@ public void testExtendedStrategyForExtendedClass () { ((TestDefault)testExtended).a = "someDefaultValue"; testExtended.a = "someExtendedValue"; FieldSerializerFactory factory = new FieldSerializerFactory(); - factory.getConfig().setCachedFieldNameStrategy(FieldSerializer.CachedFieldNameStrategy.EXTENDED); + factory.getConfig().setExtendedFieldNames(true); kryo.setDefaultSerializer(factory); kryo.register(TestExtended.class); @@ -102,7 +102,7 @@ public void setA (String a) { this.a = a; } - @Override + public boolean equals (Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -113,7 +113,7 @@ public boolean equals (Object o) { } - @Override + public int hashCode () { return a != null ? a.hashCode() : 0; } @@ -130,7 +130,7 @@ public void setA (String a) { this.a = a; } - @Override + public boolean equals (Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -141,7 +141,7 @@ public boolean equals (Object o) { return a != null ? a.equals(that.a) : that.a == null; } - @Override + public int hashCode () { return a != null ? a.hashCode() : 0; } diff --git a/test/com/esotericsoftware/kryo/FieldSerializerTest.java b/test/com/esotericsoftware/kryo/FieldSerializerTest.java index 8742c8763..42b59b63b 100644 --- a/test/com/esotericsoftware/kryo/FieldSerializerTest.java +++ b/test/com/esotericsoftware/kryo/FieldSerializerTest.java @@ -41,10 +41,9 @@ import com.esotericsoftware.kryo.serializers.FieldSerializer; import com.esotericsoftware.kryo.serializers.FieldSerializer.Bind; import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional; -import com.esotericsoftware.kryo.serializers.FieldSerializerConfig; import com.esotericsoftware.kryo.serializers.MapSerializer.BindMap; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class FieldSerializerTest extends KryoTestCase { { supportsCopy = true; @@ -79,7 +78,8 @@ public void testDefaultTypes () { FieldSerializer serializer = (FieldSerializer)kryo.getSerializer(DefaultTypes.class); serializer.getField("hasStringField").setCanBeNull(false); roundTrip(79, test); - serializer.setFixedFieldTypes(true); + serializer.getFieldSerializerConfig().setFixedFieldTypes(true); + serializer.updateFields(); serializer.getField("hasStringField").setCanBeNull(false); roundTrip(78, test); } @@ -123,8 +123,8 @@ public void testFieldRemovalOnGenerics () { serializer.removeField("y"); kryo.register(IsGeneric.class, serializer); - IsGeneric> test = new IsGeneric>(); - test.item = new IsGeneric(); + IsGeneric> test = new IsGeneric(); + test.item = new IsGeneric(); try { roundTrip(5, test); @@ -168,6 +168,7 @@ public void testOptionalRegistration () { test.byteArrayField = new byte[] {2, 1, 0, -1, -2}; kryo = new Kryo(); + kryo.setRegistrationRequired(false); roundTrip(140, test); C c = new C(); @@ -193,6 +194,7 @@ public void testReferences () { c.d.e.f.a = c.a; kryo = new Kryo(); + kryo.setRegistrationRequired(false); roundTrip(63, c); C c2 = (C)object2; assertTrue(c2.a == c2.d.e.f.a); @@ -203,7 +205,6 @@ public void testReferences () { assertTrue(c2.a == c2.d.e.f.a); kryo = new Kryo(); - kryo.setRegistrationRequired(true); kryo.register(A.class); kryo.register(B.class); kryo.register(C.class); @@ -246,7 +247,6 @@ public void testExceptionTrace () { Kryo kryoWithoutF = new Kryo(); kryoWithoutF.setReferences(false); - kryoWithoutF.setRegistrationRequired(true); kryoWithoutF.register(A.class); kryoWithoutF.register(B.class); kryoWithoutF.register(C.class); @@ -266,7 +266,6 @@ public void testExceptionTrace () { kryo.register(D.class); kryo.register(E.class); kryo.register(F.class); - kryo.setRegistrationRequired(true); output.clear(); kryo.writeClassAndObject(output, c); @@ -302,7 +301,7 @@ public SimpleNoDefaultConstructor copy (Kryo kryo, SimpleNoDefaultConstructor or roundTrip(2, object1); kryo.register(ComplexNoDefaultConstructor.class, - new FieldSerializer(kryo, ComplexNoDefaultConstructor.class) { + new FieldSerializer(kryo, ComplexNoDefaultConstructor.class) { public void write (Kryo kryo, Output output, ComplexNoDefaultConstructor object) { output.writeString(object.name); super.write(kryo, output, object); @@ -332,20 +331,22 @@ public void testNonNull () { public void testDefaultSerializerAnnotation () { kryo = new Kryo(); + kryo.setRegistrationRequired(false); roundTrip(82, new HasDefaultSerializerAnnotation(123)); } public void testOptionalAnnotation () { kryo = new Kryo(); + kryo.setRegistrationRequired(false); roundTrip(72, new HasOptionalAnnotation()); kryo = new Kryo(); + kryo.setRegistrationRequired(false); kryo.getContext().put("smurf", null); roundTrip(73, new HasOptionalAnnotation()); } public void testCyclicGrgaph () throws Exception { kryo = new Kryo(); - kryo.setRegistrationRequired(true); kryo.register(DefaultTypes.class); kryo.register(byte[].class); DefaultTypes test = new DefaultTypes(); @@ -417,7 +418,6 @@ private void testGenericTypes (boolean optimizedGenerics) { factory.getConfig().setOptimizedGenerics(optimizedGenerics); kryo.setDefaultSerializer(factory); kryo.setReferences(true); - kryo.setRegistrationRequired(true); kryo.register(HasGenerics.class); kryo.register(ArrayList.class); kryo.register(ArrayList[].class); @@ -428,7 +428,8 @@ private void testGenericTypes (boolean optimizedGenerics) { // objects directly. FieldSerializer fieldSerializer; fieldSerializer = (FieldSerializer)kryo.getSerializer(HasGenerics.class); - fieldSerializer.setOptimizedGenerics(optimizedGenerics); + fieldSerializer.getFieldSerializerConfig().setOptimizedGenerics(optimizedGenerics); + fieldSerializer.updateFields(); HasGenerics test = new HasGenerics(); test.list1 = new ArrayList(); @@ -482,14 +483,16 @@ public void testTransients () { objectWithTransients1.anotherField3 = "Field2"; FieldSerializer ser = (FieldSerializer)kryo.getSerializer(HasTransients.class); - ser.setCopyTransient(false); + ser.getFieldSerializerConfig().setCopyTransient(false); + ser.updateFields(); HasTransients objectWithTransients3 = kryo.copy(objectWithTransients1); assertTrue("Objects should be different if copy does not include transient fields", !objectWithTransients3.equals(objectWithTransients1)); assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); - ser.setCopyTransient(true); + ser.getFieldSerializerConfig().setCopyTransient(true); + ser.updateFields(); HasTransients objectWithTransients2 = kryo.copy(objectWithTransients1); assertEquals("Objects should be equal if copy includes transient fields", objectWithTransients2, objectWithTransients1); } @@ -510,7 +513,8 @@ public void testTransientsUsingGlobalConfig () { !objectWithTransients3.equals(objectWithTransients1)); assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); - ser.setCopyTransient(true); + ser.getFieldSerializerConfig().setCopyTransient(true); + ser.updateFields(); HasTransients objectWithTransients2 = kryo.copy(objectWithTransients1); assertEquals("Objects should be equal if copy includes transient fields", objectWithTransients2, objectWithTransients1); } @@ -527,8 +531,9 @@ public void testSerializeTransients () { Input input; byte[] outBytes; - FieldSerializer ser = (FieldSerializer)kryo.getSerializer(HasTransients.class); - ser.setSerializeTransient(false); + FieldSerializer ser = (FieldSerializer)kryo.getSerializer(HasTransients.class); + ser.getFieldSerializerConfig().setSerializeTransient(false); + ser.updateFields(); outputStream = new ByteArrayOutputStream(); output = new Output(outputStream); @@ -542,7 +547,8 @@ public void testSerializeTransients () { !objectWithTransients3.equals(objectWithTransients1)); assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); - ser.setSerializeTransient(true); + ser.getFieldSerializerConfig().setSerializeTransient(true); + ser.updateFields(); outputStream = new ByteArrayOutputStream(); output = new Output(outputStream); @@ -571,7 +577,7 @@ public void testSerializeTransientsUsingGlobalConfig () { Input input; byte[] outBytes; - FieldSerializer ser = (FieldSerializer)kryo.getSerializer(HasTransients.class); + FieldSerializer ser = (FieldSerializer)kryo.getSerializer(HasTransients.class); outputStream = new ByteArrayOutputStream(); output = new Output(outputStream); ser.write(kryo, output, objectWithTransients1); @@ -584,7 +590,8 @@ public void testSerializeTransientsUsingGlobalConfig () { !objectWithTransients3.equals(objectWithTransients1)); assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); - ser.setSerializeTransient(true); + ser.getFieldSerializerConfig().setSerializeTransient(true); + ser.updateFields(); outputStream = new ByteArrayOutputStream(); output = new Output(outputStream); @@ -605,12 +612,12 @@ public void testCorrectlyAnnotatedFields () { kryo.register(ArrayList.class); kryo.register(AnnotatedFields.class); AnnotatedFields obj1 = new AnnotatedFields(); - obj1.map = new HashMap(); + obj1.map = new HashMap(); obj1.map.put("key1", new int[] {1, 2, 3}); obj1.map.put("key2", new int[] {3, 4, 5}); obj1.map.put("key3", null); - obj1.collection = new ArrayList(); + obj1.collection = new ArrayList(); obj1.collection.add(new long[] {1, 2, 3}); roundTrip(31, obj1); @@ -883,8 +890,8 @@ public int hashCode () { final int prime = 31; int result = 1; result = prime * result + anotherField2; - result = prime * result + ((anotherField3 == null) ? 0 : anotherField3.hashCode()); - result = prime * result + ((transientField1 == null) ? 0 : transientField1.hashCode()); + result = prime * result + (anotherField3 == null ? 0 : anotherField3.hashCode()); + result = prime * result + (transientField1 == null ? 0 : transientField1.hashCode()); return result; } @@ -917,8 +924,8 @@ public int hashCode () { final int prime = 31; int result = 1; result = prime * result + anotherField1; - result = prime * result + ((anotherField2 == null) ? 0 : anotherField2.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + (anotherField2 == null ? 0 : anotherField2.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); return result; } @@ -1043,9 +1050,9 @@ private HasPrivateConstructor () { static public class HasGenerics { ArrayList list1; - List> list2 = new ArrayList>(); - List list3 = new ArrayList(); - ArrayList list4 = new ArrayList(); + List list2 = new ArrayList(); + List list3 = new ArrayList(); + ArrayList list4 = new ArrayList(); ArrayList list5; HashMap> map1; @@ -1141,7 +1148,7 @@ static public class IsGeneric { private int y; private int z; - @Override + public boolean equals (Object o) { if (this == o) return true; if (!(o instanceof IsGeneric)) return false; diff --git a/test/com/esotericsoftware/kryo/GarbageCollectionTest.java b/test/com/esotericsoftware/kryo/GarbageCollectionTest.java index 7a0c5a629..283d8cfda 100644 --- a/test/com/esotericsoftware/kryo/GarbageCollectionTest.java +++ b/test/com/esotericsoftware/kryo/GarbageCollectionTest.java @@ -32,7 +32,7 @@ public class GarbageCollectionTest extends TestCase { public void test () { Kryo kryo = new Kryo(new DefaultClassResolver(), new MapReferenceResolver()); - WeakReference kryoWeakRef = new WeakReference(kryo); + WeakReference kryoWeakRef = new WeakReference(kryo); kryo = null; // remove strong ref, now kryo is only weak-reachable reclaim(kryoWeakRef); } diff --git a/test/com/esotericsoftware/kryo/GenericsTest.java b/test/com/esotericsoftware/kryo/GenericsTest.java index 36cc65dc7..78374aaed 100644 --- a/test/com/esotericsoftware/kryo/GenericsTest.java +++ b/test/com/esotericsoftware/kryo/GenericsTest.java @@ -34,7 +34,7 @@ import com.esotericsoftware.kryo.SerializerFactory.FieldSerializerFactory; import com.esotericsoftware.kryo.io.Output; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ @RunWith(Parameterized.class) public class GenericsTest extends KryoTestCase { { @@ -42,7 +42,7 @@ public class GenericsTest extends KryoTestCase { } @Parameters(name = "optimizedGenerics_{0}") - static public Iterable optimizedGenerics () { + static public Iterable optimizedGenerics () { return Arrays.asList(true, false); } @@ -52,7 +52,7 @@ public GenericsTest (boolean optimizedGenerics) { this.optimizedGenerics = optimizedGenerics; } - @Override + @Before public void setUp () throws Exception { super.setUp(); @@ -69,7 +69,7 @@ public void testGenericClassWithGenericFields () throws Exception { List list = Arrays.asList(new SerializableObjectFoo("one"), new SerializableObjectFoo("two"), new SerializableObjectFoo("three")); - BaseGeneric bg1 = new BaseGeneric(list); + BaseGeneric bg1 = new BaseGeneric(list); roundTrip(108, bg1); } @@ -153,7 +153,7 @@ public SerializableObjectFoo () { name = "Default"; } - @Override + public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; @@ -181,14 +181,14 @@ protected BaseGeneric () { protected BaseGeneric (final List listPayload) { super(); // Defensive copy, listPayload is mutable - this.listPayload = new ArrayList(listPayload); + this.listPayload = new ArrayList(listPayload); } public final List getPayload () { return this.listPayload; } - @Override + public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; diff --git a/test/com/esotericsoftware/kryo/InputOutputTest.java b/test/com/esotericsoftware/kryo/InputOutputTest.java index 7acdb03fa..2866aba87 100644 --- a/test/com/esotericsoftware/kryo/InputOutputTest.java +++ b/test/com/esotericsoftware/kryo/InputOutputTest.java @@ -33,7 +33,7 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class InputOutputTest extends KryoTestCase { public void testOutputStream () throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); @@ -823,11 +823,11 @@ public void testVerySmallBuffers () throws Exception { Output out2 = new ByteBufferOutput(4, -1); for (int i = 0; i < 16; i++) { - out1.writeVarInt(92, false); + out1.writeInt(92, false); } for (int i = 0; i < 16; i++) { - out2.writeVarInt(92, false); + out2.writeInt(92, false); } assertEquals(out1.toBytes(), out2.toBytes()); diff --git a/test/com/esotericsoftware/kryo/JavaSerializerTest.java b/test/com/esotericsoftware/kryo/JavaSerializerTest.java index b5f72aa31..a386862e6 100644 --- a/test/com/esotericsoftware/kryo/JavaSerializerTest.java +++ b/test/com/esotericsoftware/kryo/JavaSerializerTest.java @@ -23,7 +23,7 @@ import com.esotericsoftware.kryo.serializers.JavaSerializer; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class JavaSerializerTest extends KryoTestCase { public void testJavaSerializer () { kryo.register(String.class, new JavaSerializer()); diff --git a/test/com/esotericsoftware/kryo/KryoStringTest.java b/test/com/esotericsoftware/kryo/KryoStringTest.java index 982d97c06..05538af56 100644 --- a/test/com/esotericsoftware/kryo/KryoStringTest.java +++ b/test/com/esotericsoftware/kryo/KryoStringTest.java @@ -19,25 +19,14 @@ package com.esotericsoftware.kryo; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.junit.Test; - -import com.esotericsoftware.kryo.FieldSerializerTest.DefaultTypes; -import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.pool.KryoFactory; import com.esotericsoftware.kryo.pool.KryoPool; -import com.esotericsoftware.kryo.serializers.MapSerializer; public class KryoStringTest extends KryoTestCase { static KryoFactory factory = new KryoFactory() { - @Override + public Kryo create () { Kryo kryo = new Kryo(); return kryo; diff --git a/test/com/esotericsoftware/kryo/KryoTestCase.java b/test/com/esotericsoftware/kryo/KryoTestCase.java index 1b2bb26d5..86475d747 100644 --- a/test/com/esotericsoftware/kryo/KryoTestCase.java +++ b/test/com/esotericsoftware/kryo/KryoTestCase.java @@ -37,7 +37,7 @@ /** Convenience methods for round tripping objects. * - * @author Nathan Sweet */ + * @author Nathan Sweet */ abstract public class KryoTestCase extends TestCase { protected Kryo kryo; protected Output output; @@ -62,8 +62,6 @@ protected void setUp () throws Exception { kryo = new Kryo(); kryo.setReferences(false); - kryo.setRegistrationRequired(true); - // kryo.useAsmBackend(false); } public T roundTrip (int length, T object1) { diff --git a/test/com/esotericsoftware/kryo/MapSerializerTest.java b/test/com/esotericsoftware/kryo/MapSerializerTest.java index 7deb3368c..b50ba06cd 100644 --- a/test/com/esotericsoftware/kryo/MapSerializerTest.java +++ b/test/com/esotericsoftware/kryo/MapSerializerTest.java @@ -36,7 +36,7 @@ import junit.framework.Assert; -/** @author Nathan Sweet */ +/** @author Nathan Sweet */ public class MapSerializerTest extends KryoTestCase { { supportsCopy = true; @@ -64,19 +64,19 @@ public void testMaps () { } public void testEmptyHashMap () { - execute(new HashMap(), 0); + execute(new HashMap(), 0); } public void testNotEmptyHashMap () { - execute(new HashMap(), 1000); + execute(new HashMap(), 1000); } public void testEmptyConcurrentHashMap () { - execute(new ConcurrentHashMap(), 0); + execute(new ConcurrentHashMap(), 0); } public void testNotEmptyConcurrentHashMap () { - execute(new ConcurrentHashMap(), 1000); + execute(new ConcurrentHashMap(), 1000); } public void testGenerics () { @@ -135,7 +135,7 @@ public void testTreeMap () { roundTrip(21, map); kryo.register(TreeMapSubclass.class); - map = new TreeMapSubclass(); + map = new TreeMapSubclass(); map.put("1", 47); map.put("2", 34); map.put("3", 65); @@ -163,7 +163,7 @@ public void testTreeMapWithReferences () { roundTrip(29, map); kryo.register(TreeMapSubclass.class); - map = new TreeMapSubclass(); + map = new TreeMapSubclass(); map.put("1", 47); map.put("2", 34); map.put("3", 65); @@ -173,6 +173,7 @@ public void testTreeMapWithReferences () { public void testSerializingMapAfterDeserializingMultipleReferencesToSameMap () throws Exception { Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(false); FieldSerializerFactory factory = new FieldSerializerFactory(); factory.getConfig().setOptimizedGenerics(false); kryo.setDefaultSerializer(factory); @@ -182,8 +183,8 @@ public void testSerializingMapAfterDeserializingMultipleReferencesToSameMap () t kryo.readClassAndObject(new Input(new ByteArrayInputStream(output.getBuffer()))); output.clear(); - Map> mapOfLists = new HashMap>(); - mapOfLists.put(1, new java.util.ArrayList()); + Map> mapOfLists = new HashMap(); + mapOfLists.put(1, new java.util.ArrayList()); kryo.writeClassAndObject(output, mapOfLists); @SuppressWarnings("unchecked") @@ -193,7 +194,7 @@ public void testSerializingMapAfterDeserializingMultipleReferencesToSameMap () t } static class HasMultipleReferenceToSameMap { - private Map mapOne = new HashMap(); + private Map mapOne = new HashMap(); private Map mapTwo = this.mapOne; } diff --git a/test/com/esotericsoftware/kryo/ReferenceTest.java b/test/com/esotericsoftware/kryo/ReferenceTest.java index ca131dffa..63a5d56be 100644 --- a/test/com/esotericsoftware/kryo/ReferenceTest.java +++ b/test/com/esotericsoftware/kryo/ReferenceTest.java @@ -51,6 +51,7 @@ public void testChildObjectBeforeReference () { stuff.put("self", stuff); Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(false); kryo.addDefaultSerializer(Stuff.class, new MapSerializer() { public void write (Kryo kryo, Output output, Map object) { kryo.writeObjectOrNull(output, ((Stuff)object).ordering, Ordering.class); diff --git a/test/com/esotericsoftware/kryo/ReflectionAssert.java b/test/com/esotericsoftware/kryo/ReflectionAssert.java index c1dd2f86c..5f1e15ff5 100644 --- a/test/com/esotericsoftware/kryo/ReflectionAssert.java +++ b/test/com/esotericsoftware/kryo/ReflectionAssert.java @@ -64,7 +64,7 @@ static void assertReflectionEquals (final Object one, final Object another) { * implementing class. If false, it's only checked if both objects are a {@link List}, {@link Set} or * {@link Map}. */ static void assertReflectionEquals (final Object one, final Object another, final boolean requireMatchingCollectionClasses) { - assertReflectionEquals(one, another, requireMatchingCollectionClasses, new IdentityHashMap(), ""); + assertReflectionEquals(one, another, requireMatchingCollectionClasses, new IdentityHashMap(), ""); } // CHECKSTYLE:OFF @@ -124,8 +124,7 @@ static private void assertReflectionEquals (final Object one, final Object anoth } if (Collection.class.isAssignableFrom(one.getClass())) { - assertCollectionEquals((Collection)one, (Collection)another, requireMatchingCollectionClasses, alreadyChecked, - path); + assertCollectionEquals((Collection)one, (Collection)another, requireMatchingCollectionClasses, alreadyChecked, path); return; } @@ -143,7 +142,7 @@ static private void assertReflectionEquals (final Object one, final Object anoth return; } - Class clazz = one.getClass(); + Class clazz = one.getClass(); while (clazz != null) { assertEqualDeclaredFields(clazz, one, another, requireMatchingCollectionClasses, alreadyChecked, path); clazz = clazz.getSuperclass(); @@ -151,13 +150,13 @@ static private void assertReflectionEquals (final Object one, final Object anoth } // CHECKSTYLE:ON - static private boolean isOnlyOneAssignable (final Class checkedClazz, final Object one, final Object another) { + static private boolean isOnlyOneAssignable (final Class checkedClazz, final Object one, final Object another) { return checkedClazz.isAssignableFrom(one.getClass()) && !checkedClazz.isAssignableFrom(another.getClass()) || checkedClazz.isAssignableFrom(another.getClass()) && !checkedClazz.isAssignableFrom(one.getClass()); } - static private boolean oneIsAssignable (final Object one, final Object another, final Class... checkedClazzes) { - for (final Class checkedClazz : checkedClazzes) { + static private boolean oneIsAssignable (final Object one, final Object another, final Class... checkedClazzes) { + for (final Class checkedClazz : checkedClazzes) { if (checkedClazz.isAssignableFrom(one.getClass()) || checkedClazz.isAssignableFrom(another.getClass())) { return true; } @@ -169,12 +168,12 @@ static private boolean oneIsAssignable (final Object one, final Object another, * TODO (MG): this assumes same iteration order, which must not be given for sets. There could be a specialized implementation * for sets. */ - static private void assertCollectionEquals (final Collection m1, final Collection m2, - final boolean requireMatchingClasses, final Map alreadyChecked, final String path) { + static private void assertCollectionEquals (final Collection m1, final Collection m2, final boolean requireMatchingClasses, + final Map alreadyChecked, final String path) { Assert.assertEquals("Collection size does not match for path '" + (StringUtils.isEmpty(path) ? "." : path) + "' - ", m1.size(), m2.size()); - final Iterator iter1 = m1.iterator(); - final Iterator iter2 = m2.iterator(); + final Iterator iter1 = m1.iterator(); + final Iterator iter2 = m2.iterator(); int i = 0; while (iter1.hasNext()) { assertReflectionEquals(iter1.next(), iter2.next(), requireMatchingClasses, alreadyChecked, path + "[" + i++ + "]"); diff --git a/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java b/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java index 82a880e11..4b6fb0319 100644 --- a/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java +++ b/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java @@ -89,31 +89,26 @@ static private SampleObject createObject () { // } public void testJavaSerialization () throws Exception { - // Warm-up phase: Perform 100000 iterations runJavaSerialization(1, WARMUP_ITERATIONS, false); runJavaSerialization(RUN_CNT, ITER_CNT, true); } public void testJavaSerializationWithoutTryCatch () throws Exception { - // Warm-up phase: Perform 100000 iterations runJavaSerializationWithoutTryCatch(1, WARMUP_ITERATIONS, false); runJavaSerializationWithoutTryCatch(RUN_CNT, ITER_CNT, true); } public void testKryoSerialization () throws Exception { - // Warm-up phase: Perform 100000 iterations runKryoSerialization(1, WARMUP_ITERATIONS, false); runKryoSerialization(RUN_CNT, ITER_CNT, true); } public void testKryoSerializationUnmodified () throws Exception { - // Warm-up phase: Perform 100000 iterations runKryoSerializationUmodified(1, WARMUP_ITERATIONS, false); runKryoSerializationUmodified(RUN_CNT, ITER_CNT, true); } public void testKryoSerializationWithoutTryCatch () throws Exception { - // Warm-up phase: Perform 100000 iterations runKryoSerializationWithoutTryCatch(1, WARMUP_ITERATIONS, false); runKryoSerializationWithoutTryCatch(RUN_CNT, ITER_CNT, true); } @@ -232,6 +227,7 @@ private void runJavaSerializationWithoutTryCatch (final int RUN_CNT, final int I private void runKryoSerialization (final int RUN_CNT, final int ITER_CNT, boolean outputResults) throws Exception { Kryo marsh = new Kryo(); + marsh.setRegistrationRequired(false); marsh.register(SampleObject.class, 40); long avgDur = 0; @@ -289,6 +285,7 @@ private void runKryoSerialization (final int RUN_CNT, final int ITER_CNT, boolea private void runKryoSerializationUmodified (final int RUN_CNT, final int ITER_CNT, boolean outputResults) throws Exception { Kryo marsh = new Kryo(); + marsh.setRegistrationRequired(false); long avgDur = 0; long bestTime = Long.MAX_VALUE; @@ -299,7 +296,6 @@ private void runKryoSerializationUmodified (final int RUN_CNT, final int ITER_CN long start = System.nanoTime(); for (int j = 0; j < ITER_CNT; j++) { - Output kryoOut = null; try { @@ -347,6 +343,7 @@ private void runKryoSerializationUmodified (final int RUN_CNT, final int ITER_CN private void runKryoSerializationWithoutTryCatch (final int RUN_CNT, final int ITER_CNT, boolean outputResults) throws Exception { Kryo marsh = new Kryo(); + marsh.setRegistrationRequired(false); marsh.register(SampleObject.class, 40); long avgDur = 0; @@ -425,7 +422,7 @@ public SampleObject () { } /** {@inheritDoc} */ - @Override + public boolean equals (Object other) { if (this == other) return true; @@ -441,7 +438,7 @@ public boolean equals (Object other) { } // Required by Kryo serialization. - @Override + public void read (Kryo kryo, Input in) { intVal = kryo.readObject(in, Integer.class); floatVal = kryo.readObject(in, Float.class); @@ -452,7 +449,7 @@ public void read (Kryo kryo, Input in) { } // Required by Java Externalizable. - @Override + public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException { intVal = in.readInt(); floatVal = in.readFloat(); @@ -463,7 +460,7 @@ public void readExternal (ObjectInput in) throws IOException, ClassNotFoundExcep } // Required by Kryo serialization. - @Override + public void write (Kryo kryo, Output out) { kryo.writeObject(out, intVal); kryo.writeObject(out, floatVal); @@ -474,7 +471,7 @@ public void write (Kryo kryo, Output out) { } // Required by Java Externalizable. - @Override + public void writeExternal (ObjectOutput out) throws IOException { out.writeInt(intVal); out.writeFloat(floatVal); diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 6e3f7ced1..e8681b30e 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -40,8 +40,7 @@ import com.esotericsoftware.kryo.io.ByteBufferOutput; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import com.esotericsoftware.kryo.serializers.FieldSerializer; -import com.esotericsoftware.kryo.serializers.FieldSerializerConfig; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; import com.esotericsoftware.minlog.Log; /** Test for serialization compatibility: data serialized with an older version (same major version) must be deserializable with @@ -68,11 +67,11 @@ * version (e.g. for 3.1.4 this is 3.0.0) - to do this just save this test and the {@link SerializationCompatTest}, go back to the * related tag and run the test (there's nothing here to automate creation of test files for a different version). */ public class SerializationCompatTest extends KryoTestCase { - static private final String ENDIANNESS = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "le" : "be"; static private final int JAVA_VERSION = Integer.parseInt(System.getProperty("java.version").split("\\.")[1]); - static private final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 8 ? 35 : 53; - static private final List> TEST_DATAS = new ArrayList>(); + static private final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 8 ? 35 : 53; // Also change + // Kryo#defaultSerializers. + static private final List TEST_DATAS = new ArrayList(); static { TEST_DATAS.add(new TestDataDescription("3.0.0", new TestData(), 1865, 1882)); @@ -84,7 +83,7 @@ private void setUp (boolean optimizedGenerics) throws Exception { FieldSerializerFactory factory = new FieldSerializerFactory(); factory.getConfig().setOptimizedGenerics(optimizedGenerics); kryo.setDefaultSerializer(factory); - kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); + kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); kryo.setReferences(true); kryo.setRegistrationRequired(false); // we register EnumSet so that the EnumSet implementation class name is not written: EnumSet implementations @@ -97,7 +96,7 @@ private void setUp (boolean optimizedGenerics) throws Exception { public void testDefaultSerializers () throws Exception { Field defaultSerializersField = Kryo.class.getDeclaredField("defaultSerializers"); defaultSerializersField.setAccessible(true); - List defaultSerializers = (List)defaultSerializersField.get(kryo); + List defaultSerializers = (List)defaultSerializersField.get(kryo); assertEquals("The registered default serializers changed.\n" + "Because serialization compatibility shall be checked" + " for default serializers, you must extend SerializationCompatTestData.TestData to have a field for the" + " type of the new default serializer.\n" + "After that's done, you must create new versions of 'test/resources/data*'" @@ -183,13 +182,12 @@ private void readAndRunTest (TestDataDescription description, boolean optimiz } } - private void runTestAndWrite (TestDataDescription description, boolean optimizedGenerics, Output out) + private void runTestAndWrite (TestDataDescription description, boolean optimizedGenerics, Output out) throws FileNotFoundException { roundTrip(optimizedGenerics ? description.lengthOptGenerics : description.lengthNonOptGenerics, description.testData); kryo.writeObject(out, description.testData); } - @Override protected void doAssertEquals (final Object one, final Object another) { try { assertReflectionEquals(one, another); diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java index ef2a86159..dde6f0aaf 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java @@ -143,7 +143,7 @@ static public class TestData implements Serializable { private StringBuilder _stringBuilder; private StringBuffer _stringBuffer; - private Class _class; + private Class _class; private Integer[] _integerArray; private Date _date; private TimeZone _timeZone; @@ -223,7 +223,7 @@ public TestData () { _timeZone = TimeZone.getTimeZone("America/Los_Angeles"); _locale = Locale.ENGLISH; - _charsets = new ArrayList(Arrays.asList(Charset.forName("ISO-8859-1"), Charset.forName("US-ASCII"), + _charsets = new ArrayList(Arrays.asList(Charset.forName("ISO-8859-1"), Charset.forName("US-ASCII"), Charset.forName("UTF-8"), Charset.forName("UTF-16"), Charset.forName("UTF-16BE"), Charset.forName("UTF-16LE"))); try { _url = new java.net.URL("https://github.com/EsotericSoftware/kryo"); @@ -242,14 +242,14 @@ public TestData () { _singletonList = Collections.singletonList("foo"); _singletonSet = Collections.singleton("foo"); _singletonMap = Collections.singletonMap("foo", "bar"); - _treeSet = new TreeSet(Arrays.asList("foo", "bar")); - _treeMap = new TreeMap(); + _treeSet = new TreeSet(Arrays.asList("foo", "bar")); + _treeMap = new TreeMap(); _treeMap.put("foo", 23); _treeMap.put("bar", 42); - _arrayList = new ArrayList(Arrays.asList("foo", "bar")); - _hashSet = new HashSet(); + _arrayList = new ArrayList(Arrays.asList("foo", "bar")); + _hashSet = new HashSet(); _hashSet.add("14"); - _hashMap = new HashMap(); + _hashMap = new HashMap(); _hashMap.put("foo", 23); _hashMap.put("bar", 42); @@ -267,20 +267,20 @@ public TestData () { _personArray[0].addFriend(_personArray[1]); _personArray[1].addFriend(_personArray[0]); - _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")); + _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")); } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @@ -294,17 +294,17 @@ public Generic (final T item) { this.item = item; } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } - @Override + public String toString () { return "Generic [item=" + item + "]"; } @@ -317,12 +317,12 @@ public GenericList (final List> holders) { this.generics = holders; } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @@ -335,12 +335,12 @@ public GenericArray (final Generic... holders) { this.holders = holders; } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @@ -351,7 +351,7 @@ static Person createPerson (final String name, final Gender gender, final Intege person.setName(name); person.setGender(gender); person.setAge(age); - final HashMap props = new HashMap(); + final HashMap props = new HashMap(); for (int i = 0; i < emailAddresses.length; i++) { final String emailAddress = emailAddresses[i]; props.put("email" + i, new Email(name, emailAddress)); @@ -370,7 +370,7 @@ static enum Gender { private Gender _gender; private Integer _age; private Map _props; - private final Collection _friends = new ArrayList(); + private final Collection _friends = new ArrayList(); public String getName () { return _name; @@ -412,23 +412,23 @@ public Collection getFriends () { return _friends; } - private boolean flatEquals (final Collection c1, final Collection c2) { + private boolean flatEquals (final Collection c1, final Collection c2) { return c1 == c2 || c1 != null && c2 != null && c1.size() == c2.size(); } - @Override + public int hashCode () { final int prime = 31; int result = 1; - result = prime * result + ((_age == null) ? 0 : _age.hashCode()); - result = prime * result + ((_friends == null) ? 0 : _friends.size()); - result = prime * result + ((_gender == null) ? 0 : _gender.hashCode()); - result = prime * result + ((_name == null) ? 0 : _name.hashCode()); - result = prime * result + ((_props == null) ? 0 : _props.hashCode()); + result = prime * result + (_age == null ? 0 : _age.hashCode()); + result = prime * result + (_friends == null ? 0 : _friends.size()); + result = prime * result + (_gender == null ? 0 : _gender.hashCode()); + result = prime * result + (_name == null ? 0 : _name.hashCode()); + result = prime * result + (_props == null ? 0 : _props.hashCode()); return result; } - @Override + public boolean equals (final Object obj) { if (this == obj) { return true; @@ -478,7 +478,7 @@ public boolean equals (final Object obj) { return true; } - @Override + public String toString () { return "Person [_age=" + _age + ", _friends.size=" + _friends.size() + ", _gender=" + _gender + ", _name=" + _name + ", _props=" + _props + "]"; @@ -518,16 +518,16 @@ public void setEmail (final String email) { _email = email; } - @Override + public int hashCode () { final int prime = 31; int result = 1; - result = prime * result + ((_email == null) ? 0 : _email.hashCode()); - result = prime * result + ((_name == null) ? 0 : _name.hashCode()); + result = prime * result + (_email == null ? 0 : _email.hashCode()); + result = prime * result + (_name == null ? 0 : _name.hashCode()); return result; } - @Override + public boolean equals (final Object obj) { if (this == obj) { return true; @@ -556,7 +556,7 @@ public boolean equals (final Object obj) { return true; } - @Override + public String toString () { return "Email [_email=" + _email + ", _name=" + _name + "]"; } @@ -573,12 +573,12 @@ public PublicClass (final PrivateClass protectedClass) { this.privateClass = protectedClass; } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @@ -591,12 +591,12 @@ public PrivateClass (String foo) { this.foo = foo; } - @Override + public int hashCode () { return HashCodeBuilder.reflectionHashCode(this); } - @Override + public boolean equals (Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } diff --git a/test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java b/test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java index c56bc9d41..7a671cfb5 100644 --- a/test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java +++ b/test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java @@ -96,10 +96,12 @@ public void testForwardCompatibility () { kryo.register(TestClass.class); kryo.register(Object[].class); TaggedFieldSerializer futureSerializer = new TaggedFieldSerializer(kryo, FutureClass.class); - futureSerializer.getConfig().setSkipUnknownTags(true); + futureSerializer.getTaggedFieldSerializerConfig().setSkipUnknownTags(true); + futureSerializer.updateFields(); kryo.register(FutureClass.class, futureSerializer); - TaggedFieldSerializer futureSerializer2 = new TaggedFieldSerializer(kryo, FutureClass2.class); - futureSerializer.getConfig().setSkipUnknownTags(true); + TaggedFieldSerializer futureSerializer2 = new TaggedFieldSerializer(kryo, FutureClass2.class); + futureSerializer2.getTaggedFieldSerializerConfig().setSkipUnknownTags(true); + futureSerializer2.updateFields(); kryo.register(FutureClass2.class, futureSerializer2); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); @@ -109,11 +111,13 @@ public void testForwardCompatibility () { byte[] futureArrayData = outStream.toByteArray(); TaggedFieldSerializer presentSerializer = new TaggedFieldSerializer(kryo, FutureClass.class); - presentSerializer.getConfig().setSkipUnknownTags(true); + presentSerializer.getTaggedFieldSerializerConfig().setSkipUnknownTags(true); + presentSerializer.updateFields(); presentSerializer.removeField("futureString"); // simulate past version of application kryo.register(FutureClass.class, presentSerializer); TaggedFieldSerializer presentSerializer2 = new TaggedFieldSerializer(kryo, FutureClass2.class); - presentSerializer2.getConfig().setSkipUnknownTags(true); + presentSerializer2.getTaggedFieldSerializerConfig().setSkipUnknownTags(true); + presentSerializer2.updateFields(); presentSerializer2.removeField("zzz"); // simulate past version of application presentSerializer2.removeField("fc2"); // simulate past version of application kryo.register(FutureClass2.class, presentSerializer2); @@ -186,7 +190,7 @@ static private class FutureClass { @Tag(1) public FutureClass2 futureClass2; @Tag(value = 2, annexed = true) public String futureString = "unchanged"; - @Override + public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; @@ -227,7 +231,7 @@ static private class FutureClass2 { @Tag(value = 3, annexed = true) public int zzz = 123; @Tag(value = 4, annexed = true) public FutureClass2 fc2; - @Override + public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; diff --git a/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java b/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java index ec276a313..bfd01c6ed 100644 --- a/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java +++ b/test/com/esotericsoftware/kryo/WarnUnregisteredClassesTest.java @@ -34,7 +34,7 @@ public class WarnUnregisteredClassesTest extends TestCase { LoggerStub log; - @Override + protected void setUp () throws Exception { super.setUp(); log = new LoggerStub(); @@ -111,7 +111,7 @@ class LoggerStub extends Logger { public List levels = new ArrayList(); public List messages = new ArrayList(); - @Override + public void log (int level, String category, String message, Throwable ex) { levels.add(level); messages.add(message); diff --git a/test/com/esotericsoftware/kryo/pool/KryoPoolBenchmarkTest.java b/test/com/esotericsoftware/kryo/pool/KryoPoolBenchmarkTest.java index fdc7220b1..7fd7d15a3 100644 --- a/test/com/esotericsoftware/kryo/pool/KryoPoolBenchmarkTest.java +++ b/test/com/esotericsoftware/kryo/pool/KryoPoolBenchmarkTest.java @@ -29,19 +29,19 @@ public class KryoPoolBenchmarkTest { - static private final int WARMUP_ITERATIONS = 10000; + static private final int WARMUP_ITERATIONS = 1000; /** Number of runs. */ - static private final int RUN_CNT = 10; + static private final int RUN_CNT = 5; /** Number of iterations. Set it to something rather big for obtaining meaningful results */ // static private final int ITER_CNT = 200000; - static private final int ITER_CNT = 10000; - static private final int SLEEP_BETWEEN_RUNS = 100; + static private final int ITER_CNT = 1000; + static private final int SLEEP_BETWEEN_RUNS = 10; // not private to prevent the synthetic accessor method static KryoFactory factory = new KryoFactory() { - @Override + public Kryo create () { Kryo kryo = new Kryo(); kryo.register(DefaultTypes.class); @@ -105,7 +105,7 @@ private void run (String description, Runnable runnable, final int runCount, fin private void runWithoutPool (final int runCount, final int iterCount, boolean outputResults) throws Exception { run("Without pool", new Runnable() { - @Override + public void run () { factory.create(); } @@ -116,7 +116,7 @@ private void runWithPool (final KryoPool.Builder builder, final int runCount, fi throws Exception { final KryoPool pool = builder.build(); run("With pool " + builder.toString(), new Runnable() { - @Override + public void run () { Kryo kryo = pool.borrow(); pool.release(kryo); @@ -150,19 +150,15 @@ public SampleObject () { this.str = str; } - /** {@inheritDoc} */ - @Override public boolean equals (Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; SampleObject obj = (SampleObject)other; - return intVal == obj.intVal && floatVal == obj.floatVal && shortVal.equals(obj.shortVal) && Arrays.equals(dblArr, obj.dblArr) && Arrays.equals(longArr, obj.longArr) && (str == null ? obj.str == null : str.equals(obj.str)); } } - } diff --git a/test/com/esotericsoftware/kryo/pool/KryoPoolTest.java b/test/com/esotericsoftware/kryo/pool/KryoPoolTest.java index 77b815d03..336e3f64d 100644 --- a/test/com/esotericsoftware/kryo/pool/KryoPoolTest.java +++ b/test/com/esotericsoftware/kryo/pool/KryoPoolTest.java @@ -36,7 +36,7 @@ public class KryoPoolTest { static private KryoFactory factory = new KryoFactory() { - @Override + public Kryo create () { Kryo kryo = new Kryo(); // configure kryo @@ -96,7 +96,7 @@ public void testSize () { @Test public void runWithKryoShouldReturnResult () { String value = pool.run(new KryoCallback() { - @Override + public String execute (Kryo kryo) { return "foo"; } @@ -108,7 +108,7 @@ public String execute (Kryo kryo) { public void runWithKryoShouldAddKryoToPool () { assertEquals(0, size()); pool.run(new KryoCallback() { - @Override + public String execute (Kryo kryo) { return null; } @@ -121,7 +121,7 @@ public void runWithKryoShouldAddKryoToPoolOnException () { assertEquals(0, size()); try { pool.run(new KryoCallback() { - @Override + public String execute (Kryo kryo) { throw new RuntimeException(); } @@ -136,7 +136,7 @@ public String execute (Kryo kryo) { @Test(expected = IllegalArgumentException.class) public void runWithKryoShouldRethrowException () { String value = pool.run(new KryoCallback() { - @Override + public String execute (Kryo kryo) { throw new IllegalArgumentException(); } diff --git a/test/com/esotericsoftware/kryo/serializers/Java8ClosureSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/Java8ClosureSerializerTest.java index d34a65166..83031dd6f 100644 --- a/test/com/esotericsoftware/kryo/serializers/Java8ClosureSerializerTest.java +++ b/test/com/esotericsoftware/kryo/serializers/Java8ClosureSerializerTest.java @@ -25,17 +25,16 @@ import org.junit.Assert; import org.objenesis.strategy.StdInstantiatorStrategy; -import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoTestCase; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; /** Test for java 8 closures. * * For jdk < 1.8 excluded from surefire tests via the "until-java8" profile in pom.xml which excludes "Java8*Tests". */ public class Java8ClosureSerializerTest extends KryoTestCase { - public void setUp () throws Exception { super.setUp(); - kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); + kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); // the following registrations are needed because registration is required kryo.register(Object[].class); kryo.register(java.lang.Class.class); @@ -47,17 +46,16 @@ public void setUp () throws Exception { public void testSerializeSerializableLambdaWithKryo () throws Exception { Callable doNothing = (Callable & java.io.Serializable)( () -> true); - roundTrip(222, doNothing); + roundTrip(175, doNothing); } // we must override equals as lambdas have no equals check built in... - @Override + protected void doAssertEquals (Object object1, Object object2) { try { - Assert.assertEquals(((Callable)object1).call(), ((Callable)object2).call()); + Assert.assertEquals(((Callable)object1).call(), ((Callable)object2).call()); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } - } diff --git a/test/com/esotericsoftware/kryo/serializers/Java8OptionalSerializersTest.java b/test/com/esotericsoftware/kryo/serializers/Java8OptionalSerializersTest.java index edfa39269..049d3c988 100644 --- a/test/com/esotericsoftware/kryo/serializers/Java8OptionalSerializersTest.java +++ b/test/com/esotericsoftware/kryo/serializers/Java8OptionalSerializersTest.java @@ -38,7 +38,7 @@ public class Java8OptionalSerializersTest extends KryoTestCase { supportsCopy = true; } - @Override + @Before public void setUp () throws Exception { super.setUp(); @@ -87,7 +87,7 @@ public TestClass (Optional maybe) { this.maybe = maybe; } - @Override + public boolean equals (Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -96,7 +96,7 @@ public boolean equals (Object o) { } - @Override + public int hashCode () { return Objects.hashCode(maybe); } diff --git a/test/com/esotericsoftware/kryo/serializers/Java8TimeSerializersTest.java b/test/com/esotericsoftware/kryo/serializers/Java8TimeSerializersTest.java index ab617de19..862745711 100644 --- a/test/com/esotericsoftware/kryo/serializers/Java8TimeSerializersTest.java +++ b/test/com/esotericsoftware/kryo/serializers/Java8TimeSerializersTest.java @@ -44,7 +44,7 @@ * "Java8*Tests". */ public class Java8TimeSerializersTest extends KryoTestCase { - @Override + @Before public void setUp () throws Exception { super.setUp(); @@ -145,7 +145,8 @@ public void testOffsetDateTime () { public void testZonedDateTime () { roundTrip(11, ZonedDateTime.of(Year.MIN_VALUE, Month.JANUARY.getValue(), 1, 0, 0, 0, 0, ZoneOffset.UTC)); roundTrip(22, ZonedDateTime.of(Year.MIN_VALUE, Month.JANUARY.getValue(), 1, 0, 0, 0, 0, ZoneId.of("Europe/Berlin"))); - roundTrip(29, ZonedDateTime.of(Year.MAX_VALUE, Month.DECEMBER.getValue(), 31, 23, 59, 59, 999999999, ZoneId.of("Europe/Berlin"))); + roundTrip(29, + ZonedDateTime.of(Year.MAX_VALUE, Month.DECEMBER.getValue(), 31, 23, 59, 59, 999999999, ZoneId.of("Europe/Berlin"))); } @Test

* Note that the field data is identified by name. The situation where a super class has a field with the same name as a subclass * must be avoided. - * @author Nathan Sweet */ -public class CompatibleFieldSerializer extends FieldSerializer { + * @author Nathan Sweet */ +public class CompatibleFieldSerializer extends FieldSerializer { /* For object with more than BINARY_SEARCH_THRESHOLD fields, use binary search instead of iterative search */ static private final int THRESHOLD_BINARY_SEARCH = 32; @@ -59,13 +60,21 @@ public void write (Kryo kryo, Output output, T object) { if (!context.containsKey(this)) { context.put(this, null); if (TRACE) trace("kryo", "Write " + fields.length + " field names."); - output.writeVarInt(fields.length, true); + output.writeInt(fields.length, true); for (int i = 0, n = fields.length; i < n; i++) - output.writeString(getCachedFieldName(fields[i])); + output.writeString(fields[i].name); } OutputChunked outputChunked = new OutputChunked(output, 1024); for (int i = 0, n = fields.length; i < n; i++) { + if (TRACE) try { + Object x = fields[i].getField().get(object); + trace("kryo", "Write value: " + Util.string(x)); + if (x != null && x.toString().equals("1234120")) // + System.out.println(); + } catch (IllegalArgumentException ex) { + } catch (IllegalAccessException ex) { + } fields[i].write(outputChunked, object); outputChunked.endChunks(); } @@ -77,7 +86,7 @@ public T read (Kryo kryo, Input input, Class type) { ObjectMap context = kryo.getGraphContext(); CachedField[] fields = (CachedField[])context.get(this); if (fields == null) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (TRACE) trace("kryo", "Read " + length + " field names."); String[] names = new String[length]; for (int i = 0; i < length; i++) @@ -91,7 +100,7 @@ public T read (Kryo kryo, Input input, Class type) { for (int i = 0; i < length; i++) { String schemaName = names[i]; for (int ii = 0, nn = allFields.length; ii < nn; ii++) { - if (getCachedFieldName(allFields[ii]).equals(schemaName)) { + if (allFields[ii].name.equals(schemaName)) { fields[i] = allFields[ii]; continue outer; } @@ -112,7 +121,7 @@ public T read (Kryo kryo, Input input, Class type) { while (low <= high) { mid = (low + high) >>> 1; - String midVal = getCachedFieldName(allFields[mid]); + String midVal = allFields[mid].name; compare = schemaName.compareTo(midVal); if (compare < 0) { @@ -139,7 +148,7 @@ public T read (Kryo kryo, Input input, Class type) { // Generic type used to instantiate this field could have // been changed in the meantime. Therefore take the most // up-to-date definition of a field - cachedField = getField(getCachedFieldName(cachedField)); + cachedField = getField(cachedField.name); } if (cachedField == null) { if (TRACE) trace("kryo", "Skip obsolete field."); diff --git a/src/com/esotericsoftware/kryo/serializers/DefaultArraySerializers.java b/src/com/esotericsoftware/kryo/serializers/DefaultArraySerializers.java index 382bb9ea0..46cec9fb4 100644 --- a/src/com/esotericsoftware/kryo/serializers/DefaultArraySerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/DefaultArraySerializers.java @@ -33,7 +33,7 @@ /** Contains many serializer classes for specific array types that are provided by {@link Kryo#addDefaultSerializer(Class, Class) * default}. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class DefaultArraySerializers { static public class ByteArraySerializer extends Serializer { { @@ -42,15 +42,15 @@ static public class ByteArraySerializer extends Serializer { public void write (Kryo kryo, Output output, byte[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeBytes(object); } public byte[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readBytes(length - 1); } @@ -69,15 +69,15 @@ static public class IntArraySerializer extends Serializer { public void write (Kryo kryo, Output output, int[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeInts(object, false); } public int[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readInts(length - 1, false); } @@ -96,15 +96,15 @@ static public class FloatArraySerializer extends Serializer { public void write (Kryo kryo, Output output, float[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeFloats(object); } public float[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readFloats(length - 1); } @@ -123,15 +123,15 @@ static public class LongArraySerializer extends Serializer { public void write (Kryo kryo, Output output, long[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeLongs(object, false); } public long[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readLongs(length - 1, false); } @@ -150,15 +150,15 @@ static public class ShortArraySerializer extends Serializer { public void write (Kryo kryo, Output output, short[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeShorts(object); } public short[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readShorts(length - 1); } @@ -177,15 +177,15 @@ static public class CharArraySerializer extends Serializer { public void write (Kryo kryo, Output output, char[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeChars(object); } public char[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readChars(length - 1); } @@ -204,15 +204,15 @@ static public class DoubleArraySerializer extends Serializer { public void write (Kryo kryo, Output output, double[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); output.writeDoubles(object); } public double[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; return input.readDoubles(length - 1); } @@ -231,16 +231,16 @@ static public class BooleanArraySerializer extends Serializer { public void write (Kryo kryo, Output output, boolean[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeBoolean(object[i]); } public boolean[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; boolean[] array = new boolean[--length]; for (int i = 0; i < length; i++) @@ -262,10 +262,10 @@ static public class StringArraySerializer extends Serializer { public void write (Kryo kryo, Output output, String[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); if (kryo.getReferences() && kryo.getReferenceResolver().useReferences(String.class)) { Serializer serializer = kryo.getSerializer(String.class); for (int i = 0, n = object.length; i < n; i++) @@ -277,7 +277,7 @@ public void write (Kryo kryo, Output output, String[] object) { } public String[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; String[] array = new String[--length]; if (kryo.getReferences() && kryo.getReferenceResolver().useReferences(String.class)) { @@ -318,14 +318,13 @@ public ObjectArraySerializer (Kryo kryo, Class type) { public void write (Kryo kryo, Output output, Object[] object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.length + 1, true); + output.writeInt(object.length + 1, true); Class elementClass = object.getClass().getComponentType(); if (elementsAreSameType || Modifier.isFinal(elementClass.getModifiers())) { Serializer elementSerializer = kryo.getSerializer(elementClass); -// if(generics!=null) elementSerializer.setGenerics(kryo, generics); for (int i = 0, n = object.length; i < n; i++) { if (elementsCanBeNull) @@ -334,47 +333,24 @@ public void write (Kryo kryo, Output output, Object[] object) { kryo.writeObject(output, object[i], elementSerializer); } } else { -// Generics genericsScope = null; -// Class componentType = type; -// while(componentType.getComponentType() != null) { -// componentType = componentType.getComponentType(); -// } -// TypeVariable[] typeVars = type.getComponentType().getTypeParameters(); -// if(typeVars != null && generics != null) { -// if(TRACE) trace("kryo", "Creating a new GenericsScope for " + type.getName() + " with type vars: " + -// Arrays.toString(typeVars)); -// genericsScope = new Generics(); -// int i = 0; -// for(TypeVariable typeVar: typeVars) { -// genericsScope.add(typeVar.getName(), generics[i]); -// i++; -// } -// kryo.pushGenericsScope(type, genericsScope); -// } -// for (int i = 0, n = object.length; i < n; i++) { - // Propagate generics? if (object[i] != null) { Serializer serializer = kryo.getSerializer(object[i].getClass()); serializer.setGenerics(kryo, generics); } kryo.writeClassAndObject(output, object[i]); } - -// if(genericsScope != null) -// kryo.popGenericsScope(); } } public Object[] read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; Object[] object = (Object[])Array.newInstance(type.getComponentType(), length - 1); kryo.reference(object); Class elementClass = object.getClass().getComponentType(); if (elementsAreSameType || Modifier.isFinal(elementClass.getModifiers())) { Serializer elementSerializer = kryo.getSerializer(elementClass); -// if(generics!=null) elementSerializer.setGenerics(kryo, generics); for (int i = 0, n = object.length; i < n; i++) { if (elementsCanBeNull) @@ -384,7 +360,6 @@ public Object[] read (Kryo kryo, Input input, Class type) { } } else { for (int i = 0, n = object.length; i < n; i++) { - // Propagate generics Registration registration = kryo.readClass(input); if (registration != null) { registration.getSerializer().setGenerics(kryo, generics); @@ -418,7 +393,7 @@ public void setElementsAreSameType (boolean elementsAreSameType) { } public void setGenerics (Kryo kryo, Class[] generics) { - if (TRACE) trace("kryo", "setting generics for ObjectArraySerializer"); + if (TRACE) trace("kryo", "Setting generics for ObjectArraySerializer."); this.generics = generics; } } diff --git a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java index 0f3432025..b46504e4d 100644 --- a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java @@ -56,15 +56,10 @@ import com.esotericsoftware.kryo.io.Output; /** Contains many serializer classes that are provided by {@link Kryo#addDefaultSerializer(Class, Class) default}. - * @author Nathan Sweet */ + * @author Nathan Sweet */ public class DefaultSerializers { - static public class VoidSerializer extends Serializer { - { - setImmutable(true); - } - + static public class VoidSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Object object) { - } public Object read (Kryo kryo, Input input, Class type) { @@ -72,11 +67,7 @@ public Object read (Kryo kryo, Input input, Class type) { } } - static public class BooleanSerializer extends Serializer { - { - setImmutable(true); - } - + static public class BooleanSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Boolean object) { output.writeBoolean(object); } @@ -86,11 +77,7 @@ public Boolean read (Kryo kryo, Input input, Class type) { } } - static public class ByteSerializer extends Serializer { - { - setImmutable(true); - } - + static public class ByteSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Byte object) { output.writeByte(object); } @@ -100,11 +87,7 @@ public Byte read (Kryo kryo, Input input, Class type) { } } - static public class CharSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CharSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Character object) { output.writeChar(object); } @@ -114,11 +97,7 @@ public Character read (Kryo kryo, Input input, Class type) { } } - static public class ShortSerializer extends Serializer { - { - setImmutable(true); - } - + static public class ShortSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Short object) { output.writeShort(object); } @@ -128,11 +107,7 @@ public Short read (Kryo kryo, Input input, Class type) { } } - static public class IntSerializer extends Serializer { - { - setImmutable(true); - } - + static public class IntSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Integer object) { output.writeInt(object, false); } @@ -142,11 +117,7 @@ public Integer read (Kryo kryo, Input input, Class type) { } } - static public class LongSerializer extends Serializer { - { - setImmutable(true); - } - + static public class LongSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Long object) { output.writeLong(object, false); } @@ -156,11 +127,7 @@ public Long read (Kryo kryo, Input input, Class type) { } } - static public class FloatSerializer extends Serializer { - { - setImmutable(true); - } - + static public class FloatSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Float object) { output.writeFloat(object); } @@ -170,11 +137,7 @@ public Float read (Kryo kryo, Input input, Class type) { } } - static public class DoubleSerializer extends Serializer { - { - setImmutable(true); - } - + static public class DoubleSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Double object) { output.writeDouble(object); } @@ -185,9 +148,8 @@ public Double read (Kryo kryo, Input input, Class type) { } /** @see Output#writeString(String) */ - static public class StringSerializer extends Serializer { + static public class StringSerializer extends ImmutableSerializer { { - setImmutable(true); setAcceptsNull(true); } @@ -202,32 +164,31 @@ public String read (Kryo kryo, Input input, Class type) { /** Serializer for {@link BigInteger} and any subclass. * @author Tumi (enhacements) */ - static public class BigIntegerSerializer extends Serializer { + static public class BigIntegerSerializer extends ImmutableSerializer { { - setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, BigInteger object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } BigInteger value = (BigInteger)object; // fast-path optimizations for BigInteger.ZERO constant if (value == BigInteger.ZERO) { - output.writeVarInt(2, true); + output.writeInt(2, true); output.writeByte(0); return; } // default behaviour byte[] bytes = value.toByteArray(); - output.writeVarInt(bytes.length + 1, true); + output.writeInt(bytes.length + 1, true); output.writeBytes(bytes); } public BigInteger read (Kryo kryo, Input input, Class type) { - int length = input.readVarInt(true); + int length = input.readInt(true); if (length == NULL) return null; byte[] bytes = input.readBytes(length - 1); if (type != BigInteger.class && type != null) { @@ -262,17 +223,16 @@ public BigInteger read (Kryo kryo, Input input, Class type) { /** Serializer for {@link BigDecimal} and any subclass. * @author Tumi (enhacements) */ - static public class BigDecimalSerializer extends Serializer { + static public class BigDecimalSerializer extends ImmutableSerializer { private final BigIntegerSerializer bigIntegerSerializer = new BigIntegerSerializer(); { setAcceptsNull(true); - setImmutable(true); } public void write (Kryo kryo, Output output, BigDecimal object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } BigDecimal value = (BigDecimal)object; @@ -315,15 +275,14 @@ public BigDecimal read (Kryo kryo, Input input, Class type) { } } - static public class ClassSerializer extends Serializer { + static public class ClassSerializer extends ImmutableSerializer { { - setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, Class object) { kryo.writeClass(output, object); - output.writeByte((object != null && object.isPrimitive()) ? 1 : 0); + output.writeByte(object != null && object.isPrimitive() ? 1 : 0); } public Class read (Kryo kryo, Input input, Class type) { @@ -331,7 +290,7 @@ public Class read (Kryo kryo, Input input, Class type) { int isPrimitive = input.read(); Class typ = registration != null ? registration.getType() : null; if (typ == null || !typ.isPrimitive()) return typ; - return (isPrimitive == 1) ? typ : getWrapperClass(typ); + return isPrimitive == 1 ? typ : getWrapperClass(typ); } } @@ -384,9 +343,8 @@ public Date copy (Kryo kryo, Date original) { } } - static public class EnumSerializer extends Serializer { + static public class EnumSerializer extends ImmutableSerializer { { - setImmutable(true); setAcceptsNull(true); } @@ -399,14 +357,14 @@ public EnumSerializer (Class type) { public void write (Kryo kryo, Output output, Enum object) { if (object == null) { - output.writeVarInt(NULL, true); + output.writeInt(NULL, true); return; } - output.writeVarInt(object.ordinal() + 1, true); + output.writeInt(object.ordinal() + 1, true); } public Enum read (Kryo kryo, Input input, Class type) { - int ordinal = input.readVarInt(true); + int ordinal = input.readInt(true); if (ordinal == NULL) return null; ordinal--; if (ordinal < 0 || ordinal > enumConstants.length - 1) @@ -447,9 +405,8 @@ public EnumSet copy (Kryo kryo, EnumSet original) { } /** @author Martin Grotzke */ - static public class CurrencySerializer extends Serializer { + static public class CurrencySerializer extends ImmutableSerializer { { - setImmutable(true); setAcceptsNull(true); } @@ -520,11 +477,7 @@ public KryoSerializable read (Kryo kryo, Input input, Class ty /** Serializer for lists created via {@link Collections#emptyList()} or that were just assigned the * {@link Collections#EMPTY_LIST}. * @author Martin Grotzke */ - static public class CollectionsEmptyListSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsEmptyListSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Object object) { } @@ -536,11 +489,7 @@ public Object read (Kryo kryo, Input input, Class type) { /** Serializer for maps created via {@link Collections#emptyMap()} or that were just assigned the * {@link Collections#EMPTY_MAP}. * @author Martin Grotzke */ - static public class CollectionsEmptyMapSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsEmptyMapSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Object object) { } @@ -552,11 +501,7 @@ public Object read (Kryo kryo, Input input, Class type) { /** Serializer for sets created via {@link Collections#emptySet()} or that were just assigned the * {@link Collections#EMPTY_SET}. * @author Martin Grotzke */ - static public class CollectionsEmptySetSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsEmptySetSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Object object) { } @@ -567,11 +512,7 @@ public Object read (Kryo kryo, Input input, Class type) { /** Serializer for lists created via {@link Collections#singletonList(Object)}. * @author Martin Grotzke */ - static public class CollectionsSingletonListSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsSingletonListSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, List object) { kryo.writeClassAndObject(output, object.get(0)); } @@ -583,11 +524,7 @@ public List read (Kryo kryo, Input input, Class type) { /** Serializer for maps created via {@link Collections#singletonMap(Object, Object)}. * @author Martin Grotzke */ - static public class CollectionsSingletonMapSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsSingletonMapSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Map object) { Entry entry = (Entry)object.entrySet().iterator().next(); kryo.writeClassAndObject(output, entry.getKey()); @@ -603,11 +540,7 @@ public Map read (Kryo kryo, Input input, Class type) { /** Serializer for sets created via {@link Collections#singleton(Object)}. * @author Martin Grotzke */ - static public class CollectionsSingletonSetSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CollectionsSingletonSetSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Set object) { kryo.writeClassAndObject(output, object.iterator().next()); } @@ -619,11 +552,7 @@ public Set read (Kryo kryo, Input input, Class type) { /** Serializer for {@link TimeZone}. Assumes the timezones are immutable. * @author Tumi */ - static public class TimeZoneSerializer extends Serializer { - { - setImmutable(true); - } - + static public class TimeZoneSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, TimeZone object) { output.writeString(object.getID()); } @@ -746,15 +675,11 @@ private TreeSet createTreeSet (Class type, Comparator comp /** Serializer for {@link Locale} (immutables). * @author Tumi */ - static public class LocaleSerializer extends Serializer { + static public class LocaleSerializer extends ImmutableSerializer { // Missing constants in j.u.Locale for common locale static public final Locale SPANISH = new Locale("es", "", ""); static public final Locale SPAIN = new Locale("es", "ES", ""); - { - setImmutable(true); - } - protected Locale create (String language, String country, String variant) { // Fast-path for default locale in this system (may not be in the Locale constants list) Locale defaultLocale = Locale.getDefault(); @@ -813,11 +738,7 @@ protected static boolean isSameLocale (Locale locale, String language, String co } /** Serializer for {@link Charset}. */ - static public class CharsetSerializer extends Serializer { - { - setImmutable(true); - } - + static public class CharsetSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, Charset object) { output.writeString(object.name()); } @@ -828,11 +749,7 @@ public Charset read (Kryo kryo, Input input, Class type) { } /** Serializer for {@link URL}. */ - static public class URLSerializer extends Serializer { - { - setImmutable(true); - } - + static public class URLSerializer extends ImmutableSerializer { public void write (Kryo kryo, Output output, URL object) { output.writeString(object.toExternalForm()); } diff --git a/src/com/esotericsoftware/kryo/serializers/EnumNameSerializer.java b/src/com/esotericsoftware/kryo/serializers/EnumNameSerializer.java index d59749bea..eb604086d 100644 --- a/src/com/esotericsoftware/kryo/serializers/EnumNameSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/EnumNameSerializer.java @@ -9,14 +9,13 @@ /** Serializes enums using the enum's name. This prevents invalidating previously serialized byts when the enum order changes. * @author KwonNam Son */ -public class EnumNameSerializer extends Serializer { +public class EnumNameSerializer extends ImmutableSerializer { private final Class enumType; private final Serializer stringSerializer; public EnumNameSerializer (Kryo kryo, Class type) { this.enumType = type; stringSerializer = kryo.getSerializer(String.class); - setImmutable(true); } public void write (Kryo kryo, Output output, Enum object) { diff --git a/src/com/esotericsoftware/kryo/serializers/ExternalizableSerializer.java b/src/com/esotericsoftware/kryo/serializers/ExternalizableSerializer.java index 1adf9ef87..f0889aac3 100644 --- a/src/com/esotericsoftware/kryo/serializers/ExternalizableSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/ExternalizableSerializer.java @@ -48,7 +48,6 @@ public class ExternalizableSerializer extends Serializer { private KryoObjectInput objectInput = null; private KryoObjectOutput objectOutput = null; - @Override public void write (Kryo kryo, Output output, Object object) { JavaSerializer serializer = getJavaSerializerIfRequired(object.getClass()); if (serializer == null) { @@ -58,7 +57,6 @@ public void write (Kryo kryo, Output output, Object object) { } } - @Override public Object read (Kryo kryo, Input input, Class type) { JavaSerializer serializer = getJavaSerializerIfRequired(type); if (serializer == null) { @@ -124,7 +122,7 @@ private JavaSerializer getJavaSerializerIfRequired (Class type) { private JavaSerializer getCachedSerializer (Class type) { if (javaSerializerByType == null) { - javaSerializerByType = new ObjectMap(); + javaSerializerByType = new ObjectMap(); return null; } return javaSerializerByType.get(type); @@ -137,7 +135,7 @@ private boolean isJavaSerializerRequired (Class type) { /* find out if there are any pesky serialization extras on this class */ static private boolean hasInheritableReplaceMethod (Class type, String methodName) { Method method = null; - Class current = type; + Class current = type; while (current != null) { try { method = current.getDeclaredMethod(methodName); diff --git a/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java index e800f37f4..8ceb0604a 100644 --- a/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java @@ -26,25 +26,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.security.AccessControlException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.NotNull; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import com.esotericsoftware.kryo.util.IntArray; -import com.esotericsoftware.kryo.util.ObjectMap; -import com.esotericsoftware.kryo.util.Util; import com.esotericsoftware.reflectasm.FieldAccess; // BOZO - Make primitive serialization with ReflectASM configurable? @@ -60,356 +47,65 @@ * @see VersionFieldSerializer * @see TaggedFieldSerializer * @see CompatibleFieldSerializer - * @author Nathan Sweet + * @author Nathan Sweet * @author Roman Levenstein */ -public class FieldSerializer extends Serializer - implements Comparator { - +public class FieldSerializer extends Serializer { final Kryo kryo; final Class type; - /** type variables declared for this type */ - final TypeVariable[] typeParameters; final Class componentType; - protected final C config; - private CachedField[] fields = new CachedField[0]; - private CachedField[] transientFields = new CachedField[0]; - protected HashSet removedFields = new HashSet(); - Object access; - - private FieldSerializerGenericsUtil genericsUtil; - - private FieldSerializerAnnotationsUtil annotationsUtil; - - /** Concrete classes passed as values for type variables */ - private Class[] generics; - - private Generics genericsScope; - - /** If set, this serializer tries to use a variable length encoding for int and long fields */ - private boolean varIntsEnabled; - - private boolean hasObjectFields = false; - - static CachedFieldFactory asmFieldFactory; - static CachedFieldFactory objectFieldFactory; - - { - varIntsEnabled = true; - if (TRACE) trace("kryo", "Optimize ints: " + varIntsEnabled); - } + final FieldSerializerConfig config; + final CachedFields cachedFields; + final TypeVariable[] typeParameters; public FieldSerializer (Kryo kryo, Class type) { this(kryo, type, null); } public FieldSerializer (Kryo kryo, Class type, Class[] generics) { - this(kryo, type, generics, (C)new FieldSerializerConfig()); + this(kryo, type, generics, new FieldSerializerConfig()); } - public FieldSerializer (Kryo kryo, Class type, Class[] generics, C config) { + public FieldSerializer (Kryo kryo, Class type, Class[] generics, FieldSerializerConfig config) { if (config == null) throw new IllegalArgumentException("config cannot be null."); - this.config = config; this.kryo = kryo; this.type = type; - this.generics = generics; - this.typeParameters = type.getTypeParameters(); - if (this.typeParameters == null || this.typeParameters.length == 0) - this.componentType = type.getComponentType(); - else - this.componentType = null; - this.genericsUtil = new FieldSerializerGenericsUtil(this); - this.annotationsUtil = new FieldSerializerAnnotationsUtil(this); - rebuildCachedFields(); - } - - /** Called when the list of cached fields must be rebuilt. This is done any time settings are changed that affect which fields - * will be used. It is called from the constructor for FieldSerializer, but not for subclasses. Subclasses must call this from - * their constructor. */ - protected void rebuildCachedFields () { - rebuildCachedFields(false); - } - - /** Rebuilds the list of cached fields. - * @param minorRebuild if set, processing due to changes in generic type parameters will be optimized */ - protected void rebuildCachedFields (boolean minorRebuild) { - /** TODO: Optimize rebuildCachedFields invocations performed due to changes in generic type parameters */ - - if (TRACE && generics != null) trace("kryo", "Generic type parameters: " + Arrays.toString(generics)); - if (type.isInterface()) { - fields = new CachedField[0]; // No fields to serialize. - return; - } - - hasObjectFields = false; - - if (config.getOptimizedGenerics()) { - // For generic classes, generate a mapping from type variable names to the concrete types - // This mapping is the same for the whole class. - Generics genScope = genericsUtil.buildGenericsScope(type, generics); - genericsScope = genScope; - - // Push proper scopes at serializer construction time - if (genericsScope != null) kryo.getGenericsResolver().pushScope(type, genericsScope); - } - - List validFields; - List validTransientFields; - IntArray useAsm = new IntArray(); - - if (!minorRebuild) { - // Collect all fields. - List allFields = new ArrayList(); - Class nextClass = type; - while (nextClass != Object.class) { - Field[] declaredFields = nextClass.getDeclaredFields(); - if (declaredFields != null) { - for (Field f : declaredFields) { - if (Modifier.isStatic(f.getModifiers())) continue; - allFields.add(f); - } - } - nextClass = nextClass.getSuperclass(); - } - - ObjectMap context = kryo.getContext(); - - // TODO: useAsm is modified as a side effect, this should be pulled out of buildValidFields - // Build a list of valid non-transient fields - validFields = buildValidFields(false, allFields, context, useAsm); - // Build a list of valid transient fields - validTransientFields = buildValidFields(true, allFields, context, useAsm); - - // Use ReflectASM for any public fields. - if (!Util.isAndroid && Modifier.isPublic(type.getModifiers()) && useAsm.indexOf(1) != -1) { - try { - access = FieldAccess.get(type); - } catch (RuntimeException ignored) { - } - } - } else { - // It is a minor rebuild - validFields = buildValidFieldsFromCachedFields(fields, useAsm); - // Build a list of valid transient fields - validTransientFields = buildValidFieldsFromCachedFields(transientFields, useAsm); - } - - List cachedFields = new ArrayList(validFields.size()); - List cachedTransientFields = new ArrayList(validTransientFields.size()); - - // Process non-transient fields - createCachedFields(useAsm, validFields, cachedFields, 0); - // Process transient fields - createCachedFields(useAsm, validTransientFields, cachedTransientFields, validFields.size()); - - Collections.sort(cachedFields, this); - fields = cachedFields.toArray(new CachedField[cachedFields.size()]); - - Collections.sort(cachedTransientFields, this); - transientFields = cachedTransientFields.toArray(new CachedField[cachedTransientFields.size()]); - - initializeCachedFields(); - - if (genericsScope != null) kryo.getGenericsResolver().popScope(); - - if (!minorRebuild) { - for (CachedField field : removedFields) - removeField(field); - } - - annotationsUtil.processAnnotatedFields(this); - } - - private List buildValidFieldsFromCachedFields (CachedField[] cachedFields, IntArray useAsm) { - ArrayList fields = new ArrayList(cachedFields.length); - for (CachedField f : cachedFields) { - fields.add(f.field); - useAsm.add((f.accessIndex > -1) ? 1 : 0); - } - return fields; - } - - private List buildValidFields (boolean transientFields, List allFields, ObjectMap context, IntArray useAsm) { - List result = new ArrayList(allFields.size()); - - for (int i = 0, n = allFields.size(); i < n; i++) { - Field field = allFields.get(i); - - int modifiers = field.getModifiers(); - if (Modifier.isTransient(modifiers) != transientFields) continue; - if (Modifier.isStatic(modifiers)) continue; - if (field.isSynthetic() && config.getIgnoreSyntheticFields()) continue; - - if (!field.isAccessible()) { - if (!config.getSetFieldsAsAccessible()) continue; - try { - field.setAccessible(true); - } catch (AccessControlException ex) { - continue; - } - } - - Optional optional = field.getAnnotation(Optional.class); - if (optional != null && !context.containsKey(optional.value())) continue; + this.config = config; - result.add(field); + typeParameters = type.getTypeParameters(); + if (typeParameters == null || typeParameters.length == 0) + componentType = type.getComponentType(); + else + componentType = null; - // BOZO - Must be public? - useAsm.add( - !Modifier.isFinal(modifiers) && Modifier.isPublic(modifiers) && Modifier.isPublic(field.getType().getModifiers()) ? 1 - : 0); - } - return result; + cachedFields = new CachedFields(this, generics); + cachedFields.update(false); } - private void createCachedFields (IntArray useAsm, List validFields, List cachedFields, int baseIndex) { - for (int i = 0, n = validFields.size(); i < n; i++) { - Field field = validFields.get(i); - int accessIndex = -1; - if (access != null && useAsm.get(baseIndex + i) == 1) accessIndex = ((FieldAccess)access).getIndex(field.getName()); - cachedFields.add(newCachedField(field, cachedFields.size(), accessIndex)); - } + public void updateFields () { + cachedFields.update(false); } public void setGenerics (Kryo kryo, Class[] generics) { - if (!config.getOptimizedGenerics()) return; - this.generics = generics; + if (!config.optimizedGenerics) return; + cachedFields.generics = generics; if (typeParameters != null && typeParameters.length > 0) { // There is no need to rebuild all cached fields from scratch. // Generic parameter types do not affect the set of fields, offsets of fields, // transient and non-transient properties. They only affect the type of // fields and serializers selected for each field. - rebuildCachedFields(true); + cachedFields.update(true); } } /** Get generic type parameters of the class controlled by this serializer. - * @return generic type parameters or null, if there are none. */ + * @return generic type parameters or null, if there are none or {@link FieldSerializerConfig#optimizedGenerics} is false. */ public Class[] getGenerics () { - return generics; + return cachedFields.generics; } protected void initializeCachedFields () { } - CachedField newCachedField (Field field, int fieldIndex, int accessIndex) { - Class[] fieldClass = new Class[] {field.getType()}; - Type fieldGenericType = (config.getOptimizedGenerics()) ? field.getGenericType() : null; - CachedField cachedField; - - if (!config.getOptimizedGenerics() || fieldGenericType == fieldClass[0]) { - // For optimized generics this is a field without generic type parameters - if (TRACE) trace("kryo", "Field " + field.getName() + ": " + fieldClass[0]); - cachedField = newMatchingCachedField(field, accessIndex, fieldClass[0], fieldGenericType, null); - } else { - cachedField = genericsUtil.newCachedFieldOfGenericType(field, accessIndex, fieldClass, fieldGenericType); - } - - if (cachedField instanceof ObjectField) { - hasObjectFields = true; - } - - cachedField.field = field; - cachedField.varIntsEnabled = varIntsEnabled; - cachedField.access = (FieldAccess)access; - cachedField.accessIndex = accessIndex; - cachedField.canBeNull = config.getFieldsCanBeNull() && !fieldClass[0].isPrimitive() - && !field.isAnnotationPresent(NotNull.class); - - // Always use the same serializer for this field if the field's class is final. - if (kryo.isFinal(fieldClass[0]) || config.getFixedFieldTypes()) cachedField.valueClass = fieldClass[0]; - - return cachedField; - } - - CachedField newMatchingCachedField (Field field, int accessIndex, Class fieldClass, Type fieldGenericType, - Class[] fieldGenerics) { - CachedField cachedField; - if (accessIndex != -1) { - cachedField = getAsmFieldFactory().createCachedField(fieldClass, field, this); - } else { - cachedField = getObjectFieldFactory().createCachedField(fieldClass, field, this); - if (config.getOptimizedGenerics()) { - if (fieldGenerics != null) - ((ObjectField)cachedField).generics = fieldGenerics; - else if (fieldGenericType != null) { - Class[] cachedFieldGenerics = FieldSerializerGenericsUtil.getGenerics(fieldGenericType, kryo); - ((ObjectField)cachedField).generics = cachedFieldGenerics; - if (TRACE) trace("kryo", "Field generics: " + Arrays.toString(cachedFieldGenerics)); - } - } - } - return cachedField; - } - - private CachedFieldFactory getAsmFieldFactory () { - if (asmFieldFactory == null) asmFieldFactory = new AsmCachedFieldFactory(); - return asmFieldFactory; - } - - private CachedFieldFactory getObjectFieldFactory () { - if (objectFieldFactory == null) objectFieldFactory = new ObjectCachedFieldFactory(); - return objectFieldFactory; - } - - public int compare (CachedField o1, CachedField o2) { - // Fields are sorted by alpha so the order of the data is known. - return getCachedFieldName(o1).compareTo(getCachedFieldName(o2)); - } - - /** Sets the default value for {@link CachedField#setCanBeNull(boolean)}. Calling this method resets the {@link #getFields() - * cached fields}. - * @param fieldsCanBeNull False if none of the fields are null. Saves 0-1 byte per field. True if it is not known (default). */ - public void setFieldsCanBeNull (boolean fieldsCanBeNull) { - config.setFieldsCanBeNull(fieldsCanBeNull); - rebuildCachedFields(); - } - - /** Controls which fields are serialized. Calling this method resets the {@link #getFields() cached fields}. - * @param setFieldsAsAccessible If true, all non-transient fields (inlcuding private fields) will be serialized and - * {@link Field#setAccessible(boolean) set as accessible} if necessary (default). If false, only fields in the public - * API will be serialized. */ - public void setFieldsAsAccessible (boolean setFieldsAsAccessible) { - config.setFieldsAsAccessible(setFieldsAsAccessible); - rebuildCachedFields(); - } - - /** Controls if synthetic fields are serialized. Default is true. Calling this method resets the {@link #getFields() cached - * fields}. - * @param ignoreSyntheticFields If true, only non-synthetic fields will be serialized. */ - public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) { - config.setIgnoreSyntheticFields(ignoreSyntheticFields); - rebuildCachedFields(); - } - - /** Sets the default value for {@link CachedField#setClass(Class)} to the field's declared type. This allows FieldSerializer to - * be more efficient, since it knows field values will not be a subclass of their declared type. Default is false. Calling this - * method resets the {@link #getFields() cached fields}. */ - public void setFixedFieldTypes (boolean fixedFieldTypes) { - config.setFixedFieldTypes(fixedFieldTypes); - rebuildCachedFields(); - } - - // Enable/disable copying of transient fields - public void setCopyTransient (boolean setCopyTransient) { - config.setCopyTransient(setCopyTransient); - } - - // Enable/disable serialization of transient fields - public void setSerializeTransient (boolean setSerializeTransient) { - config.setSerializeTransient(setSerializeTransient); - } - - /** Controls if the serialization of generics should be optimized for smaller size. - *