pax_global_header00006660000000000000000000000064121057563470014524gustar00rootroot0000000000000052 comment=928c81e4e20cb237bcc4d71e747409d0b1c86e92 libkryo-java-2.20/000077500000000000000000000000001210575634700140415ustar00rootroot00000000000000libkryo-java-2.20/java/000077500000000000000000000000001210575634700147625ustar00rootroot00000000000000libkryo-java-2.20/java/.classpath000066400000000000000000000012221210575634700167420ustar00rootroot00000000000000 libkryo-java-2.20/java/.project000066400000000000000000000005751210575634700164400ustar00rootroot00000000000000 kryo2 org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature libkryo-java-2.20/java/.settings/000077500000000000000000000000001210575634700167005ustar00rootroot00000000000000libkryo-java-2.20/java/.settings/org.eclipse.jdt.core.prefs000066400000000000000000000156241210575634700236720ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.doc.comment.support=enabled org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning org.eclipse.jdt.core.compiler.problem.deadCode=ignore org.eclipse.jdt.core.compiler.problem.deprecation=ignore org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=warning org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled 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.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled org.eclipse.jdt.core.compiler.problem.unusedImport=ignore org.eclipse.jdt.core.compiler.problem.unusedLabel=warning org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning libkryo-java-2.20/java/license.txt000066400000000000000000000027311210575634700171500ustar00rootroot00000000000000Copyright (c) 2008, 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. libkryo-java-2.20/java/pom.xml000066400000000000000000000116461210575634700163070ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.esotericsoftware.kryo kryo 2.20-SNAPSHOT jar Kryo Fast, efficient Java serialization http://code.google.com/p/kryo/ New BSD License http://www.opensource.org/licenses/bsd-license.php repo http://kryo.googlecode.com/svn/ scm:svn:http://kryo.googlecode.com/svn/ nathan.sweet Nathan Sweet nathan.sweet@gmail.com UTF-8 com.esotericsoftware.reflectasm reflectasm 1.07 shaded com.esotericsoftware.minlog minlog 1.2 org.objenesis objenesis 1.2 junit junit 4.8.2 test src test org.apache.maven.plugins maven-compiler-plugin true 1.5 1.5 utf-8 maven-resources-plugin 2.5 default-resources none default-testResources none org.apache.maven.plugins maven-jar-plugin 2.4 **/.svn/* org.apache.maven.plugins maven-shade-plugin 1.7 true shaded com.esotericsoftware.reflectasm:reflectasm:shaded com.esotericsoftware.minlog:minlog org.objenesis:objenesis org.objenesis com.esotericsoftware.shaded.org.objenesis package shade requireSnapshot maven-enforcer-plugin enforce "${project.version}".endsWith("-SNAPSHOT") Jenkins should only build -SNAPSHOT versions true sonatype-releases sonatype releases repo https://oss.sonatype.org/content/repositories/releases libkryo-java-2.20/java/project.yaml000066400000000000000000000001151210575634700173110ustar00rootroot00000000000000name: kryo version: 2.20 --- Build.build(project); Build.oneJAR(project);libkryo-java-2.20/java/src/000077500000000000000000000000001210575634700155515ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/000077500000000000000000000000001210575634700163275ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/000077500000000000000000000000001210575634700217175ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/kryo/000077500000000000000000000000001210575634700227035ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/kryo/ClassResolver.java000066400000000000000000000027141210575634700263410ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** Handles class registration, writing class identifiers to bytes, and reading class identifiers from bytes. * @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); /** Stores the specified registration. * @see Kryo#register(Registration) */ public Registration register (Registration registration); /** Called when an unregistered type is encountered and {@link Kryo#setRegistrationRequired(boolean)} is false. */ public Registration registerImplicit (Class type); /** Returns the registration for the specified class, or null if the class is not registered. */ public Registration getRegistration (Class type); /** Returns the registration for the specified ID, or null if no class is registered with that ID. */ public Registration getRegistration (int classID); /** Writes a class and returns its registration. * @param type May be null. * @return Will be null if type is null. */ public Registration writeClass (Output output, Class type); /** Reads a class and returns its registration. * @return May be null. */ public Registration readClass (Input input); /** Called by {@link Kryo#reset()}. */ public void reset (); } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/DefaultSerializer.java000066400000000000000000000011651210575634700271670ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** 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 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DefaultSerializer { Class value(); } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/Kryo.java000066400000000000000000001361041210575634700244770ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Currency; import java.util.Date; import java.util.EnumSet; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; import org.objenesis.instantiator.ObjectInstantiator; import org.objenesis.strategy.InstantiatorStrategy; import org.objenesis.strategy.SerializingInstantiatorStrategy; import org.objenesis.strategy.StdInstantiatorStrategy; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.CollectionSerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.BooleanArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ByteArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.CharArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.DoubleArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.FloatArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.IntArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.LongArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ObjectArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ShortArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.StringArraySerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.BigDecimalSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.BigIntegerSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.BooleanSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.ByteSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CalendarSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CharSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.ClassSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsEmptyListSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsEmptyMapSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsEmptySetSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsSingletonListSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsSingletonMapSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CollectionsSingletonSetSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.CurrencySerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.DateSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.DoubleSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.EnumSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.EnumSetSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.FloatSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.IntSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.KryoSerializableSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.LongSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.ShortSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringBufferSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringBuilderSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.TimeZoneSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.TreeMapSerializer; import com.esotericsoftware.kryo.serializers.FieldSerializer; import com.esotericsoftware.kryo.serializers.MapSerializer; import com.esotericsoftware.kryo.util.DefaultClassResolver; 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; import static com.esotericsoftware.kryo.util.Util.*; import static com.esotericsoftware.minlog.Log.*; /** Maps classes to serializers so object graphs can be serialized automatically. * @author Nathan Sweet */ public class Kryo { static public final byte NULL = 0; static public final byte NOT_NULL = 1; static private final int REF = -1; static private final int NO_REF = -2; private Class defaultSerializer = FieldSerializer.class; private final ArrayList defaultSerializers = new ArrayList(32); private final int lowPriorityDefaultSerializerCount; private final ClassResolver classResolver; private int nextRegisterID; private Class memoizedClass; private Registration memoizedClassValue; private ClassLoader classLoader = getClass().getClassLoader(); private InstantiatorStrategy strategy; private boolean registrationRequired; private int depth, maxDepth = Integer.MAX_VALUE; private boolean autoReset = true; private volatile Thread thread; private ObjectMap context, graphContext; private ReferenceResolver referenceResolver; private final IntArray readReferenceIds = new IntArray(0); private boolean references; private Object readObject; private int copyDepth; private boolean copyShallow; private IdentityMap originalToCopy; private Object needsCopyReference; /** Creates a new Kryo with a {@link DefaultClassResolver} and a {@link MapReferenceResolver}. */ public Kryo () { this(new DefaultClassResolver(), new MapReferenceResolver()); } /** Creates a new Kryo with a {@link DefaultClassResolver}. * @param referenceResolver May be null to disable references. */ public Kryo (ReferenceResolver referenceResolver) { this(new DefaultClassResolver(), referenceResolver); } /** @param referenceResolver May be null to disable references. */ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { if (classResolver == null) throw new IllegalArgumentException("classResolver cannot be null."); this.classResolver = classResolver; classResolver.setKryo(this); this.referenceResolver = referenceResolver; if (referenceResolver != null) { referenceResolver.setKryo(this); references = true; } addDefaultSerializer(byte[].class, ByteArraySerializer.class); addDefaultSerializer(char[].class, CharArraySerializer.class); addDefaultSerializer(short[].class, ShortArraySerializer.class); addDefaultSerializer(int[].class, IntArraySerializer.class); addDefaultSerializer(long[].class, LongArraySerializer.class); addDefaultSerializer(float[].class, FloatArraySerializer.class); addDefaultSerializer(double[].class, DoubleArraySerializer.class); addDefaultSerializer(boolean[].class, BooleanArraySerializer.class); addDefaultSerializer(String[].class, StringArraySerializer.class); addDefaultSerializer(Object[].class, ObjectArraySerializer.class); addDefaultSerializer(BigInteger.class, BigIntegerSerializer.class); addDefaultSerializer(BigDecimal.class, BigDecimalSerializer.class); addDefaultSerializer(Class.class, ClassSerializer.class); addDefaultSerializer(Date.class, DateSerializer.class); addDefaultSerializer(Enum.class, EnumSerializer.class); addDefaultSerializer(EnumSet.class, EnumSetSerializer.class); addDefaultSerializer(Currency.class, CurrencySerializer.class); addDefaultSerializer(StringBuffer.class, StringBufferSerializer.class); addDefaultSerializer(StringBuilder.class, StringBuilderSerializer.class); addDefaultSerializer(Collections.EMPTY_LIST.getClass(), CollectionsEmptyListSerializer.class); addDefaultSerializer(Collections.EMPTY_MAP.getClass(), CollectionsEmptyMapSerializer.class); addDefaultSerializer(Collections.EMPTY_SET.getClass(), CollectionsEmptySetSerializer.class); addDefaultSerializer(Collections.singletonList(null).getClass(), CollectionsSingletonListSerializer.class); addDefaultSerializer(Collections.singletonMap(null, null).getClass(), CollectionsSingletonMapSerializer.class); addDefaultSerializer(Collections.singleton(null).getClass(), CollectionsSingletonSetSerializer.class); addDefaultSerializer(Collection.class, CollectionSerializer.class); addDefaultSerializer(TreeMap.class, TreeMapSerializer.class); addDefaultSerializer(Map.class, MapSerializer.class); addDefaultSerializer(KryoSerializable.class, KryoSerializableSerializer.class); addDefaultSerializer(TimeZone.class, TimeZoneSerializer.class); addDefaultSerializer(Calendar.class, CalendarSerializer.class); lowPriorityDefaultSerializerCount = defaultSerializers.size(); // Primitives and string. Primitive wrappers automatically use the same registration as primitives. register(int.class, new IntSerializer()); register(String.class, new StringSerializer()); register(float.class, new FloatSerializer()); register(boolean.class, new BooleanSerializer()); register(byte.class, new ByteSerializer()); register(char.class, new CharSerializer()); register(short.class, new ShortSerializer()); register(long.class, new LongSerializer()); register(double.class, new DoubleSerializer()); } // --- Default serializers --- /** Sets the serailzer to use when no {@link #addDefaultSerializer(Class, Class) default serializers} match an object's type. * Default is {@link FieldSerializer}. * @see #newDefaultSerializer(Class) */ public void setDefaultSerializer (Class serializer) { if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); defaultSerializer = serializer; } /** Instances of the specified class will use the specified serializer. * @see #setDefaultSerializer(Class) */ public void addDefaultSerializer (Class type, Serializer serializer) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); DefaultSerializerEntry entry = new DefaultSerializerEntry(); entry.type = type; entry.serializer = serializer; defaultSerializers.add(defaultSerializers.size() - lowPriorityDefaultSerializerCount, entry); } /** Instances of the specified class will use the specified serializer. Serializer instances are created as needed via * {@link #newSerializer(Class, Class)}. By default, the following classes have a default serializer set: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
booleanBooleanbyteBytechar
CharactershortShortintInteger
longLongfloatFloatdouble
DoubleStringbyte[]char[]short[]
int[]long[]float[]double[]String[]
Object[]MapBigIntegerBigDecimalKryoSerializable
CollectionDateCollections.emptyListCollections.singletonCurrency
StringBuilderEnumCollections.emptyMapCollections.emptySetCalendar
StringBufferClassCollections.singletonListCollections.singletonMapTimeZone
TreeMapEnumSet
*

* Note that the order default serializers are added is important for a class that may match multiple types. The above default * serializers always have a lower priority than subsequent default serializers that are added. */ public void addDefaultSerializer (Class type, Class serializerClass) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (serializerClass == null) throw new IllegalArgumentException("serializerClass cannot be null."); DefaultSerializerEntry entry = new DefaultSerializerEntry(); entry.type = type; entry.serializerClass = serializerClass; defaultSerializers.add(defaultSerializers.size() - lowPriorityDefaultSerializerCount, entry); } /** Returns the best matching serializer for a class. This method can be overridden to implement custom logic to choose a * serializer. */ public Serializer getDefaultSerializer (Class type) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (type.isAnnotationPresent(DefaultSerializer.class)) return newSerializer(((DefaultSerializer)type.getAnnotation(DefaultSerializer.class)).value(), type); for (int i = 0, n = defaultSerializers.size(); i < n; i++) { DefaultSerializerEntry entry = defaultSerializers.get(i); if (entry.type.isAssignableFrom(type)) { if (entry.serializer != null) return entry.serializer; return newSerializer(entry.serializerClass, type); } } return newDefaultSerializer(type); } /** Called by {@link #getDefaultSerializer(Class)} when no default serializers matched the type. Subclasses can override this * method to customize behavior. The default implementation calls {@link #newSerializer(Class, Class)} using the * {@link #setDefaultSerializer(Class) default serializer}. */ protected Serializer newDefaultSerializer (Class type) { return newSerializer(defaultSerializer, 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). */ public Serializer newSerializer (Class serializerClass, Class type) { try { try { return serializerClass.getConstructor(Kryo.class, Class.class).newInstance(this, type); } catch (NoSuchMethodException ex1) { try { return serializerClass.getConstructor(Kryo.class).newInstance(this); } catch (NoSuchMethodException ex2) { try { return serializerClass.getConstructor(Class.class).newInstance(type); } catch (NoSuchMethodException ex3) { return serializerClass.newInstance(); } } } } catch (Exception ex) { throw new IllegalArgumentException("Unable to create serializer \"" + serializerClass.getName() + "\" for class: " + className(type), ex); } } // --- Registration --- /** Registers the class using the lowest, next available integer ID and the {@link Kryo#getDefaultSerializer(Class) default * serializer}. If the class is already registered, the existing entry is updated with the new serializer. Registering a * primitive also affects the corresponding primitive wrapper. *

* Because the ID assigned is affected by the IDs registered before it, the order classes are registered is important when * using this method. The order must be the same at deserialization as it was for serialization. */ public Registration register (Class type) { Registration registration = classResolver.getRegistration(type); if (registration != null) return registration; return register(type, getDefaultSerializer(type)); } /** Registers the class using the specified ID and the {@link Kryo#getDefaultSerializer(Class) default serializer}. If the ID is * already in use by the same type, the old entry is overwritten. If the ID is already in use by a different type, a * {@link KryoException} is thrown. Registering a primitive also affects the corresponding primitive wrapper. *

* IDs must be the same at deserialization as they were for serialization. * @param id Must be >= 0. Smaller IDs are serialized more efficiently. */ public Registration register (Class type, int id) { Registration registration = classResolver.getRegistration(type); if (registration != null) return registration; return register(type, getDefaultSerializer(type), id); } /** Registers the class using the lowest, next available integer ID and the specified serializer. If the class is already * registered, the existing entry is updated with the new serializer. Registering a primitive also affects the corresponding * primitive wrapper. *

* Because the ID assigned is affected by the IDs registered before it, the order classes are registered is important when * using this method. The order must be the same at deserialization as it was for serialization. */ public Registration register (Class type, Serializer serializer) { Registration registration = classResolver.getRegistration(type); if (registration != null) { registration.setSerializer(serializer); return registration; } return classResolver.register(new Registration(type, serializer, getNextRegistrationId())); } /** Registers the class using the specified ID and serializer. If the ID is already in use by the same type, the old entry is * overwritten. If the ID is already in use by a different type, a {@link KryoException} is thrown. Registering a primitive * also affects the corresponding primitive wrapper. *

* IDs must be the same at deserialization as they were for serialization. * @param id Must be >= 0. Smaller IDs are serialized more efficiently. */ public Registration register (Class type, Serializer serializer, int id) { if (id < 0) throw new IllegalArgumentException("id must be >= 0: " + id); return register(new Registration(type, serializer, id)); } /** Stores the specified registration. If the ID is already in use by the same type, the old entry is overwritten. If the ID is * already in use by a different type, a {@link KryoException} is thrown. Registering a primitive also affects the * corresponding primitive wrapper. *

* IDs must be the same at deserialization as they were for serialization. *

* Registration can be suclassed to efficiently store per type information, accessible in serializers via * {@link Kryo#getRegistration(Class)}. */ public Registration register (Registration registration) { int id = registration.getId(); if (id < 0) throw new IllegalArgumentException("id must be > 0: " + id); Registration existing = getRegistration(registration.getId()); if (existing != null && existing.getType() != registration.getType()) { throw new KryoException("An existing registration with a different type already uses ID: " + registration.getId() + "\nExisting registration: " + existing + "\nUnable to set registration: " + registration); } return classResolver.register(registration); } /** Returns the lowest, next available integer ID. */ public int getNextRegistrationId () { int id = nextRegisterID; while (true) { if (classResolver.getRegistration(id) == null) return id; id++; } } /** @throws IllegalArgumentException if the class is not registered and {@link Kryo#setRegistrationRequired(boolean)} is true. * @see ClassResolver#getRegistration(Class) */ public Registration getRegistration (Class type) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (type == memoizedClass) return memoizedClassValue; Registration registration = classResolver.getRegistration(type); if (registration == null) { if (Proxy.isProxyClass(type)) { // If a Proxy class, treat it like an InvocationHandler because the concrete class for a proxy is generated. registration = getRegistration(InvocationHandler.class); } else if (!type.isEnum() && Enum.class.isAssignableFrom(type)) { // This handles an enum value that is an inner class. Eg: enum A {b{}}; registration = getRegistration(type.getEnclosingClass()); } else if (EnumSet.class.isAssignableFrom(type)) { registration = classResolver.getRegistration(EnumSet.class); } if (registration == null) { if (registrationRequired) { throw new IllegalArgumentException("Class is not registered: " + className(type) + "\nNote: To register this class use: kryo.register(" + className(type) + ".class);"); } registration = classResolver.registerImplicit(type); } } memoizedClass = type; memoizedClassValue = registration; return registration; } /** @see ClassResolver#getRegistration(int) */ public Registration getRegistration (int classID) { return classResolver.getRegistration(classID); } /** Returns the serializer for the registration for the specified class. * @see #getRegistration(Class) * @see Registration#getSerializer() */ public Serializer getSerializer (Class type) { return getRegistration(type).getSerializer(); } // --- Serialization --- /** Writes a class and returns its registration. * @param type May be null. * @return Will be null if type is null. * @see ClassResolver#writeClass(Output, Class) */ public Registration writeClass (Output output, Class type) { if (output == null) throw new IllegalArgumentException("output cannot be null."); try { return classResolver.writeClass(output, type); } finally { if (depth == 0 && autoReset) reset(); } } /** Writes an object using the registered serializer. */ public void writeObject (Output output, Object object) { if (output == null) throw new IllegalArgumentException("output cannot be null."); if (object == null) throw new IllegalArgumentException("object cannot be null."); beginObject(); try { if (references && writeReferenceOrNull(output, object, false)) return; if (TRACE || (DEBUG && depth == 1)) log("Write", object); getRegistration(object.getClass()).getSerializer().write(this, output, object); } finally { if (--depth == 0 && autoReset) reset(); } } /** Writes an object using the specified serializer. The registered serializer is ignored. */ public void writeObject (Output output, Object object, Serializer serializer) { if (output == null) throw new IllegalArgumentException("output cannot be null."); if (object == null) throw new IllegalArgumentException("object cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); beginObject(); try { if (references && writeReferenceOrNull(output, object, false)) return; if (TRACE || (DEBUG && depth == 1)) log("Write", object); serializer.write(this, output, object); } finally { if (--depth == 0 && autoReset) reset(); } } /** Writes an object or null using the registered serializer for the specified type. * @param object May be null. */ public void writeObjectOrNull (Output output, Object object, Class type) { if (output == null) throw new IllegalArgumentException("output cannot be null."); beginObject(); try { Serializer serializer = getRegistration(type).getSerializer(); if (references) { if (writeReferenceOrNull(output, object, true)) return; } else if (!serializer.getAcceptsNull()) { if (object == null) { if (TRACE || (DEBUG && depth == 1)) log("Write", object); output.writeByte(NULL); return; } output.writeByte(NOT_NULL); } if (TRACE || (DEBUG && depth == 1)) log("Write", object); serializer.write(this, output, object); } finally { if (--depth == 0 && autoReset) reset(); } } /** Writes an object or null using the specified serializer. The registered serializer is ignored. * @param object May be null. */ public void writeObjectOrNull (Output output, Object object, Serializer serializer) { if (output == null) throw new IllegalArgumentException("output cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); beginObject(); try { if (references) { if (writeReferenceOrNull(output, object, true)) return; } else if (!serializer.getAcceptsNull()) { if (object == null) { if (TRACE || (DEBUG && depth == 1)) log("Write", null); output.writeByte(NULL); return; } output.writeByte(NOT_NULL); } if (TRACE || (DEBUG && depth == 1)) log("Write", object); serializer.write(this, output, object); } finally { if (--depth == 0 && autoReset) reset(); } } /** Writes the class and object or null using the registered serializer. * @param object May be null. */ public void writeClassAndObject (Output output, Object object) { if (output == null) throw new IllegalArgumentException("output cannot be null."); beginObject(); try { if (object == null) { writeClass(output, null); return; } Registration registration = writeClass(output, object.getClass()); if (references && writeReferenceOrNull(output, object, false)) return; if (TRACE || (DEBUG && depth == 1)) log("Write", object); registration.getSerializer().write(this, output, object); } finally { if (--depth == 0 && autoReset) reset(); } } /** @param object May be null if mayBeNull is true. * @return true if no bytes need to be written for the object. */ boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) { if (object == null) { if (TRACE || (DEBUG && depth == 1)) log("Write", null); output.writeByte(Kryo.NULL); return true; } if (!referenceResolver.useReferences(object.getClass())) { if (mayBeNull) output.writeByte(Kryo.NOT_NULL); return false; } // Determine if this object has already been seen in this object graph. int id = referenceResolver.getWrittenId(object); // If not the first time encountered, only write reference ID. if (id != -1) { if (DEBUG) debug("kryo", "Write object reference " + id + ": " + string(object)); 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.writeByte(NOT_NULL); if (TRACE) trace("kryo", "Write initial object reference " + id + ": " + string(object)); return false; } /** Reads a class and returns its registration. * @return May be null. * @see ClassResolver#readClass(Input) */ public Registration readClass (Input input) { if (input == null) throw new IllegalArgumentException("input cannot be null."); try { return classResolver.readClass(input); } finally { if (depth == 0 && autoReset) reset(); } } /** Reads an object using the registered serializer. */ public T readObject (Input input, Class type) { if (input == null) throw new IllegalArgumentException("input cannot be null."); if (type == null) throw new IllegalArgumentException("type cannot be null."); beginObject(); try { T object; if (references) { int stackSize = readReferenceOrNull(input, type, false); if (stackSize == REF) return (T)readObject; object = (T)getRegistration(type).getSerializer().read(this, input, type); if (stackSize == readReferenceIds.size) reference(object); } else object = (T)getRegistration(type).getSerializer().read(this, input, type); if (TRACE || (DEBUG && depth == 1)) log("Read", object); return object; } finally { if (--depth == 0 && autoReset) reset(); } } /** Reads an object using the specified serializer. The registered serializer is ignored. */ public T readObject (Input input, Class type, Serializer serializer) { if (input == null) throw new IllegalArgumentException("input cannot be null."); if (type == null) throw new IllegalArgumentException("type cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); beginObject(); try { T object; if (references) { int stackSize = readReferenceOrNull(input, type, false); if (stackSize == REF) return (T)readObject; object = (T)serializer.read(this, input, type); if (stackSize == readReferenceIds.size) reference(object); } else object = (T)serializer.read(this, input, type); if (TRACE || (DEBUG && depth == 1)) log("Read", object); return object; } finally { if (--depth == 0 && autoReset) reset(); } } /** Reads an object or null using the registered serializer. * @return May be null. */ public T readObjectOrNull (Input input, Class type) { if (input == null) throw new IllegalArgumentException("input cannot be null."); if (type == null) throw new IllegalArgumentException("type cannot be null."); beginObject(); try { T object; if (references) { int stackSize = readReferenceOrNull(input, type, true); if (stackSize == REF) return (T)readObject; object = (T)getRegistration(type).getSerializer().read(this, input, type); if (stackSize == readReferenceIds.size) reference(object); } else { Serializer serializer = getRegistration(type).getSerializer(); if (!serializer.getAcceptsNull() && input.readByte() == NULL) { if (TRACE || (DEBUG && depth == 1)) log("Read", null); return null; } object = (T)serializer.read(this, input, type); } if (TRACE || (DEBUG && depth == 1)) log("Read", object); return object; } finally { if (--depth == 0 && autoReset) reset(); } } /** Reads an object or null using the specified serializer. The registered serializer is ignored. * @return May be null. */ public T readObjectOrNull (Input input, Class type, Serializer serializer) { if (input == null) throw new IllegalArgumentException("input cannot be null."); if (type == null) throw new IllegalArgumentException("type cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); beginObject(); try { T object; if (references) { int stackSize = readReferenceOrNull(input, type, true); if (stackSize == REF) return (T)readObject; object = (T)serializer.read(this, input, type); if (stackSize == readReferenceIds.size) reference(object); } else { if (!serializer.getAcceptsNull() && input.readByte() == NULL) { if (TRACE || (DEBUG && depth == 1)) log("Read", null); return null; } object = (T)serializer.read(this, input, type); } if (TRACE || (DEBUG && depth == 1)) log("Read", object); return object; } finally { if (--depth == 0 && autoReset) reset(); } } /** Reads the class and object or null using the registered serializer. * @return May be null. */ public Object readClassAndObject (Input input) { if (input == null) throw new IllegalArgumentException("input cannot be null."); beginObject(); try { Registration registration = readClass(input); if (registration == null) return null; Class type = registration.getType(); Object object; if (references) { int stackSize = readReferenceOrNull(input, type, false); if (stackSize == REF) return readObject; object = registration.getSerializer().read(this, input, type); if (stackSize == readReferenceIds.size) reference(object); } else object = registration.getSerializer().read(this, input, type); if (TRACE || (DEBUG && depth == 1)) log("Read", object); return object; } finally { if (--depth == 0 && autoReset) reset(); } } /** Returns {@link #REF} if a reference to a previously read object was read, which is stored in {@link #readObject}. Returns a * stack size (> 0) if a reference ID has been put on the stack. */ int readReferenceOrNull (Input input, Class type, boolean mayBeNull) { if (type.isPrimitive()) type = getWrapperClass(type); boolean referencesSupported = referenceResolver.useReferences(type); int id; if (mayBeNull) { id = input.readInt(true); if (id == Kryo.NULL) { if (TRACE || (DEBUG && depth == 1)) log("Read", null); readObject = null; return REF; } if (!referencesSupported) { readReferenceIds.add(NO_REF); return readReferenceIds.size; } } else { if (!referencesSupported) { readReferenceIds.add(NO_REF); return readReferenceIds.size; } id = input.readInt(true); } if (id == NOT_NULL) { // First time object has been encountered. id = referenceResolver.nextReadId(type); if (TRACE) trace("kryo", "Read initial object reference " + id + ": " + className(type)); readReferenceIds.add(id); return readReferenceIds.size; } // The id is an object reference. id -= 2; // - 2 because 0 and 1 are used for NULL and NOT_NULL. readObject = referenceResolver.getReadObject(type, id); if (DEBUG) debug("kryo", "Read object reference " + id + ": " + string(readObject)); return REF; } /** Called by {@link Serializer#read(Kryo, Input, Class)} and {@link Serializer#copy(Kryo, Object)} before Kryo can be used to * deserialize or copy child objects. Calling this method is unnecessary if Kryo is not used to deserialize or copy child * objects. * @param object May be null, unless calling this method from {@link Serializer#copy(Kryo, Object)}. */ public void reference (Object object) { if (copyDepth > 0) { if (needsCopyReference != null) { if (object == null) throw new IllegalArgumentException("object cannot be null."); originalToCopy.put(needsCopyReference, object); needsCopyReference = null; } } else if (references && object != null) { int id = readReferenceIds.pop(); if (id != NO_REF) referenceResolver.addReadObject(id, object); } } /** Resets unregistered class names. references to previously serialized or deserialized objects. and the * {@link #getGraphContext() graph context}. If {@link #setAutoReset(boolean) auto reset} is true, this method is called * automatically when an object graph has been completely serialized or deserialized. If overridden, the super method must be * called. */ public void reset () { depth = 0; if (graphContext != null) graphContext.clear(); classResolver.reset(); if (references) { referenceResolver.reset(); readObject = null; } copyDepth = 0; if (originalToCopy != null) originalToCopy.clear(); if (TRACE) trace("kryo", "Object graph complete."); } /** Returns a deep copy of the object. Serializers for the classes involved must support {@link Serializer#copy(Kryo, Object)}. * @param object May be null. */ public T copy (T object) { if (object == null) return null; if (copyShallow) return object; copyDepth++; try { if (originalToCopy == null) originalToCopy = new IdentityMap(); Object existingCopy = originalToCopy.get(object); if (existingCopy != null) return (T)existingCopy; needsCopyReference = object; Object copy; if (object instanceof KryoCopyable) copy = ((KryoCopyable)object).copy(this); else copy = getSerializer(object.getClass()).copy(this, object); if (needsCopyReference != null) reference(copy); if (TRACE || (DEBUG && copyDepth == 1)) log("Copy", copy); return (T)copy; } finally { if (--copyDepth == 0) reset(); } } /** Returns a deep copy of the object using the specified serializer. Serializers for the classes involved must support * {@link Serializer#copy(Kryo, Object)}. * @param object May be null. */ public T copy (T object, Serializer serializer) { if (object == null) return null; if (copyShallow) return object; copyDepth++; try { if (originalToCopy == null) originalToCopy = new IdentityMap(); Object existingCopy = originalToCopy.get(object); if (existingCopy != null) return (T)existingCopy; needsCopyReference = object; Object copy; if (object instanceof KryoCopyable) copy = ((KryoCopyable)object).copy(this); else copy = serializer.copy(this, object); if (needsCopyReference != null) reference(copy); if (TRACE || (DEBUG && copyDepth == 1)) log("Copy", copy); return (T)copy; } finally { if (--copyDepth == 0) reset(); } } /** Returns a shallow copy of the object. Serializers for the classes involved must support * {@link Serializer#copy(Kryo, Object)}. * @param object May be null. */ public T copyShallow (T object) { if (object == null) return null; copyDepth++; copyShallow = true; try { if (originalToCopy == null) originalToCopy = new IdentityMap(); Object existingCopy = originalToCopy.get(object); if (existingCopy != null) return (T)existingCopy; needsCopyReference = object; Object copy; if (object instanceof KryoCopyable) copy = ((KryoCopyable)object).copy(this); else copy = getSerializer(object.getClass()).copy(this, object); if (needsCopyReference != null) reference(copy); if (TRACE || (DEBUG && copyDepth == 1)) log("Shallow copy", copy); return (T)copy; } finally { copyShallow = false; if (--copyDepth == 0) reset(); } } /** Returns a shallow copy of the object using the specified serializer. Serializers for the classes involved must support * {@link Serializer#copy(Kryo, Object)}. * @param object May be null. */ public T copyShallow (T object, Serializer serializer) { if (object == null) return null; copyDepth++; copyShallow = true; try { if (originalToCopy == null) originalToCopy = new IdentityMap(); Object existingCopy = originalToCopy.get(object); if (existingCopy != null) return (T)existingCopy; needsCopyReference = object; Object copy; if (object instanceof KryoCopyable) copy = ((KryoCopyable)object).copy(this); else copy = serializer.copy(this, object); if (needsCopyReference != null) reference(copy); if (TRACE || (DEBUG && copyDepth == 1)) log("Shallow copy", copy); return (T)copy; } finally { copyShallow = false; if (--copyDepth == 0) reset(); } } // --- Utility --- private void beginObject () { if (DEBUG) { if (depth == 0) thread = Thread.currentThread(); else if (thread != Thread.currentThread()) throw new ConcurrentModificationException("Kryo must not be accessed concurrently by multiple threads."); } if (depth == maxDepth) throw new KryoException("Max depth exceeded: " + depth); depth++; } public ClassResolver getClassResolver () { return classResolver; } /** @return May be null. */ public ReferenceResolver getReferenceResolver () { return referenceResolver; } /** Sets the classloader to resolve unregistered class names to classes. The default is the loader that loaded the Kryo class. */ public void setClassLoader (ClassLoader classLoader) { if (classLoader == null) throw new IllegalArgumentException("classLoader cannot be null."); this.classLoader = classLoader; } public ClassLoader getClassLoader () { return classLoader; } /** If true, an exception is thrown when an unregistered class is encountered. Default is false. *

* 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. */ public void setRegistrationRequired (boolean registrationRequired) { this.registrationRequired = registrationRequired; if (TRACE) trace("kryo", "Registration required: " + registrationRequired); } public boolean isRegistrationRequired () { return registrationRequired; } /** If true, each appearance of an object in the graph after the first is stored as an integer ordinal. When set to true, * {@link MapReferenceResolver} is used. This enables references to the same object and cyclic graphs to be serialized, but * typically adds overhead of one byte per object. Default is true. * @return The previous value. */ public boolean setReferences (boolean references) { if (references == this.references) return references; this.references = references; if (references && referenceResolver == null) referenceResolver = new MapReferenceResolver(); if (TRACE) trace("kryo", "References: " + references); return !references; } /** Sets the reference resolver and enables references. */ public void setReferenceResolver (ReferenceResolver referenceResolver) { if (referenceResolver == null) throw new IllegalArgumentException("referenceResolver cannot be null."); this.references = true; this.referenceResolver = referenceResolver; if (TRACE) trace("kryo", "Reference resolver: " + referenceResolver.getClass().getName()); } public boolean getReferences () { return references; } /** Sets the strategy used by {@link #newInstantiator(Class)} for creating objects. See {@link StdInstantiatorStrategy} to * create objects via without calling any constructor. See {@link SerializingInstantiatorStrategy} to mimic Java's built-in * serialization. * @param strategy May be null. */ public void setInstantiatorStrategy (InstantiatorStrategy strategy) { this.strategy = strategy; } /** Returns a new instantiator for creating new instances of the specified type. By default, an instantiator is returned that * uses reflection if the class has a zero argument constructor, an exception is thrown. If a * {@link #setInstantiatorStrategy(InstantiatorStrategy) strategy} is set, it will be used instead of throwing an exception. */ protected ObjectInstantiator newInstantiator (final Class type) { if (!Util.isAndroid) { // ReflectASM. 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 (strategy == null) { if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) throw new KryoException("Class cannot be created (non-static member class): " + className(type)); else throw new KryoException("Class cannot be created (missing no-arg constructor): " + className(type)); } // InstantiatorStrategy. return strategy.newInstantiatorOf(type); } /** Creates a new instance of a class using {@link Registration#getInstantiator()}. If the registration's instantiator is null, * a new one is set using {@link #newInstantiator(Class)}. */ public T newInstance (Class type) { Registration registration = getRegistration(type); ObjectInstantiator instantiator = registration.getInstantiator(); if (instantiator == null) { instantiator = newInstantiator(type); registration.setInstantiator(instantiator); } return (T)instantiator.newInstance(); } /** Name/value pairs that are available to all serializers. */ public ObjectMap getContext () { if (context == null) context = new ObjectMap(); return context; } /** Name/value pairs that are available to all serializers and are cleared after each object graph is serialized or * deserialized. */ public ObjectMap getGraphContext () { if (graphContext == null) graphContext = new ObjectMap(); return graphContext; } /** Returns the number of child objects away from the object graph root. */ public int getDepth () { return depth; } /** If true (the default), {@link #reset()} is called automatically after an entire object graph has been read or written. If * false, {@link #reset()} must be called manually, which allows unregistered class names, references, and other information to * span multiple object graphs. */ public void setAutoReset (boolean autoReset) { this.autoReset = autoReset; } /** Sets the maxiumum depth of an object graph. This can be used to prevent malicious data from causing a stack overflow. * Default is {@link Integer#MAX_VALUE}. */ public void setMaxDepth (int maxDepth) { if (maxDepth <= 0) throw new IllegalArgumentException("maxDepth must be > 0."); this.maxDepth = maxDepth; } /** Returns true if the specified type is final. Final types can be serialized more efficiently because they are * non-polymorphic. *

* This can be overridden to force non-final classes to be treated as final. Eg, if an application uses ArrayList extensively * but never uses an ArrayList subclass, treating ArrayList as final could allow FieldSerializer to save 1-2 bytes per * ArrayList field. */ public boolean isFinal (Class type) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (type.isArray()) return Modifier.isFinal(Util.getElementClass(type).getModifiers()); return Modifier.isFinal(type.getModifiers()); } /** Returns the first level of classes or interfaces for a generic type. * @return null if the specified type is not generic or its generic types are not classes. */ static public Class[] getGenerics (Type genericType) { if (!(genericType instanceof ParameterizedType)) return null; Type[] actualTypes = ((ParameterizedType)genericType).getActualTypeArguments(); Class[] generics = new Class[actualTypes.length]; int count = 0; for (int i = 0, n = actualTypes.length; i < n; i++) { Type actualType = actualTypes[i]; if (actualType instanceof Class) generics[i] = (Class)actualType; else if (actualType instanceof ParameterizedType) generics[i] = (Class)((ParameterizedType)actualType).getRawType(); else continue; count++; } if (count == 0) return null; return generics; } static final class DefaultSerializerEntry { Class type; Serializer serializer; Class serializerClass; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/KryoCopyable.java000066400000000000000000000012221210575634700261460ustar00rootroot00000000000000 package com.esotericsoftware.kryo; /** 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 */ 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. * @see Serializer#copy(Kryo, Object) */ public T copy (Kryo kryo); } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/KryoException.java000066400000000000000000000023531210575634700263540ustar00rootroot00000000000000 package com.esotericsoftware.kryo; /** General Kryo RuntimeException. * @author Nathan Sweet */ public class KryoException extends RuntimeException { private StringBuffer trace; public KryoException () { super(); } public KryoException (String message, Throwable cause) { super(message, cause); } public KryoException (String message) { super(message); } public KryoException (Throwable cause) { super(cause); } public String getMessage () { if (trace == null) return super.getMessage(); StringBuffer buffer = new StringBuffer(512); buffer.append(super.getMessage()); if (buffer.length() > 0) buffer.append('\n'); buffer.append("Serialization trace:"); buffer.append(trace); return buffer.toString(); } /** Adds information to the exception message about where in the the object graph serialization failure occurred. * {@link Serializer Serializers} can catch {@link KryoException}, add trace information, and rethrow the exception. */ public void addTrace (String info) { if (info == null) throw new IllegalArgumentException("info cannot be null."); if (trace == null) trace = new StringBuffer(512); trace.append('\n'); trace.append(info); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/KryoSerializable.java000066400000000000000000000012761210575634700270270ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.DefaultSerializers.KryoSerializableSerializer; /** Allows implementing classes to perform their own serialization. Hand written serialization can be more efficient in some cases. *

* The default serializer for KryoSerializable is {@link KryoSerializableSerializer}, which uses {@link Kryo#newInstance(Class)} * to construct the class. * @author Nathan Sweet */ public interface KryoSerializable { public void write (Kryo kryo, Output output); public void read (Kryo kryo, Input input); } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/NotNull.java000066400000000000000000000011301210575634700251340ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.esotericsoftware.kryo.serializers.FieldSerializer; /** 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 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NotNull { } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/ReferenceResolver.java000066400000000000000000000034351210575634700271730ustar00rootroot00000000000000 package com.esotericsoftware.kryo; /** 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 */ 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); /** Returns an ID for the object if it has been written previously, otherwise returns -1. */ public int getWrittenId (Object object); /** Returns a new ID for an object that is being written for the first time. * @return The ID, which is stored more efficiently if it is positive and must not be -1 or -2. */ public int addWrittenObject (Object object); /** Returns the ID for the next object that will be read. This is called only the first time an object is encountered. * @param type The type of object that will be read. * @return The ID, which is stored more efficiently if it is positive and must not be -1 or -2. */ public int nextReadId (Class type); /** Adds an object that has been read for the first time. * @param id The ID from {@link #nextReadId(Class)}. */ public void addReadObject (int id, Object object); /** Returns the object for the specified ID. The ID and object are guaranteed to have been previously passed in a call to * {@link #addReadObject(int, Object)}. */ public Object getReadObject (Class type, int id); /** Called by {@link Kryo#reset()}. */ public void reset (); /** Returns true if references will be written for the specified type. * @param type Will never be a primitive type, but may be a primitive type wrapper. */ public boolean useReferences (Class type); } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/Registration.java000066400000000000000000000035201210575634700262200ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import org.objenesis.instantiator.ObjectInstantiator; import static com.esotericsoftware.kryo.util.Util.*; import static com.esotericsoftware.minlog.Log.*; /** Describes the {@link Serializer} and class ID to use for a class. * @author Nathan Sweet */ public class Registration { private final Class type; private final int id; private Serializer serializer; private ObjectInstantiator instantiator; public Registration (Class type, Serializer serializer, int id) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); this.type = type; this.serializer = serializer; this.id = id; } public Class getType () { return type; } /** Returns the registered class ID. * @see Kryo#register(Class) */ public int getId () { return id; } public Serializer getSerializer () { return serializer; } public void setSerializer (Serializer serializer) { if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); this.serializer = serializer; if (TRACE) trace("kryo", "Update registered serializer: " + type.getName() + " (" + serializer.getClass().getName() + ")"); } /** @return May be null if not yet set. */ public ObjectInstantiator getInstantiator () { return instantiator; } /** Sets the instantiator that will create a new instance of the type in {@link Kryo#newInstance(Class)}. */ public void setInstantiator (ObjectInstantiator instantiator) { if (instantiator == null) throw new IllegalArgumentException("instantiator cannot be null."); this.instantiator = instantiator; } public String toString () { return "[" + id + ", " + className(type) + "]"; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/Serializer.java000066400000000000000000000075721210575634700256720ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** Reads and writes objects to and from bytes. * @author Nathan Sweet */ public abstract class Serializer { private boolean acceptsNull, immutable; public Serializer () { } /** @see #setAcceptsNull(boolean) */ public Serializer (boolean acceptsNull) { this.acceptsNull = acceptsNull; } /** @see #setAcceptsNull(boolean) * @see #setImmutable(boolean) */ public Serializer (boolean acceptsNull, boolean immutable) { this.acceptsNull = acceptsNull; this.immutable = immutable; } /** Writes the bytes for the object to the output. *

* This method should not be called directly, instead this serializer can be passed to {@link Kryo} write methods that accept a * serialier. * @param object May be null if {@link #getAcceptsNull()} is true. */ abstract public void write (Kryo kryo, Output output, T object); /** Reads bytes and returns a new object of the specified concrete type. *

* Before Kryo can be used to read child objects, {@link Kryo#reference(Object)} must be called with the parent object to * ensure it can be referenced by the child objects. Any serializer that uses {@link Kryo} to read a child object may need to * be reentrant. *

* This method should not be called directly, instead this serializer can be passed to {@link Kryo} read methods that accept a * serialier. * @return May be null if {@link #getAcceptsNull()} is true. */ abstract public T read (Kryo kryo, Input input, Class type); public boolean getAcceptsNull () { return acceptsNull; } /** If true, this serializer will handle writing and reading null values. If false, the Kryo framework handles null values and * the serializer will never receive null. *

* This can be set to true on a serializer that does not accept nulls if it is known that the serializer will never encounter * null. Doing this will prevent the framework from writing a byte to denote null. */ public void setAcceptsNull (boolean acceptsNull) { this.acceptsNull = acceptsNull; } public boolean isImmutable () { return immutable; } /** If true, the type this serializer will be used for is considered immutable. This causes {@link #copy(Kryo, Object)} to * return the original object. */ public void setImmutable (boolean immutable) { this.immutable = immutable; } /** Sets the generic types of the field or method this serializer will be used for on the next call to read or write. Subsequent * calls to read and write must not use this generic type information. The default implementation does nothing. Subclasses may * use the information provided to this method for more efficient serialization, eg to use the same type for all items in a * list. * @param generics Some (but never all) elements may be null if there is no generic type information at that index. */ public void setGenerics (Kryo kryo, Class[] generics) { } /** Returns a copy of the specified object. The default implementation returns the original if {@link #isImmutable()} is true, * else throws {@link KryoException}. Subclasses should override this method if needed to support {@link Kryo#copy(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. Any serializer that uses {@link Kryo} to copy a child object may need to be reentrant. *

* This method should not be called directly, instead this serializer can be passed to {@link Kryo} copy methods that accept a * serialier. */ public T copy (Kryo kryo, T original) { if (immutable) return original; throw new KryoException("Serializer does not support copy: " + getClass().getName()); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/000077500000000000000000000000001210575634700233125ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java000066400000000000000000000026151210575634700304120ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; /** An InputStream whose source is a {@link ByteBuffer}. * @author Nathan Sweet */ public class ByteBufferInputStream extends InputStream { private ByteBuffer byteBuffer; public ByteBufferInputStream () { } /** Creates a stream with a new non-direct buffer of the specified size. The position and limit of the buffer is zero. */ public ByteBufferInputStream (int bufferSize) { this(ByteBuffer.allocate(bufferSize)); byteBuffer.flip(); } /** Creates an uninitialized stream that cannot be used until {@link #setByteBuffer(ByteBuffer)} is called. */ public ByteBufferInputStream (ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } public ByteBuffer getByteBuffer () { return byteBuffer; } public void setByteBuffer (ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } public int read () throws IOException { if (!byteBuffer.hasRemaining()) return -1; return byteBuffer.get(); } public int read (byte[] bytes, int offset, int length) throws IOException { int count = Math.min(byteBuffer.remaining(), length); if (count == 0) return -1; byteBuffer.get(bytes, offset, count); return count; } public int available () throws IOException { return byteBuffer.remaining(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java000066400000000000000000000025271210575634700306150ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; /** 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 */ public class ByteBufferOutputStream extends OutputStream { private ByteBuffer byteBuffer; /** Creates an uninitialized stream that cannot be used until {@link #setByteBuffer(ByteBuffer)} is called. */ public ByteBufferOutputStream () { } /** Creates a stream with a new non-direct buffer of the specified size. */ public ByteBufferOutputStream (int bufferSize) { this(ByteBuffer.allocate(bufferSize)); } public ByteBufferOutputStream (ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } public ByteBuffer getByteBuffer () { return byteBuffer; } public void setByteBuffer (ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } public void write (int b) throws IOException { if (!byteBuffer.hasRemaining()) flush(); byteBuffer.put((byte)b); } public void write (byte[] bytes, int offset, int length) throws IOException { if (byteBuffer.remaining() < length) flush(); byteBuffer.put(bytes, offset, length); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/Input.java000066400000000000000000000542261210575634700252650ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.InputStream; import com.esotericsoftware.kryo.KryoException; /** An InputStream that reads data from a byte array and optionally fills the byte array from another OutputStream as needed. * Utility methods are provided for efficiently reading primitive types and strings. * @author Nathan Sweet */ public class Input extends InputStream { private byte[] buffer; private int capacity, position, limit, total; private char[] chars = new char[32]; private InputStream inputStream; /** Creates an uninitialized Input. {@link #setBuffer(byte[])} must be called before the Input is used. */ public Input () { } /** Creates a new Input for reading from a byte array. * @param bufferSize The size of the buffer. An exception is thrown if more bytes than this are read. */ public Input (int bufferSize) { this.capacity = bufferSize; buffer = new byte[bufferSize]; } /** Creates a new Input for reading from a byte array. * @param buffer An exception is thrown if more bytes than this are read. */ public Input (byte[] buffer) { setBuffer(buffer, 0, buffer.length); } /** Creates a new Input for reading from a byte array. * @param buffer An exception is thrown if more bytes than this are read. */ public Input (byte[] buffer, int offset, int count) { setBuffer(buffer, offset, count); } /** Creates a new Input for reading from an InputStream with a buffer size of 4096. */ public Input (InputStream inputStream) { this(4096); if (inputStream == null) throw new IllegalArgumentException("inputStream cannot be null."); this.inputStream = inputStream; } /** Creates a new Input for reading from an InputStream. */ public Input (InputStream inputStream, int bufferSize) { this(bufferSize); if (inputStream == null) throw new IllegalArgumentException("inputStream cannot be null."); this.inputStream = inputStream; } /** Sets a new buffer. The position and total are reset, discarding any buffered bytes. */ public void setBuffer (byte[] bytes) { setBuffer(bytes, 0, bytes.length); } /** Sets a new buffer. The position and total are reset, discarding any buffered bytes. */ public void setBuffer (byte[] bytes, int offset, int count) { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); buffer = bytes; position = offset; limit = count; capacity = bytes.length; total = 0; inputStream = null; } public byte[] getBuffer () { return buffer; } public InputStream getInputStream () { return inputStream; } /** Sets a new InputStream. The position and total are reset, discarding any buffered bytes. * @param inputStream May be null. */ public void setInputStream (InputStream inputStream) { this.inputStream = inputStream; limit = 0; rewind(); } /** Returns the number of bytes read. */ public int total () { return total + position; } /** Sets the number of bytes read. */ public void setTotal (int total) { this.total = total; } /** Returns the current position in the buffer. */ public int position () { return position; } /** Sets the current position in the buffer. */ public void setPosition (int position) { this.position = position; } /** Returns the limit for the buffer. */ public int limit () { return limit; } /** Sets the limit in the buffer. */ public void setLimit (int limit) { this.limit = limit; } /** Sets the position and total to zero. */ public void rewind () { position = 0; total = 0; } /** Discards the specified number of bytes. */ public void skip (int count) throws KryoException { int skipCount = Math.min(limit - position, count); while (true) { position += skipCount; count -= skipCount; if (count == 0) break; skipCount = Math.min(count, capacity); require(skipCount); } } /** Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream. */ protected int fill (byte[] buffer, int offset, int count) throws KryoException { if (inputStream == null) return -1; try { return inputStream.read(buffer, offset, count); } catch (IOException ex) { throw new KryoException(ex); } } /** @param required Must be > 0. The buffer is filled until it has at least this many bytes. * @return the number of bytes remaining. * @throws KryoException if EOS is reached before required bytes are read (buffer underflow). */ private int require (int required) throws KryoException { int remaining = limit - position; if (remaining >= required) return remaining; if (required > capacity) throw new KryoException("Buffer too small: capacity: " + capacity + ", required: " + required); // Compact. System.arraycopy(buffer, position, buffer, 0, remaining); total += position; position = 0; while (true) { int count = fill(buffer, remaining, capacity - remaining); if (count == -1) { if (remaining >= required) break; throw new KryoException("Buffer underflow."); } remaining += count; if (remaining >= required) break; // Enough has been read. } limit = remaining; return remaining; } /** @param optional Try to fill the buffer with this many bytes. * @return the number of bytes remaining, but not more than optional, or -1 if the EOS was reached and the buffer is empty. */ private int optional (int optional) throws KryoException { int remaining = limit - position; if (remaining >= optional) return optional; optional = Math.min(optional, capacity); // Compact. System.arraycopy(buffer, position, buffer, 0, remaining); total += position; position = 0; while (true) { int count = fill(buffer, remaining, capacity - remaining); if (count == -1) break; remaining += count; if (remaining >= optional) break; // Enough has been read. } limit = remaining; return remaining == 0 ? -1 : Math.min(remaining, optional); } // InputStream /** Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available. */ public int read () throws KryoException { if (optional(1) == 0) return -1; return buffer[position++] & 0xFF; } /** Reads bytes.length bytes or less and writes them to the specified byte[], starting at 0, and returns the number of bytes * read. */ public int read (byte[] bytes) throws KryoException { return read(bytes, 0, bytes.length); } /** Reads count bytes or less and writes them to the specified byte[], starting at offset, and returns the number of bytes read * or -1 if no more bytes are available. */ public int read (byte[] bytes, int offset, int count) throws KryoException { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); int startingCount = count; int copyCount = Math.min(limit - position, count); while (true) { System.arraycopy(buffer, position, bytes, offset, copyCount); position += copyCount; count -= copyCount; if (count == 0) break; offset += copyCount; copyCount = optional(count); if (copyCount == -1) { // End of data. if (startingCount == count) return -1; break; } if (position == limit) break; } return startingCount - count; } /** Discards the specified number of bytes. */ public long skip (long count) throws KryoException { long remaining = count; while (remaining > 0) { int skip = Math.max(Integer.MAX_VALUE, (int)remaining); skip(skip); remaining -= skip; } return count; } /** Closes the underlying InputStream, if any. */ public void close () throws KryoException { if (inputStream != null) { try { inputStream.close(); } catch (IOException ignored) { } } } // byte /** Reads a single byte. */ public byte readByte () throws KryoException { require(1); return buffer[position++]; } /** Reads a byte as an int from 0 to 255. */ public int readByteUnsigned () throws KryoException { require(1); return buffer[position++] & 0xFF; } /** Reads the specified number of bytes into a new byte[]. */ public byte[] readBytes (int length) throws KryoException { byte[] bytes = new byte[length]; readBytes(bytes, 0, length); return bytes; } /** Reads bytes.length bytes and writes them to the specified byte[], starting at index 0. */ public void readBytes (byte[] bytes) throws KryoException { readBytes(bytes, 0, bytes.length); } /** Reads count bytes and writes them to the specified byte[], starting at offset. */ public void readBytes (byte[] bytes, int offset, int count) throws KryoException { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); int copyCount = Math.min(limit - position, count); while (true) { System.arraycopy(buffer, position, bytes, offset, copyCount); position += copyCount; count -= copyCount; if (count == 0) break; offset += copyCount; copyCount = Math.min(count, capacity); require(copyCount); } } // int /** Reads a 4 byte int. */ public int readInt () throws KryoException { require(4); byte[] buffer = this.buffer; int position = this.position; this.position = position + 4; return (buffer[position] & 0xFF) << 24 // | (buffer[position + 1] & 0xFF) << 16 // | (buffer[position + 2] & 0xFF) << 8 // | buffer[position + 3] & 0xFF; } /** Reads a 1-5 byte int. */ public int readInt (boolean optimizePositive) throws KryoException { if (require(1) < 5) return readInt_slow(optimizePositive); int b = buffer[position++]; int result = b & 0x7F; if ((b & 0x80) != 0) { byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 28; } } } } return optimizePositive ? result : ((result >>> 1) ^ -(result & 1)); } private int readInt_slow (boolean optimizePositive) { // The buffer is guaranteed to have at least 1 byte. int b = buffer[position++]; int result = b & 0x7F; if ((b & 0x80) != 0) { require(1); byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 28; } } } } return optimizePositive ? result : ((result >>> 1) ^ -(result & 1)); } /** Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}. */ public boolean canReadInt () throws KryoException { if (limit - position >= 5) return true; if (optional(5) <= 0) return false; int p = position; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; return true; } /** Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}. */ public boolean canReadLong () throws KryoException { if (limit - position >= 9) return true; if (optional(5) <= 0) return false; int p = position; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; if ((buffer[p++] & 0x80) == 0) return true; if (p == limit) return false; return true; } // string /** Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)} * , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}. * @return May be null. */ public String readString () { int available = require(1); int b = buffer[position++]; if ((b & 0x80) == 0) return readAscii(); // ASCII. // Null, empty, or UTF8. int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b); switch (charCount) { case 0: return null; case 1: return ""; } charCount--; if (chars.length < charCount) chars = new char[charCount]; readUtf8(charCount); return new String(chars, 0, charCount); } private int readUtf8Length (int b) { int result = b & 0x3F; // Mask all but first 6 bits. if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8. byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 6; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 13; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 20; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 27; } } } } return result; } private int readUtf8Length_slow (int b) { int result = b & 0x3F; // Mask all but first 6 bits. if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8. require(1); byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 6; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 13; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 20; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 27; } } } } return result; } private void readUtf8 (int charCount) { byte[] buffer = this.buffer; char[] chars = this.chars; // Try to read 7 bit ASCII chars. int charIndex = 0; int count = Math.min(require(1), charCount); int position = this.position; int b; while (charIndex < count) { b = buffer[position++]; if (b < 0) { position--; break; } chars[charIndex++] = (char)b; } this.position = position; // If buffer didn't hold all chars or any were not ASCII, use slow path for remainder. if (charIndex < charCount) readUtf8_slow(charCount, charIndex); } private void readUtf8_slow (int charCount, int charIndex) { char[] chars = this.chars; byte[] buffer = this.buffer; while (charIndex < charCount) { if (position == limit) require(1); int b = buffer[position++] & 0xFF; switch (b >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: chars[charIndex] = (char)b; break; case 12: case 13: if (position == limit) require(1); chars[charIndex] = (char)((b & 0x1F) << 6 | buffer[position++] & 0x3F); break; case 14: require(2); chars[charIndex] = (char)((b & 0x0F) << 12 | (buffer[position++] & 0x3F) << 6 | buffer[position++] & 0x3F); break; } charIndex++; } } private String readAscii () { byte[] buffer = this.buffer; int end = position; int start = end - 1; int limit = this.limit; int b; do { if (end == limit) return readAscii_slow(); b = buffer[end++]; } while ((b & 0x80) == 0); buffer[end - 1] &= 0x7F; // Mask end of ascii bit. String value = new String(buffer, 0, start, end - start); buffer[end - 1] |= 0x80; position = end; return value; } private String readAscii_slow () { position--; // Re-read the first byte. // Copy chars currently in buffer. int charCount = limit - position; if (charCount > chars.length) chars = new char[charCount * 2]; char[] chars = this.chars; byte[] buffer = this.buffer; for (int i = position, ii = 0, n = limit; i < n; i++, ii++) chars[ii] = (char)buffer[i]; position = limit; // Copy additional chars one by one. while (true) { require(1); int b = buffer[position++]; if (charCount == chars.length) { char[] newChars = new char[charCount * 2]; System.arraycopy(chars, 0, newChars, 0, charCount); chars = newChars; this.chars = newChars; } if ((b & 0x80) == 0x80) { chars[charCount++] = (char)(b & 0x7F); break; } chars[charCount++] = (char)b; } return new String(chars, 0, charCount); } /** Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)} * , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}. * @return May be null. */ public StringBuilder readStringBuilder () { int available = require(1); int b = buffer[position++]; if ((b & 0x80) == 0) return new StringBuilder(readAscii()); // ASCII. // Null, empty, or UTF8. int charCount = available >= 5 ? readUtf8Length(b) : readUtf8Length_slow(b); switch (charCount) { case 0: return null; case 1: return new StringBuilder(""); } charCount--; if (chars.length < charCount) chars = new char[charCount]; readUtf8(charCount); StringBuilder builder = new StringBuilder(charCount); builder.append(chars, 0, charCount); return builder; } // float /** Reads a 4 byte float. */ public float readFloat () throws KryoException { return Float.intBitsToFloat(readInt()); } /** Reads a 1-5 byte float with reduced precision. */ public float readFloat (float precision, boolean optimizePositive) throws KryoException { return readInt(optimizePositive) / (float)precision; } // short /** Reads a 2 byte short. */ public short readShort () throws KryoException { require(2); return (short)(((buffer[position++] & 0xFF) << 8) | (buffer[position++] & 0xFF)); } /** Reads a 2 byte short as an int from 0 to 65535. */ public int readShortUnsigned () throws KryoException { require(2); return ((buffer[position++] & 0xFF) << 8) | (buffer[position++] & 0xFF); } // long /** Reads an 8 byte long. */ public long readLong () throws KryoException { require(8); byte[] buffer = this.buffer; return (long)buffer[position++] << 56 // | (long)(buffer[position++] & 0xFF) << 48 // | (long)(buffer[position++] & 0xFF) << 40 // | (long)(buffer[position++] & 0xFF) << 32 // | (long)(buffer[position++] & 0xFF) << 24 // | (buffer[position++] & 0xFF) << 16 // | (buffer[position++] & 0xFF) << 8 // | buffer[position++] & 0xFF; } /** Reads a 1-9 byte long. */ public long readLong (boolean optimizePositive) throws KryoException { if (require(1) < 9) return readLong_slow(optimizePositive); int b = buffer[position++]; long result = b & 0x7F; if ((b & 0x80) != 0) { byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (long)(b & 0x7F) << 28; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (long)(b & 0x7F) << 35; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (long)(b & 0x7F) << 42; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (long)(b & 0x7F) << 49; if ((b & 0x80) != 0) { b = buffer[position++]; result |= (long)b << 56; } } } } } } } } if (!optimizePositive) result = (result >>> 1) ^ -(result & 1); return result; } private long readLong_slow (boolean optimizePositive) { // The buffer is guaranteed to have at least 1 byte. int b = buffer[position++]; long result = b & 0x7F; if ((b & 0x80) != 0) { require(1); byte[] buffer = this.buffer; b = buffer[position++]; result |= (b & 0x7F) << 7; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 14; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (b & 0x7F) << 21; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (long)(b & 0x7F) << 28; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (long)(b & 0x7F) << 35; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (long)(b & 0x7F) << 42; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (long)(b & 0x7F) << 49; if ((b & 0x80) != 0) { require(1); b = buffer[position++]; result |= (long)b << 56; } } } } } } } } if (!optimizePositive) result = (result >>> 1) ^ -(result & 1); return result; } // boolean /** Reads a 1 byte boolean. */ public boolean readBoolean () throws KryoException { require(1); return buffer[position++] == 1; } // char /** Reads a 2 byte char. */ public char readChar () throws KryoException { require(2); return (char)(((buffer[position++] & 0xFF) << 8) | (buffer[position++] & 0xFF)); } // double /** Reads an 8 bytes double. */ public double readDouble () throws KryoException { return Double.longBitsToDouble(readLong()); } /** Reads a 1-9 byte double with reduced precision. */ public double readDouble (double precision, boolean optimizePositive) throws KryoException { return readLong(optimizePositive) / (double)precision; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/InputChunked.java000066400000000000000000000052131210575634700265570ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.InputStream; import com.esotericsoftware.kryo.KryoException; import static com.esotericsoftware.minlog.Log.*; /** An InputStream that reads lengths and chunks of data from another OutputStream, allowing chunks to be skipped. * @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. */ public InputChunked () { super(2048); } /** Creates an uninitialized InputChunked. The InputStream must be set before it can be used. */ public InputChunked (int bufferSize) { super(bufferSize); } /** Creates an InputChunked with a buffer size of 2048. */ public InputChunked (InputStream inputStream) { super(inputStream, 2048); } public InputChunked (InputStream inputStream, int bufferSize) { super(inputStream, bufferSize); } public void setInputStream (InputStream inputStream) { super.setInputStream(inputStream); chunkSize = -1; } public void setBuffer (byte[] bytes, int offset, int count) { super.setBuffer(bytes, offset, count); chunkSize = -1; } public void rewind () { super.rewind(); chunkSize = -1; } protected int fill (byte[] buffer, int offset, int count) throws KryoException { if (chunkSize == -1) // No current chunk, expect a new chunk. readChunkSize(); else if (chunkSize == 0) // End of chunks. return -1; int actual = super.fill(buffer, offset, Math.min(chunkSize, count)); chunkSize -= actual; if (chunkSize == 0) readChunkSize(); // Read next chunk size. return actual; } private void readChunkSize () { try { InputStream inputStream = getInputStream(); for (int offset = 0, result = 0; offset < 32; offset += 7) { int b = inputStream.read(); if (b == -1) throw new KryoException("Buffer underflow."); result |= (b & 0x7F) << offset; if ((b & 0x80) == 0) { chunkSize = result; if (TRACE) trace("kryo", "Read chunk: " + chunkSize); return; } } } catch (IOException ex) { throw new KryoException(ex); } throw new KryoException("Malformed integer."); } /** Advances the stream to the next set of chunks. InputChunked will appear to hit the end of the data until this method is * called. */ public void nextChunks () { if (chunkSize == -1) readChunkSize(); // No current chunk, expect a new chunk. while (chunkSize > 0) skip(chunkSize); chunkSize = -1; if (TRACE) trace("kryo", "Next chunks."); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/Output.java000066400000000000000000000536521210575634700254700ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.OutputStream; import com.esotericsoftware.kryo.KryoException; /** 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. * @author Nathan Sweet */ public class Output extends OutputStream { private int maxCapacity, capacity, position, total; private byte[] buffer; private OutputStream outputStream; /** Creates an uninitialized Output. {@link #setBuffer(byte[], int)} must be called before the Output is used. */ public Output () { } /** Creates a new Output for writing to a byte array. * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ public Output (int bufferSize) { this(bufferSize, bufferSize); } /** Creates a new Output for writing to a byte array. * @param bufferSize The initial size of the buffer. * @param maxBufferSize The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown. */ public Output (int bufferSize, int maxBufferSize) { if (maxBufferSize < -1) throw new IllegalArgumentException("maxBufferSize cannot be < -1: " + maxBufferSize); this.capacity = bufferSize; this.maxCapacity = maxBufferSize == -1 ? Integer.MAX_VALUE : maxBufferSize; buffer = new byte[bufferSize]; } /** Creates a new Output for writing to a byte array. * @see #setBuffer(byte[]) */ public Output (byte[] buffer) { this(buffer, buffer.length); } /** Creates a new Output for writing to a byte array. * @see #setBuffer(byte[], int) */ public Output (byte[] buffer, int maxBufferSize) { if (buffer == null) throw new IllegalArgumentException("buffer cannot be null."); setBuffer(buffer, maxBufferSize); } /** Creates a new Output for writing to an OutputStream. A buffer size of 4096 is used. */ public Output (OutputStream outputStream) { this(4096, 4096); if (outputStream == null) throw new IllegalArgumentException("outputStream cannot be null."); this.outputStream = outputStream; } /** Creates a new Output for writing to an OutputStream. */ public Output (OutputStream outputStream, int bufferSize) { this(bufferSize, bufferSize); if (outputStream == null) throw new IllegalArgumentException("outputStream cannot be null."); this.outputStream = outputStream; } public OutputStream getOutputStream () { return outputStream; } /** Sets a new OutputStream. The position and total are reset, discarding any buffered bytes. * @param outputStream May be null. */ public void setOutputStream (OutputStream outputStream) { this.outputStream = outputStream; position = 0; total = 0; } /** Sets the buffer that will be written to. {@link #setBuffer(byte[], int)} is called with the specified buffer's length as the * maxBufferSize. */ public void setBuffer (byte[] buffer) { setBuffer(buffer, buffer.length); } /** Sets the buffer that will be written to. The position and total are reset, discarding any buffered bytes. The * {@link #setOutputStream(OutputStream) OutputStream} is set to null. * @param maxBufferSize The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown. */ public void setBuffer (byte[] buffer, int maxBufferSize) { if (buffer == null) throw new IllegalArgumentException("buffer cannot be null."); if (maxBufferSize < -1) throw new IllegalArgumentException("maxBufferSize cannot be < -1: " + maxBufferSize); this.buffer = buffer; this.maxCapacity = maxBufferSize == -1 ? Integer.MAX_VALUE : maxBufferSize; capacity = buffer.length; position = 0; total = 0; outputStream = null; } /** Returns the buffer. The bytes between zero and {@link #position()} are the data that has been written. */ public byte[] getBuffer () { return buffer; } /** Returns a new byte array containing the bytes currently in the buffer between zero and {@link #position()}. */ public byte[] toBytes () { byte[] newBuffer = new byte[position]; System.arraycopy(buffer, 0, newBuffer, 0, position); return newBuffer; } /** Returns the current position in the buffer. This is the number of bytes that have not been flushed. */ public int position () { return position; } /** Sets the current position in the buffer. */ public void setPosition (int position) { this.position = position; } /** Returns the total number of bytes written. This may include bytes that have not been flushed. */ public int total () { return total + position; } /** Sets the position and total to zero. */ public void clear () { position = 0; total = 0; } /** @return true if the buffer has been resized. */ private boolean require (int required) throws KryoException { if (capacity - position >= required) return false; if (required > maxCapacity) throw new KryoException("Buffer overflow. Max capacity: " + maxCapacity + ", required: " + required); flush(); while (capacity - position < required) { if (capacity == maxCapacity) throw new KryoException("Buffer overflow. Available: " + (capacity - position) + ", required: " + required); // Grow buffer. capacity = Math.min(capacity * 2, maxCapacity); if (capacity < 0) capacity = maxCapacity; byte[] newBuffer = new byte[capacity]; System.arraycopy(buffer, 0, newBuffer, 0, position); buffer = newBuffer; } return true; } // OutputStream /** Writes the buffered bytes to the underlying OutputStream, if any. */ public void flush () throws KryoException { if (outputStream == null) return; try { outputStream.write(buffer, 0, position); } catch (IOException ex) { throw new KryoException(ex); } total += position; position = 0; } /** Flushes any buffered bytes and closes the underlying OutputStream, if any. */ public void close () throws KryoException { flush(); if (outputStream != null) { try { outputStream.close(); } catch (IOException ignored) { } } } /** Writes a byte. */ public void write (int value) throws KryoException { if (position == capacity) require(1); buffer[position++] = (byte)value; } /** Writes the bytes. Note the byte[] length is not written. */ public void write (byte[] bytes) throws KryoException { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); writeBytes(bytes, 0, bytes.length); } /** Writes the bytes. Note the byte[] length is not written. */ public void write (byte[] bytes, int offset, int length) throws KryoException { writeBytes(bytes, offset, length); } // byte public void writeByte (byte value) throws KryoException { if (position == capacity) require(1); buffer[position++] = value; } public void writeByte (int value) throws KryoException { if (position == capacity) require(1); buffer[position++] = (byte)value; } /** Writes the bytes. Note the byte[] length is not written. */ public void writeBytes (byte[] bytes) throws KryoException { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); writeBytes(bytes, 0, bytes.length); } /** Writes the bytes. Note the byte[] length is not written. */ public void writeBytes (byte[] bytes, int offset, int count) throws KryoException { if (bytes == null) throw new IllegalArgumentException("bytes cannot be null."); int copyCount = Math.min(capacity - position, count); while (true) { System.arraycopy(bytes, offset, buffer, position, copyCount); position += copyCount; count -= copyCount; if (count == 0) return; offset += copyCount; copyCount = Math.min(capacity, count); require(copyCount); } } // int /** Writes a 4 byte int. */ public void writeInt (int value) throws KryoException { require(4); byte[] buffer = this.buffer; buffer[position++] = (byte)(value >> 24); buffer[position++] = (byte)(value >> 16); buffer[position++] = (byte)(value >> 8); buffer[position++] = (byte)value; } /** 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 { if (!optimizePositive) value = (value << 1) ^ (value >> 31); if (value >>> 7 == 0) { require(1); buffer[position++] = (byte)value; return 1; } if (value >>> 14 == 0) { require(2); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7); return 2; } if (value >>> 21 == 0) { require(3); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14); return 3; } if (value >>> 28 == 0) { require(4); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21); return 4; } require(5); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28); return 5; } // string /** Writes the length and string, or null. Short strings are checked and if ASCII they are written more efficiently, else they * are written as UTF8. If a string is known to be ASCII, {@link #writeAscii(String)} may be used. The string can be read using * {@link Input#readString()} or {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeString (String value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } // Detect ASCII. boolean ascii = false; if (charCount > 1 && charCount < 64) { ascii = true; for (int i = 0; i < charCount; i++) { int c = value.charAt(i); if (c > 127) { ascii = false; break; } } } if (ascii) { if (capacity - position < charCount) writeAscii_slow(value, charCount); else { value.getBytes(0, charCount, buffer, position); position += charCount; } buffer[position - 1] |= 0x80; } else { writeUtf8Length(charCount + 1); int charIndex = 0; if (capacity - position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.buffer; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) break; buffer[position++] = (byte)c; } this.position = position; } if (charIndex < charCount) writeString_slow(value, charCount, charIndex); } } /** Writes the length and CharSequence as UTF8, or null. The string can be read using {@link Input#readString()} or * {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeString (CharSequence value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } writeUtf8Length(charCount + 1); int charIndex = 0; if (capacity - position >= charCount) { // Try to write 8 bit chars. byte[] buffer = this.buffer; int position = this.position; for (; charIndex < charCount; charIndex++) { int c = value.charAt(charIndex); if (c > 127) break; buffer[position++] = (byte)c; } this.position = position; } if (charIndex < charCount) writeString_slow(value, charCount, charIndex); } /** Writes a string that is known to contain only ASCII characters. Non-ASCII strings passed to this method will be corrupted. * Each byte is a 7 bit character with the remaining byte denoting if another character is available. This is slightly more * efficient than {@link #writeString(String)}. The string can be read using {@link Input#readString()} or * {@link Input#readStringBuilder()}. * @param value May be null. */ public void writeAscii (String value) throws KryoException { if (value == null) { writeByte(0x80); // 0 means null, bit 8 means UTF8. return; } int charCount = value.length(); if (charCount == 0) { writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8. return; } if (capacity - position < charCount) writeAscii_slow(value, charCount); else { value.getBytes(0, charCount, buffer, position); position += charCount; } buffer[position - 1] |= 0x80; // Bit 8 means end of ASCII. } /** Writes the length of a string, which is a variable length encoded int except the first byte uses bit 8 to denote UTF8 and * bit 7 to denote if another byte is present. */ private void writeUtf8Length (int value) { if (value >>> 6 == 0) { require(1); buffer[position++] = (byte)(value | 0x80); // Set bit 8. } else if (value >>> 13 == 0) { require(2); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)(value >>> 6); } else if (value >>> 20 == 0) { require(3); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 13); } else if (value >>> 27 == 0) { require(4); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 20); } else { require(5); byte[] buffer = this.buffer; buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8. buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8. buffer[position++] = (byte)((value >>> 20) | 0x80); // Set bit 8. buffer[position++] = (byte)(value >>> 27); } } private void writeString_slow (CharSequence value, int charCount, int charIndex) { for (; charIndex < charCount; charIndex++) { if (position == capacity) require(Math.min(capacity, charCount - charIndex)); int c = value.charAt(charIndex); if (c <= 0x007F) { buffer[position++] = (byte)c; } else if (c > 0x07FF) { buffer[position++] = (byte)(0xE0 | c >> 12 & 0x0F); require(2); buffer[position++] = (byte)(0x80 | c >> 6 & 0x3F); buffer[position++] = (byte)(0x80 | c & 0x3F); } else { buffer[position++] = (byte)(0xC0 | c >> 6 & 0x1F); require(1); buffer[position++] = (byte)(0x80 | c & 0x3F); } } } private void writeAscii_slow (String value, int charCount) throws KryoException { byte[] buffer = this.buffer; int charIndex = 0; int charsToWrite = Math.min(charCount, capacity - position); while (charIndex < charCount) { value.getBytes(charIndex, charIndex + charsToWrite, buffer, position); charIndex += charsToWrite; position += charsToWrite; charsToWrite = Math.min(charCount - charIndex, capacity); if (require(charsToWrite)) buffer = this.buffer; } } // float /** Writes a 4 byte float. */ public void writeFloat (float value) throws KryoException { writeInt(Float.floatToIntBits(value)); } /** Writes a 1-5 byte float with reduced precision. * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (5 bytes). */ public int writeFloat (float value, float precision, boolean optimizePositive) throws KryoException { return writeInt((int)(value * precision), optimizePositive); } // short /** Writes a 2 byte short. */ public void writeShort (int value) throws KryoException { require(2); buffer[position++] = (byte)(value >>> 8); buffer[position++] = (byte)value; } // long /** Writes an 8 byte long. */ public void writeLong (long value) throws KryoException { require(8); byte[] buffer = this.buffer; buffer[position++] = (byte)(value >>> 56); buffer[position++] = (byte)(value >>> 48); buffer[position++] = (byte)(value >>> 40); buffer[position++] = (byte)(value >>> 32); buffer[position++] = (byte)(value >>> 24); buffer[position++] = (byte)(value >>> 16); buffer[position++] = (byte)(value >>> 8); buffer[position++] = (byte)value; } /** 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 { if (!optimizePositive) value = (value << 1) ^ (value >> 63); if (value >>> 7 == 0) { require(1); buffer[position++] = (byte)value; return 1; } if (value >>> 14 == 0) { require(2); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7); return 2; } if (value >>> 21 == 0) { require(3); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14); return 3; } if (value >>> 28 == 0) { require(4); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21); return 4; } if (value >>> 35 == 0) { require(5); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28); return 5; } if (value >>> 42 == 0) { require(6); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28 | 0x80); buffer[position++] = (byte)(value >>> 35); return 6; } if (value >>> 49 == 0) { require(7); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28 | 0x80); buffer[position++] = (byte)(value >>> 35 | 0x80); buffer[position++] = (byte)(value >>> 42); return 7; } if (value >>> 56 == 0) { require(8); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28 | 0x80); buffer[position++] = (byte)(value >>> 35 | 0x80); buffer[position++] = (byte)(value >>> 42 | 0x80); buffer[position++] = (byte)(value >>> 49); return 8; } require(9); buffer[position++] = (byte)((value & 0x7F) | 0x80); buffer[position++] = (byte)(value >>> 7 | 0x80); buffer[position++] = (byte)(value >>> 14 | 0x80); buffer[position++] = (byte)(value >>> 21 | 0x80); buffer[position++] = (byte)(value >>> 28 | 0x80); buffer[position++] = (byte)(value >>> 35 | 0x80); buffer[position++] = (byte)(value >>> 42 | 0x80); buffer[position++] = (byte)(value >>> 49 | 0x80); buffer[position++] = (byte)(value >>> 56); return 9; } // boolean /** Writes a 1 byte boolean. */ public void writeBoolean (boolean value) throws KryoException { require(1); buffer[position++] = (byte)(value ? 1 : 0); } // char /** Writes a 2 byte char. */ public void writeChar (char value) throws KryoException { require(2); buffer[position++] = (byte)(value >>> 8); buffer[position++] = (byte)value; } // double /** Writes an 8 byte double. */ public void writeDouble (double value) throws KryoException { writeLong(Double.doubleToLongBits(value)); } /** Writes a 1-9 byte double with reduced precision. * @param optimizePositive If true, small positive numbers will be more efficient (1 byte) and small negative numbers will be * inefficient (9 bytes). */ public int writeDouble (double value, double precision, boolean optimizePositive) throws KryoException { 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; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/io/OutputChunked.java000066400000000000000000000050361210575634700267630ustar00rootroot00000000000000 package com.esotericsoftware.kryo.io; import java.io.IOException; import java.io.OutputStream; import com.esotericsoftware.kryo.KryoException; import static com.esotericsoftware.minlog.Log.*; /** 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 */ 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 * used. */ public OutputChunked () { super(2048); } /** 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); } /** Creates an OutputChunked with a maximum chunk size of 2048. */ public OutputChunked (OutputStream outputStream) { super(outputStream, 2048); } /** @param bufferSize The maximum size of a chunk. */ public OutputChunked (OutputStream outputStream, int bufferSize) { super(outputStream, bufferSize); } public void flush () throws KryoException { if (position() > 0) { try { writeChunkSize(); } catch (IOException ex) { throw new KryoException(ex); } } super.flush(); } private void writeChunkSize () throws IOException { int size = position(); if (TRACE) trace("kryo", "Write chunk: " + size); OutputStream outputStream = getOutputStream(); if ((size & ~0x7F) == 0) { outputStream.write(size); return; } outputStream.write((size & 0x7F) | 0x80); size >>>= 7; if ((size & ~0x7F) == 0) { outputStream.write(size); return; } outputStream.write((size & 0x7F) | 0x80); size >>>= 7; if ((size & ~0x7F) == 0) { outputStream.write(size); return; } outputStream.write((size & 0x7F) | 0x80); size >>>= 7; if ((size & ~0x7F) == 0) { outputStream.write(size); return; } outputStream.write((size & 0x7F) | 0x80); size >>>= 7; outputStream.write(size); } /** Marks the end of some data that may have been written by any number of chunks. These chunks can then be skipped when * reading. */ public void endChunks () { flush(); // Flush any partial chunk. if (TRACE) trace("kryo", "End chunks."); try { getOutputStream().write(0); // Zero length chunk. } catch (IOException ex) { throw new KryoException(ex); } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/000077500000000000000000000000001210575634700252375ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/BeanSerializer.java000066400000000000000000000166751210575634700310200ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.reflectasm.MethodAccess; import static com.esotericsoftware.minlog.Log.*; /** Serializes Java beans using bean accessor methods. Only bean properties with both a getter and setter are serialized. This * class is not as fast as {@link FieldSerializer} but is much faster and more efficient than Java serialization. Bytecode * generation is used to invoke the bean propert methods, if possible. *

* BeanSerializer does not write header data, only the object data is stored. If the type of a bean property is not final (note * primitives are final) then an extra byte is written for that property. * @see Serializer * @see Kryo#register(Class, Serializer) * @author Nathan Sweet */ public class BeanSerializer extends Serializer { static final Object[] noArgs = {}; private final Kryo kryo; private CachedProperty[] properties; Object access; public BeanSerializer (Kryo kryo, Class type) { this.kryo = kryo; BeanInfo info; try { info = Introspector.getBeanInfo(type); } catch (IntrospectionException ex) { throw new KryoException("Error getting bean info.", ex); } // Methods are sorted by alpha so the order of the data is known. PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); Arrays.sort(descriptors, new Comparator() { public int compare (PropertyDescriptor o1, PropertyDescriptor o2) { return o1.getName().compareTo(o2.getName()); } }); ArrayList cachedProperties = new ArrayList(descriptors.length); for (int i = 0, n = descriptors.length; i < n; i++) { PropertyDescriptor property = descriptors[i]; String name = property.getName(); if (name.equals("class")) continue; Method getMethod = property.getReadMethod(); Method setMethod = property.getWriteMethod(); if (getMethod == null || setMethod == null) continue; // Require both a getter and setter. // Always use the same serializer for this property if the properties' class is final. Serializer serializer = null; Class returnType = getMethod.getReturnType(); if (kryo.isFinal(returnType)) serializer = kryo.getRegistration(returnType).getSerializer(); CachedProperty cachedProperty = new CachedProperty(); cachedProperty.name = name; cachedProperty.getMethod = getMethod; cachedProperty.setMethod = setMethod; cachedProperty.serializer = serializer; cachedProperty.setMethodType = setMethod.getParameterTypes()[0]; cachedProperties.add(cachedProperty); } properties = cachedProperties.toArray(new CachedProperty[cachedProperties.size()]); try { access = MethodAccess.get(type); for (int i = 0, n = properties.length; i < n; i++) { CachedProperty property = properties[i]; property.getterAccessIndex = ((MethodAccess)access).getIndex(property.getMethod.getName()); property.setterAccessIndex = ((MethodAccess)access).getIndex(property.setMethod.getName()); } } catch (Throwable ignored) { // ReflectASM is not available on Android. } } public void write (Kryo kryo, Output output, T object) { Class type = object.getClass(); for (int i = 0, n = properties.length; i < n; i++) { CachedProperty property = properties[i]; try { if (TRACE) trace("kryo", "Write property: " + property + " (" + type.getName() + ")"); Object value = property.get(object); Serializer serializer = property.serializer; if (serializer != null) kryo.writeObjectOrNull(output, value, serializer); else kryo.writeClassAndObject(output, value); } catch (IllegalAccessException ex) { throw new KryoException("Error accessing getter method: " + property + " (" + type.getName() + ")", ex); } catch (InvocationTargetException ex) { throw new KryoException("Error invoking getter method: " + property + " (" + type.getName() + ")", ex); } catch (KryoException ex) { ex.addTrace(property + " (" + type.getName() + ")"); throw ex; } catch (RuntimeException runtimeEx) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(property + " (" + type.getName() + ")"); throw ex; } } } public T read (Kryo kryo, Input input, Class type) { T object = kryo.newInstance(type); kryo.reference(object); for (int i = 0, n = properties.length; i < n; i++) { CachedProperty property = properties[i]; try { if (TRACE) trace("kryo", "Read property: " + property + " (" + object.getClass() + ")"); Object value; Serializer serializer = property.serializer; if (serializer != null) value = kryo.readObjectOrNull(input, property.setMethodType, serializer); else value = kryo.readClassAndObject(input); property.set(object, value); } catch (IllegalAccessException ex) { throw new KryoException("Error accessing setter method: " + property + " (" + object.getClass().getName() + ")", ex); } catch (InvocationTargetException ex) { throw new KryoException("Error invoking setter method: " + property + " (" + object.getClass().getName() + ")", ex); } catch (KryoException ex) { ex.addTrace(property + " (" + object.getClass().getName() + ")"); throw ex; } catch (RuntimeException runtimeEx) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(property + " (" + object.getClass().getName() + ")"); throw ex; } } return object; } public T copy (Kryo kryo, T original) { T copy = (T)kryo.newInstance(original.getClass()); for (int i = 0, n = properties.length; i < n; i++) { CachedProperty property = properties[i]; try { Object value = property.get(original); property.set(copy, value); } catch (KryoException ex) { ex.addTrace(property + " (" + copy.getClass().getName() + ")"); throw ex; } catch (RuntimeException runtimeEx) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(property + " (" + copy.getClass().getName() + ")"); throw ex; } catch (Exception ex) { throw new KryoException("Error copying bean property: " + property + " (" + copy.getClass().getName() + ")", ex); } } return copy; } class CachedProperty { String name; Method getMethod, setMethod; Class setMethodType; Serializer serializer; int getterAccessIndex, setterAccessIndex; public String toString () { return name; } Object get (Object object) throws IllegalAccessException, InvocationTargetException { if (access != null) return ((MethodAccess)access).invoke(object, getterAccessIndex); return getMethod.invoke(object, noArgs); } void set (Object object, Object value) throws IllegalAccessException, InvocationTargetException { if (access != null) { ((MethodAccess)access).invoke(object, setterAccessIndex, value); return; } setMethod.invoke(object, new Object[] {value}); } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java000066400000000000000000000037201210575634700317130ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.io.IOException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.spec.SecretKeySpec; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** Encrypts data using the blowfish cipher. * @author Nathan Sweet */ public class BlowfishSerializer extends Serializer { private final Serializer serializer; static private SecretKeySpec keySpec; public BlowfishSerializer (Serializer serializer, byte[] key) { this.serializer = serializer; keySpec = new SecretKeySpec(key, "Blowfish"); } public void write (Kryo kryo, Output output, Object object) { Cipher cipher = getCipher(Cipher.ENCRYPT_MODE); CipherOutputStream cipherStream = new CipherOutputStream(output, cipher); Output cipherOutput = new Output(cipherStream, 256) { public void close () throws KryoException { // Don't allow the CipherOutputStream to close the output. } }; kryo.writeObject(cipherOutput, object, serializer); cipherOutput.flush(); try { cipherStream.close(); } catch (IOException ex) { throw new KryoException(ex); } } public Object read (Kryo kryo, Input input, Class type) { Cipher cipher = getCipher(Cipher.DECRYPT_MODE); CipherInputStream cipherInput = new CipherInputStream(input, cipher); return kryo.readObject(new Input(cipherInput, 256), type, serializer); } public Object copy (Kryo kryo, Object original) { return serializer.copy(kryo, original); } static private Cipher getCipher (int mode) { try { Cipher cipher = Cipher.getInstance("Blowfish"); cipher.init(mode, keySpec); return cipher; } catch (Exception ex) { throw new KryoException(ex); } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java000066400000000000000000000113661210575634700322360ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.util.ArrayList; import java.util.Collection; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** Serializes objects that implement the {@link Collection} interface. *

* 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 */ public class CollectionSerializer extends Serializer { private boolean elementsCanBeNull = true; private Serializer serializer; private Class elementClass; private Class genericType; public CollectionSerializer () { } /** @see #setElementClass(Class, Serializer) */ public CollectionSerializer (Class elementClass, Serializer serializer) { setElementClass(elementClass, serializer); } /** @see #setElementClass(Class, Serializer) * @see #setElementsCanBeNull(boolean) */ public CollectionSerializer (Class elementClass, Serializer serializer, boolean elementsCanBeNull) { setElementClass(elementClass, serializer); this.elementsCanBeNull = elementsCanBeNull; } /** @param elementsCanBeNull False if all elements are not null. This saves 1 byte per element if elementClass is set. True if it * is not known (default). */ public void setElementsCanBeNull (boolean elementsCanBeNull) { this.elementsCanBeNull = elementsCanBeNull; } /** @param elementClass The concrete class of each element. This saves 1-2 bytes per element. Set to null if the class is not * known or varies per element (default). * @param serializer The serializer to use for each element. */ public void setElementClass (Class elementClass, Serializer serializer) { this.elementClass = elementClass; this.serializer = serializer; } public void setGenerics (Kryo kryo, Class[] generics) { if (kryo.isFinal(generics[0])) genericType = generics[0]; } public void write (Kryo kryo, Output output, Collection collection) { int length = collection.size(); output.writeInt(length, true); Serializer serializer = this.serializer; if (genericType != null) { if (serializer == null) serializer = kryo.getSerializer(genericType); genericType = null; } if (serializer != null) { if (elementsCanBeNull) { for (Object element : collection) kryo.writeObjectOrNull(output, element, serializer); } else { for (Object element : collection) kryo.writeObject(output, element, serializer); } } else { for (Object element : collection) kryo.writeClassAndObject(output, element); } } /** Used by {@link #read(Kryo, Input, Class)} 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 Collection create (Kryo kryo, Input input, Class type) { return kryo.newInstance(type); } public Collection read (Kryo kryo, Input input, Class type) { Collection collection = create(kryo, input, type); kryo.reference(collection); int length = input.readInt(true); if (collection instanceof ArrayList) ((ArrayList)collection).ensureCapacity(length); Class elementClass = this.elementClass; Serializer serializer = this.serializer; if (genericType != null) { if (serializer == null) { elementClass = genericType; serializer = kryo.getSerializer(genericType); } genericType = null; } if (serializer != null) { if (elementsCanBeNull) { for (int i = 0; i < length; i++) collection.add(kryo.readObjectOrNull(input, elementClass, serializer)); } else { for (int i = 0; i < length; i++) collection.add(kryo.readObject(input, elementClass, serializer)); } } else { for (int i = 0; i < length; i++) collection.add(kryo.readClassAndObject(input)); } return collection; } /** Used by {@link #copy(Kryo, Collection)} 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 Collection createCopy (Kryo kryo, Collection original) { return kryo.newInstance(original.getClass()); } public Collection copy (Kryo kryo, Collection original) { Collection copy = createCopy(kryo, original); kryo.reference(copy); for (Object element : original) copy.add(kryo.copy(element)); return copy; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java000066400000000000000000000065401210575634700331640ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.InputChunked; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.io.OutputChunked; import com.esotericsoftware.kryo.util.ObjectMap; import static com.esotericsoftware.minlog.Log.*; /** Serializes objects using direct field assignment, with limited support for forward and backward compatibility. Fields can be * added or removed without invalidating previously serialized bytes. Note that changing the type of a field is not supported. *

* There is additional overhead compared to {@link FieldSerializer}. A header is output the first time an object of a given type * is serialized. The header consists of an int for the number of fields, then a String for each field name. Also, to support * skipping the bytes for a field that no longer exists, for each field value an int is written that is the length of the value in * bytes. *

* 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 { public CompatibleFieldSerializer (Kryo kryo, Class type) { super(kryo, type); } public void write (Kryo kryo, Output output, T object) { CachedField[] fields = getFields(); ObjectMap context = kryo.getGraphContext(); if (!context.containsKey(this)) { context.put(this, null); if (TRACE) trace("kryo", "Write " + fields.length + " field names."); output.writeInt(fields.length, true); for (int i = 0, n = fields.length; i < n; i++) output.writeString(fields[i].field.getName()); } OutputChunked outputChunked = new OutputChunked(output, 1024); for (int i = 0, n = fields.length; i < n; i++) { fields[i].write(outputChunked, object); outputChunked.endChunks(); } } public T read (Kryo kryo, Input input, Class type) { T object = kryo.newInstance(type); kryo.reference(object); ObjectMap context = kryo.getGraphContext(); CachedField[] fields = (CachedField[])context.get(this); if (fields == null) { 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++) names[i] = input.readString(); fields = new CachedField[length]; CachedField[] allFields = getFields(); outer: for (int i = 0, n = names.length; i < n; i++) { String schemaName = names[i]; for (int ii = 0, nn = allFields.length; ii < nn; ii++) { if (allFields[ii].field.getName().equals(schemaName)) { fields[i] = allFields[ii]; continue outer; } } if (TRACE) trace("kryo", "Ignore obsolete field: " + schemaName); } context.put(this, fields); } InputChunked inputChunked = new InputChunked(input, 1024); for (int i = 0, n = fields.length; i < n; i++) { CachedField cachedField = fields[i]; if (cachedField == null) { if (TRACE) trace("kryo", "Skip obsolete field."); inputChunked.nextChunks(); continue; } cachedField.read(inputChunked, object); inputChunked.nextChunks(); } return object; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/DefaultArraySerializers.java000066400000000000000000000254731210575634700327150ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.lang.reflect.Array; import java.lang.reflect.Modifier; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import static com.esotericsoftware.kryo.Kryo.*; /** Contains many serializer classes for specific array types that are provided by {@link Kryo#addDefaultSerializer(Class, Class) * default}. * @author Nathan Sweet */ public class DefaultArraySerializers { static public class ByteArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, byte[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); output.writeBytes(object); } public byte[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; return input.readBytes(length - 1); } public byte[] copy (Kryo kryo, byte[] original) { byte[] copy = new byte[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class IntArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, int[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeInt(object[i], false); } public int[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; int[] array = new int[--length]; for (int i = 0; i < length; i++) array[i] = input.readInt(false); return array; } public int[] copy (Kryo kryo, int[] original) { int[] copy = new int[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class FloatArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, float[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeFloat(object[i]); } public float[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; float[] array = new float[--length]; for (int i = 0; i < length; i++) array[i] = input.readFloat(); return array; } public float[] copy (Kryo kryo, float[] original) { float[] copy = new float[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class LongArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, long[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeLong(object[i], false); } public long[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; long[] array = new long[--length]; for (int i = 0; i < length; i++) array[i] = input.readLong(false); return array; } public long[] copy (Kryo kryo, long[] original) { long[] copy = new long[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class ShortArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, short[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeShort(object[i]); } public short[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; short[] array = new short[--length]; for (int i = 0; i < length; i++) array[i] = input.readShort(); return array; } public short[] copy (Kryo kryo, short[] original) { short[] copy = new short[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class CharArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, char[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeChar(object[i]); } public char[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; char[] array = new char[--length]; for (int i = 0; i < length; i++) array[i] = input.readChar(); return array; } public char[] copy (Kryo kryo, char[] original) { char[] copy = new char[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class DoubleArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, double[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeDouble(object[i]); } public double[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; double[] array = new double[--length]; for (int i = 0; i < length; i++) array[i] = input.readDouble(); return array; } public double[] copy (Kryo kryo, double[] original) { double[] copy = new double[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class BooleanArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, boolean[] object) { if (object == null) { output.writeByte(NULL); return; } 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.readInt(true); if (length == NULL) return null; boolean[] array = new boolean[--length]; for (int i = 0; i < length; i++) array[i] = input.readBoolean(); return array; } public boolean[] copy (Kryo kryo, boolean[] original) { boolean[] copy = new boolean[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class StringArraySerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, String[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); for (int i = 0, n = object.length; i < n; i++) output.writeString(object[i]); } public String[] read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; String[] array = new String[--length]; for (int i = 0; i < length; i++) array[i] = input.readString(); return array; } public String[] copy (Kryo kryo, String[] original) { String[] copy = new String[original.length]; System.arraycopy(original, 0, copy, 0, copy.length); return copy; } } static public class ObjectArraySerializer extends Serializer { private boolean elementsAreSameType; private boolean elementsCanBeNull = true; { setAcceptsNull(true); } public void write (Kryo kryo, Output output, Object[] object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.length + 1, true); Class elementClass = object.getClass().getComponentType(); if (elementsAreSameType || Modifier.isFinal(elementClass.getModifiers())) { Serializer elementSerializer = kryo.getSerializer(elementClass); for (int i = 0, n = object.length; i < n; i++) { if (elementsCanBeNull) kryo.writeObjectOrNull(output, object[i], elementSerializer); else kryo.writeObject(output, object[i], elementSerializer); } } else { for (int i = 0, n = object.length; i < n; i++) kryo.writeClassAndObject(output, object[i]); } } public Object[] read (Kryo kryo, Input input, Class type) { 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); for (int i = 0, n = object.length; i < n; i++) { if (elementsCanBeNull) object[i] = kryo.readObjectOrNull(input, elementClass, elementSerializer); else object[i] = kryo.readObject(input, elementClass, elementSerializer); } } else { for (int i = 0, n = object.length; i < n; i++) object[i] = kryo.readClassAndObject(input); } return object; } public Object[] copy (Kryo kryo, Object[] original) { Object[] copy = (Object[])Array.newInstance(original.getClass().getComponentType(), original.length); System.arraycopy(original, 0, copy, 0, copy.length); return copy; } /** @param elementsCanBeNull False if all elements are not null. This saves 1 byte per element if the array type is final or * elementsAreSameClassAsType is true. True if it is not known (default). */ public void setElementsCanBeNull (boolean elementsCanBeNull) { this.elementsCanBeNull = elementsCanBeNull; } /** @param elementsAreSameType True if all elements are the same type as the array (ie they don't extend the array type). This * saves 1 byte per element if the array type is not final. Set to false if the array type is final or elements * extend the array type (default). */ public void setElementsAreSameType (boolean elementsAreSameType) { this.elementsAreSameType = elementsAreSameType; } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java000066400000000000000000000407231210575634700317110ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Currency; import java.util.Date; import java.util.EnumSet; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoSerializable; import com.esotericsoftware.kryo.Registration; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import static com.esotericsoftware.kryo.Kryo.*; /** Contains many serializer classes that are provided by {@link Kryo#addDefaultSerializer(Class, Class) default}. * @author Nathan Sweet */ public class DefaultSerializers { static public class BooleanSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Boolean object) { output.writeBoolean(object); } public Boolean read (Kryo kryo, Input input, Class type) { return input.readBoolean(); } } static public class ByteSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Byte object) { output.writeByte(object); } public Byte read (Kryo kryo, Input input, Class type) { return input.readByte(); } } static public class CharSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Character object) { output.writeChar(object); } public Character read (Kryo kryo, Input input, Class type) { return input.readChar(); } } static public class ShortSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Short object) { output.writeShort(object); } public Short read (Kryo kryo, Input input, Class type) { return input.readShort(); } } static public class IntSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Integer object) { output.writeInt(object, false); } public Integer read (Kryo kryo, Input input, Class type) { return input.readInt(false); } } static public class LongSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Long object) { output.writeLong(object, false); } public Long read (Kryo kryo, Input input, Class type) { return input.readLong(false); } } static public class FloatSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Float object) { output.writeFloat(object); } public Float read (Kryo kryo, Input input, Class type) { return input.readFloat(); } } static public class DoubleSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Double object) { output.writeDouble(object); } public Double read (Kryo kryo, Input input, Class type) { return input.readDouble(); } } /** @see Output#writeString(String) */ static public class StringSerializer extends Serializer { { setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, String object) { output.writeString(object); } public String read (Kryo kryo, Input input, Class type) { return input.readString(); } } static public class BigIntegerSerializer extends Serializer { { setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, BigInteger object) { if (object == null) { output.writeByte(NULL); return; } BigInteger value = (BigInteger)object; byte[] bytes = value.toByteArray(); output.writeInt(bytes.length + 1, true); output.writeBytes(bytes); } public BigInteger read (Kryo kryo, Input input, Class type) { int length = input.readInt(true); if (length == NULL) return null; byte[] bytes = input.readBytes(length - 1); return new BigInteger(bytes); } } static public class BigDecimalSerializer extends Serializer { private BigIntegerSerializer bigIntegerSerializer = new BigIntegerSerializer(); { setAcceptsNull(true); setImmutable(true); } public void write (Kryo kryo, Output output, BigDecimal object) { if (object == null) { output.writeByte(NULL); return; } BigDecimal value = (BigDecimal)object; bigIntegerSerializer.write(kryo, output, value.unscaledValue()); output.writeInt(value.scale(), false); } public BigDecimal read (Kryo kryo, Input input, Class type) { BigInteger unscaledValue = bigIntegerSerializer.read(kryo, input, null); if (unscaledValue == null) return null; int scale = input.readInt(false); return new BigDecimal(unscaledValue, scale); } } static public class ClassSerializer extends Serializer { { setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, Class object) { kryo.writeClass(output, object); } public Class read (Kryo kryo, Input input, Class type) { return kryo.readClass(input).getType(); } } static public class DateSerializer extends Serializer { public void write (Kryo kryo, Output output, Date object) { output.writeLong(object.getTime(), true); } public Date read (Kryo kryo, Input input, Class type) { return new Date(input.readLong(true)); } public Date copy (Kryo kryo, Date original) { return new Date(original.getTime()); } } static public class EnumSerializer extends Serializer { { setImmutable(true); setAcceptsNull(true); } private Object[] enumConstants; public EnumSerializer (Class type) { enumConstants = type.getEnumConstants(); if (enumConstants == null) throw new IllegalArgumentException("The type must be an enum: " + type); } public void write (Kryo kryo, Output output, Enum object) { if (object == null) { output.writeByte(NULL); return; } output.writeInt(object.ordinal() + 1, true); } public Enum read (Kryo kryo, Input input, Class type) { int ordinal = input.readInt(true); if (ordinal == NULL) return null; ordinal--; if (ordinal < 0 || ordinal > enumConstants.length - 1) throw new KryoException("Invalid ordinal for enum \"" + type.getName() + "\": " + ordinal); Object constant = enumConstants[ordinal]; return (Enum)constant; } } static public class EnumSetSerializer extends Serializer { public void write (Kryo kryo, Output output, EnumSet object) { if (object.isEmpty()) throw new KryoException("An empty EnumSet cannot be serialized."); Serializer serializer = kryo.writeClass(output, object.iterator().next().getClass()).getSerializer(); output.writeInt(object.size(), true); for (Object element : object) serializer.write(kryo, output, element); } public EnumSet read (Kryo kryo, Input input, Class type) { Registration registration = kryo.readClass(input); EnumSet object = EnumSet.noneOf(registration.getType()); Serializer serializer = registration.getSerializer(); int length = input.readInt(true); for (int i = 0; i < length; i++) object.add(serializer.read(kryo, input, null)); return object; } public EnumSet copy (Kryo kryo, EnumSet original) { return EnumSet.copyOf(original); } } /** @author Martin Grotzke */ static public class CurrencySerializer extends Serializer { { setImmutable(true); setAcceptsNull(true); } public void write (Kryo kryo, Output output, Currency object) { output.writeString(object == null ? null : object.getCurrencyCode()); } public Currency read (Kryo kryo, Input input, Class type) { String currencyCode = input.readString(); if (currencyCode == null) return null; return Currency.getInstance(currencyCode); } } /** @author Martin Grotzke */ static public class StringBufferSerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, StringBuffer object) { output.writeString(object); } public StringBuffer read (Kryo kryo, Input input, Class type) { String value = input.readString(); if (value == null) return null; return new StringBuffer(value); } public StringBuffer copy (Kryo kryo, StringBuffer original) { return new StringBuffer(original); } } /** @author Martin Grotzke */ static public class StringBuilderSerializer extends Serializer { { setAcceptsNull(true); } public void write (Kryo kryo, Output output, StringBuilder object) { output.writeString(object); } public StringBuilder read (Kryo kryo, Input input, Class type) { return input.readStringBuilder(); } public StringBuilder copy (Kryo kryo, StringBuilder original) { return new StringBuilder(original); } } static public class KryoSerializableSerializer extends Serializer { public void write (Kryo kryo, Output output, KryoSerializable object) { object.write(kryo, output); } public KryoSerializable read (Kryo kryo, Input input, Class type) { KryoSerializable object = kryo.newInstance(type); kryo.reference(object); object.read(kryo, input); return object; } } /** 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); } public void write (Kryo kryo, Output output, Object object) { } public Object read (Kryo kryo, Input input, Class type) { return Collections.EMPTY_LIST; } } /** 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); } public void write (Kryo kryo, Output output, Object object) { } public Object read (Kryo kryo, Input input, Class type) { return Collections.EMPTY_MAP; } } /** 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); } public void write (Kryo kryo, Output output, Object object) { } public Object read (Kryo kryo, Input input, Class type) { return Collections.EMPTY_SET; } } /** Serializer for lists created via {@link Collections#singletonList(Object)}. * @author Martin Grotzke */ static public class CollectionsSingletonListSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, List object) { kryo.writeClassAndObject(output, object.get(0)); } public List read (Kryo kryo, Input input, Class type) { return Collections.singletonList(kryo.readClassAndObject(input)); } } /** Serializer for maps created via {@link Collections#singletonMap(Object, Object)}. * @author Martin Grotzke */ static public class CollectionsSingletonMapSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Map object) { Entry entry = (Entry)object.entrySet().iterator().next(); kryo.writeClassAndObject(output, entry.getKey()); kryo.writeClassAndObject(output, entry.getValue()); } public Map read (Kryo kryo, Input input, Class type) { Object key = kryo.readClassAndObject(input); Object value = kryo.readClassAndObject(input); return Collections.singletonMap(key, value); } } /** Serializer for sets created via {@link Collections#singleton(Object)}. * @author Martin Grotzke */ static public class CollectionsSingletonSetSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, Set object) { kryo.writeClassAndObject(output, object.iterator().next()); } public Set read (Kryo kryo, Input input, Class type) { return Collections.singleton(kryo.readClassAndObject(input)); } } /** Serializer for {@link TimeZone}. Assumes the timezones are immutable. * @author serverperformance */ static public class TimeZoneSerializer extends Serializer { { setImmutable(true); } public void write (Kryo kryo, Output output, TimeZone object) { output.writeString(object.getID()); } public TimeZone read (Kryo kryo, Input input, Class type) { return TimeZone.getTimeZone(input.readString()); } } /** Serializer for {@link GregorianCalendar}, java.util.JapaneseImperialCalendar, and sun.util.BuddhistCalendar. * @author serverperformance */ static public class CalendarSerializer extends Serializer { // The default value of gregorianCutover. static private final long DEFAULT_GREGORIAN_CUTOVER = -12219292800000L; TimeZoneSerializer timeZoneSerializer = new TimeZoneSerializer(); public void write (Kryo kryo, Output output, Calendar object) { timeZoneSerializer.write(kryo, output, object.getTimeZone()); // can't be null output.writeLong(object.getTimeInMillis(), true); output.writeBoolean(object.isLenient()); output.writeInt(object.getFirstDayOfWeek(), true); output.writeInt(object.getMinimalDaysInFirstWeek(), true); if (object instanceof GregorianCalendar) output.writeLong(((GregorianCalendar)object).getGregorianChange().getTime(), false); else output.writeLong(DEFAULT_GREGORIAN_CUTOVER, false); } public Calendar read (Kryo kryo, Input input, Class type) { Calendar result = Calendar.getInstance(timeZoneSerializer.read(kryo, input, TimeZone.class)); result.setTimeInMillis(input.readLong(true)); result.setLenient(input.readBoolean()); result.setFirstDayOfWeek(input.readInt(true)); result.setMinimalDaysInFirstWeek(input.readInt(true)); long gregorianChange = input.readLong(false); if (gregorianChange != DEFAULT_GREGORIAN_CUTOVER) if (result instanceof GregorianCalendar) ((GregorianCalendar)result).setGregorianChange(new Date(gregorianChange)); return result; } public Calendar copy (Kryo kryo, Calendar original) { return (Calendar)original.clone(); } } static public class TreeMapSerializer extends MapSerializer { public void write (Kryo kryo, Output output, Map map) { TreeMap treeMap = (TreeMap)map; boolean references = kryo.setReferences(false); kryo.writeClassAndObject(output, treeMap.comparator()); kryo.setReferences(references); super.write(kryo, output, map); } protected Map create (Kryo kryo, Input input, Class type) { // Child objects can't be deserialized before {@link Kryo#reference(Object)} is called, but in this case it is required // to create the object, so references must be temporarily disabled. boolean references = kryo.setReferences(false); Comparator comparator = (Comparator)kryo.readClassAndObject(input); kryo.setReferences(references); return new TreeMap(comparator); } protected Map createCopy (Kryo kryo, Map original) { return new TreeMap(((TreeMap)original).comparator()); } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/DeflateSerializer.java000066400000000000000000000034261210575634700315050ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.io.IOException; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; public class DeflateSerializer extends Serializer { private final Serializer serializer; private boolean noHeaders = true; private int compressionLevel = 4; public DeflateSerializer (Serializer serializer) { this.serializer = serializer; } public void write (Kryo kryo, Output output, Object object) { Deflater deflater = new Deflater(compressionLevel, noHeaders); DeflaterOutputStream deflaterStream = new DeflaterOutputStream(output, deflater); Output deflaterOutput = new Output(deflaterStream, 256); kryo.writeObject(deflaterOutput, object, serializer); deflaterOutput.flush(); try { deflaterStream.finish(); } catch (IOException ex) { throw new KryoException(ex); } } public Object read (Kryo kryo, Input input, Class type) { Inflater inflater = new Inflater(noHeaders); InflaterInputStream inflaterInput = new InflaterInputStream(input, inflater); return kryo.readObject(new Input(inflaterInput, 256), type, serializer); } public void setNoHeaders (boolean noHeaders) { this.noHeaders = noHeaders; } /** Default is 4. * @see Deflater#setLevel(int) */ public void setCompressionLevel (int compressionLevel) { this.compressionLevel = compressionLevel; } public Object copy (Kryo kryo, Object original) { return serializer.copy(kryo, original); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/FieldSerializer.java000066400000000000000000000604311210575634700311630ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.NotNull; import com.esotericsoftware.kryo.Registration; 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; import static com.esotericsoftware.minlog.Log.*; // BOZO - Make primitive serialization with ReflectASM configurable? /** Serializes objects using direct field assignment. No header or schema data is stored, only the data for each field. This * reduces output size but means if any field is added or removed, previously serialized bytes are invalidated. If fields are * public, bytecode generation will be used instead of reflection. * @see Serializer * @see Kryo#register(Class, Serializer) * @author Nathan Sweet */ public class FieldSerializer extends Serializer implements Comparator { final Kryo kryo; final Class type; private CachedField[] fields = new CachedField[0]; Object access; private boolean fieldsCanBeNull = true, setFieldsAsAccessible = true; private boolean ignoreSyntheticFields = true; private boolean fixedFieldTypes; // BOZO - Get rid of kryo here? public FieldSerializer (Kryo kryo, Class type) { this.kryo = kryo; this.type = type; 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. */ private void rebuildCachedFields () { if (type.isInterface()) { fields = new CachedField[0]; // No fields to serialize. return; } // Collect all fields. ArrayList allFields = new ArrayList(); Class nextClass = type; while (nextClass != Object.class) { Collections.addAll(allFields, nextClass.getDeclaredFields()); nextClass = nextClass.getSuperclass(); } ObjectMap context = kryo.getContext(); IntArray useAsm = new IntArray(); ArrayList validFields = 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)) continue; if (Modifier.isStatic(modifiers)) continue; if (field.isSynthetic() && ignoreSyntheticFields) continue; if (!field.isAccessible()) { if (!setFieldsAsAccessible) continue; try { field.setAccessible(true); } catch (AccessControlException ex) { continue; } } Optional optional = field.getAnnotation(Optional.class); if (optional != null && !context.containsKey(optional.value())) continue; validFields.add(field); // BOZO - Must be public? useAsm.add(!Modifier.isFinal(modifiers) && Modifier.isPublic(modifiers) && Modifier.isPublic(field.getType().getModifiers()) ? 1 : 0); } // 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) { } } ArrayList cachedFields = new ArrayList(validFields.size()); for (int i = 0, n = validFields.size(); i < n; i++) { Field field = validFields.get(i); int accessIndex = -1; if (access != null && useAsm.get(i) == 1) accessIndex = ((FieldAccess)access).getIndex(field.getName()); cachedFields.add(newCachedField(field, cachedFields.size(), accessIndex)); } Collections.sort(cachedFields, this); fields = cachedFields.toArray(new CachedField[cachedFields.size()]); initializeCachedFields(); } protected void initializeCachedFields () { } private CachedField newCachedField (Field field, int fieldIndex, int accessIndex) { Class fieldClass = field.getType(); CachedField cachedField; if (accessIndex != -1) { if (fieldClass.isPrimitive()) { if (fieldClass == boolean.class) cachedField = new BooleanField(); else if (fieldClass == byte.class) cachedField = new ByteField(); else if (fieldClass == char.class) cachedField = new CharField(); else if (fieldClass == short.class) cachedField = new ShortField(); else if (fieldClass == int.class) cachedField = new IntField(); else if (fieldClass == long.class) cachedField = new LongField(); else if (fieldClass == float.class) cachedField = new FloatField(); else if (fieldClass == double.class) cachedField = new DoubleField(); else cachedField = new ObjectField(); } else if (fieldClass == String.class && (!kryo.getReferences() || !kryo.getReferenceResolver().useReferences(String.class))) { cachedField = new StringField(); } else cachedField = new ObjectField(); } else { cachedField = new ObjectField(); ((ObjectField)cachedField).generics = Kryo.getGenerics(field.getGenericType()); } cachedField.field = field; cachedField.accessIndex = accessIndex; cachedField.canBeNull = 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) || fixedFieldTypes) cachedField.valueClass = fieldClass; return cachedField; } public int compare (CachedField o1, CachedField o2) { // Fields are sorted by alpha so the order of the data is known. return o1.field.getName().compareTo(o2.field.getName()); } /** 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) { this.fieldsCanBeNull = 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) { this.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) { this.ignoreSyntheticFields = 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) { this.fixedFieldTypes = fixedFieldTypes; rebuildCachedFields(); } public void write (Kryo kryo, Output output, T object) { CachedField[] fields = this.fields; for (int i = 0, n = fields.length; i < n; i++) fields[i].write(output, object); } public T read (Kryo kryo, Input input, Class type) { 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); return object; } /** Used by {@link #read(Kryo, Input, Class)} 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 create (Kryo kryo, Input input, Class type) { return kryo.newInstance(type); } /** Allows specific fields to be optimized. */ public CachedField getField (String fieldName) { for (CachedField cachedField : fields) if (cachedField.field.getName().equals(fieldName)) return cachedField; throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); } /** 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.field.getName().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; return; } } throw new IllegalArgumentException("Field \"" + fieldName + "\" not found on class: " + type.getName()); } public CachedField[] getFields () { return fields; } public Class getType () { return type; } /** 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) { return (T)kryo.newInstance(original.getClass()); } public T copy (Kryo kryo, T original) { T copy = createCopy(kryo, original); kryo.reference(copy); for (int i = 0, n = fields.length; i < n; i++) fields[i].copy(original, copy); return copy; } /** Controls how a field will be serialized. */ public abstract class CachedField { Field field; Class valueClass; Serializer serializer; boolean canBeNull; int accessIndex = -1; /** @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 final * or the values for this field will not vary. */ public void setClass (Class valueClass) { this.valueClass = valueClass; this.serializer = null; } /** @param valueClass The concrete class of the values for this field. This saves 1-2 bytes. Only set to a non-null value if * the field type in the class definition is final or the values for this field will not vary. */ public void setClass (Class valueClass, Serializer serializer) { this.valueClass = valueClass; this.serializer = serializer; } public void setSerializer (Serializer serializer) { this.serializer = serializer; } public void setCanBeNull (boolean canBeNull) { this.canBeNull = canBeNull; } public Field getField () { return field; } public String toString () { return field.getName(); } abstract public void write (Output output, Object object); abstract public void read (Input input, Object object); abstract public void copy (Object original, Object copy); } abstract class AsmCachedField extends CachedField { FieldAccess access = (FieldAccess)FieldSerializer.this.access; } class IntField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write int: " + access.getInt(object, accessIndex)); } output.writeInt(access.getInt(object, accessIndex), false); } public void read (Input input, Object object) { if (TRACE) { int value = input.readInt(false); access.setInt(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read int: " + value); } else access.setInt(object, accessIndex, input.readInt(false)); } public void copy (Object original, Object copy) { access.setInt(copy, accessIndex, access.getInt(original, accessIndex)); } } class FloatField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write float: " + access.getFloat(object, accessIndex)); } output.writeFloat(access.getFloat(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { float value = input.readFloat(); access.setFloat(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read float: " + value); } else access.setFloat(object, accessIndex, input.readFloat()); } public void copy (Object original, Object copy) { access.setFloat(copy, accessIndex, access.getFloat(original, accessIndex)); } } class ShortField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write short: " + access.getShort(object, accessIndex)); } output.writeShort(access.getShort(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { short value = input.readShort(); access.setShort(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read short: " + value); } else access.setShort(object, accessIndex, input.readShort()); } public void copy (Object original, Object copy) { access.setShort(copy, accessIndex, access.getShort(original, accessIndex)); } } class ByteField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write byte: " + access.getByte(object, accessIndex)); } output.writeByte(access.getByte(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { byte value = input.readByte(); access.setByte(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read byte: " + value); } else access.setByte(object, accessIndex, input.readByte()); } public void copy (Object original, Object copy) { access.setByte(copy, accessIndex, access.getByte(original, accessIndex)); } } class BooleanField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write boolean: " + access.getBoolean(object, accessIndex)); } output.writeBoolean(access.getBoolean(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { boolean value = input.readBoolean(); access.setBoolean(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read boolean: " + value); } else access.setBoolean(object, accessIndex, input.readBoolean()); } public void copy (Object original, Object copy) { access.setBoolean(copy, accessIndex, access.getBoolean(original, accessIndex)); } } class CharField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write char: " + access.getChar(object, accessIndex)); } output.writeChar(access.getChar(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { char value = input.readChar(); access.setChar(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read char: " + value); } else access.setChar(object, accessIndex, input.readChar()); } public void copy (Object original, Object copy) { access.setChar(copy, accessIndex, access.getChar(original, accessIndex)); } } class LongField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write long: " + access.getLong(object, accessIndex)); } output.writeLong(access.getLong(object, accessIndex), false); } public void read (Input input, Object object) { if (TRACE) { long value = input.readLong(false); access.setLong(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read long: " + value); } else access.setLong(object, accessIndex, input.readLong(false)); } public void copy (Object original, Object copy) { access.setLong(copy, accessIndex, access.getLong(original, accessIndex)); } } class DoubleField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write double: " + access.getDouble(object, accessIndex)); } output.writeDouble(access.getDouble(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { double value = input.readDouble(); access.setDouble(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read double: " + value); } else access.setDouble(object, accessIndex, input.readDouble()); } public void copy (Object original, Object copy) { access.setDouble(copy, accessIndex, access.getDouble(original, accessIndex)); } } class StringField extends AsmCachedField { public void write (Output output, Object object) { if (TRACE) { trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Write string: " + access.getString(object, accessIndex)); } output.writeString(access.getString(object, accessIndex)); } public void read (Input input, Object object) { if (TRACE) { String value = input.readString(); access.set(object, accessIndex, value); trace("kryo", "Read field: " + this + " (" + object.getClass().getName() + ")"); trace("kryo", "Read string: " + value); } else access.set(object, accessIndex, input.readString()); } public void copy (Object original, Object copy) { access.set(copy, accessIndex, access.getString(original, accessIndex)); } } class ObjectField extends CachedField { Class[] generics; public void write (Output output, Object object) { try { if (TRACE) trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")"); Object value; if (accessIndex != -1) value = ((FieldAccess)access).get(object, accessIndex); else value = field.get(object); Serializer serializer = this.serializer; if (valueClass == null) { // The concrete type of the field is unknown, write the class first. if (value == null) { kryo.writeClass(output, null); return; } 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) { kryo.writeObjectOrNull(output, value, serializer); } else { if (value == null) { throw new KryoException("Field value is null but canBeNull is false: " + this + " (" + object.getClass().getName() + ")"); } kryo.writeObject(output, value, serializer); } } } catch (IllegalAccessException ex) { throw new KryoException("Error accessing field: " + this + " (" + object.getClass().getName() + ")", ex); } catch (KryoException ex) { ex.addTrace(this + " (" + object.getClass().getName() + ")"); throw ex; } catch (RuntimeException runtimeEx) { KryoException ex = new KryoException(runtimeEx); ex.addTrace(this + " (" + object.getClass().getName() + ")"); throw ex; } } public void read (Input input, Object object) { try { if (TRACE) trace("kryo", "Read field: " + this + " (" + type.getName() + ")"); Object value; Class concreteType = valueClass; Serializer serializer = this.serializer; if (concreteType == null) { Registration registration = kryo.readClass(input); if (registration == null) 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); else value = kryo.readObject(input, concreteType, serializer); } if (accessIndex != -1) ((FieldAccess)access).set(object, accessIndex, value); else field.set(object, value); } catch (IllegalAccessException ex) { throw new KryoException("Error accessing field: " + this + " (" + type.getName() + ")", ex); } 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; } } public void copy (Object original, Object copy) { try { if (accessIndex != -1) { FieldAccess access = (FieldAccess)FieldSerializer.this.access; access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); } else field.set(copy, kryo.copy(field.get(original))); } catch (IllegalAccessException ex) { throw new KryoException("Error accessing field: " + this + " (" + type.getName() + ")", ex); } 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; } } } /** 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 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) static public @interface Optional { public String value(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/JavaSerializer.java000066400000000000000000000026061210575634700310210ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoSerializable; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** Serializes objects using Java's built in serialization mechanism. Note that this is very inefficient and should be avoided if * possible. * @see Serializer * @see FieldSerializer * @see KryoSerializable * @author Nathan Sweet */ public class JavaSerializer extends Serializer { private ObjectOutputStream objectStream; private Output lastOutput; public void write (Kryo kryo, Output output, Object object) { try { if (output != lastOutput) { objectStream = new ObjectOutputStream(output); lastOutput = output; } else objectStream.reset(); objectStream.writeObject(object); objectStream.flush(); } catch (Exception ex) { throw new KryoException("Error during Java serialization.", ex); } } public Object read (Kryo kryo, Input input, Class type) { try { return new ObjectInputStream(input).readObject(); } catch (Exception ex) { throw new KryoException("Error during Java deserialization.", ex); } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/MapSerializer.java000066400000000000000000000130321210575634700306500ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** 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 */ public class MapSerializer extends Serializer { private Class keyClass, valueClass; private Serializer keySerializer, valueSerializer; private boolean keysCanBeNull = true, valuesCanBeNull = true; private Class keyGenericType, valueGenericType; /** @param keysCanBeNull False if all keys are not null. This saves 1 byte per key if keyClass is set. True if it is not known * (default). */ public void setKeysCanBeNull (boolean keysCanBeNull) { this.keysCanBeNull = keysCanBeNull; } /** @param keyClass The concrete class of each key. This saves 1 byte per key. Set to null if the class is not known or varies * per key (default). * @param keySerializer The serializer to use for each key. */ public void setKeyClass (Class keyClass, Serializer keySerializer) { this.keyClass = keyClass; this.keySerializer = keySerializer; } /** @param valueClass The concrete class of each value. This saves 1 byte per value. Set to null if the class is not known or * varies per value (default). * @param valueSerializer The serializer to use for each value. */ public void setValueClass (Class valueClass, Serializer valueSerializer) { this.valueClass = valueClass; this.valueSerializer = valueSerializer; } /** @param valuesCanBeNull True if values are not null. This saves 1 byte per value if keyClass is set. False if it is not known * (default). */ public void setValuesCanBeNull (boolean valuesCanBeNull) { this.valuesCanBeNull = valuesCanBeNull; } public void setGenerics (Kryo kryo, Class[] generics) { if (generics[0] != null && kryo.isFinal(generics[0])) keyGenericType = generics[0]; if (generics[1] != null && kryo.isFinal(generics[1])) valueGenericType = generics[1]; } public void write (Kryo kryo, Output output, Map map) { int length = map.size(); output.writeInt(length, true); Serializer keySerializer = this.keySerializer; if (keyGenericType != null) { if (keySerializer == null) keySerializer = kryo.getSerializer(keyGenericType); keyGenericType = null; } Serializer valueSerializer = this.valueSerializer; if (valueGenericType != null) { if (valueSerializer == null) valueSerializer = kryo.getSerializer(valueGenericType); valueGenericType = null; } for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { Entry entry = (Entry)iter.next(); if (keySerializer != null) { if (keysCanBeNull) kryo.writeObjectOrNull(output, entry.getKey(), keySerializer); else kryo.writeObject(output, entry.getKey(), keySerializer); } else kryo.writeClassAndObject(output, entry.getKey()); if (valueSerializer != null) { if (valuesCanBeNull) kryo.writeObjectOrNull(output, entry.getValue(), valueSerializer); else kryo.writeObject(output, entry.getValue(), valueSerializer); } else kryo.writeClassAndObject(output, entry.getValue()); } } /** Used by {@link #read(Kryo, Input, Class)} 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 Map create (Kryo kryo, Input input, Class type) { return kryo.newInstance(type); } public Map read (Kryo kryo, Input input, Class type) { Map map = create(kryo, input, type); int length = input.readInt(true); Class keyClass = this.keyClass; Class valueClass = this.valueClass; Serializer keySerializer = this.keySerializer; if (keyGenericType != null) { keyClass = keyGenericType; if (keySerializer == null) keySerializer = kryo.getSerializer(keyClass); keyGenericType = null; } Serializer valueSerializer = this.valueSerializer; if (valueGenericType != null) { valueClass = valueGenericType; if (valueSerializer == null) valueSerializer = kryo.getSerializer(valueClass); valueGenericType = null; } kryo.reference(map); for (int i = 0; i < length; i++) { Object key; if (keySerializer != null) { if (keysCanBeNull) key = kryo.readObjectOrNull(input, keyClass, keySerializer); else key = kryo.readObject(input, keyClass, keySerializer); } else key = kryo.readClassAndObject(input); Object value; if (valueSerializer != null) { if (valuesCanBeNull) value = kryo.readObjectOrNull(input, valueClass, valueSerializer); else value = kryo.readObject(input, valueClass, valueSerializer); } else value = kryo.readClassAndObject(input); map.put(key, value); } return map; } protected Map createCopy (Kryo kryo, Map original) { return kryo.newInstance(original.getClass()); } public Map copy (Kryo kryo, Map original) { Map copy = createCopy(kryo, original); for (Iterator iter = original.entrySet().iterator(); iter.hasNext();) { Entry entry = (Entry)iter.next(); copy.put(kryo.copy(entry.getKey()), kryo.copy(entry.getValue())); } return copy; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java000066400000000000000000000062751210575634700323050ustar00rootroot00000000000000 package com.esotericsoftware.kryo.serializers; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import static com.esotericsoftware.minlog.Log.*; /** Serializes objects using direct field assignment for fields that have been {@link Tag tagged}. Fields without the {@link Tag} * annotation are not serialized. New tagged fields can be added without invalidating previously serialized bytes. If any tagged * field is removed, previously serialized bytes are invalidated. Instead of removing fields, apply the {@link Deprecated} * annotation and they will not be serialized. If fields are public, bytecode generation will be used instead of reflection. * @author Nathan Sweet */ public class TaggedFieldSerializer extends FieldSerializer { private int[] tags; public TaggedFieldSerializer (Kryo kryo, Class type) { super(kryo, type); } protected void initializeCachedFields () { CachedField[] fields = getFields(); // Remove unwanted fields. for (int i = 0, n = fields.length; i < n; i++) { Field field = fields[i].getField(); Tag tag = field.getAnnotation(Tag.class); Deprecated deprecated = field.getAnnotation(Deprecated.class); if (tag == null || deprecated != null) { if (TRACE) { if (tag == null) trace("kryo", "Ignoring field without tag: " + fields[i]); else trace("kryo", "Ignoring deprecated field: " + fields[i]); } super.removeField(field.getName()); } } // Cache tags. fields = getFields(); tags = new int[fields.length]; for (int i = 0, n = fields.length; i < n; i++) tags[i] = fields[i].getField().getAnnotation(Tag.class).value(); } public void removeField (String fieldName) { super.removeField(fieldName); initializeCachedFields(); } public void write (Kryo kryo, Output output, T object) { CachedField[] fields = getFields(); output.writeInt(fields.length, true); for (int i = 0, n = fields.length; i < n; i++) { output.writeInt(tags[i], true); fields[i].write(output, object); } } public T read (Kryo kryo, Input input, Class type) { T object = kryo.newInstance(type); kryo.reference(object); int fieldCount = input.readInt(true); int[] tags = this.tags; CachedField[] fields = getFields(); for (int i = 0, n = fieldCount; i < n; i++) { int tag = input.readInt(true); CachedField cachedField = null; for (int ii = 0, nn = tags.length; ii < nn; ii++) { if (tags[ii] == tag) { cachedField = fields[ii]; break; } } if (cachedField == null) throw new KryoException("Unknown field tag: " + tag + " (" + getType().getName() + ")"); cachedField.read(input, object); } return object; } /** If true, this field will not be serialized. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Tag { int value(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/000077500000000000000000000000001210575634700236605ustar00rootroot00000000000000libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/DefaultClassResolver.java000066400000000000000000000131651210575634700306250ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import com.esotericsoftware.kryo.ClassResolver; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.Registration; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import static com.esotericsoftware.kryo.util.Util.*; import static com.esotericsoftware.minlog.Log.*; /** Resolves classes by ID or by fully qualified class name. * @author Nathan Sweet */ public class DefaultClassResolver implements ClassResolver { static public final byte NAME = -1; protected Kryo kryo; protected final IntMap idToRegistration = new IntMap(); protected final ObjectMap classToRegistration = new ObjectMap(); protected IdentityObjectIntMap classToNameId; protected IntMap nameIdToClass; protected ObjectMap nameToClass; protected int nextNameId; private int memoizedClassId = -1; private Registration memoizedClassIdValue; public void setKryo (Kryo kryo) { this.kryo = kryo; } public Registration register (Registration registration) { if (registration == null) throw new IllegalArgumentException("registration cannot be null."); if (TRACE) { if (registration.getId() == NAME) { trace("kryo", "Register class name: " + className(registration.getType()) + " (" + registration.getSerializer().getClass().getName() + ")"); } else { trace("kryo", "Register class ID " + registration.getId() + ": " + className(registration.getType()) + " (" + registration.getSerializer().getClass().getName() + ")"); } } classToRegistration.put(registration.getType(), registration); idToRegistration.put(registration.getId(), registration); if (registration.getType().isPrimitive()) classToRegistration.put(getWrapperClass(registration.getType()), registration); return registration; } public Registration registerImplicit (Class type) { return register(new Registration(type, kryo.getDefaultSerializer(type), NAME)); } /** If the class is not registered and {@link Kryo#setRegistrationRequired(boolean)} is false, it is automatically registered * using the {@link Kryo#addDefaultSerializer(Class, Class) default serializer}. */ public Registration getRegistration (Class type) { return classToRegistration.get(type); } public Registration getRegistration (int classID) { return idToRegistration.get(classID); } public Registration writeClass (Output output, Class type) { if (type == null) { if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Write", null); output.writeByte(Kryo.NULL); return null; } Registration registration = kryo.getRegistration(type); if (registration.getId() == NAME) writeName(output, type, registration); else { if (TRACE) trace("kryo", "Write class " + registration.getId() + ": " + className(type)); output.writeInt(registration.getId() + 2, true); } return registration; } protected void writeName (Output output, Class type, Registration registration) { output.writeByte(NAME + 2); if (classToNameId != null) { int nameId = classToNameId.get(type, -1); if (nameId != -1) { if (TRACE) trace("kryo", "Write class name reference " + nameId + ": " + className(type)); output.writeInt(nameId, true); return; } } // Only write the class name the first time encountered in object graph. if (TRACE) trace("kryo", "Write class name: " + className(type)); int nameId = nextNameId++; if (classToNameId == null) classToNameId = new IdentityObjectIntMap(); classToNameId.put(type, nameId); output.write(nameId); output.writeString(type.getName()); } public Registration readClass (Input input) { int classID = input.readInt(true); switch (classID) { case Kryo.NULL: if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Read", null); return null; case NAME + 2: // Offset for NAME and NULL. return readName(input); } if (classID == memoizedClassId) return memoizedClassIdValue; Registration registration = idToRegistration.get(classID - 2); if (registration == null) throw new KryoException("Encountered unregistered class ID: " + (classID - 2)); if (TRACE) trace("kryo", "Read class " + (classID - 2) + ": " + className(registration.getType())); memoizedClassId = classID; memoizedClassIdValue = registration; return registration; } protected Registration readName (Input input) { int nameId = input.readInt(true); if (nameIdToClass == null) nameIdToClass = new IntMap(); Class type = nameIdToClass.get(nameId); if (type == null) { // Only read the class name the first time encountered in object graph. String className = input.readString(); if (nameToClass != null) type = nameToClass.get(className); if (type == null) { try { type = Class.forName(className, false, kryo.getClassLoader()); } catch (ClassNotFoundException ex) { throw new KryoException("Unable to find class: " + className, ex); } if (nameToClass == null) nameToClass = new ObjectMap(); nameToClass.put(className, type); } nameIdToClass.put(nameId, type); if (TRACE) trace("kryo", "Read class name: " + className); } else { if (TRACE) trace("kryo", "Read class name reference " + nameId + ": " + className(type)); } return kryo.getRegistration(type); } public void reset () { if (!kryo.isRegistrationRequired()) { if (classToNameId != null) classToNameId.clear(); if (nameIdToClass != null) nameIdToClass.clear(); nextNameId = 0; } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/IdentityMap.java000066400000000000000000000457641210575634700267720ustar00rootroot00000000000000/******************************************************************************* * Copyright 2011 See AUTHORS file. * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. ******************************************************************************/ package com.esotericsoftware.kryo.util; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; /** An unordered map that uses identity comparison for keys. This implementation is a cuckoo hash map using 3 hashes, random * walking, and a small stash for problematic keys. Null keys are not allowed. Null values are allowed. No allocation is done * except when growing the table size.
*
* This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the * next higher POT size. * @author Nathan Sweet */ public class IdentityMap { private static final int PRIME1 = 0xbe1f14b1; private static final int PRIME2 = 0xb4b82e39; private static final int PRIME3 = 0xced1c241; public int size; K[] keyTable; V[] valueTable; int capacity, stashSize; private float loadFactor; private int hashShift, mask, threshold; private int stashCapacity; private int pushIterations; private Entries entries; private Values values; private Keys keys; /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the * backing table. */ public IdentityMap () { this(32, 0.8f); } /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing * table. */ public IdentityMap (int initialCapacity) { this(initialCapacity, 0.8f); } /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items * before growing the backing table. */ public IdentityMap (int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); if (capacity > 1 << 30) throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); capacity = ObjectMap.nextPowerOfTwo(initialCapacity); if (loadFactor <= 0) throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); mask = capacity - 1; hashShift = 31 - Integer.numberOfTrailingZeros(capacity); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(capacity)) * 2); pushIterations = Math.max(Math.min(capacity, 8), (int)Math.sqrt(capacity) / 8); keyTable = (K[])new Object[capacity + stashCapacity]; valueTable = (V[])new Object[keyTable.length]; } public V put (K key, V value) { if (key == null) throw new IllegalArgumentException("key cannot be null."); K[] keyTable = this.keyTable; // Check for existing keys. int hashCode = System.identityHashCode(key); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key1 == key) { V oldValue = valueTable[index1]; valueTable[index1] = value; return oldValue; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key2 == key) { V oldValue = valueTable[index2]; valueTable[index2] = value; return oldValue; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key3 == key) { V oldValue = valueTable[index3]; valueTable[index3] = value; return oldValue; } // Update key in the stash. for (int i = capacity, n = i + stashSize; i < n; i++) { if (keyTable[i] == key) { V oldValue = valueTable[i]; valueTable[i] = value; return oldValue; } } // Check for empty buckets. if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return null; } push(key, value, index1, key1, index2, key2, index3, key3); return null; } /** Skips checks for existing keys. */ private void putResize (K key, V value) { // Check for empty buckets. int hashCode = System.identityHashCode(key); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return; } push(key, value, index1, key1, index2, key2, index3, key3); } private void push (K insertKey, V insertValue, int index1, K key1, int index2, K key2, int index3, K key3) { K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int mask = this.mask; // Push keys until an empty bucket is found. K evictedKey; V evictedValue; int i = 0, pushIterations = this.pushIterations; do { // Replace the key and value for one of the hashes. switch (ObjectMap.random.nextInt(3)) { case 0: evictedKey = key1; evictedValue = valueTable[index1]; keyTable[index1] = insertKey; valueTable[index1] = insertValue; break; case 1: evictedKey = key2; evictedValue = valueTable[index2]; keyTable[index2] = insertKey; valueTable[index2] = insertValue; break; default: evictedKey = key3; evictedValue = valueTable[index3]; keyTable[index3] = insertKey; valueTable[index3] = insertValue; break; } // If the evicted key hashes to an empty bucket, put it there and stop. int hashCode = System.identityHashCode(evictedKey); index1 = hashCode & mask; key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = evictedKey; valueTable[index1] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index2 = hash2(hashCode); key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = evictedKey; valueTable[index2] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index3 = hash3(hashCode); key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = evictedKey; valueTable[index3] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } if (++i == pushIterations) break; insertKey = evictedKey; insertValue = evictedValue; } while (true); putStash(evictedKey, evictedValue); } private void putStash (K key, V value) { if (stashSize == stashCapacity) { // Too many pushes occurred and the stash is full, increase the table size. resize(capacity << 1); put(key, value); return; } // Store key in the stash. int index = capacity + stashSize; keyTable[index] = key; valueTable[index] = value; stashSize++; size++; } public V get (K key) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return getStash(key, null); } } return valueTable[index]; } public V get (K key, V defaultValue) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return getStash(key, defaultValue); } } return valueTable[index]; } private V getStash (K key, V defaultValue) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (keyTable[i] == key) return valueTable[i]; return defaultValue; } public V remove (K key) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (keyTable[index] == key) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash2(hashCode); if (keyTable[index] == key) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash3(hashCode); if (keyTable[index] == key) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } return removeStash(key); } V removeStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) { if (keyTable[i] == key) { V oldValue = valueTable[i]; removeStashIndex(i); size--; return oldValue; } } return null; } void removeStashIndex (int index) { // If the removed location was not last, move the last tuple to the removed location. stashSize--; int lastIndex = capacity + stashSize; if (index < lastIndex) { keyTable[index] = keyTable[lastIndex]; valueTable[index] = valueTable[lastIndex]; valueTable[lastIndex] = null; } else valueTable[index] = null; } public void clear () { K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) { keyTable[i] = null; valueTable[i] = null; } size = 0; stashSize = 0; } /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be * an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public boolean containsValue (Object value, boolean identity) { V[] valueTable = this.valueTable; if (value == null) { K[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != null && valueTable[i] == null) return true; } else if (identity) { for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return true; } else { for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return true; } return false; } public boolean containsKey (K key) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return containsKeyStash(key); } } return true; } private boolean containsKeyStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (keyTable[i] == key) return true; return false; } /** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares * every value, which may be an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public K findKey (Object value, boolean identity) { V[] valueTable = this.valueTable; if (value == null) { K[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != null && valueTable[i] == null) return keyTable[i]; } else if (identity) { for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return keyTable[i]; } else { for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return keyTable[i]; } return null; } /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many * items to avoid multiple backing array resizes. */ public void ensureCapacity (int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= threshold) resize(ObjectMap.nextPowerOfTwo((int)(sizeNeeded / loadFactor))); } private void resize (int newSize) { int oldEndIndex = capacity + stashSize; capacity = newSize; threshold = (int)(newSize * loadFactor); mask = newSize - 1; hashShift = 31 - Integer.numberOfTrailingZeros(newSize); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); K[] oldKeyTable = keyTable; V[] oldValueTable = valueTable; keyTable = (K[])new Object[newSize + stashCapacity]; valueTable = (V[])new Object[newSize + stashCapacity]; size = 0; stashSize = 0; for (int i = 0; i < oldEndIndex; i++) { K key = oldKeyTable[i]; if (key != null) putResize(key, oldValueTable[i]); } } private int hash2 (long h) { h *= PRIME2; return (int)((h ^ h >>> hashShift) & mask); } private int hash3 (long h) { h *= PRIME3; return (int)((h ^ h >>> hashShift) & mask); } public String toString () { if (size == 0) return "[]"; StringBuilder buffer = new StringBuilder(32); buffer.append('['); K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int i = keyTable.length; while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); break; } while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(", "); buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); } buffer.append(']'); return buffer.toString(); } /** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Entries entries () { if (entries == null) entries = new Entries(this); else entries.reset(); return entries; } /** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Values values () { if (values == null) values = new Values(this); else values.reset(); return values; } /** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each time * this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Keys keys () { if (keys == null) keys = new Keys(this); else keys.reset(); return keys; } static public class Entry { public K key; public V value; public String toString () { return key + "=" + value; } } static private class MapIterator { public boolean hasNext; final IdentityMap map; int nextIndex, currentIndex; public MapIterator (IdentityMap map) { this.map = map; reset(); } public void reset () { currentIndex = -1; nextIndex = -1; findNextIndex(); } void findNextIndex () { hasNext = false; K[] keyTable = map.keyTable; for (int n = map.capacity + map.stashSize; ++nextIndex < n;) { if (keyTable[nextIndex] != null) { hasNext = true; break; } } } public void remove () { if (currentIndex < 0) throw new IllegalStateException("next must be called before remove."); if (currentIndex >= map.capacity) { map.removeStashIndex(currentIndex); } else { map.keyTable[currentIndex] = null; map.valueTable[currentIndex] = null; } currentIndex = -1; map.size--; } } static public class Entries extends MapIterator implements Iterable>, Iterator> { private Entry entry = new Entry(); public Entries (IdentityMap map) { super(map); } /** Note the same entry instance is returned each time this method is called. */ public Entry next () { if (!hasNext) throw new NoSuchElementException(); K[] keyTable = map.keyTable; entry.key = keyTable[nextIndex]; entry.value = map.valueTable[nextIndex]; currentIndex = nextIndex; findNextIndex(); return entry; } public boolean hasNext () { return hasNext; } public Iterator> iterator () { return this; } } static public class Values extends MapIterator implements Iterable, Iterator { public Values (IdentityMap map) { super((IdentityMap)map); } public boolean hasNext () { return hasNext; } public V next () { V value = map.valueTable[nextIndex]; currentIndex = nextIndex; findNextIndex(); return value; } public Iterator iterator () { return this; } /** Returns a new array containing the remaining values. */ public ArrayList toArray () { ArrayList array = new ArrayList(map.size); while (hasNext) array.add(next()); return array; } /** Returns a new array containing the remaining values. */ public void toArray (ArrayList array) { while (hasNext) array.add(next()); } } static public class Keys extends MapIterator implements Iterable, Iterator { public Keys (IdentityMap map) { super((IdentityMap)map); } public boolean hasNext () { return hasNext; } public K next () { K key = map.keyTable[nextIndex]; currentIndex = nextIndex; findNextIndex(); return key; } public Iterator iterator () { return this; } /** Returns a new array containing the remaining keys. */ public ArrayList toArray () { ArrayList array = new ArrayList(map.size); while (hasNext) array.add(next()); return array; } } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/IdentityObjectIntMap.java000066400000000000000000000323711210575634700305620ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; /** An unordered map where identity comparison is used for keys and the values are ints. This implementation is a cuckoo hash map * using 3 hashes, random walking, and a small stash for problematic keys. Null keys are not allowed. No allocation is done except * when growing the table size.
*
* This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the * next higher POT size. * @author Nathan Sweet */ public class IdentityObjectIntMap { private static final int PRIME1 = 0xbe1f14b1; private static final int PRIME2 = 0xb4b82e39; private static final int PRIME3 = 0xced1c241; public int size; K[] keyTable; int[] valueTable; int capacity, stashSize; private float loadFactor; private int hashShift, mask, threshold; private int stashCapacity; private int pushIterations; /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the * backing table. */ public IdentityObjectIntMap () { this(32, 0.8f); } /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing * table. */ public IdentityObjectIntMap (int initialCapacity) { this(initialCapacity, 0.8f); } /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items * before growing the backing table. */ public IdentityObjectIntMap (int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); if (capacity > 1 << 30) throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); capacity = ObjectMap.nextPowerOfTwo(initialCapacity); if (loadFactor <= 0) throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); mask = capacity - 1; hashShift = 31 - Integer.numberOfTrailingZeros(capacity); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(capacity)) * 2); pushIterations = Math.max(Math.min(capacity, 8), (int)Math.sqrt(capacity) / 8); keyTable = (K[])new Object[capacity + stashCapacity]; valueTable = new int[keyTable.length]; } public void put (K key, int value) { if (key == null) throw new IllegalArgumentException("key cannot be null."); K[] keyTable = this.keyTable; // Check for existing keys. int hashCode = System.identityHashCode(key); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key == key1) { valueTable[index1] = value; return; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key == key2) { valueTable[index2] = value; return; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key == key3) { valueTable[index3] = value; return; } // Update key in the stash. for (int i = capacity, n = i + stashSize; i < n; i++) { if (keyTable[i] == key) { valueTable[i] = value; return; } } // Check for empty buckets. if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return; } if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return; } if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return; } push(key, value, index1, key1, index2, key2, index3, key3); } /** Skips checks for existing keys. */ private void putResize (K key, int value) { // Check for empty buckets. int hashCode = System.identityHashCode(key); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return; } push(key, value, index1, key1, index2, key2, index3, key3); } private void push (K insertKey, int insertValue, int index1, K key1, int index2, K key2, int index3, K key3) { K[] keyTable = this.keyTable; int[] valueTable = this.valueTable; int mask = this.mask; // Push keys until an empty bucket is found. K evictedKey; int evictedValue; int i = 0, pushIterations = this.pushIterations; do { // Replace the key and value for one of the hashes. switch (ObjectMap.random.nextInt(3)) { case 0: evictedKey = key1; evictedValue = valueTable[index1]; keyTable[index1] = insertKey; valueTable[index1] = insertValue; break; case 1: evictedKey = key2; evictedValue = valueTable[index2]; keyTable[index2] = insertKey; valueTable[index2] = insertValue; break; default: evictedKey = key3; evictedValue = valueTable[index3]; keyTable[index3] = insertKey; valueTable[index3] = insertValue; break; } // If the evicted key hashes to an empty bucket, put it there and stop. int hashCode = System.identityHashCode(evictedKey); index1 = hashCode & mask; key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = evictedKey; valueTable[index1] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index2 = hash2(hashCode); key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = evictedKey; valueTable[index2] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index3 = hash3(hashCode); key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = evictedKey; valueTable[index3] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } if (++i == pushIterations) break; insertKey = evictedKey; insertValue = evictedValue; } while (true); putStash(evictedKey, evictedValue); } private void putStash (K key, int value) { if (stashSize == stashCapacity) { // Too many pushes occurred and the stash is full, increase the table size. resize(capacity << 1); put(key, value); return; } // Store key in the stash. int index = capacity + stashSize; keyTable[index] = key; valueTable[index] = value; stashSize++; size++; } /** @param defaultValue Returned if the key was not associated with a value. */ public int get (K key, int defaultValue) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return getStash(key, defaultValue); } } return valueTable[index]; } private int getStash (K key, int defaultValue) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (key == keyTable[i]) return valueTable[i]; return defaultValue; } /** Returns the key's current value and increments the stored value. If the key is not in the map, defaultValue + increment is * put into the map. */ public int getAndIncrement (K key, int defaultValue, int increment) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return getAndIncrementStash(key, defaultValue, increment); } } int value = valueTable[index]; valueTable[index] = value + increment; return value; } private int getAndIncrementStash (K key, int defaultValue, int increment) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (key == keyTable[i]) { int value = valueTable[i]; valueTable[i] = value + increment; return value; } put(key, defaultValue + increment); return defaultValue; } public int remove (K key, int defaultValue) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key == keyTable[index]) { keyTable[index] = null; int oldValue = valueTable[index]; size--; return oldValue; } index = hash2(hashCode); if (key == keyTable[index]) { keyTable[index] = null; int oldValue = valueTable[index]; size--; return oldValue; } index = hash3(hashCode); if (key == keyTable[index]) { keyTable[index] = null; int oldValue = valueTable[index]; size--; return oldValue; } return removeStash(key, defaultValue); } int removeStash (K key, int defaultValue) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) { if (key == keyTable[i]) { int oldValue = valueTable[i]; removeStashIndex(i); size--; return oldValue; } } return defaultValue; } void removeStashIndex (int index) { // If the removed location was not last, move the last tuple to the removed location. stashSize--; int lastIndex = capacity + stashSize; if (index < lastIndex) { keyTable[index] = keyTable[lastIndex]; valueTable[index] = valueTable[lastIndex]; } } public void clear () { K[] keyTable = this.keyTable; int[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) { keyTable[i] = null; } size = 0; stashSize = 0; } /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be * an expensive operation. */ public boolean containsValue (int value) { int[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return true; return false; } public boolean containsKey (K key) { int hashCode = System.identityHashCode(key); int index = hashCode & mask; if (key != keyTable[index]) { index = hash2(hashCode); if (key != keyTable[index]) { index = hash3(hashCode); if (key != keyTable[index]) return containsKeyStash(key); } } return true; } private boolean containsKeyStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (key == keyTable[i]) return true; return false; } /** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares * every value, which may be an expensive operation. */ public K findKey (int value) { int[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return keyTable[i]; return null; } /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many * items to avoid multiple backing array resizes. */ public void ensureCapacity (int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= threshold) resize(ObjectMap.nextPowerOfTwo((int)(sizeNeeded / loadFactor))); } private void resize (int newSize) { int oldEndIndex = capacity + stashSize; capacity = newSize; threshold = (int)(newSize * loadFactor); mask = newSize - 1; hashShift = 31 - Integer.numberOfTrailingZeros(newSize); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); K[] oldKeyTable = keyTable; int[] oldValueTable = valueTable; keyTable = (K[])new Object[newSize + stashCapacity]; valueTable = new int[newSize + stashCapacity]; size = 0; stashSize = 0; for (int i = 0; i < oldEndIndex; i++) { K key = oldKeyTable[i]; if (key != null) putResize(key, oldValueTable[i]); } } private int hash2 (int h) { h *= PRIME2; return (h ^ h >>> hashShift) & mask; } private int hash3 (int h) { h *= PRIME3; return (h ^ h >>> hashShift) & mask; } public String toString () { if (size == 0) return "{}"; StringBuilder buffer = new StringBuilder(32); buffer.append('{'); K[] keyTable = this.keyTable; int[] valueTable = this.valueTable; int i = keyTable.length; while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); break; } while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(", "); buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); } buffer.append('}'); return buffer.toString(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/IntArray.java000066400000000000000000000165451210575634700262670ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import java.util.Arrays; /** A resizable, ordered or unordered int array. Avoids the boxing that occurs with ArrayList. If unordered, this class * avoids a memory copy when removing elements (the last element is moved to the removed element's position). * @author Nathan Sweet */ public class IntArray { public int[] items; public int size; public boolean ordered; /** Creates an ordered array with a capacity of 16. */ public IntArray () { this(true, 16); } /** Creates an ordered array with the specified capacity. */ public IntArray (int capacity) { this(true, capacity); } /** @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a * memory copy. * @param capacity Any elements added beyond this will cause the backing array to be grown. */ public IntArray (boolean ordered, int capacity) { this.ordered = ordered; items = new int[capacity]; } /** Creates a new array containing the elements in the specific array. The new array will be ordered if the specific array is * ordered. The capacity is set to the number of elements, so any subsequent elements added will cause the backing array to be * grown. */ public IntArray (IntArray array) { this.ordered = array.ordered; size = array.size; items = new int[size]; System.arraycopy(array.items, 0, items, 0, size); } /** Creates a new ordered array containing the elements in the specified array. The capacity is set to the number of elements, * so any subsequent elements added will cause the backing array to be grown. */ public IntArray (int[] array) { this(true, array); } /** Creates a new array containing the elements in the specified array. The capacity is set to the number of elements, so any * subsequent elements added will cause the backing array to be grown. * @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a * memory copy. */ public IntArray (boolean ordered, int[] array) { this(ordered, array.length); size = array.length; System.arraycopy(array, 0, items, 0, size); } public void add (int value) { int[] items = this.items; if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f))); items[size++] = value; } public void addAll (IntArray array) { addAll(array, 0, array.size); } public void addAll (IntArray array, int offset, int length) { if (offset + length > array.size) throw new IllegalArgumentException("offset + length must be <= size: " + offset + " + " + length + " <= " + array.size); addAll(array.items, offset, length); } public void addAll (int[] array) { addAll(array, 0, array.length); } public void addAll (int[] array, int offset, int length) { int[] items = this.items; int sizeNeeded = size + length - offset; if (sizeNeeded >= items.length) items = resize(Math.max(8, (int)(sizeNeeded * 1.75f))); System.arraycopy(array, offset, items, size, length); size += length; } public int get (int index) { if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); return items[index]; } public void set (int index, int value) { if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); items[index] = value; } public void insert (int index, int value) { int[] items = this.items; if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f))); if (ordered) System.arraycopy(items, index, items, index + 1, size - index); else items[size] = items[index]; size++; items[index] = value; } public void swap (int first, int second) { if (first >= size) throw new IndexOutOfBoundsException(String.valueOf(first)); if (second >= size) throw new IndexOutOfBoundsException(String.valueOf(second)); int[] items = this.items; int firstValue = items[first]; items[first] = items[second]; items[second] = firstValue; } public boolean contains (int value) { int i = size - 1; int[] items = this.items; while (i >= 0) if (items[i--] == value) return true; return false; } public int indexOf (int value) { int[] items = this.items; for (int i = 0, n = size; i < n; i++) if (items[i] == value) return i; return -1; } public boolean removeValue (int value) { int[] items = this.items; for (int i = 0, n = size; i < n; i++) { if (items[i] == value) { removeIndex(i); return true; } } return false; } /** Removes and returns the item at the specified index. */ public int removeIndex (int index) { if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); int[] items = this.items; int value = items[index]; size--; if (ordered) System.arraycopy(items, index + 1, items, index, size - index); else items[index] = items[size]; return value; } /** Removes and returns the last item. */ public int pop () { return items[--size]; } /** Returns the last item. */ public int peek () { return items[size - 1]; } public void clear () { size = 0; } /** Reduces the size of the backing array to the size of the actual items. This is useful to release memory when many items have * been removed, or if it is known that more items will not be added. */ public void shrink () { resize(size); } /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many * items to avoid multiple backing array resizes. * @return {@link #items} */ public int[] ensureCapacity (int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= items.length) resize(Math.max(8, sizeNeeded)); return items; } protected int[] resize (int newSize) { int[] newItems = new int[newSize]; int[] items = this.items; System.arraycopy(items, 0, newItems, 0, Math.min(items.length, newItems.length)); this.items = newItems; return newItems; } public void sort () { Arrays.sort(items, 0, size); } public void reverse () { for (int i = 0, lastIndex = size - 1, n = size / 2; i < n; i++) { int ii = lastIndex - i; int temp = items[i]; items[i] = items[ii]; items[ii] = temp; } } /** Reduces the size of the array to the specified size. If the array is already smaller than the specified size, no action is * taken. */ public void truncate (int newSize) { if (size > newSize) size = newSize; } public int[] toArray () { int[] array = new int[size]; System.arraycopy(items, 0, array, 0, size); return array; } public String toString () { if (size == 0) return "[]"; int[] items = this.items; StringBuilder buffer = new StringBuilder(32); buffer.append('['); buffer.append(items[0]); for (int i = 1; i < size; i++) { buffer.append(", "); buffer.append(items[i]); } buffer.append(']'); return buffer.toString(); } public String toString (String separator) { if (size == 0) return ""; int[] items = this.items; StringBuilder buffer = new StringBuilder(32); buffer.append(items[0]); for (int i = 1; i < size; i++) { buffer.append(separator); buffer.append(items[i]); } return buffer.toString(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/IntMap.java000066400000000000000000000357401210575634700257240ustar00rootroot00000000000000/******************************************************************************* * Copyright 2011 See AUTHORS file. * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. ******************************************************************************/ package com.esotericsoftware.kryo.util; /** An unordered map that uses int keys. This implementation is a cuckoo hash map using 3 hashes, random walking, and a small stash * for problematic keys. Null values are allowed. No allocation is done except when growing the table size.
*
* This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the * next higher POT size. * @author Nathan Sweet */ public class IntMap { private static final int PRIME1 = 0xbe1f14b1; private static final int PRIME2 = 0xb4b82e39; private static final int PRIME3 = 0xced1c241; private static final int EMPTY = 0; public int size; int[] keyTable; V[] valueTable; int capacity, stashSize; V zeroValue; boolean hasZeroValue; private float loadFactor; private int hashShift, mask, threshold; private int stashCapacity; private int pushIterations; /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the * backing table. */ public IntMap () { this(32, 0.8f); } /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing * table. */ public IntMap (int initialCapacity) { this(initialCapacity, 0.8f); } /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items * before growing the backing table. */ public IntMap (int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); if (capacity > 1 << 30) throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); capacity = ObjectMap.nextPowerOfTwo(initialCapacity); if (loadFactor <= 0) throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); mask = capacity - 1; hashShift = 31 - Integer.numberOfTrailingZeros(capacity); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(capacity)) * 2); pushIterations = Math.max(Math.min(capacity, 8), (int)Math.sqrt(capacity) / 8); keyTable = new int[capacity + stashCapacity]; valueTable = (V[])new Object[keyTable.length]; } public V put (int key, V value) { if (key == 0) { V oldValue = zeroValue; zeroValue = value; hasZeroValue = true; size++; return oldValue; } int[] keyTable = this.keyTable; // Check for existing keys. int index1 = key & mask; int key1 = keyTable[index1]; if (key1 == key) { V oldValue = valueTable[index1]; valueTable[index1] = value; return oldValue; } int index2 = hash2(key); int key2 = keyTable[index2]; if (key2 == key) { V oldValue = valueTable[index2]; valueTable[index2] = value; return oldValue; } int index3 = hash3(key); int key3 = keyTable[index3]; if (key3 == key) { V oldValue = valueTable[index3]; valueTable[index3] = value; return oldValue; } // Update key in the stash. for (int i = capacity, n = i + stashSize; i < n; i++) { if (key == keyTable[i]) { V oldValue = valueTable[i]; valueTable[i] = value; return oldValue; } } // Check for empty buckets. if (key1 == EMPTY) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key2 == EMPTY) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key3 == EMPTY) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return null; } push(key, value, index1, key1, index2, key2, index3, key3); return null; } /** Skips checks for existing keys. */ private void putResize (int key, V value) { if (key == 0) { zeroValue = value; hasZeroValue = true; return; } // Check for empty buckets. int index1 = key & mask; int key1 = keyTable[index1]; if (key1 == EMPTY) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index2 = hash2(key); int key2 = keyTable[index2]; if (key2 == EMPTY) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index3 = hash3(key); int key3 = keyTable[index3]; if (key3 == EMPTY) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return; } push(key, value, index1, key1, index2, key2, index3, key3); } private void push (int insertKey, V insertValue, int index1, int key1, int index2, int key2, int index3, int key3) { int[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int mask = this.mask; // Push keys until an empty bucket is found. int evictedKey; V evictedValue; int i = 0, pushIterations = this.pushIterations; do { // Replace the key and value for one of the hashes. switch (ObjectMap.random.nextInt(3)) { case 0: evictedKey = key1; evictedValue = valueTable[index1]; keyTable[index1] = insertKey; valueTable[index1] = insertValue; break; case 1: evictedKey = key2; evictedValue = valueTable[index2]; keyTable[index2] = insertKey; valueTable[index2] = insertValue; break; default: evictedKey = key3; evictedValue = valueTable[index3]; keyTable[index3] = insertKey; valueTable[index3] = insertValue; break; } // If the evicted key hashes to an empty bucket, put it there and stop. index1 = evictedKey & mask; key1 = keyTable[index1]; if (key1 == EMPTY) { keyTable[index1] = evictedKey; valueTable[index1] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index2 = hash2(evictedKey); key2 = keyTable[index2]; if (key2 == EMPTY) { keyTable[index2] = evictedKey; valueTable[index2] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index3 = hash3(evictedKey); key3 = keyTable[index3]; if (key3 == EMPTY) { keyTable[index3] = evictedKey; valueTable[index3] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } if (++i == pushIterations) break; insertKey = evictedKey; insertValue = evictedValue; } while (true); putStash(evictedKey, evictedValue); } private void putStash (int key, V value) { if (stashSize == stashCapacity) { // Too many pushes occurred and the stash is full, increase the table size. resize(capacity << 1); put(key, value); return; } // Store key in the stash. int index = capacity + stashSize; keyTable[index] = key; valueTable[index] = value; stashSize++; size++; } public V get (int key) { if (key == 0) return zeroValue; int index = key & mask; if (keyTable[index] != key) { index = hash2(key); if (keyTable[index] != key) { index = hash3(key); if (keyTable[index] != key) return getStash(key, null); } } return valueTable[index]; } public V get (int key, V defaultValue) { if (key == 0) return zeroValue; int index = key & mask; if (keyTable[index] != key) { index = hash2(key); if (keyTable[index] != key) { index = hash3(key); if (keyTable[index] != key) return getStash(key, defaultValue); } } return valueTable[index]; } private V getStash (int key, V defaultValue) { int[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (keyTable[i] == key) return valueTable[i]; return defaultValue; } public V remove (int key) { if (key == 0) { if (!hasZeroValue) return null; V oldValue = zeroValue; zeroValue = null; hasZeroValue = false; size--; return oldValue; } int index = key & mask; if (keyTable[index] == key) { keyTable[index] = EMPTY; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash2(key); if (keyTable[index] == key) { keyTable[index] = EMPTY; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash3(key); if (keyTable[index] == key) { keyTable[index] = EMPTY; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } return removeStash(key); } V removeStash (int key) { int[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) { if (keyTable[i] == key) { V oldValue = valueTable[i]; removeStashIndex(i); size--; return oldValue; } } return null; } void removeStashIndex (int index) { // If the removed location was not last, move the last tuple to the removed location. stashSize--; int lastIndex = capacity + stashSize; if (index < lastIndex) { keyTable[index] = keyTable[lastIndex]; valueTable[index] = valueTable[lastIndex]; valueTable[lastIndex] = null; } else valueTable[index] = null; } public void clear () { int[] keyTable = this.keyTable; V[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) { keyTable[i] = EMPTY; valueTable[i] = null; } size = 0; stashSize = 0; zeroValue = null; hasZeroValue = false; } /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be * an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public boolean containsValue (Object value, boolean identity) { V[] valueTable = this.valueTable; if (value == null) { if (hasZeroValue && zeroValue == null) return true; int[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != EMPTY && valueTable[i] == null) return true; } else if (identity) { if (value == zeroValue) return true; for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return true; } else { if (hasZeroValue && value.equals(zeroValue)) return true; for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return true; } return false; } public boolean containsKey (int key) { if (key == 0) return hasZeroValue; int index = key & mask; if (keyTable[index] != key) { index = hash2(key); if (keyTable[index] != key) { index = hash3(key); if (keyTable[index] != key) return containsKeyStash(key); } } return true; } private boolean containsKeyStash (int key) { int[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (keyTable[i] == key) return true; return false; } /** Returns the key for the specified value, or notFound if it is not in the map. Note this traverses the entire map * and compares every value, which may be an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public int findKey (Object value, boolean identity, int notFound) { V[] valueTable = this.valueTable; if (value == null) { if (hasZeroValue && zeroValue == null) return 0; int[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != EMPTY && valueTable[i] == null) return keyTable[i]; } else if (identity) { if (value == zeroValue) return 0; for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return keyTable[i]; } else { if (hasZeroValue && value.equals(zeroValue)) return 0; for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return keyTable[i]; } return notFound; } /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many * items to avoid multiple backing array resizes. */ public void ensureCapacity (int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= threshold) resize(ObjectMap.nextPowerOfTwo((int)(sizeNeeded / loadFactor))); } private void resize (int newSize) { int oldEndIndex = capacity + stashSize; capacity = newSize; threshold = (int)(newSize * loadFactor); mask = newSize - 1; hashShift = 31 - Integer.numberOfTrailingZeros(newSize); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); int[] oldKeyTable = keyTable; V[] oldValueTable = valueTable; keyTable = new int[newSize + stashCapacity]; valueTable = (V[])new Object[newSize + stashCapacity]; size = hasZeroValue ? 1 : 0; stashSize = 0; for (int i = 0; i < oldEndIndex; i++) { int key = oldKeyTable[i]; if (key != EMPTY) putResize(key, oldValueTable[i]); } } private int hash2 (int h) { h *= PRIME2; return (h ^ h >>> hashShift) & mask; } private int hash3 (int h) { h *= PRIME3; return (h ^ h >>> hashShift) & mask; } public String toString () { if (size == 0) return "[]"; StringBuilder buffer = new StringBuilder(32); buffer.append('['); int[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int i = keyTable.length; while (i-- > 0) { int key = keyTable[i]; if (key == EMPTY) continue; buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); break; } while (i-- > 0) { int key = keyTable[i]; if (key == EMPTY) continue; buffer.append(", "); buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); } buffer.append(']'); return buffer.toString(); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/ListReferenceResolver.java000066400000000000000000000033331210575634700310010ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import java.util.ArrayList; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.ReferenceResolver; /** Uses an {@link ArrayList} to track objects that have already been written. This is more efficient than * {@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 */ public class ListReferenceResolver implements ReferenceResolver { protected Kryo kryo; protected final ArrayList seenObjects = new ArrayList(); public void setKryo (Kryo kryo) { this.kryo = kryo; } public int addWrittenObject (Object object) { int id = seenObjects.size(); seenObjects.add(object); return id; } public int getWrittenId (Object object) { for (int i = 0, n = seenObjects.size(); i < n; i++) if (seenObjects.get(i) == object) return i; return -1; } public int nextReadId (Class type) { return seenObjects.size(); } public void addReadObject (int id, Object object) { if (id == seenObjects.size()) seenObjects.add(object); else { while (id >= seenObjects.size()) seenObjects.add(null); seenObjects.set(id, object); } } public Object getReadObject (Class type, int id) { if (id < seenObjects.size()) return seenObjects.get(id); return null; } public void reset () { seenObjects.clear(); } /** Returns false for Boolean, Byte, Character, and Short. */ public boolean useReferences (Class type) { return !Util.isWrapperClass(type); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/MapReferenceResolver.java000066400000000000000000000031511210575634700306010ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import java.util.ArrayList; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.ReferenceResolver; /** Uses an {@link IdentityObjectIntMap} to track objects that have already been written. This can handle graph with any number of * objects, but is slightly slower than {@link ListReferenceResolver} for graphs with few objects. * @author Nathan Sweet */ public class MapReferenceResolver implements ReferenceResolver { protected Kryo kryo; protected final IdentityObjectIntMap writtenObjects = new IdentityObjectIntMap(); protected final ArrayList readObjects = new ArrayList(); public void setKryo (Kryo kryo) { this.kryo = kryo; } public int addWrittenObject (Object object) { int id = writtenObjects.size; writtenObjects.put(object, id); return id; } public int getWrittenId (Object object) { return writtenObjects.get(object, -1); } public int nextReadId (Class type) { return readObjects.size(); } public void addReadObject (int id, Object object) { if (id == readObjects.size()) readObjects.add(object); else { while (id >= readObjects.size()) readObjects.add(null); readObjects.set(id, object); } } public Object getReadObject (Class type, int id) { if (id < readObjects.size()) return readObjects.get(id); return null; } public void reset () { readObjects.clear(); writtenObjects.clear(); } /** Returns false for all primitive wrappers. */ public boolean useReferences (Class type) { return !Util.isWrapperClass(type); } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/ObjectMap.java000066400000000000000000000445531210575634700264020ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Random; /** An unordered map. This implementation is a cuckoo hash map using 3 hashes, random walking, and a small stash for problematic * keys. Null keys are not allowed. Null values are allowed. No allocation is done except when growing the table size.
*
* This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the * next higher POT size. * @author Nathan Sweet */ public class ObjectMap { static private final int PRIME1 = 0xbe1f14b1; static private final int PRIME2 = 0xb4b82e39; static private final int PRIME3 = 0xced1c241; static Random random = new Random(); public int size; K[] keyTable; V[] valueTable; int capacity, stashSize; private float loadFactor; private int hashShift, mask, threshold; private int stashCapacity; private int pushIterations; private Entries entries; private Values values; private Keys keys; /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the * backing table. */ public ObjectMap () { this(32, 0.8f); } /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing * table. */ public ObjectMap (int initialCapacity) { this(initialCapacity, 0.8f); } /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items * before growing the backing table. */ public ObjectMap (int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); if (capacity > 1 << 30) throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); capacity = nextPowerOfTwo(initialCapacity); if (loadFactor <= 0) throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); mask = capacity - 1; hashShift = 31 - Integer.numberOfTrailingZeros(capacity); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(capacity)) * 2); pushIterations = Math.max(Math.min(capacity, 8), (int)Math.sqrt(capacity) / 8); keyTable = (K[])new Object[capacity + stashCapacity]; valueTable = (V[])new Object[keyTable.length]; } /** Returns the old value associated with the specified key, or null. */ public V put (K key, V value) { if (key == null) throw new IllegalArgumentException("key cannot be null."); return put_internal(key, value); } private V put_internal (K key, V value) { K[] keyTable = this.keyTable; // Check for existing keys. int hashCode = key.hashCode(); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key.equals(key1)) { V oldValue = valueTable[index1]; valueTable[index1] = value; return oldValue; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key.equals(key2)) { V oldValue = valueTable[index2]; valueTable[index2] = value; return oldValue; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key.equals(key3)) { V oldValue = valueTable[index3]; valueTable[index3] = value; return oldValue; } // Update key in the stash. for (int i = capacity, n = i + stashSize; i < n; i++) { if (key.equals(keyTable[i])) { V oldValue = valueTable[i]; valueTable[i] = value; return oldValue; } } // Check for empty buckets. if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return null; } if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return null; } push(key, value, index1, key1, index2, key2, index3, key3); return null; } public void putAll (ObjectMap map) { for (Entry entry : map.entries()) put(entry.key, entry.value); } /** Skips checks for existing keys. */ private void putResize (K key, V value) { // Check for empty buckets. int hashCode = key.hashCode(); int index1 = hashCode & mask; K key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = key; valueTable[index1] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index2 = hash2(hashCode); K key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = key; valueTable[index2] = value; if (size++ >= threshold) resize(capacity << 1); return; } int index3 = hash3(hashCode); K key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = key; valueTable[index3] = value; if (size++ >= threshold) resize(capacity << 1); return; } push(key, value, index1, key1, index2, key2, index3, key3); } private void push (K insertKey, V insertValue, int index1, K key1, int index2, K key2, int index3, K key3) { K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int mask = this.mask; // Push keys until an empty bucket is found. K evictedKey; V evictedValue; int i = 0, pushIterations = this.pushIterations; do { // Replace the key and value for one of the hashes. switch (random.nextInt(3)) { case 0: evictedKey = key1; evictedValue = valueTable[index1]; keyTable[index1] = insertKey; valueTable[index1] = insertValue; break; case 1: evictedKey = key2; evictedValue = valueTable[index2]; keyTable[index2] = insertKey; valueTable[index2] = insertValue; break; default: evictedKey = key3; evictedValue = valueTable[index3]; keyTable[index3] = insertKey; valueTable[index3] = insertValue; break; } // If the evicted key hashes to an empty bucket, put it there and stop. int hashCode = evictedKey.hashCode(); index1 = hashCode & mask; key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = evictedKey; valueTable[index1] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index2 = hash2(hashCode); key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = evictedKey; valueTable[index2] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } index3 = hash3(hashCode); key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = evictedKey; valueTable[index3] = evictedValue; if (size++ >= threshold) resize(capacity << 1); return; } if (++i == pushIterations) break; insertKey = evictedKey; insertValue = evictedValue; } while (true); putStash(evictedKey, evictedValue); } private void putStash (K key, V value) { if (stashSize == stashCapacity) { // Too many pushes occurred and the stash is full, increase the table size. resize(capacity << 1); put_internal(key, value); return; } // Store key in the stash. int index = capacity + stashSize; keyTable[index] = key; valueTable[index] = value; stashSize++; size++; } public V get (K key) { int hashCode = key.hashCode(); int index = hashCode & mask; if (!key.equals(keyTable[index])) { index = hash2(hashCode); if (!key.equals(keyTable[index])) { index = hash3(hashCode); if (!key.equals(keyTable[index])) return getStash(key); } } return valueTable[index]; } private V getStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (key.equals(keyTable[i])) return valueTable[i]; return null; } public V remove (K key) { int hashCode = key.hashCode(); int index = hashCode & mask; if (key.equals(keyTable[index])) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash2(hashCode); if (key.equals(keyTable[index])) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } index = hash3(hashCode); if (key.equals(keyTable[index])) { keyTable[index] = null; V oldValue = valueTable[index]; valueTable[index] = null; size--; return oldValue; } return removeStash(key); } V removeStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) { if (key.equals(keyTable[i])) { V oldValue = valueTable[i]; removeStashIndex(i); size--; return oldValue; } } return null; } void removeStashIndex (int index) { // If the removed location was not last, move the last tuple to the removed location. stashSize--; int lastIndex = capacity + stashSize; if (index < lastIndex) { keyTable[index] = keyTable[lastIndex]; valueTable[index] = valueTable[lastIndex]; valueTable[lastIndex] = null; } else valueTable[index] = null; } public void clear () { K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; for (int i = capacity + stashSize; i-- > 0;) { keyTable[i] = null; valueTable[i] = null; } size = 0; stashSize = 0; } /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be * an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public boolean containsValue (Object value, boolean identity) { V[] valueTable = this.valueTable; if (value == null) { K[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != null && valueTable[i] == null) return true; } else if (identity) { for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return true; } else { for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return true; } return false; } public boolean containsKey (K key) { int hashCode = key.hashCode(); int index = hashCode & mask; if (!key.equals(keyTable[index])) { index = hash2(hashCode); if (!key.equals(keyTable[index])) { index = hash3(hashCode); if (!key.equals(keyTable[index])) return containsKeyStash(key); } } return true; } private boolean containsKeyStash (K key) { K[] keyTable = this.keyTable; for (int i = capacity, n = i + stashSize; i < n; i++) if (key.equals(keyTable[i])) return true; return false; } /** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares * every value, which may be an expensive operation. * @param identity If true, uses == to compare the specified value with values in the map. If false, uses * {@link #equals(Object)}. */ public K findKey (Object value, boolean identity) { V[] valueTable = this.valueTable; if (value == null) { K[] keyTable = this.keyTable; for (int i = capacity + stashSize; i-- > 0;) if (keyTable[i] != null && valueTable[i] == null) return keyTable[i]; } else if (identity) { for (int i = capacity + stashSize; i-- > 0;) if (valueTable[i] == value) return keyTable[i]; } else { for (int i = capacity + stashSize; i-- > 0;) if (value.equals(valueTable[i])) return keyTable[i]; } return null; } /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many * items to avoid multiple backing array resizes. */ public void ensureCapacity (int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= threshold) resize(nextPowerOfTwo((int)(sizeNeeded / loadFactor))); } private void resize (int newSize) { int oldEndIndex = capacity + stashSize; capacity = newSize; threshold = (int)(newSize * loadFactor); mask = newSize - 1; hashShift = 31 - Integer.numberOfTrailingZeros(newSize); stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); K[] oldKeyTable = keyTable; V[] oldValueTable = valueTable; keyTable = (K[])new Object[newSize + stashCapacity]; valueTable = (V[])new Object[newSize + stashCapacity]; size = 0; stashSize = 0; for (int i = 0; i < oldEndIndex; i++) { K key = oldKeyTable[i]; if (key != null) putResize(key, oldValueTable[i]); } } private int hash2 (long h) { h *= PRIME2; return (int)((h ^ h >>> hashShift) & mask); } private int hash3 (long h) { h *= PRIME3; return (int)((h ^ h >>> hashShift) & mask); } public String toString () { if (size == 0) return "{}"; StringBuilder buffer = new StringBuilder(32); buffer.append('{'); K[] keyTable = this.keyTable; V[] valueTable = this.valueTable; int i = keyTable.length; while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); break; } while (i-- > 0) { K key = keyTable[i]; if (key == null) continue; buffer.append(", "); buffer.append(key); buffer.append('='); buffer.append(valueTable[i]); } buffer.append('}'); return buffer.toString(); } /** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Entries entries () { if (entries == null) entries = new Entries(this); else entries.reset(); return entries; } /** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Values values () { if (values == null) values = new Values(this); else values.reset(); return values; } /** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each time * this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ public Keys keys () { if (keys == null) keys = new Keys(this); else keys.reset(); return keys; } static public class Entry { public K key; public V value; public String toString () { return key + "=" + value; } } static private class MapIterator { public boolean hasNext; protected final ObjectMap map; int currentIndex; protected int nextIndex; public MapIterator (ObjectMap map) { this.map = map; reset(); } public void reset () { currentIndex = -1; nextIndex = -1; advance(); } protected void advance () { hasNext = false; K[] keyTable = map.keyTable; for (int n = map.capacity + map.stashSize; ++nextIndex < n;) { if (keyTable[nextIndex] != null) { hasNext = true; break; } } } public void remove () { if (currentIndex < 0) throw new IllegalStateException("next must be called before remove."); if (currentIndex >= map.capacity) { map.removeStashIndex(currentIndex); } else { map.keyTable[currentIndex] = null; map.valueTable[currentIndex] = null; } currentIndex = -1; map.size--; } } static public class Entries extends MapIterator implements Iterable>, Iterator> { protected Entry entry = new Entry(); public Entries (ObjectMap map) { super(map); } /** Note the same entry instance is returned each time this method is called. */ public Entry next () { if (!hasNext) throw new NoSuchElementException(); K[] keyTable = map.keyTable; entry.key = keyTable[nextIndex]; entry.value = map.valueTable[nextIndex]; currentIndex = nextIndex; advance(); return entry; } public boolean hasNext () { return hasNext; } public Iterator> iterator () { return this; } } static public class Values extends MapIterator implements Iterable, Iterator { public Values (ObjectMap map) { super((ObjectMap)map); } public boolean hasNext () { return hasNext; } public V next () { V value = map.valueTable[nextIndex]; currentIndex = nextIndex; advance(); return value; } public Iterator iterator () { return this; } /** Returns a new array containing the remaining values. */ public ArrayList toArray () { ArrayList array = new ArrayList(map.size); while (hasNext) array.add(next()); return array; } /** Adds the remaining values to the specified array. */ public void toArray (ArrayList array) { while (hasNext) array.add(next()); } } static public class Keys extends MapIterator implements Iterable, Iterator { public Keys (ObjectMap map) { super((ObjectMap)map); } public boolean hasNext () { return hasNext; } public K next () { K key = map.keyTable[nextIndex]; currentIndex = nextIndex; advance(); return key; } public Iterator iterator () { return this; } /** Returns a new array containing the remaining keys. */ public ArrayList toArray () { ArrayList array = new ArrayList(map.size); while (hasNext) array.add(next()); return array; } } static public int nextPowerOfTwo (int value) { if (value == 0) return 1; value--; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return value + 1; } } libkryo-java-2.20/java/src/com/esotericsoftware/kryo/util/Util.java000066400000000000000000000073641210575634700254520ustar00rootroot00000000000000 package com.esotericsoftware.kryo.util; import static com.esotericsoftware.minlog.Log.*; /** A few utility methods, mostly for private use. * @author Nathan Sweet */ public class Util { static public boolean isAndroid; static { try { Class.forName("android.os.Process"); isAndroid = true; } catch (Exception ignored) { } } /** Returns the primitive wrapper class for a primitive class. * @param type Must be a primitive class. */ static public Class getWrapperClass (Class type) { if (type == int.class) return Integer.class; else if (type == float.class) return Float.class; else if (type == boolean.class) return Boolean.class; else if (type == long.class) return Long.class; else if (type == byte.class) return Byte.class; else if (type == char.class) return Character.class; else if (type == short.class) // return Short.class; return Double.class; } static public boolean isWrapperClass (Class type) { return type == Integer.class || type == Float.class || type == Boolean.class || type == Long.class || type == Byte.class || type == Character.class || type == Short.class || type == Double.class; } /** Logs a message about an object. The log level and the string format of the object depend on the object type. */ static public void log (String message, Object object) { if (object == null) { if (TRACE) trace("kryo", message + ": null"); return; } Class type = object.getClass(); if (type.isPrimitive() || type == Boolean.class || type == Byte.class || type == Character.class || type == Short.class || type == Integer.class || type == Long.class || type == Float.class || type == Double.class || type == String.class) { if (TRACE) trace("kryo", message + ": " + string(object)); } else { debug("kryo", message + ": " + string(object)); } } /** Returns the object formatted as a string. The format depends on the object's type and whether {@link Object#toString()} has * been overridden. */ static public String string (Object object) { if (object == null) return "null"; Class type = object.getClass(); if (type.isArray()) return className(type); try { if (type.getMethod("toString", new Class[0]).getDeclaringClass() == Object.class) return TRACE ? className(type) : type.getSimpleName(); } catch (Exception ignored) { } return String.valueOf(object); } /** Returns the class formatted as a string. The format varies depending on the type. */ static public String className (Class type) { if (type.isArray()) { Class elementClass = getElementClass(type); StringBuilder buffer = new StringBuilder(16); for (int i = 0, n = getDimensionCount(type); i < n; i++) buffer.append("[]"); return className(elementClass) + buffer; } if (type.isPrimitive() || type == Object.class || type == Boolean.class || type == Byte.class || type == Character.class || type == Short.class || type == Integer.class || type == Long.class || type == Float.class || type == Double.class || type == String.class) { return type.getSimpleName(); } return type.getName(); } /** Returns the number of dimensions of an array. */ static public int getDimensionCount (Class arrayClass) { int depth = 0; Class nextClass = arrayClass.getComponentType(); while (nextClass != null) { depth++; nextClass = nextClass.getComponentType(); } return depth; } /** Returns the base element type of an n-dimensional array class. */ static public Class getElementClass (Class arrayClass) { Class elementClass = arrayClass; while (elementClass.getComponentType() != null) elementClass = elementClass.getComponentType(); return elementClass; } } libkryo-java-2.20/java/test/000077500000000000000000000000001210575634700157415ustar00rootroot00000000000000libkryo-java-2.20/java/test/com/000077500000000000000000000000001210575634700165175ustar00rootroot00000000000000libkryo-java-2.20/java/test/com/esotericsoftware/000077500000000000000000000000001210575634700221075ustar00rootroot00000000000000libkryo-java-2.20/java/test/com/esotericsoftware/kryo/000077500000000000000000000000001210575634700230735ustar00rootroot00000000000000libkryo-java-2.20/java/test/com/esotericsoftware/kryo/ArraySerializerTest.java000066400000000000000000000036551210575634700277170ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ObjectArraySerializer; /** @author Nathan Sweet */ public class ArraySerializerTest extends KryoTestCase { { supportsCopy = true; } public void testArrays () { kryo.register(int[].class); kryo.register(int[][].class); kryo.register(int[][][].class); kryo.register(String[].class); kryo.register(Object[].class); roundTrip(6, new int[] {1, 2, 3, 4}); roundTrip(7, new int[] {1, 2, -100, 4}); roundTrip(9, new int[] {1, 2, -100, 40000}); roundTrip(9, new int[][] { {1, 2}, {100, 4}}); roundTrip(11, new int[][] { {1}, {2}, {100}, {4}}); 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}}}); ObjectArraySerializer serializer = new ObjectArraySerializer(); kryo.register(String[].class, serializer); serializer.setElementsAreSameType(true); roundTrip(11, new String[] {"11", "2222", null, "4"}); serializer.setElementsAreSameType(false); roundTrip(11, new String[] {"11", "2222", null, "4"}); roundTrip(5, new String[] {null, null, null}); roundTrip(2, new String[] {}); serializer.setElementsAreSameType(true); roundTrip(12, new String[] {"11", "2222", "3", "4"}); serializer.setElementsCanBeNull(false); roundTrip(12, new String[] {"11", "2222", "3", "4"}); serializer = new ObjectArraySerializer(); kryo.register(Float[][].class, serializer); kryo.register(Float[].class, serializer); Float[][] array = new Float[4][]; array[0] = new Float[] {0.0f, 1.0f}; array[1] = null; array[2] = new Float[] {2.0f, 3.0f}; array[3] = new Float[] {3.0f}; roundTrip(31, array); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/BeanSerializerTest.java000066400000000000000000000046521210575634700275040ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.serializers.BeanSerializer; /** @author Nathan Sweet */ public class BeanSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testBeanSerializer () { kryo.register(TestClass.class, new BeanSerializer(kryo, TestClass.class)); TestClass test = new TestClass(); test.setOptional(12); test.setNullField("value"); test.setText("123"); test.setChild(new TestClass()); roundTrip(37, test); test.setNullField(null); roundTrip(33, test); } static public class TestClass { private String text = "something"; private String nullField; private TestClass child; private TestClass child2; private float abc = 1.2f; private int optional; public String getText () { return text; } public void setText (String text) { this.text = text; } public String getNullField () { return nullField; } public void setNullField (String nullField) { this.nullField = nullField; } public TestClass getChild () { return child; } public void setChild (TestClass child) { this.child = child; } public TestClass getChild2 () { return child2; } public void setChild2 (TestClass child2) { this.child2 = child2; } public float getAbc () { return abc; } public void setAbc (float abc) { this.abc = abc; } public int getOptional () { return optional; } public void setOptional (int optional) { this.optional = optional; } public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestClass other = (TestClass)obj; if (Float.floatToIntBits(abc) != Float.floatToIntBits(other.abc)) return false; if (child == null) { if (other.child != null) return false; } else if (child != this && !child.equals(other.child)) return false; if (child2 == null) { if (other.child2 != null) return false; } else if (child2 != this && !child2.equals(other.child2)) return false; if (nullField == null) { if (other.nullField != null) return false; } else if (!nullField.equals(other.nullField)) return false; if (text == null) { if (other.text != null) return false; } else if (!text.equals(other.text)) return false; return true; } } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/BlowfishSerializerTest.java000066400000000000000000000011461210575634700304070ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import javax.crypto.KeyGenerator; import com.esotericsoftware.kryo.serializers.BlowfishSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; /** @author Nathan Sweet */ public class BlowfishSerializerTest extends KryoTestCase { public void testZip () throws Exception { byte[] key = KeyGenerator.getInstance("Blowfish").generateKey().getEncoded(); kryo.register(String.class, new BlowfishSerializer(new StringSerializer(), key)); roundTrip(49, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/ChunkedTest.java000066400000000000000000000024341210575634700261620ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.InputChunked; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.io.OutputChunked; /** @author Nathan Sweet */ public class ChunkedTest extends KryoTestCase { public void testChunks () { Output output = new Output(512); output.writeInt(1234); OutputChunked outputChunked = new OutputChunked(output); outputChunked.writeInt(1); outputChunked.endChunks(); outputChunked.writeInt(2); outputChunked.endChunks(); outputChunked.writeInt(3); outputChunked.endChunks(); outputChunked.writeInt(4); outputChunked.endChunks(); outputChunked.writeInt(5); outputChunked.endChunks(); output.writeInt(5678); output.close(); Input input = new Input(output.getBuffer()); assertEquals(1234, input.readInt()); InputChunked inputChunked = new InputChunked(input); assertEquals(1, inputChunked.readInt()); inputChunked.nextChunks(); inputChunked.nextChunks(); // skip 3 assertEquals(3, inputChunked.readInt()); inputChunked.nextChunks(); inputChunked.nextChunks(); // skip 4 assertEquals(5, inputChunked.readInt()); assertEquals(5678, input.readInt()); input.close(); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/CollectionSerializerTest.java000066400000000000000000000027571210575634700307360ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.util.ArrayList; import java.util.LinkedList; import java.util.concurrent.CopyOnWriteArrayList; import com.esotericsoftware.kryo.serializers.CollectionSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; /** @author Nathan Sweet */ public class CollectionSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testCollections () { kryo.register(ArrayList.class); kryo.register(LinkedList.class); kryo.register(CopyOnWriteArrayList.class); roundTrip(11, list("1", "2", "3")); roundTrip(13, list("1", "2", null, 1, 2)); roundTrip(15, list("1", "2", null, 1, 2, 5)); roundTrip(11, list("1", "2", "3")); roundTrip(11, list("1", "2", "3")); roundTrip(13, list("1", "2", list("3"))); roundTrip(13, new LinkedList(list("1", "2", list("3")))); roundTrip(13, new CopyOnWriteArrayList(list("1", "2", list("3")))); CollectionSerializer serializer = new CollectionSerializer(); kryo.register(ArrayList.class, serializer); kryo.register(LinkedList.class, serializer); kryo.register(CopyOnWriteArrayList.class, serializer); serializer.setElementClass(String.class, kryo.getSerializer(String.class)); roundTrip(8, list("1", "2", "3")); serializer.setElementClass(String.class, new StringSerializer()); roundTrip(8, list("1", "2", "3")); serializer.setElementsCanBeNull(false); roundTrip(8, list("1", "2", "3")); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java000066400000000000000000000053541210575634700316620ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.io.FileNotFoundException; import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer; /** @author Nathan Sweet */ public class CompatibleFieldSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testCompatibleFieldSerializer () throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); object1.other = new AnotherClass(); object1.other.value = "meow"; kryo.setDefaultSerializer(CompatibleFieldSerializer.class); kryo.register(TestClass.class); kryo.register(AnotherClass.class); roundTrip(100, object1); } public void testAddedField () throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); object1.other = new AnotherClass(); object1.other.value = "meow"; CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); serializer.removeField("text"); kryo.register(TestClass.class, serializer); kryo.register(AnotherClass.class, new CompatibleFieldSerializer(kryo, AnotherClass.class)); roundTrip(74, object1); kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); Object object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); } public void testRemovedField () throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); roundTrip(88, object1); CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); serializer.removeField("text"); kryo.register(TestClass.class, serializer); Object object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); } static public class TestClass { public String text = "something"; public int moo = 120; public long moo2 = 1234120; public TestClass child; public int zzz = 123; public AnotherClass other; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestClass other = (TestClass)obj; if (child == null) { if (other.child != null) return false; } else if (!child.equals(other.child)) return false; if (moo != other.moo) return false; if (moo2 != other.moo2) return false; if (text == null) { if (other.text != null) return false; } else if (!text.equals(other.text)) return false; if (zzz != other.zzz) return false; return true; } } static public class AnotherClass { String value; } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/CopyTest.java000066400000000000000000000054621210575634700255170ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.util.ArrayList; public class CopyTest extends KryoTestCase { protected void setUp () throws Exception { super.setUp(); kryo.setRegistrationRequired(false); } public void testBasic () { ArrayList test = new ArrayList(); test.add("one"); test.add("two"); test.add("three"); ArrayList copy = kryo.copy(test); assertTrue(test != copy); assertEquals(test, copy); } public void testNested () { ArrayList test = new ArrayList(); test.add("one"); test.add("two"); test.add("three"); ArrayList test2 = new ArrayList(); test2.add(1); test2.add(2f); test2.add(3d); test2.add((byte)4); test2.add((short)5); test.add(test2); ArrayList copy = kryo.copy(test); assertTrue(test != copy); assertTrue(test.get(3) != copy.get(3)); assertEquals(test, copy); } public void testReferences () { ArrayList test = new ArrayList(); test.add("one"); test.add("two"); test.add("three"); ArrayList test2 = new ArrayList(); test2.add(1); test2.add(2f); test2.add(3d); test2.add((byte)4); test2.add((short)5); test.add(test2); test.add(test2); test.add(test2); ArrayList copy = kryo.copy(test); assertTrue(test != copy); assertEquals(test, copy); assertTrue(test.get(3) != copy.get(4)); assertTrue(copy.get(3) == copy.get(4)); assertTrue(copy.get(3) == copy.get(5)); } public void testCircularReferences () { ArrayList test = new ArrayList(); test.add("one"); test.add("two"); test.add("three"); test.add(test); ArrayList copy = kryo.copy(test); assertTrue(test != copy); assertEquals(copy.get(0), "one"); assertEquals(copy.get(1), "two"); assertEquals(copy.get(2), "three"); assertTrue(copy.get(3) == copy); Moo root = new Moo(); Moo moo1 = new Moo(); Moo moo2 = new Moo(); Moo moo3 = new Moo(); root.moo = moo1; moo1.moo = moo2; moo2.moo = moo3; moo3.moo = root; Moo root2 = kryo.copy(root); assertTrue(root != root2); assertTrue(root.moo != root2.moo); assertTrue(root.moo.moo != root2.moo.moo); assertTrue(root.moo.moo.moo != root2.moo.moo.moo); assertTrue(root.moo.moo.moo.moo != root2.moo.moo.moo.moo); assertTrue(root.moo.moo.moo.moo == root); assertTrue(root2.moo.moo.moo.moo == root2); } public void testShallow () { ArrayList test = new ArrayList(); test.add("one"); test.add("two"); test.add("three"); ArrayList test2 = new ArrayList(); test2.add(1); test2.add(2f); test2.add(3d); test2.add((byte)4); test2.add((short)5); test.add(test2); ArrayList copy = kryo.copyShallow(test); assertTrue(test != copy); assertTrue(test.get(3) == copy.get(3)); assertEquals(test, copy); } static public class Moo { Moo moo; } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/DefaultSerializersTest.java000066400000000000000000000132261210575634700304030ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.TimeZone; /** @author Nathan Sweet */ public class DefaultSerializersTest extends KryoTestCase { { supportsCopy = true; } public void testBoolean () { roundTrip(2, true); roundTrip(2, false); } public void testByte () { roundTrip(2, (byte)1); roundTrip(2, (byte)125); roundTrip(2, (byte)-125); } public void testChar () { roundTrip(3, 'a'); roundTrip(3, 'z'); } public void testDouble () { roundTrip(9, 0d); roundTrip(9, 1234d); roundTrip(9, 1234.5678d); } public void testFloat () { roundTrip(5, 0f); roundTrip(5, 123f); roundTrip(5, 123.456f); } public void testInt () { roundTrip(2, 0); roundTrip(2, 63); roundTrip(3, 64); roundTrip(3, 127); roundTrip(3, 128); roundTrip(3, 8191); roundTrip(4, 8192); roundTrip(4, 16383); roundTrip(4, 16384); roundTrip(5, 2097151); roundTrip(4, 1048575); roundTrip(5, 134217727); roundTrip(6, 268435455); roundTrip(6, 134217728); roundTrip(6, 268435456); roundTrip(2, -64); roundTrip(3, -65); roundTrip(3, -8192); roundTrip(4, -1048576); roundTrip(5, -134217728); roundTrip(6, -134217729); } public void testLong () { roundTrip(2, 0l); roundTrip(2, 63l); roundTrip(3, 64l); roundTrip(3, 127l); roundTrip(3, 128l); roundTrip(3, 8191l); roundTrip(4, 8192l); roundTrip(4, 16383l); roundTrip(4, 16384l); roundTrip(5, 2097151l); roundTrip(4, 1048575l); roundTrip(5, 134217727l); roundTrip(6, 268435455l); roundTrip(6, 134217728l); roundTrip(6, 268435456l); roundTrip(2, -64l); roundTrip(3, -65l); roundTrip(3, -8192l); roundTrip(4, -1048576l); roundTrip(5, -134217728l); roundTrip(6, -134217729l); roundTrip(10, 2368365495612416452l); roundTrip(10, -2368365495612416452l); } public void testShort () { roundTrip(3, (short)0); roundTrip(3, (short)123); roundTrip(3, (short)123); roundTrip(3, (short)-123); roundTrip(3, (short)250); roundTrip(3, (short)123); roundTrip(3, (short)400); } public void testString () { kryo = new Kryo(); kryo.setRegistrationRequired(true); roundTrip(6, "meow"); roundTrip(70, "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"); kryo.setReferences(false); roundTrip(5, "meow"); roundTrip(3, "a"); roundTrip(3, "\n"); roundTrip(2, ""); roundTrip(100, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\rabcdefghijklmnopqrstuvwxyz\n1234567890\t\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"); roundTrip(21, "abcdef\u00E1\u00E9\u00ED\u00F3\u00FA\u7C9F"); } public void testNull () { kryo = new Kryo(); kryo.setRegistrationRequired(true); kryo.register(ArrayList.class); roundTrip(1, null); testNull(Long.class); testNull(ArrayList.class); kryo.setReferences(false); roundTrip(1, null); testNull(Long.class); testNull(ArrayList.class); } private void testNull (Class type) { kryo.writeObjectOrNull(output, null, type); input.setBuffer(output.toBytes()); Object object = kryo.readObjectOrNull(input, type); assertNull(object); } public void testDateSerializer () { kryo.register(Date.class); roundTrip(2, new Date(0)); roundTrip(4, new Date(1234567)); roundTrip(10, new Date(-1234567)); } public void testBigDecimalSerializer () { kryo.register(BigDecimal.class); roundTrip(5, BigDecimal.valueOf(12345, 2)); } public void testBigIntegerSerializer () { kryo.register(BigInteger.class); roundTrip(8, BigInteger.valueOf(1270507903945L)); } public void testEnumSerializer () { kryo.register(TestEnum.class); roundTrip(2, TestEnum.a); roundTrip(2, TestEnum.b); roundTrip(2, TestEnum.c); kryo = new Kryo(); kryo.setRegistrationRequired(false); // 1 byte identifying it's a class name // 1 byte for the class name id // 57 bytes for the class name characters // 1 byte for the reference id // 1 byte for the enum value roundTrip(61, TestEnum.c); } public void testEnumSetSerializer () { kryo.register(EnumSet.class); kryo.register(TestEnum.class); roundTrip(5, EnumSet.of(TestEnum.a, TestEnum.c)); roundTrip(4, EnumSet.of(TestEnum.a)); roundTrip(6, EnumSet.allOf(TestEnum.class)); kryo = new Kryo(); kryo.setRegistrationRequired(false); roundTrip(89, EnumSet.of(TestEnum.a, TestEnum.c)); } public void testEnumSerializerWithMethods () { kryo.register(TestEnumWithMethods.class); roundTrip(2, TestEnumWithMethods.a); roundTrip(2, TestEnumWithMethods.b); roundTrip(2, TestEnumWithMethods.c); kryo = new Kryo(); kryo.setRegistrationRequired(false); roundTrip(76, TestEnumWithMethods.c); } public void testCollectionsMethods () { kryo.setRegistrationRequired(false); ArrayList test = new ArrayList(); test.add(Collections.EMPTY_LIST); test.add(Collections.EMPTY_MAP); test.add(Collections.EMPTY_SET); test.add(Collections.singletonList("meow")); test.add(Collections.singletonMap("moo", 1234)); test.add(Collections.singleton(12.34)); roundTrip(249, test); } public void testCalendar () { kryo.setRegistrationRequired(false); Calendar calendar = Calendar.getInstance(); calendar.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); calendar.set(1980, 7, 26, 12, 22, 46); roundTrip(64, calendar); } public enum TestEnum { a, b, c } public enum TestEnumWithMethods { a { }, b { }, c { } } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/DeflateSerializerTest.java000066400000000000000000000007271210575634700302020ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; import com.esotericsoftware.kryo.serializers.DeflateSerializer; /** @author Nathan Sweet */ public class DeflateSerializerTest extends KryoTestCase { public void testZip () { kryo.register(String.class, new DeflateSerializer(new StringSerializer())); roundTrip(13, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/FieldSerializerTest.java000066400000000000000000000535621210575634700276660ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.objenesis.strategy.StdInstantiatorStrategy; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.FieldSerializer; import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional; /** @author Nathan Sweet */ public class FieldSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testDefaultTypes () { kryo.register(DefaultTypes.class); kryo.register(byte[].class); DefaultTypes test = new DefaultTypes(); test.booleanField = true; test.byteField = 123; test.charField = 'Z'; test.shortField = 12345; test.intField = 123456; test.longField = 123456789; test.floatField = 123.456f; test.doubleField = 1.23456d; test.BooleanField = true; test.ByteField = -12; test.CharacterField = 'X'; test.ShortField = -12345; test.IntegerField = -123456; test.LongField = -123456789l; test.FloatField = -123.3f; test.DoubleField = -0.121231d; test.StringField = "stringvalue"; test.byteArrayField = new byte[] {2, 1, 0, -1, -2}; roundTrip(78, test); kryo.register(HasStringField.class); test.hasStringField = new HasStringField(); FieldSerializer serializer = (FieldSerializer)kryo.getSerializer(DefaultTypes.class); serializer.getField("hasStringField").setCanBeNull(false); roundTrip(79, test); serializer.setFixedFieldTypes(true); serializer.getField("hasStringField").setCanBeNull(false); roundTrip(78, test); } public void testFieldRemoval () { kryo.register(DefaultTypes.class); kryo.register(byte[].class); kryo.register(HasStringField.class); HasStringField hasStringField = new HasStringField(); hasStringField.text = "moo"; roundTrip(4, hasStringField); DefaultTypes test = new DefaultTypes(); test.intField = 12; test.StringField = "value"; test.CharacterField = 'X'; test.child = new DefaultTypes(); roundTrip(71, test); supportsCopy = false; test.StringField = null; roundTrip(67, test); FieldSerializer serializer = (FieldSerializer)kryo.getSerializer(DefaultTypes.class); serializer.removeField("LongField"); serializer.removeField("floatField"); serializer.removeField("FloatField"); roundTrip(55, test); supportsCopy = true; } public void testOptionalRegistration () { kryo.setRegistrationRequired(false); DefaultTypes test = new DefaultTypes(); test.intField = 12; test.StringField = "value"; test.CharacterField = 'X'; test.hasStringField = new HasStringField(); test.child = new DefaultTypes(); test.child.hasStringField = new HasStringField(); roundTrip(195, test); test.hasStringField = null; roundTrip(193, test); test = new DefaultTypes(); test.booleanField = true; test.byteField = 123; test.charField = 1234; test.shortField = 12345; test.intField = 123456; test.longField = 123456789; test.floatField = 123.456f; test.doubleField = 1.23456d; test.BooleanField = true; test.ByteField = -12; test.CharacterField = 123; test.ShortField = -12345; test.IntegerField = -123456; test.LongField = -123456789l; test.FloatField = -123.3f; test.DoubleField = -0.121231d; test.StringField = "stringvalue"; test.byteArrayField = new byte[] {2, 1, 0, -1, -2}; kryo = new Kryo(); roundTrip(140, test); C c = new C(); c.a = new A(); c.a.value = 123; c.a.b = new B(); c.a.b.value = 456; c.d = new D(); c.d.e = new E(); c.d.e.f = new F(); roundTrip(63, c); } public void testReferences () { C c = new C(); c.a = new A(); c.a.value = 123; c.a.b = new B(); c.a.b.value = 456; c.d = new D(); c.d.e = new E(); c.d.e.f = new F(); c.d.e.f.a = c.a; kryo = new Kryo(); roundTrip(63, c); C c2 = (C)object2; assertTrue(c2.a == c2.d.e.f.a); // Test reset clears unregistered class names. roundTrip(63, c); c2 = (C)object2; 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); kryo.register(D.class); kryo.register(E.class); kryo.register(F.class); roundTrip(15, c); c2 = (C)object2; assertTrue(c2.a == c2.d.e.f.a); } public void testRegistrationOrder () { A a = new A(); a.value = 100; a.b = new B(); a.b.value = 200; a.b.a = new A(); a.b.a.value = 300; kryo.register(A.class); kryo.register(B.class); roundTrip(10, a); kryo = new Kryo(); kryo.setReferences(false); kryo.register(B.class); kryo.register(A.class); roundTrip(10, a); } public void testExceptionTrace () { C c = new C(); c.a = new A(); c.a.value = 123; c.a.b = new B(); c.a.b.value = 456; c.d = new D(); c.d.e = new E(); c.d.e.f = new F(); Kryo kryoWithoutF = new Kryo(); kryoWithoutF.setReferences(false); kryoWithoutF.setRegistrationRequired(true); kryoWithoutF.register(A.class); kryoWithoutF.register(B.class); kryoWithoutF.register(C.class); kryoWithoutF.register(D.class); kryoWithoutF.register(E.class); Output output = new Output(512); try { kryoWithoutF.writeClassAndObject(output, c); fail("Should have failed because F is not registered."); } catch (KryoException ignored) { } kryo.register(A.class); kryo.register(B.class); kryo.register(C.class); kryo.register(D.class); kryo.register(E.class); kryo.register(F.class); kryo.setRegistrationRequired(true); output.clear(); kryo.writeClassAndObject(output, c); output.flush(); assertEquals(14, output.total()); Input input = new Input(output.getBuffer()); kryo.readClassAndObject(input); try { input.setPosition(0); kryoWithoutF.readClassAndObject(input); fail("Should have failed because F is not registered."); } catch (KryoException ignored) { } } public void testNoDefaultConstructor () { kryo.register(SimpleNoDefaultConstructor.class, new Serializer() { public SimpleNoDefaultConstructor read (Kryo kryo, Input input, Class type) { return new SimpleNoDefaultConstructor(input.readInt(true)); } public void write (Kryo kryo, Output output, SimpleNoDefaultConstructor object) { output.writeInt(object.constructorValue, true); } public SimpleNoDefaultConstructor copy (Kryo kryo, SimpleNoDefaultConstructor original) { return new SimpleNoDefaultConstructor(original.constructorValue); } }); SimpleNoDefaultConstructor object1 = new SimpleNoDefaultConstructor(2); roundTrip(2, object1); kryo.register(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); } protected ComplexNoDefaultConstructor create (Kryo kryo, Input input, Class type) { String name = input.readString(); return new ComplexNoDefaultConstructor(name); } protected ComplexNoDefaultConstructor createCopy (Kryo kryo, ComplexNoDefaultConstructor original) { return new ComplexNoDefaultConstructor(original.name); } }); ComplexNoDefaultConstructor object2 = new ComplexNoDefaultConstructor("has no zero arg constructor!"); object2.anotherField1 = 1234; object2.anotherField2 = "abcd"; roundTrip(35, object2); } public void testNonNull () { kryo.register(HasNonNull.class); HasNonNull nonNullValue = new HasNonNull(); nonNullValue.nonNullText = "moo"; roundTrip(4, nonNullValue); } public void testDefaultSerializerAnnotation () { kryo = new Kryo(); roundTrip(82, new HasDefaultSerializerAnnotation(123)); } public void testOptionalAnnotation () { kryo = new Kryo(); roundTrip(72, new HasOptionalAnnotation()); kryo = new Kryo(); 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(); test.child = test; roundTrip(35, test); } @SuppressWarnings("synthetic-access") public void testInstantiatorStrategy () { kryo.register(HasArgumentConstructor.class); kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); HasArgumentConstructor test = new HasArgumentConstructor("cow"); roundTrip(4, test); kryo.register(HasPrivateConstructor.class); test = new HasPrivateConstructor(); roundTrip(4, test); } public void testGenericTypes () { kryo = new Kryo(); kryo.setRegistrationRequired(true); kryo.register(HasGenerics.class); kryo.register(ArrayList.class); kryo.register(HashMap.class); HasGenerics test = new HasGenerics(); test.list1 = new ArrayList(); test.list1.add(1); test.list1.add(2); test.list1.add(3); test.list1.add(4); test.list1.add(5); test.list1.add(6); test.list1.add(7); test.list1.add(8); test.list2 = new ArrayList(); test.list2.add(test.list1); test.map1 = new HashMap(); test.map1.put("a", test.list1); test.list3 = new ArrayList(); test.list3.add(null); test.list4 = new ArrayList(); test.list4.add(null); test.list5 = new ArrayList(); test.list5.add("one"); test.list5.add("two"); roundTrip(53, test); } public void testRegistration () { int id = kryo.getNextRegistrationId(); kryo.register(DefaultTypes.class, id); kryo.register(DefaultTypes.class, id); kryo.register(new Registration(byte[].class, kryo.getDefaultSerializer(byte[].class), id + 1)); kryo.register(byte[].class, kryo.getDefaultSerializer(byte[].class), id + 1); kryo.register(HasStringField.class, kryo.getDefaultSerializer(HasStringField.class)); DefaultTypes test = new DefaultTypes(); test.intField = 12; test.StringField = "meow"; test.CharacterField = 'z'; test.byteArrayField = new byte[] {0, 1, 2, 3, 4}; test.child = new DefaultTypes(); roundTrip(75, test); } static public class DefaultTypes { // Primitives. public boolean booleanField; public byte byteField; public char charField; public short shortField; public int intField; public long longField; public float floatField; public double doubleField; // Primitive wrappers. public Boolean BooleanField; public Byte ByteField; public Character CharacterField; public Short ShortField; public Integer IntegerField; public Long LongField; public Float FloatField; public Double DoubleField; // Other. public String StringField; public byte[] byteArrayField; DefaultTypes child; HasStringField hasStringField; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DefaultTypes other = (DefaultTypes)obj; if (BooleanField == null) { if (other.BooleanField != null) return false; } else if (!BooleanField.equals(other.BooleanField)) return false; if (ByteField == null) { if (other.ByteField != null) return false; } else if (!ByteField.equals(other.ByteField)) return false; if (CharacterField == null) { if (other.CharacterField != null) return false; } else if (!CharacterField.equals(other.CharacterField)) return false; if (DoubleField == null) { if (other.DoubleField != null) return false; } else if (!DoubleField.equals(other.DoubleField)) return false; if (FloatField == null) { if (other.FloatField != null) return false; } else if (!FloatField.equals(other.FloatField)) return false; if (IntegerField == null) { if (other.IntegerField != null) return false; } else if (!IntegerField.equals(other.IntegerField)) return false; if (LongField == null) { if (other.LongField != null) return false; } else if (!LongField.equals(other.LongField)) return false; if (ShortField == null) { if (other.ShortField != null) return false; } else if (!ShortField.equals(other.ShortField)) return false; if (StringField == null) { if (other.StringField != null) return false; } else if (!StringField.equals(other.StringField)) return false; if (booleanField != other.booleanField) return false; Object list1 = arrayToList(byteArrayField); Object list2 = arrayToList(other.byteArrayField); if (list1 != list2) { if (list1 == null || list2 == null) return false; if (!list1.equals(list2)) return false; } if (child != other.child) { if (child == null || other.child == null) return false; if (child != this && !child.equals(other.child)) return false; } if (byteField != other.byteField) return false; if (charField != other.charField) return false; if (Double.doubleToLongBits(doubleField) != Double.doubleToLongBits(other.doubleField)) return false; if (Float.floatToIntBits(floatField) != Float.floatToIntBits(other.floatField)) return false; if (intField != other.intField) return false; if (longField != other.longField) return false; if (shortField != other.shortField) return false; return true; } } static public final class A { public int value; public B b; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; A other = (A)obj; if (b == null) { if (other.b != null) return false; } else if (!b.equals(other.b)) return false; if (value != other.value) return false; return true; } } static public final class B { public int value; public A a; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; B other = (B)obj; if (a == null) { if (other.a != null) return false; } else if (!a.equals(other.a)) return false; if (value != other.value) return false; return true; } } static public final class C { public A a; public D d; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; C other = (C)obj; if (a == null) { if (other.a != null) return false; } else if (!a.equals(other.a)) return false; if (d == null) { if (other.d != null) return false; } else if (!d.equals(other.d)) return false; return true; } } static public final class D { public E e; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; D other = (D)obj; if (e == null) { if (other.e != null) return false; } else if (!e.equals(other.e)) return false; return true; } } static public final class E { public F f; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; E other = (E)obj; if (f == null) { if (other.f != null) return false; } else if (!f.equals(other.f)) return false; return true; } } static public final class F { public int value; public final int finalValue = 12; public A a; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; F other = (F)obj; if (finalValue != other.finalValue) return false; if (value != other.value) return false; return true; } } static public class SimpleNoDefaultConstructor { int constructorValue; public SimpleNoDefaultConstructor (int constructorValue) { this.constructorValue = constructorValue; } public int getConstructorValue () { return constructorValue; } public int hashCode () { final int prime = 31; int result = 1; result = prime * result + constructorValue; return result; } public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SimpleNoDefaultConstructor other = (SimpleNoDefaultConstructor)obj; if (constructorValue != other.constructorValue) return false; return true; } } static public class ComplexNoDefaultConstructor { public transient String name; public int anotherField1; public String anotherField2; public ComplexNoDefaultConstructor (String name) { this.name = name; } 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()); return result; } public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ComplexNoDefaultConstructor other = (ComplexNoDefaultConstructor)obj; if (anotherField1 != other.anotherField1) return false; if (anotherField2 == null) { if (other.anotherField2 != null) return false; } else if (!anotherField2.equals(other.anotherField2)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } static public class HasNonNull { @NotNull public String nonNullText; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasNonNull other = (HasNonNull)obj; if (nonNullText == null) { if (other.nonNullText != null) return false; } else if (!nonNullText.equals(other.nonNullText)) return false; return true; } } static public class HasStringField { public String text; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasStringField other = (HasStringField)obj; if (text == null) { if (other.text != null) return false; } else if (!text.equals(other.text)) return false; return true; } } static public class HasOptionalAnnotation { @Optional("smurf") int moo; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasOptionalAnnotation other = (HasOptionalAnnotation)obj; if (moo != other.moo) return false; return true; } } @DefaultSerializer(HasDefaultSerializerAnnotationSerializer.class) static public class HasDefaultSerializerAnnotation { long time; public HasDefaultSerializerAnnotation (long time) { this.time = time; } public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasDefaultSerializerAnnotation other = (HasDefaultSerializerAnnotation)obj; if (time != other.time) return false; return true; } } static public class HasDefaultSerializerAnnotationSerializer extends Serializer { public void write (Kryo kryo, Output output, HasDefaultSerializerAnnotation object) { output.writeLong(object.time, true); } public HasDefaultSerializerAnnotation read (Kryo kryo, Input input, Class type) { return new HasDefaultSerializerAnnotation(input.readLong(true)); } public HasDefaultSerializerAnnotation copy (Kryo kryo, HasDefaultSerializerAnnotation original) { return new HasDefaultSerializerAnnotation(original.time); } } static public class HasArgumentConstructor { public String moo; public HasArgumentConstructor (String moo) { this.moo = moo; } public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasArgumentConstructor other = (HasArgumentConstructor)obj; if (moo == null) { if (other.moo != null) return false; } else if (!moo.equals(other.moo)) return false; return true; } } static public class HasPrivateConstructor extends HasArgumentConstructor { private HasPrivateConstructor () { super("cow"); } } static public class HasGenerics { ArrayList list1; List> list2 = new ArrayList>(); List list3 = new ArrayList(); ArrayList list4 = new ArrayList(); ArrayList list5; HashMap> map1; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HasGenerics other = (HasGenerics)obj; if (list1 == null) { if (other.list1 != null) return false; } else if (!list1.equals(other.list1)) return false; if (list2 == null) { if (other.list2 != null) return false; } else if (!list2.equals(other.list2)) return false; if (list3 == null) { if (other.list3 != null) return false; } else if (!list3.equals(other.list3)) return false; if (list4 == null) { if (other.list4 != null) return false; } else if (!list4.equals(other.list4)) return false; if (list5 == null) { if (other.list5 != null) return false; } else if (!list5.equals(other.list5)) return false; if (map1 == null) { if (other.map1 != null) return false; } else if (!map1.equals(other.map1)) return false; return true; } } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/InputOutputTest.java000066400000000000000000000726271210575634700271340ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Random; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; /** @author Nathan Sweet */ public class InputOutputTest extends KryoTestCase { public void testOutputStream () throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); Output output = new Output(buffer, 2); output.writeBytes(new byte[] {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}); output.writeBytes(new byte[] {31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46}); output.writeBytes(new byte[] {51, 52, 53, 54, 55, 56, 57, 58}); output.writeBytes(new byte[] {61, 62, 63, 64, 65}); output.flush(); assertEquals(new byte[] { // 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, // 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, // 51, 52, 53, 54, 55, 56, 57, 58, // 61, 62, 63, 64, 65}, buffer.toByteArray()); } public void testInputStream () throws IOException { byte[] bytes = new byte[] { // 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, // 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, // 51, 52, 53, 54, 55, 56, 57, 58, // 61, 62, 63, 64, 65}; ByteArrayInputStream buffer = new ByteArrayInputStream(bytes); Input input = new Input(buffer, 2); byte[] temp = new byte[1024]; int count = input.read(temp, 512, bytes.length); assertEquals(bytes.length, count); byte[] temp2 = new byte[count]; System.arraycopy(temp, 512, temp2, 0, count); assertEquals(bytes, temp2); input = new Input(bytes); count = input.read(temp, 512, 512); assertEquals(bytes.length, count); temp2 = new byte[count]; System.arraycopy(temp, 512, temp2, 0, count); assertEquals(bytes, temp2); } public void testWriteBytes () throws IOException { Output buffer = new Output(512); buffer.writeBytes(new byte[] {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}); buffer.writeBytes(new byte[] {31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46}); buffer.writeByte(51); buffer.writeBytes(new byte[] {52, 53, 54, 55, 56, 57, 58}); buffer.writeByte(61); buffer.writeByte(62); buffer.writeByte(63); buffer.writeByte(64); buffer.writeByte(65); buffer.flush(); assertEquals(new byte[] { // 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, // 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, // 51, 52, 53, 54, 55, 56, 57, 58, // 61, 62, 63, 64, 65}, buffer.toBytes()); } public void testStrings () throws IOException { runStringTest(new Output(4096)); runStringTest(new Output(897)); runStringTest(new Output(new ByteArrayOutputStream())); Output write = new Output(21); String value = "abcdef\u00E1\u00E9\u00ED\u00F3\u00FA\u1234"; write.writeString(value); Input read = new Input(write.toBytes()); assertEquals(value, read.readString()); runStringTest(127); runStringTest(256); runStringTest(1024 * 1023); runStringTest(1024 * 1024); runStringTest(1024 * 1025); runStringTest(1024 * 1026); runStringTest(1024 * 1024 * 2); } public void runStringTest (int length) throws IOException { Output write = new Output(1024, -1); StringBuilder buffer = new StringBuilder(); for (int i = 0; i < length; i++) buffer.append((char)i); String value = buffer.toString(); write.writeString(value); write.writeString(value); Input read = new Input(write.toBytes()); assertEquals(value, read.readString()); assertEquals(value, read.readStringBuilder().toString()); write.clear(); write.writeString(buffer); write.writeString(buffer); read = new Input(write.toBytes()); assertEquals(value, read.readStringBuilder().toString()); assertEquals(value, read.readString()); if (length <= 127) { write.clear(); write.writeAscii(value); write.writeAscii(value); read = new Input(write.toBytes()); assertEquals(value, read.readStringBuilder().toString()); assertEquals(value, read.readString()); } } public void runStringTest (Output write) throws IOException { String value1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\rabcdefghijklmnopqrstuvwxyz\n1234567890\t\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"; String value2 = "abcdef\u00E1\u00E9\u00ED\u00F3\u00FA\u1234"; write.writeString(""); write.writeString("1"); write.writeString("22"); write.writeString("uno"); write.writeString("dos"); write.writeString("tres"); write.writeString(null); write.writeString(value1); write.writeString(value2); for (int i = 0; i < 127; i++) write.writeString(String.valueOf((char)i)); for (int i = 0; i < 127; i++) write.writeString(String.valueOf((char)i) + "abc"); Input read = new Input(write.toBytes()); assertEquals("", read.readString()); assertEquals("1", read.readString()); assertEquals("22", read.readString()); assertEquals("uno", read.readString()); assertEquals("dos", read.readString()); assertEquals("tres", read.readString()); assertEquals(null, read.readString()); assertEquals(value1, read.readString()); assertEquals(value2, read.readString()); for (int i = 0; i < 127; i++) assertEquals(String.valueOf((char)i), read.readString()); for (int i = 0; i < 127; i++) assertEquals(String.valueOf((char)i) + "abc", read.readString()); read.rewind(); assertEquals("", read.readStringBuilder().toString()); assertEquals("1", read.readStringBuilder().toString()); assertEquals("22", read.readStringBuilder().toString()); assertEquals("uno", read.readStringBuilder().toString()); assertEquals("dos", read.readStringBuilder().toString()); assertEquals("tres", read.readStringBuilder().toString()); assertEquals(null, read.readStringBuilder()); assertEquals(value1, read.readStringBuilder().toString()); assertEquals(value2, read.readStringBuilder().toString()); for (int i = 0; i < 127; i++) assertEquals(String.valueOf((char)i), read.readStringBuilder().toString()); for (int i = 0; i < 127; i++) assertEquals(String.valueOf((char)i) + "abc", read.readStringBuilder().toString()); } public void testCanReadInt () throws IOException { Output write = new Output(new ByteArrayOutputStream()); Input read = new Input(write.toBytes()); assertEquals(false, read.canReadInt()); write.writeInt(400, true); read = new Input(write.toBytes()); assertEquals(true, read.canReadInt()); read.setLimit(read.limit() - 1); assertEquals(false, read.canReadInt()); } public void testInts () throws IOException { runIntTest(new Output(4096)); runIntTest(new Output(new ByteArrayOutputStream())); } private void runIntTest (Output write) throws IOException { write.writeInt(0); write.writeInt(63); write.writeInt(64); write.writeInt(127); write.writeInt(128); write.writeInt(8192); write.writeInt(16384); write.writeInt(2097151); write.writeInt(1048575); write.writeInt(134217727); write.writeInt(268435455); write.writeInt(134217728); write.writeInt(268435456); write.writeInt(-2097151); write.writeInt(-1048575); write.writeInt(-134217727); write.writeInt(-268435455); write.writeInt(-134217728); write.writeInt(-268435456); assertEquals(1, write.writeInt(0, true)); assertEquals(1, write.writeInt(0, false)); assertEquals(1, write.writeInt(63, true)); assertEquals(1, write.writeInt(63, false)); assertEquals(1, write.writeInt(64, true)); assertEquals(2, write.writeInt(64, false)); assertEquals(1, write.writeInt(127, true)); assertEquals(2, write.writeInt(127, false)); assertEquals(2, write.writeInt(128, true)); assertEquals(2, write.writeInt(128, false)); assertEquals(2, write.writeInt(8191, true)); assertEquals(2, write.writeInt(8191, false)); assertEquals(2, write.writeInt(8192, true)); assertEquals(3, write.writeInt(8192, false)); assertEquals(2, write.writeInt(16383, true)); assertEquals(3, write.writeInt(16383, false)); assertEquals(3, write.writeInt(16384, true)); assertEquals(3, write.writeInt(16384, false)); assertEquals(3, write.writeInt(2097151, true)); assertEquals(4, write.writeInt(2097151, false)); assertEquals(3, write.writeInt(1048575, true)); assertEquals(3, write.writeInt(1048575, false)); assertEquals(4, write.writeInt(134217727, true)); assertEquals(4, write.writeInt(134217727, false)); assertEquals(4, write.writeInt(268435455, true)); assertEquals(5, write.writeInt(268435455, false)); assertEquals(4, write.writeInt(134217728, true)); assertEquals(5, write.writeInt(134217728, false)); assertEquals(5, write.writeInt(268435456, true)); assertEquals(5, write.writeInt(268435456, false)); assertEquals(1, write.writeInt(-64, false)); assertEquals(5, write.writeInt(-64, true)); assertEquals(2, write.writeInt(-65, false)); assertEquals(5, write.writeInt(-65, true)); assertEquals(2, write.writeInt(-8192, false)); assertEquals(5, write.writeInt(-8192, true)); assertEquals(3, write.writeInt(-1048576, false)); assertEquals(5, write.writeInt(-1048576, true)); assertEquals(4, write.writeInt(-134217728, false)); assertEquals(5, write.writeInt(-134217728, true)); assertEquals(5, write.writeInt(-134217729, false)); assertEquals(5, write.writeInt(-134217729, true)); Input read = new Input(write.toBytes()); assertEquals(0, read.readInt()); assertEquals(63, read.readInt()); assertEquals(64, read.readInt()); assertEquals(127, read.readInt()); assertEquals(128, read.readInt()); assertEquals(8192, read.readInt()); assertEquals(16384, read.readInt()); assertEquals(2097151, read.readInt()); assertEquals(1048575, read.readInt()); assertEquals(134217727, read.readInt()); assertEquals(268435455, read.readInt()); assertEquals(134217728, read.readInt()); assertEquals(268435456, read.readInt()); assertEquals(-2097151, read.readInt()); assertEquals(-1048575, read.readInt()); assertEquals(-134217727, read.readInt()); assertEquals(-268435455, read.readInt()); assertEquals(-134217728, read.readInt()); assertEquals(-268435456, read.readInt()); assertEquals(true, read.canReadInt()); assertEquals(true, read.canReadInt()); assertEquals(true, read.canReadInt()); assertEquals(0, read.readInt(true)); assertEquals(0, read.readInt(false)); assertEquals(63, read.readInt(true)); assertEquals(63, read.readInt(false)); assertEquals(64, read.readInt(true)); assertEquals(64, read.readInt(false)); assertEquals(127, read.readInt(true)); assertEquals(127, read.readInt(false)); assertEquals(128, read.readInt(true)); assertEquals(128, read.readInt(false)); assertEquals(8191, read.readInt(true)); assertEquals(8191, read.readInt(false)); assertEquals(8192, read.readInt(true)); assertEquals(8192, read.readInt(false)); assertEquals(16383, read.readInt(true)); assertEquals(16383, read.readInt(false)); assertEquals(16384, read.readInt(true)); assertEquals(16384, read.readInt(false)); assertEquals(2097151, read.readInt(true)); assertEquals(2097151, read.readInt(false)); assertEquals(1048575, read.readInt(true)); assertEquals(1048575, read.readInt(false)); assertEquals(134217727, read.readInt(true)); assertEquals(134217727, read.readInt(false)); assertEquals(268435455, read.readInt(true)); assertEquals(268435455, read.readInt(false)); assertEquals(134217728, read.readInt(true)); assertEquals(134217728, read.readInt(false)); assertEquals(268435456, read.readInt(true)); assertEquals(268435456, read.readInt(false)); assertEquals(-64, read.readInt(false)); assertEquals(-64, read.readInt(true)); assertEquals(-65, read.readInt(false)); assertEquals(-65, read.readInt(true)); assertEquals(-8192, read.readInt(false)); assertEquals(-8192, read.readInt(true)); assertEquals(-1048576, read.readInt(false)); assertEquals(-1048576, read.readInt(true)); assertEquals(-134217728, read.readInt(false)); assertEquals(-134217728, read.readInt(true)); assertEquals(-134217729, read.readInt(false)); assertEquals(-134217729, read.readInt(true)); assertEquals(false, read.canReadInt()); Random random = new Random(); for (int i = 0; i < 10000; i++) { int value = random.nextInt(); write.clear(); write.writeInt(value); write.writeInt(value, true); write.writeInt(value, false); read.setBuffer(write.toBytes()); assertEquals(value, read.readInt()); assertEquals(value, read.readInt(true)); assertEquals(value, read.readInt(false)); } } public void testLongs () throws IOException { runLongTest(new Output(4096)); runLongTest(new Output(new ByteArrayOutputStream())); } private void runLongTest (Output write) throws IOException { write.writeLong(0); write.writeLong(63); write.writeLong(64); write.writeLong(127); write.writeLong(128); write.writeLong(8192); write.writeLong(16384); write.writeLong(2097151); write.writeLong(1048575); write.writeLong(134217727); write.writeLong(268435455); write.writeLong(134217728); write.writeLong(268435456); write.writeLong(-2097151); write.writeLong(-1048575); write.writeLong(-134217727); write.writeLong(-268435455); write.writeLong(-134217728); write.writeLong(-268435456); assertEquals(1, write.writeLong(0, true)); assertEquals(1, write.writeLong(0, false)); assertEquals(1, write.writeLong(63, true)); assertEquals(1, write.writeLong(63, false)); assertEquals(1, write.writeLong(64, true)); assertEquals(2, write.writeLong(64, false)); assertEquals(1, write.writeLong(127, true)); assertEquals(2, write.writeLong(127, false)); assertEquals(2, write.writeLong(128, true)); assertEquals(2, write.writeLong(128, false)); assertEquals(2, write.writeLong(8191, true)); assertEquals(2, write.writeLong(8191, false)); assertEquals(2, write.writeLong(8192, true)); assertEquals(3, write.writeLong(8192, false)); assertEquals(2, write.writeLong(16383, true)); assertEquals(3, write.writeLong(16383, false)); assertEquals(3, write.writeLong(16384, true)); assertEquals(3, write.writeLong(16384, false)); assertEquals(3, write.writeLong(2097151, true)); assertEquals(4, write.writeLong(2097151, false)); assertEquals(3, write.writeLong(1048575, true)); assertEquals(3, write.writeLong(1048575, false)); assertEquals(4, write.writeLong(134217727, true)); assertEquals(4, write.writeLong(134217727, false)); assertEquals(4, write.writeLong(268435455l, true)); assertEquals(5, write.writeLong(268435455l, false)); assertEquals(4, write.writeLong(134217728l, true)); assertEquals(5, write.writeLong(134217728l, false)); assertEquals(5, write.writeLong(268435456l, true)); assertEquals(5, write.writeLong(268435456l, false)); assertEquals(1, write.writeLong(-64, false)); assertEquals(9, write.writeLong(-64, true)); assertEquals(2, write.writeLong(-65, false)); assertEquals(9, write.writeLong(-65, true)); assertEquals(2, write.writeLong(-8192, false)); assertEquals(9, write.writeLong(-8192, true)); assertEquals(3, write.writeLong(-1048576, false)); assertEquals(9, write.writeLong(-1048576, true)); assertEquals(4, write.writeLong(-134217728, false)); assertEquals(9, write.writeLong(-134217728, true)); assertEquals(5, write.writeLong(-134217729, false)); assertEquals(9, write.writeLong(-134217729, true)); Input read = new Input(write.toBytes()); assertEquals(0, read.readLong()); assertEquals(63, read.readLong()); assertEquals(64, read.readLong()); assertEquals(127, read.readLong()); assertEquals(128, read.readLong()); assertEquals(8192, read.readLong()); assertEquals(16384, read.readLong()); assertEquals(2097151, read.readLong()); assertEquals(1048575, read.readLong()); assertEquals(134217727, read.readLong()); assertEquals(268435455, read.readLong()); assertEquals(134217728, read.readLong()); assertEquals(268435456, read.readLong()); assertEquals(-2097151, read.readLong()); assertEquals(-1048575, read.readLong()); assertEquals(-134217727, read.readLong()); assertEquals(-268435455, read.readLong()); assertEquals(-134217728, read.readLong()); assertEquals(-268435456, read.readLong()); assertEquals(0, read.readLong(true)); assertEquals(0, read.readLong(false)); assertEquals(63, read.readLong(true)); assertEquals(63, read.readLong(false)); assertEquals(64, read.readLong(true)); assertEquals(64, read.readLong(false)); assertEquals(127, read.readLong(true)); assertEquals(127, read.readLong(false)); assertEquals(128, read.readLong(true)); assertEquals(128, read.readLong(false)); assertEquals(8191, read.readLong(true)); assertEquals(8191, read.readLong(false)); assertEquals(8192, read.readLong(true)); assertEquals(8192, read.readLong(false)); assertEquals(16383, read.readLong(true)); assertEquals(16383, read.readLong(false)); assertEquals(16384, read.readLong(true)); assertEquals(16384, read.readLong(false)); assertEquals(2097151, read.readLong(true)); assertEquals(2097151, read.readLong(false)); assertEquals(1048575, read.readLong(true)); assertEquals(1048575, read.readLong(false)); assertEquals(134217727, read.readLong(true)); assertEquals(134217727, read.readLong(false)); assertEquals(268435455, read.readLong(true)); assertEquals(268435455, read.readLong(false)); assertEquals(134217728, read.readLong(true)); assertEquals(134217728, read.readLong(false)); assertEquals(268435456, read.readLong(true)); assertEquals(268435456, read.readLong(false)); assertEquals(-64, read.readLong(false)); assertEquals(-64, read.readLong(true)); assertEquals(-65, read.readLong(false)); assertEquals(-65, read.readLong(true)); assertEquals(-8192, read.readLong(false)); assertEquals(-8192, read.readLong(true)); assertEquals(-1048576, read.readLong(false)); assertEquals(-1048576, read.readLong(true)); assertEquals(-134217728, read.readLong(false)); assertEquals(-134217728, read.readLong(true)); assertEquals(-134217729, read.readLong(false)); assertEquals(-134217729, read.readLong(true)); Random random = new Random(); for (int i = 0; i < 10000; i++) { long value = random.nextLong(); write.clear(); write.writeLong(value); write.writeLong(value, true); write.writeLong(value, false); read.setBuffer(write.toBytes()); assertEquals(value, read.readLong()); assertEquals(value, read.readLong(true)); assertEquals(value, read.readLong(false)); } } public void testShorts () throws IOException { runShortTest(new Output(4096)); runShortTest(new Output(new ByteArrayOutputStream())); } private void runShortTest (Output write) throws IOException { write.writeShort(0); write.writeShort(63); write.writeShort(64); write.writeShort(127); write.writeShort(128); write.writeShort(8192); write.writeShort(16384); write.writeShort(32767); write.writeShort(-63); write.writeShort(-64); write.writeShort(-127); write.writeShort(-128); write.writeShort(-8192); write.writeShort(-16384); write.writeShort(-32768); Input read = new Input(write.toBytes()); assertEquals(0, read.readShort()); assertEquals(63, read.readShort()); assertEquals(64, read.readShort()); assertEquals(127, read.readShort()); assertEquals(128, read.readShort()); assertEquals(8192, read.readShort()); assertEquals(16384, read.readShort()); assertEquals(32767, read.readShort()); assertEquals(-63, read.readShort()); assertEquals(-64, read.readShort()); assertEquals(-127, read.readShort()); assertEquals(-128, read.readShort()); assertEquals(-8192, read.readShort()); assertEquals(-16384, read.readShort()); assertEquals(-32768, read.readShort()); } public void testFloats () throws IOException { runFloatTest(new Output(4096)); runFloatTest(new Output(new ByteArrayOutputStream())); } private void runFloatTest (Output write) throws IOException { write.writeFloat(0); write.writeFloat(63); write.writeFloat(64); write.writeFloat(127); write.writeFloat(128); write.writeFloat(8192); write.writeFloat(16384); write.writeFloat(32767); write.writeFloat(-63); write.writeFloat(-64); write.writeFloat(-127); write.writeFloat(-128); write.writeFloat(-8192); write.writeFloat(-16384); write.writeFloat(-32768); assertEquals(1, write.writeFloat(0, 1000, true)); assertEquals(1, write.writeFloat(0, 1000, false)); assertEquals(3, write.writeFloat(63, 1000, true)); assertEquals(3, write.writeFloat(63, 1000, false)); assertEquals(3, write.writeFloat(64, 1000, true)); assertEquals(3, write.writeFloat(64, 1000, false)); assertEquals(3, write.writeFloat(127, 1000, true)); assertEquals(3, write.writeFloat(127, 1000, false)); assertEquals(3, write.writeFloat(128, 1000, true)); assertEquals(3, write.writeFloat(128, 1000, false)); assertEquals(4, write.writeFloat(8191, 1000, true)); assertEquals(4, write.writeFloat(8191, 1000, false)); assertEquals(4, write.writeFloat(8192, 1000, true)); assertEquals(4, write.writeFloat(8192, 1000, false)); assertEquals(4, write.writeFloat(16383, 1000, true)); assertEquals(4, write.writeFloat(16383, 1000, false)); assertEquals(4, write.writeFloat(16384, 1000, true)); assertEquals(4, write.writeFloat(16384, 1000, false)); assertEquals(4, write.writeFloat(32767, 1000, true)); assertEquals(4, write.writeFloat(32767, 1000, false)); assertEquals(3, write.writeFloat(-64, 1000, false)); assertEquals(5, write.writeFloat(-64, 1000, true)); assertEquals(3, write.writeFloat(-65, 1000, false)); assertEquals(5, write.writeFloat(-65, 1000, true)); assertEquals(4, write.writeFloat(-8192, 1000, false)); assertEquals(5, write.writeFloat(-8192, 1000, true)); Input read = new Input(write.toBytes()); assertEquals(read.readFloat(), 0f); assertEquals(read.readFloat(), 63f); assertEquals(read.readFloat(), 64f); assertEquals(read.readFloat(), 127f); assertEquals(read.readFloat(), 128f); assertEquals(read.readFloat(), 8192f); assertEquals(read.readFloat(), 16384f); assertEquals(read.readFloat(), 32767f); assertEquals(read.readFloat(), -63f); assertEquals(read.readFloat(), -64f); assertEquals(read.readFloat(), -127f); assertEquals(read.readFloat(), -128f); assertEquals(read.readFloat(), -8192f); assertEquals(read.readFloat(), -16384f); assertEquals(read.readFloat(), -32768f); assertEquals(read.readFloat(1000, true), 0f); assertEquals(read.readFloat(1000, false), 0f); assertEquals(read.readFloat(1000, true), 63f); assertEquals(read.readFloat(1000, false), 63f); assertEquals(read.readFloat(1000, true), 64f); assertEquals(read.readFloat(1000, false), 64f); assertEquals(read.readFloat(1000, true), 127f); assertEquals(read.readFloat(1000, false), 127f); assertEquals(read.readFloat(1000, true), 128f); assertEquals(read.readFloat(1000, false), 128f); assertEquals(read.readFloat(1000, true), 8191f); assertEquals(read.readFloat(1000, false), 8191f); assertEquals(read.readFloat(1000, true), 8192f); assertEquals(read.readFloat(1000, false), 8192f); assertEquals(read.readFloat(1000, true), 16383f); assertEquals(read.readFloat(1000, false), 16383f); assertEquals(read.readFloat(1000, true), 16384f); assertEquals(read.readFloat(1000, false), 16384f); assertEquals(read.readFloat(1000, true), 32767f); assertEquals(read.readFloat(1000, false), 32767f); assertEquals(read.readFloat(1000, false), -64f); assertEquals(read.readFloat(1000, true), -64f); assertEquals(read.readFloat(1000, false), -65f); assertEquals(read.readFloat(1000, true), -65f); assertEquals(read.readFloat(1000, false), -8192f); assertEquals(read.readFloat(1000, true), -8192f); } public void testDoubles () throws IOException { runDoubleTest(new Output(4096)); runDoubleTest(new Output(new ByteArrayOutputStream())); } private void runDoubleTest (Output write) throws IOException { write.writeDouble(0); write.writeDouble(63); write.writeDouble(64); write.writeDouble(127); write.writeDouble(128); write.writeDouble(8192); write.writeDouble(16384); write.writeDouble(32767); write.writeDouble(-63); write.writeDouble(-64); write.writeDouble(-127); write.writeDouble(-128); write.writeDouble(-8192); write.writeDouble(-16384); write.writeDouble(-32768); assertEquals(1, write.writeDouble(0, 1000, true)); assertEquals(1, write.writeDouble(0, 1000, false)); assertEquals(3, write.writeDouble(63, 1000, true)); assertEquals(3, write.writeDouble(63, 1000, false)); assertEquals(3, write.writeDouble(64, 1000, true)); assertEquals(3, write.writeDouble(64, 1000, false)); assertEquals(3, write.writeDouble(127, 1000, true)); assertEquals(3, write.writeDouble(127, 1000, false)); assertEquals(3, write.writeDouble(128, 1000, true)); assertEquals(3, write.writeDouble(128, 1000, false)); assertEquals(4, write.writeDouble(8191, 1000, true)); assertEquals(4, write.writeDouble(8191, 1000, false)); assertEquals(4, write.writeDouble(8192, 1000, true)); assertEquals(4, write.writeDouble(8192, 1000, false)); assertEquals(4, write.writeDouble(16383, 1000, true)); assertEquals(4, write.writeDouble(16383, 1000, false)); assertEquals(4, write.writeDouble(16384, 1000, true)); assertEquals(4, write.writeDouble(16384, 1000, false)); assertEquals(4, write.writeDouble(32767, 1000, true)); assertEquals(4, write.writeDouble(32767, 1000, false)); assertEquals(3, write.writeDouble(-64, 1000, false)); assertEquals(9, write.writeDouble(-64, 1000, true)); assertEquals(3, write.writeDouble(-65, 1000, false)); assertEquals(9, write.writeDouble(-65, 1000, true)); assertEquals(4, write.writeDouble(-8192, 1000, false)); assertEquals(9, write.writeDouble(-8192, 1000, true)); write.writeDouble(1.23456d); Input read = new Input(write.toBytes()); assertEquals(read.readDouble(), 0d); assertEquals(read.readDouble(), 63d); assertEquals(read.readDouble(), 64d); assertEquals(read.readDouble(), 127d); assertEquals(read.readDouble(), 128d); assertEquals(read.readDouble(), 8192d); assertEquals(read.readDouble(), 16384d); assertEquals(read.readDouble(), 32767d); assertEquals(read.readDouble(), -63d); assertEquals(read.readDouble(), -64d); assertEquals(read.readDouble(), -127d); assertEquals(read.readDouble(), -128d); assertEquals(read.readDouble(), -8192d); assertEquals(read.readDouble(), -16384d); assertEquals(read.readDouble(), -32768d); assertEquals(read.readDouble(1000, true), 0d); assertEquals(read.readDouble(1000, false), 0d); assertEquals(read.readDouble(1000, true), 63d); assertEquals(read.readDouble(1000, false), 63d); assertEquals(read.readDouble(1000, true), 64d); assertEquals(read.readDouble(1000, false), 64d); assertEquals(read.readDouble(1000, true), 127d); assertEquals(read.readDouble(1000, false), 127d); assertEquals(read.readDouble(1000, true), 128d); assertEquals(read.readDouble(1000, false), 128d); assertEquals(read.readDouble(1000, true), 8191d); assertEquals(read.readDouble(1000, false), 8191d); assertEquals(read.readDouble(1000, true), 8192d); assertEquals(read.readDouble(1000, false), 8192d); assertEquals(read.readDouble(1000, true), 16383d); assertEquals(read.readDouble(1000, false), 16383d); assertEquals(read.readDouble(1000, true), 16384d); assertEquals(read.readDouble(1000, false), 16384d); assertEquals(read.readDouble(1000, true), 32767d); assertEquals(read.readDouble(1000, false), 32767d); assertEquals(read.readDouble(1000, false), -64d); assertEquals(read.readDouble(1000, true), -64d); assertEquals(read.readDouble(1000, false), -65d); assertEquals(read.readDouble(1000, true), -65d); assertEquals(read.readDouble(1000, false), -8192d); assertEquals(read.readDouble(1000, true), -8192d); assertEquals(1.23456d, read.readDouble()); } public void testBooleans () throws IOException { runBooleanTest(new Output(4096)); runBooleanTest(new Output(new ByteArrayOutputStream())); } private void runBooleanTest (Output write) throws IOException { for (int i = 0; i < 100; i++) { write.writeBoolean(true); write.writeBoolean(false); } Input read = new Input(write.toBytes()); for (int i = 0; i < 100; i++) { assertEquals(true, read.readBoolean()); assertEquals(false, read.readBoolean()); } } public void testChars () throws IOException { runCharTest(new Output(4096)); runCharTest(new Output(new ByteArrayOutputStream())); } private void runCharTest (Output write) throws IOException { write.writeChar((char)0); write.writeChar((char)63); write.writeChar((char)64); write.writeChar((char)127); write.writeChar((char)128); write.writeChar((char)8192); write.writeChar((char)16384); write.writeChar((char)32767); write.writeChar((char)65535); Input read = new Input(write.toBytes()); assertEquals(0, read.readChar()); assertEquals(63, read.readChar()); assertEquals(64, read.readChar()); assertEquals(127, read.readChar()); assertEquals(128, read.readChar()); assertEquals(8192, read.readChar()); assertEquals(16384, read.readChar()); assertEquals(32767, read.readChar()); assertEquals(65535, read.readChar()); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/JavaSerializerTest.java000066400000000000000000000022351210575634700275130ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.io.Serializable; import com.esotericsoftware.kryo.serializers.JavaSerializer; /** @author Nathan Sweet */ public class JavaSerializerTest extends KryoTestCase { public void testJavaSerializer () { kryo.register(String.class, new JavaSerializer()); roundTrip(50, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); roundTrip(12, "meow"); kryo.register(TestClass.class, new JavaSerializer()); TestClass test = new TestClass(); test.stringField = "fubar"; test.intField = 54321; roundTrip(134, test); roundTrip(134, test); roundTrip(134, test); } static public class TestClass implements Serializable { String stringField; int intField; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestClass other = (TestClass)obj; if (intField != other.intField) return false; if (stringField == null) { if (other.stringField != null) return false; } else if (!stringField.equals(other.stringField)) return false; return true; } } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/KryoTestCase.java000066400000000000000000000074601210575634700263250ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.lang.reflect.Array; import java.util.ArrayList; import junit.framework.TestCase; import org.junit.Assert; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.minlog.Log; /** Convenience methods for round tripping objects. * @author Nathan Sweet */ abstract public class KryoTestCase extends TestCase { protected Kryo kryo; protected Output output; protected Input input; protected Object object1, object2; protected boolean supportsCopy; protected void setUp () throws Exception { Log.TRACE(); kryo = new Kryo(); kryo.setReferences(false); kryo.setRegistrationRequired(true); } public T roundTrip (int length, T object1) { this.object1 = object1; // Test output to stream, large buffer. ByteArrayOutputStream outStream = new ByteArrayOutputStream(); output = new Output(outStream, 4096); kryo.writeClassAndObject(output, object1); output.flush(); // Test input from stream, large buffer. input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096); object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); assertEquals("Incorrect number of bytes read.", length, input.total()); assertEquals("Incorrect number of bytes read.", length, output.total()); // Test output to stream, small buffer. outStream = new ByteArrayOutputStream(); output = new Output(outStream, 10); kryo.writeClassAndObject(output, object1); output.flush(); // Test input from stream, small buffer. input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 10); object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); assertEquals("Incorrect number of bytes read.", length, input.total()); if (object1 != null) { // Test null with serializer. Serializer serializer = kryo.getRegistration(object1.getClass()).getSerializer(); output.clear(); outStream.reset(); kryo.writeObjectOrNull(output, null, serializer); output.flush(); // Test null from byte array with and without serializer. input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 10); assertEquals(null, kryo.readObjectOrNull(input, object1.getClass(), serializer)); input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 10); assertEquals(null, kryo.readObjectOrNull(input, object1.getClass())); } // Test output to byte array. output = new Output(length * 2, -1); kryo.writeClassAndObject(output, object1); output.flush(); // Test input from byte array. input = new Input(output.toBytes()); object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); assertEquals("Incorrect length.", length, output.total()); assertEquals("Incorrect number of bytes read.", length, input.total()); input.rewind(); if (supportsCopy) { // Test copy. T copy = kryo.copy(object1); assertEquals(object1, copy); copy = kryo.copyShallow(object1); assertEquals(object1, copy); } return (T)object2; } static public void assertEquals (Object object1, Object object2) { Assert.assertEquals(arrayToList(object1), arrayToList(object2)); } static public Object arrayToList (Object array) { if (array == null || !array.getClass().isArray()) return array; ArrayList list = new ArrayList(Array.getLength(array)); for (int i = 0, n = Array.getLength(array); i < n; i++) list.add(arrayToList(Array.get(array, i))); return list; } static public ArrayList list (Object... items) { ArrayList list = new ArrayList(); for (Object item : items) list.add(item); return list; } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/MapSerializerTest.java000066400000000000000000000071461210575634700273550ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import junit.framework.Assert; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.MapSerializer; /** @author Nathan Sweet */ public class MapSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testMaps () { kryo.register(HashMap.class); kryo.register(LinkedHashMap.class); HashMap map = new HashMap(); map.put("123", "456"); map.put("789", "abc"); roundTrip(18, map); roundTrip(2, new LinkedHashMap()); roundTrip(18, new LinkedHashMap(map)); MapSerializer serializer = new MapSerializer(); kryo.register(HashMap.class, serializer); kryo.register(LinkedHashMap.class, serializer); serializer.setKeyClass(String.class, kryo.getSerializer(String.class)); serializer.setKeysCanBeNull(false); serializer.setValueClass(String.class, kryo.getSerializer(String.class)); roundTrip(14, map); serializer.setValuesCanBeNull(false); roundTrip(14, map); } public void testEmptyHashMap () { execute(new HashMap(), 0); } public void testNotEmptyHashMap () { execute(new HashMap(), 1000); } public void testEmptyConcurrentHashMap () { execute(new ConcurrentHashMap(), 0); } public void testNotEmptyConcurrentHashMap () { execute(new ConcurrentHashMap(), 1000); } public void testGenerics () { kryo.register(HasGenerics.class); kryo.register(Integer[].class); kryo.register(HashMap.class); HasGenerics test = new HasGenerics(); test.map.put("moo", new Integer[] {1, 2}); output = new Output(4096); kryo.writeClassAndObject(output, test); output.flush(); input = new Input(output.toBytes()); HasGenerics test2 = (HasGenerics)kryo.readClassAndObject(input); assertEquals(test.map.get("moo"), test2.map.get("moo")); } private void execute (Map map, int inserts) { Random random = new Random(); for (int i = 0; i < inserts; i++) map.put(random.nextLong(), random.nextBoolean()); Kryo kryo = new Kryo(); kryo.register(HashMap.class, new MapSerializer()); kryo.register(ConcurrentHashMap.class, new MapSerializer()); Output output = new Output(2048, -1); kryo.writeClassAndObject(output, map); output.close(); Input input = new Input(output.toBytes()); Object deserialized = kryo.readClassAndObject(input); input.close(); Assert.assertEquals(map, deserialized); } public void testTreeMap () { kryo.register(TreeMap.class); TreeMap map = new TreeMap(); map.put("123", "456"); map.put("789", "abc"); roundTrip(19, map); kryo.register(KeyThatIsntComparable.class); kryo.register(KeyComparator.class); map = new TreeMap(new KeyComparator()); KeyThatIsntComparable key = new KeyThatIsntComparable(); key.value = "123"; map.put(key, "456"); roundTrip(11, map); } static public class HasGenerics { public HashMap map = new HashMap(); public HashMap map2 = new HashMap(); } static public class KeyComparator implements Comparator { public int compare (KeyThatIsntComparable o1, KeyThatIsntComparable o2) { return o1.value.compareTo(o2.value); } } static public class KeyThatIsntComparable { public String value; } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/ReferenceTest.java000066400000000000000000000032711210575634700264770ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.util.Map; import java.util.TreeMap; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.MapSerializer; public class ReferenceTest extends KryoTestCase { static public class Ordering { public String order; } static public class Stuff extends TreeMap { public Ordering ordering; public Stuff (Ordering ordering) { this.ordering = ordering; } } public void testChildObjectBeforeReference () { Ordering ordering = new Ordering(); ordering.order = "assbackwards"; Stuff stuff = new Stuff(ordering); stuff.put("key", "value"); stuff.put("something", 456); stuff.put("self", stuff); Kryo kryo = new Kryo(); kryo.addDefaultSerializer(Stuff.class, new MapSerializer() { public void write (Kryo kryo, Output output, Map object) { kryo.writeObjectOrNull(output, ((Stuff)object).ordering, Ordering.class); super.write(kryo, output, object); } protected Map create (Kryo kryo, Input input, Class type) { Ordering ordering = kryo.readObjectOrNull(input, Ordering.class); return new Stuff(ordering); } }); Output output = new Output(512, -1); kryo.writeObject(output, stuff); Input input = new Input(output.getBuffer(), 0, output.position()); Stuff stuff2 = kryo.readObject(input, Stuff.class); assertEquals(stuff.ordering.order, stuff2.ordering.order); assertEquals(stuff.get("key"), stuff2.get("key")); assertEquals(stuff.get("something"), stuff2.get("something")); assertTrue(stuff.get("self") == stuff); assertTrue(stuff2.get("self") == stuff2); } } libkryo-java-2.20/java/test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java000066400000000000000000000046661210575634700310030ustar00rootroot00000000000000 package com.esotericsoftware.kryo; import java.io.FileNotFoundException; import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer; import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer.Tag; public class TaggedFieldSerializerTest extends KryoTestCase { { supportsCopy = true; } public void testTaggedFieldSerializer () throws FileNotFoundException { TestClass object1 = new TestClass(); object1.moo = 2; object1.child = new TestClass(); object1.child.moo = 5; object1.other = new AnotherClass(); object1.other.value = "meow"; object1.ignored = 32; kryo.setDefaultSerializer(TaggedFieldSerializer.class); kryo.register(TestClass.class); kryo.register(AnotherClass.class); TestClass object2 = roundTrip(57, object1); assertTrue(object2.ignored == 0); } public void testAddedField () throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); object1.other = new AnotherClass(); object1.other.value = "meow"; TaggedFieldSerializer serializer = new TaggedFieldSerializer(kryo, TestClass.class); serializer.removeField("text"); kryo.register(TestClass.class, serializer); kryo.register(AnotherClass.class, new TaggedFieldSerializer(kryo, AnotherClass.class)); roundTrip(39, object1); kryo.register(TestClass.class, new TaggedFieldSerializer(kryo, TestClass.class)); Object object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); } static public class TestClass { @Tag(0) public String text = "something"; @Tag(1) public int moo = 120; @Tag(2) public long moo2 = 1234120; @Tag(3) public TestClass child; @Tag(4) public int zzz = 123; @Tag(5) public AnotherClass other; @Tag(6) @Deprecated public int ignored; public boolean equals (Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestClass other = (TestClass)obj; if (child == null) { if (other.child != null) return false; } else if (!child.equals(other.child)) return false; if (moo != other.moo) return false; if (moo2 != other.moo2) return false; if (text == null) { if (other.text != null) return false; } else if (!text.equals(other.text)) return false; if (zzz != other.zzz) return false; return true; } } static public class AnotherClass { @Tag(1) String value; } }