├── .classpath ├── .project ├── .settings ├── org.eclipse.jdt.core.prefs └── org.eclipse.jdt.ui.prefs ├── build ├── junit-4.6.jar ├── minlog-1.2.jar └── minlog-none-1.2.jar ├── lib ├── asm-4.0.jar ├── minlog-1.2.jar ├── objenesis-1.2.jar └── reflectasm-1.07.jar ├── license.txt ├── pom.xml ├── project.yaml ├── src └── com │ └── esotericsoftware │ └── kryo │ ├── ClassResolver.java │ ├── DefaultSerializer.java │ ├── Generics.java │ ├── Kryo.java │ ├── KryoCopyable.java │ ├── KryoException.java │ ├── KryoSerializable.java │ ├── NotNull.java │ ├── ReferenceResolver.java │ ├── Registration.java │ ├── Serializer.java │ ├── StreamFactory.java │ ├── factories │ ├── PseudoSerializerFactory.java │ ├── ReflectionSerializerFactory.java │ └── SerializerFactory.java │ ├── io │ ├── ByteBufferInput.java │ ├── ByteBufferInputStream.java │ ├── ByteBufferOutput.java │ ├── ByteBufferOutputStream.java │ ├── FastInput.java │ ├── FastOutput.java │ ├── Input.java │ ├── InputChunked.java │ ├── Output.java │ ├── OutputChunked.java │ ├── UnsafeInput.java │ ├── UnsafeMemoryInput.java │ ├── UnsafeMemoryOutput.java │ └── UnsafeOutput.java │ ├── serializers │ ├── AsmCacheFields.java │ ├── AsmCachedFieldFactory.java │ ├── BeanSerializer.java │ ├── BlowfishSerializer.java │ ├── CollectionSerializer.java │ ├── CompatibleFieldSerializer.java │ ├── DefaultArraySerializers.java │ ├── DefaultSerializers.java │ ├── DeflateSerializer.java │ ├── FieldSerializer.java │ ├── FieldSerializerGenericsUtil.java │ ├── FieldSerializerUnsafeUtil.java │ ├── JavaSerializer.java │ ├── MapSerializer.java │ ├── ObjectCachedFieldFactory.java │ ├── ObjectField.java │ ├── TaggedFieldSerializer.java │ ├── UnsafeCacheFields.java │ └── UnsafeCachedFieldFactory.java │ └── util │ ├── DefaultClassResolver.java │ ├── DefaultStreamFactory.java │ ├── FastestStreamFactory.java │ ├── IdentityMap.java │ ├── IdentityObjectIntMap.java │ ├── IntArray.java │ ├── IntMap.java │ ├── ListReferenceResolver.java │ ├── MapReferenceResolver.java │ ├── ObjectMap.java │ ├── UnsafeUtil.java │ └── Util.java └── test └── com └── esotericsoftware └── kryo ├── ArraySerializerTest.java ├── BeanSerializerTest.java ├── BlowfishSerializerTest.java ├── ChunkedTest.java ├── CollectionSerializerTest.java ├── CompatibleFieldSerializerTest.java ├── CopyTest.java ├── DefaultSerializersTest.java ├── DeflateSerializerTest.java ├── FieldSerializerTest.java ├── InputOutputTest.java ├── JavaSerializerTest.java ├── KryoTestCase.java ├── MapSerializerTest.java ├── ReferenceTest.java ├── SerializationBenchmarkTest.java ├── TaggedFieldSerializerTest.java ├── UnsafeInputOutputTest.java └── UnsafeMemoryInputOutputTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | kryo2 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | formatter_profile=_libgdx 3 | formatter_settings_version=12 4 | -------------------------------------------------------------------------------- /build/junit-4.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/build/junit-4.6.jar -------------------------------------------------------------------------------- /build/minlog-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/build/minlog-1.2.jar -------------------------------------------------------------------------------- /build/minlog-none-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/build/minlog-none-1.2.jar -------------------------------------------------------------------------------- /lib/asm-4.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/lib/asm-4.0.jar -------------------------------------------------------------------------------- /lib/minlog-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/lib/minlog-1.2.jar -------------------------------------------------------------------------------- /lib/objenesis-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/lib/objenesis-1.2.jar -------------------------------------------------------------------------------- /lib/reflectasm-1.07.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svn2github/kryo/5735e1b339ae6947050872a60c67b575238d221d/lib/reflectasm-1.07.jar -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, Nathan Sweet 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * 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. 8 | * 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. 9 | 10 | 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. 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | com.esotericsoftware.kryo 12 | kryo 13 | 2.23-SNAPSHOT 14 | bundle 15 | Kryo 16 | Fast, efficient Java serialization 17 | http://code.google.com/p/kryo/ 18 | 19 | 20 | 21 | New BSD License 22 | http://www.opensource.org/licenses/bsd-license.php 23 | repo 24 | 25 | 26 | 27 | 28 | http://kryo.googlecode.com/svn/ 29 | scm:svn:http://kryo.googlecode.com/svn/ 30 | 31 | 32 | 33 | 34 | nathan.sweet 35 | Nathan Sweet 36 | nathan.sweet@gmail.com 37 | 38 | 39 | 40 | 41 | UTF-8 42 | 43 | 44 | 45 | 46 | com.esotericsoftware.reflectasm 47 | reflectasm 48 | 1.07 49 | shaded 50 | 51 | 52 | org.ow2.asm 53 | asm 54 | 55 | 56 | 57 | 58 | com.esotericsoftware.minlog 59 | minlog 60 | 1.2 61 | 62 | 63 | org.objenesis 64 | objenesis 65 | 1.2 66 | 67 | 68 | junit 69 | junit 70 | 4.8.2 71 | test 72 | 73 | 74 | 75 | 76 | 77 | src 78 | test 79 | 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-compiler-plugin 85 | true 86 | 87 | 1.5 88 | 1.5 89 | utf-8 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | maven-resources-plugin 98 | 2.5 99 | 100 | 101 | default-resources 102 | none 103 | 104 | 105 | default-testResources 106 | none 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-jar-plugin 113 | 2.4 114 | 115 | 116 | **/.svn/* 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-shade-plugin 123 | 2.1 124 | 125 | 126 | true 127 | false 128 | 129 | 130 | com.esotericsoftware.reflectasm:reflectasm:shaded 131 | com.esotericsoftware.minlog:minlog 132 | org.objenesis:objenesis 133 | 134 | 135 | 136 | 137 | org.objenesis 138 | com.esotericsoftware.shaded.org.objenesis 139 | 140 | 141 | 142 | 143 | 144 | package 145 | 146 | shade 147 | 148 | 149 | 150 | 151 | 152 | org.apache.felix 153 | maven-bundle-plugin 154 | true 155 | 156 | 157 | 164 | 165 | com.esotericsoftware*, org.objenesis* 166 | *;scope=compile|runtime 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | requireSnapshot 176 | 178 | 179 | 180 | 181 | 182 | maven-enforcer-plugin 183 | 184 | 185 | 186 | enforce 187 | 188 | 189 | 190 | 191 | "${project.version}".endsWith("-SNAPSHOT") 192 | Jenkins should only build -SNAPSHOT versions 193 | 194 | 195 | true 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | sonatype-releases 208 | sonatype releases repo 209 | https://oss.sonatype.org/content/repositories/releases 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /project.yaml: -------------------------------------------------------------------------------- 1 | name: kryo 2 | version: 2.22 3 | --- 4 | Build.build(project); 5 | Build.oneJAR(project); -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/ClassResolver.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | 7 | /** Handles class registration, writing class identifiers to bytes, and reading class identifiers from bytes. 8 | * @author Nathan Sweet */ 9 | public interface ClassResolver { 10 | /** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */ 11 | public void setKryo (Kryo kryo); 12 | 13 | /** Stores the specified registration. 14 | * @see Kryo#register(Registration) */ 15 | public Registration register (Registration registration); 16 | 17 | /** Called when an unregistered type is encountered and {@link Kryo#setRegistrationRequired(boolean)} is false. */ 18 | public Registration registerImplicit (Class type); 19 | 20 | /** Returns the registration for the specified class, or null if the class is not registered. */ 21 | public Registration getRegistration (Class type); 22 | 23 | /** Returns the registration for the specified ID, or null if no class is registered with that ID. */ 24 | public Registration getRegistration (int classID); 25 | 26 | /** Writes a class and returns its registration. 27 | * @param type May be null. 28 | * @return Will be null if type is null. */ 29 | public Registration writeClass (Output output, Class type); 30 | 31 | /** Reads a class and returns its registration. 32 | * @return May be null. */ 33 | public Registration readClass (Input input); 34 | 35 | /** Called by {@link Kryo#reset()}. */ 36 | public void reset (); 37 | } 38 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/DefaultSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** Sets the default serializer to use for the annotated class. The specified Serializer class must have a constructor taking a 10 | * Kryo instance and a class, a Kryo instance, a class, or no arguments. 11 | * @see Kryo#register(Class) 12 | * @author Nathan Sweet */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | public @interface DefaultSerializer { 16 | Class value(); 17 | } 18 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/Generics.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo; 2 | 3 | import static com.esotericsoftware.minlog.Log.TRACE; 4 | import static com.esotericsoftware.minlog.Log.trace; 5 | 6 | import java.lang.reflect.Array; 7 | import java.lang.reflect.GenericArrayType; 8 | import java.lang.reflect.ParameterizedType; 9 | import java.lang.reflect.Type; 10 | import java.lang.reflect.TypeVariable; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /*** 15 | * Helper class to map type name variables to concrete classes that are used during instantiation 16 | 17 | * @author Roman Levenstein 18 | * 19 | */ 20 | public class Generics { 21 | private Map typeVar2class; 22 | 23 | private Generics parentScope; 24 | 25 | public Generics() { 26 | typeVar2class = new HashMap(); 27 | parentScope = null; 28 | } 29 | 30 | public Generics(Map mappings) { 31 | typeVar2class = new HashMap(mappings); 32 | parentScope = null; 33 | } 34 | 35 | public Generics(Generics parentScope) { 36 | typeVar2class = new HashMap(); 37 | this.parentScope = parentScope; 38 | } 39 | 40 | public void add(String typeVar, Class clazz) { 41 | typeVar2class.put(typeVar, clazz); 42 | } 43 | 44 | public Class getConcreteClass(String typeVar) { 45 | Class clazz = typeVar2class.get(typeVar); 46 | if(clazz == null && parentScope != null) 47 | return parentScope.getConcreteClass(typeVar); 48 | return clazz; 49 | } 50 | 51 | public void setParentScope(Generics scope) { 52 | if(parentScope != null) 53 | throw new RuntimeException("Parent scope can be set just once"); 54 | parentScope = scope; 55 | } 56 | 57 | public Generics getParentScope() { 58 | return parentScope; 59 | } 60 | 61 | public String toString() { 62 | return typeVar2class.toString(); 63 | } 64 | 65 | public void resetParentScope() { 66 | parentScope = null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/KryoCopyable.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | /** Allows implementing classes to perform their own copying. Hand written copying can be more efficient in some cases. 5 | *

6 | * This method is used instead of the registered serializer {@link Serializer#copy(Kryo, Object)} method. 7 | * @author Nathan Sweet */ 8 | public interface KryoCopyable { 9 | /** Returns a copy that has the same values as this object. Before Kryo can be used to copy child objects, 10 | * {@link Kryo#reference(Object)} must be called with the copy to ensure it can be referenced by the child objects. 11 | * @see Serializer#copy(Kryo, Object) */ 12 | public T copy (Kryo kryo); 13 | } 14 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/KryoException.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | /** General Kryo RuntimeException. 5 | * @author Nathan Sweet */ 6 | public class KryoException extends RuntimeException { 7 | private StringBuffer trace; 8 | 9 | public KryoException () { 10 | super(); 11 | } 12 | 13 | public KryoException (String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public KryoException (String message) { 18 | super(message); 19 | } 20 | 21 | public KryoException (Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public String getMessage () { 26 | if (trace == null) return super.getMessage(); 27 | StringBuffer buffer = new StringBuffer(512); 28 | buffer.append(super.getMessage()); 29 | if (buffer.length() > 0) buffer.append('\n'); 30 | buffer.append("Serialization trace:"); 31 | buffer.append(trace); 32 | return buffer.toString(); 33 | } 34 | 35 | /** Adds information to the exception message about where in the the object graph serialization failure occurred. 36 | * {@link Serializer Serializers} can catch {@link KryoException}, add trace information, and rethrow the exception. */ 37 | public void addTrace (String info) { 38 | if (info == null) throw new IllegalArgumentException("info cannot be null."); 39 | if (trace == null) trace = new StringBuffer(512); 40 | trace.append('\n'); 41 | trace.append(info); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/KryoSerializable.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.esotericsoftware.kryo.serializers.DefaultSerializers.KryoSerializableSerializer; 7 | 8 | /** Allows implementing classes to perform their own serialization. Hand written serialization can be more efficient in some cases. 9 | *

10 | * The default serializer for KryoSerializable is {@link KryoSerializableSerializer}, which uses {@link Kryo#newInstance(Class)} 11 | * to construct the class. 12 | * @author Nathan Sweet */ 13 | public interface KryoSerializable { 14 | public void write (Kryo kryo, Output output); 15 | 16 | public void read (Kryo kryo, Input input); 17 | } 18 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/NotNull.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | import com.esotericsoftware.kryo.serializers.FieldSerializer; 10 | 11 | /** Indicates a field can never be null when it is being serialized and deserialized. Some serializers use this to save space. Eg, 12 | * {@link FieldSerializer} may save 1 byte per field. 13 | * @author Nathan Sweet */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface NotNull { 17 | } 18 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/ReferenceResolver.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | /** When references are enabled, this tracks objects that have already been read or written, provides an ID for objects that are 5 | * written, and looks up by ID objects that have been read. 6 | * @author Nathan Sweet */ 7 | public interface ReferenceResolver { 8 | /** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */ 9 | public void setKryo (Kryo kryo); 10 | 11 | /** Returns an ID for the object if it has been written previously, otherwise returns -1. */ 12 | public int getWrittenId (Object object); 13 | 14 | /** Returns a new ID for an object that is being written for the first time. 15 | * @return The ID, which is stored more efficiently if it is positive and must not be -1 or -2. */ 16 | public int addWrittenObject (Object object); 17 | 18 | /** Reserves the ID for the next object that will be read. This is called only the first time an object is encountered. 19 | * @param type The type of object that will be read. 20 | * @return The ID, which is stored more efficiently if it is positive and must not be -1 or -2. */ 21 | public int nextReadId (Class type); 22 | 23 | /** Sets the ID for an object that has been read. 24 | * @param id The ID from {@link #nextReadId(Class)}. */ 25 | public void setReadObject (int id, Object object); 26 | 27 | /** Returns the object for the specified ID. The ID and object are guaranteed to have been previously passed in a call to 28 | * {@link #setReadObject(int, Object)}. */ 29 | public Object getReadObject (Class type, int id); 30 | 31 | /** Called by {@link Kryo#reset()}. */ 32 | public void reset (); 33 | 34 | /** Returns true if references will be written for the specified type. 35 | * @param type Will never be a primitive type, but may be a primitive type wrapper. */ 36 | public boolean useReferences (Class type); 37 | } 38 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/Registration.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import org.objenesis.instantiator.ObjectInstantiator; 5 | 6 | import static com.esotericsoftware.kryo.util.Util.*; 7 | import static com.esotericsoftware.minlog.Log.*; 8 | 9 | /** Describes the {@link Serializer} and class ID to use for a class. 10 | * @author Nathan Sweet */ 11 | public class Registration { 12 | private final Class type; 13 | private final int id; 14 | private Serializer serializer; 15 | private ObjectInstantiator instantiator; 16 | 17 | public Registration (Class type, Serializer serializer, int id) { 18 | if (type == null) throw new IllegalArgumentException("type cannot be null."); 19 | if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); 20 | this.type = type; 21 | this.serializer = serializer; 22 | this.id = id; 23 | } 24 | 25 | public Class getType () { 26 | return type; 27 | } 28 | 29 | /** Returns the registered class ID. 30 | * @see Kryo#register(Class) */ 31 | public int getId () { 32 | return id; 33 | } 34 | 35 | public Serializer getSerializer () { 36 | return serializer; 37 | } 38 | 39 | public void setSerializer (Serializer serializer) { 40 | if (serializer == null) throw new IllegalArgumentException("serializer cannot be null."); 41 | this.serializer = serializer; 42 | if (TRACE) trace("kryo", "Update registered serializer: " + type.getName() + " (" + serializer.getClass().getName() + ")"); 43 | } 44 | 45 | /** @return May be null if not yet set. */ 46 | public ObjectInstantiator getInstantiator () { 47 | return instantiator; 48 | } 49 | 50 | /** Sets the instantiator that will create a new instance of the type in {@link Kryo#newInstance(Class)}. */ 51 | public void setInstantiator (ObjectInstantiator instantiator) { 52 | if (instantiator == null) throw new IllegalArgumentException("instantiator cannot be null."); 53 | this.instantiator = instantiator; 54 | } 55 | 56 | public String toString () { 57 | return "[" + id + ", " + className(type) + "]"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/Serializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | 7 | /** Reads and writes objects to and from bytes. 8 | * @author Nathan Sweet */ 9 | public abstract class Serializer { 10 | private boolean acceptsNull, immutable; 11 | 12 | public Serializer () { 13 | } 14 | 15 | /** @see #setAcceptsNull(boolean) */ 16 | public Serializer (boolean acceptsNull) { 17 | this.acceptsNull = acceptsNull; 18 | } 19 | 20 | /** @see #setAcceptsNull(boolean) 21 | * @see #setImmutable(boolean) */ 22 | public Serializer (boolean acceptsNull, boolean immutable) { 23 | this.acceptsNull = acceptsNull; 24 | this.immutable = immutable; 25 | } 26 | 27 | /** Writes the bytes for the object to the output. 28 | *

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

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

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

52 | * This can be set to true on a serializer that does not accept nulls if it is known that the serializer will never encounter 53 | * null. Doing this will prevent the framework from writing a byte to denote null. */ 54 | public void setAcceptsNull (boolean acceptsNull) { 55 | this.acceptsNull = acceptsNull; 56 | } 57 | 58 | public boolean isImmutable () { 59 | return immutable; 60 | } 61 | 62 | /** If true, the type this serializer will be used for is considered immutable. This causes {@link #copy(Kryo, Object)} to 63 | * return the original object. */ 64 | public void setImmutable (boolean immutable) { 65 | this.immutable = immutable; 66 | } 67 | 68 | /** Sets the generic types of the field or method this serializer will be used for on the next call to read or write. Subsequent 69 | * calls to read and write must not use this generic type information. The default implementation does nothing. Subclasses may 70 | * use the information provided to this method for more efficient serialization, eg to use the same type for all items in a 71 | * list. 72 | * @param generics Some (but never all) elements may be null if there is no generic type information at that index. */ 73 | public void setGenerics (Kryo kryo, Class[] generics) { 74 | } 75 | 76 | /** Returns a copy of the specified object. The default implementation returns the original if {@link #isImmutable()} is true, 77 | * else throws {@link KryoException}. Subclasses should override this method if needed to support {@link Kryo#copy(Object)}. 78 | *

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

82 | * This method should not be called directly, instead this serializer can be passed to {@link Kryo} copy methods that accept a 83 | * serialier. */ 84 | public T copy (Kryo kryo, T original) { 85 | if (immutable) return original; 86 | throw new KryoException("Serializer does not support copy: " + getClass().getName()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/StreamFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | import com.esotericsoftware.kryo.io.Input; 7 | import com.esotericsoftware.kryo.io.Output; 8 | 9 | /** Provides input and output streams based on system settings. 10 | * @author Roman Levenstein */ 11 | public interface StreamFactory { 12 | 13 | /** Creates an uninitialized Input. */ 14 | public Input getInput (); 15 | 16 | /** Creates a new Input for reading from a byte array. 17 | * @param bufferSize The size of the buffer. An exception is thrown if more bytes than this are read. */ 18 | public Input getInput(int bufferSize); 19 | 20 | /** Creates a new Input for reading from a byte array. 21 | * @param buffer An exception is thrown if more bytes than this are read. */ 22 | public Input getInput(byte[] buffer) ; 23 | 24 | /** Creates a new Input for reading from a byte array. 25 | * @param buffer An exception is thrown if more bytes than this are read. */ 26 | public Input getInput(byte[] buffer, int offset, int count) ; 27 | 28 | /** Creates a new Input for reading from an InputStream with a buffer size of 4096. */ 29 | public Input getInput(InputStream inputStream); 30 | 31 | /** Creates a new Input for reading from an InputStream. */ 32 | public Input getInput(InputStream inputStream, int bufferSize); 33 | 34 | /** Creates an uninitialized Output. {@link Output#setBuffer(byte[], int)} must be called before the Output is used. */ 35 | public Output getOutput(); 36 | 37 | /** Creates a new Output for writing to a byte array. 38 | * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ 39 | public Output getOutput(int bufferSize); 40 | 41 | /** Creates a new Output for writing to a byte array. 42 | * @param bufferSize The initial size of the buffer. 43 | * @param maxBufferSize The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown. Can be -1 44 | * for no maximum. */ 45 | public Output getOutput(int bufferSize, int maxBufferSize); 46 | 47 | /** Creates a new Output for writing to a byte array. 48 | * @see Output#setBuffer(byte[]) */ 49 | public Output getOutput(byte[] buffer); 50 | 51 | /** Creates a new Output for writing to a byte array. 52 | * @see Output#setBuffer(byte[], int) */ 53 | public Output getOutput(byte[] buffer, int maxBufferSize); 54 | 55 | /** Creates a new Output for writing to an OutputStream. A buffer size of 4096 is used. */ 56 | public Output getOutput(OutputStream outputStream); 57 | 58 | /** Creates a new Output for writing to an OutputStream. */ 59 | public Output getOutput(OutputStream outputStream, int bufferSize); 60 | 61 | public void setKryo(Kryo kryo); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/factories/PseudoSerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.factories; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | 6 | /** 7 | * A serializer factory that always returns a given serializer instance. This implementation of {@link SerializerFactory} 8 | * is not a real factory since it only provides a given instance instead of dynamically creating new serializers. It can 9 | * be used when all types should be serialized by the same serializer. This also allows serializers to be shared among different 10 | * {@link Kryo} instances. 11 | * 12 | * @author Rafael Winterhalter 13 | */ 14 | public class PseudoSerializerFactory implements SerializerFactory { 15 | 16 | private final Serializer serializer; 17 | 18 | public PseudoSerializerFactory (Serializer serializer) { 19 | this.serializer = serializer; 20 | } 21 | 22 | @Override 23 | public Serializer makeSerializer (Kryo kryo, Class type) { 24 | return serializer; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/factories/ReflectionSerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.factories; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | 6 | import static com.esotericsoftware.kryo.util.Util.className; 7 | 8 | /** 9 | * This factory instantiates new serializers of a given class via reflection. The constructors of the given {@code serializerClass} 10 | * must either take an instance of {@link Kryo} and an instance of {@link Class} as its parameter, take only a {@link Kryo} or {@link Class} 11 | * as its only argument or take no arguments. If several of the described constructors are found, the first found constructor is used, 12 | * in the order as they were just described. 13 | * 14 | * @author Rafael Winterhalter 15 | */ 16 | public class ReflectionSerializerFactory implements SerializerFactory { 17 | 18 | private final Class serializerClass; 19 | 20 | public ReflectionSerializerFactory (Class serializerClass) { 21 | this.serializerClass = serializerClass; 22 | } 23 | 24 | @Override 25 | public Serializer makeSerializer (Kryo kryo, Class type) { 26 | return makeSerializer(kryo, serializerClass, type); 27 | } 28 | 29 | /** Creates a new instance of the specified serializer for serializing the specified class. Serializers must have a zero 30 | * argument constructor or one that takes (Kryo), (Class), or (Kryo, Class). 31 | */ 32 | public static Serializer makeSerializer (Kryo kryo, Class serializerClass, Class type) { 33 | try { 34 | try { 35 | return serializerClass.getConstructor(Kryo.class, Class.class).newInstance(kryo, type); 36 | } catch (NoSuchMethodException ex1) { 37 | try { 38 | return serializerClass.getConstructor(Kryo.class).newInstance(kryo); 39 | } catch (NoSuchMethodException ex2) { 40 | try { 41 | return serializerClass.getConstructor(Class.class).newInstance(type); 42 | } catch (NoSuchMethodException ex3) { 43 | return serializerClass.newInstance(); 44 | } 45 | } 46 | } 47 | } catch (Exception ex) { 48 | throw new IllegalArgumentException("Unable to create serializer \"" + serializerClass.getName() + "\" for class: " + className(type), ex); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/factories/SerializerFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.factories; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.Serializer; 5 | 6 | /** 7 | * A serializer factory that allows the creation of serializers. This factory will be called when a {@link Kryo} 8 | * serializer discovers a new type for which no serializer is yet known. For example, when a factory is registered 9 | * via {@link Kryo#setDefaultSerializer(SerializerFactory)} a different serializer can be created dependent on the 10 | * type of a class. 11 | * 12 | * @author Rafael Winterhalter 13 | */ 14 | public interface SerializerFactory { 15 | 16 | /** 17 | * Creates a new serializer 18 | * @param kryo The serializer instance requesting the new serializer. 19 | * @param type The type of the object that is to be serialized. 20 | * @return An implementation of a serializer that is able to serialize an object of type {@code type}. 21 | */ 22 | Serializer makeSerializer (Kryo kryo, Class type); 23 | } 24 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.ByteBuffer; 7 | 8 | /** An InputStream whose source is a {@link ByteBuffer}. 9 | * @author Nathan Sweet */ 10 | public class ByteBufferInputStream extends InputStream { 11 | private ByteBuffer byteBuffer; 12 | 13 | public ByteBufferInputStream () { 14 | } 15 | 16 | /** Creates a stream with a new non-direct buffer of the specified size. The position and limit of the buffer is zero. */ 17 | public ByteBufferInputStream (int bufferSize) { 18 | this(ByteBuffer.allocate(bufferSize)); 19 | byteBuffer.flip(); 20 | } 21 | 22 | /** Creates an uninitialized stream that cannot be used until {@link #setByteBuffer(ByteBuffer)} is called. */ 23 | public ByteBufferInputStream (ByteBuffer byteBuffer) { 24 | this.byteBuffer = byteBuffer; 25 | } 26 | 27 | public ByteBuffer getByteBuffer () { 28 | return byteBuffer; 29 | } 30 | 31 | public void setByteBuffer (ByteBuffer byteBuffer) { 32 | this.byteBuffer = byteBuffer; 33 | } 34 | 35 | public int read () throws IOException { 36 | if (!byteBuffer.hasRemaining()) return -1; 37 | return byteBuffer.get(); 38 | } 39 | 40 | public int read (byte[] bytes, int offset, int length) throws IOException { 41 | int count = Math.min(byteBuffer.remaining(), length); 42 | if (count == 0) return -1; 43 | byteBuffer.get(bytes, offset, count); 44 | return count; 45 | } 46 | 47 | public int available () throws IOException { 48 | return byteBuffer.remaining(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/ByteBufferOutputStream.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.nio.ByteBuffer; 7 | 8 | /** An OutputStream whose target is a {@link ByteBuffer}. If bytes would be written that would overflow the buffer, 9 | * {@link #flush()} is called. Subclasses can override flush to empty the buffer. 10 | * @author Nathan Sweet */ 11 | public class ByteBufferOutputStream extends OutputStream { 12 | private ByteBuffer byteBuffer; 13 | 14 | /** Creates an uninitialized stream that cannot be used until {@link #setByteBuffer(ByteBuffer)} is called. */ 15 | public ByteBufferOutputStream () { 16 | } 17 | 18 | /** Creates a stream with a new non-direct buffer of the specified size. */ 19 | public ByteBufferOutputStream (int bufferSize) { 20 | this(ByteBuffer.allocate(bufferSize)); 21 | } 22 | 23 | public ByteBufferOutputStream (ByteBuffer byteBuffer) { 24 | this.byteBuffer = byteBuffer; 25 | } 26 | 27 | public ByteBuffer getByteBuffer () { 28 | return byteBuffer; 29 | } 30 | 31 | public void setByteBuffer (ByteBuffer byteBuffer) { 32 | this.byteBuffer = byteBuffer; 33 | } 34 | 35 | public void write (int b) throws IOException { 36 | if (!byteBuffer.hasRemaining()) flush(); 37 | byteBuffer.put((byte)b); 38 | } 39 | 40 | public void write (byte[] bytes, int offset, int length) throws IOException { 41 | if (byteBuffer.remaining() < length) flush(); 42 | byteBuffer.put(bytes, offset, length); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/FastInput.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | import sun.misc.Unsafe; 9 | 10 | import com.esotericsoftware.kryo.KryoException; 11 | 12 | /** Same as Input, but does not use variable length encoding for integer types. 13 | * @author Roman Levenstein */ 14 | public final class FastInput extends Input { 15 | 16 | /** Creates an uninitialized Output. {@link #setBuffer(byte[], int, int)} must be called before the Output is used. */ 17 | public FastInput () { 18 | } 19 | 20 | /** Creates a new Output for writing to a byte array. 21 | * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ 22 | public FastInput (int bufferSize) { 23 | super(bufferSize); 24 | } 25 | 26 | /** Creates a new Output for writing to a byte array. 27 | * @see #setBuffer(byte[]) */ 28 | public FastInput (byte[] buffer) { 29 | super(buffer); 30 | } 31 | 32 | /** Creates a new Output for writing to a byte array. 33 | * @see #setBuffer(byte[], int, int) */ 34 | public FastInput (byte[] buffer, int offset, int count) { 35 | super(buffer, offset, count); 36 | } 37 | 38 | /** Creates a new Output for writing to an OutputStream. A buffer size of 4096 is used. */ 39 | public FastInput (InputStream outputStream) { 40 | super(outputStream); 41 | } 42 | 43 | /** Creates a new Output for writing to an OutputStream. */ 44 | public FastInput (InputStream outputStream, int bufferSize) { 45 | super(outputStream, bufferSize); 46 | } 47 | 48 | public int readInt (boolean optimizePositive) throws KryoException { 49 | return readInt(); 50 | } 51 | 52 | public long readLong (boolean optimizePositive) throws KryoException { 53 | return readLong(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/FastOutput.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.DataOutput; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | 8 | import sun.misc.Unsafe; 9 | 10 | import com.esotericsoftware.kryo.KryoException; 11 | 12 | /** Same as Output, but does not use variable length encoding for integer types. 13 | * @author Roman Levenstein */ 14 | public final class FastOutput extends Output { 15 | 16 | /** Creates an uninitialized Output. {@link #setBuffer(byte[], int)} must be called before the Output is used. */ 17 | public FastOutput () { 18 | } 19 | 20 | /** Creates a new Output for writing to a byte array. 21 | * 22 | * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ 23 | public FastOutput (int bufferSize) { 24 | this(bufferSize, bufferSize); 25 | } 26 | 27 | /** Creates a new Output for writing to a byte array. 28 | * 29 | * @param bufferSize The initial size of the buffer. 30 | * @param maxBufferSize The buffer is doubled as needed until it exceeds maxBufferSize and an exception is thrown. */ 31 | public FastOutput (int bufferSize, int maxBufferSize) { 32 | super(bufferSize, maxBufferSize); 33 | } 34 | 35 | /** Creates a new Output for writing to a byte array. 36 | * 37 | * @see #setBuffer(byte[]) */ 38 | public FastOutput (byte[] buffer) { 39 | this(buffer, buffer.length); 40 | } 41 | 42 | /** Creates a new Output for writing to a byte array. 43 | * 44 | * @see #setBuffer(byte[], int) */ 45 | public FastOutput (byte[] buffer, int maxBufferSize) { 46 | super(buffer, maxBufferSize); 47 | } 48 | 49 | /** Creates a new Output for writing to an OutputStream. A buffer size of 4096 is used. */ 50 | public FastOutput (OutputStream outputStream) { 51 | super(outputStream); 52 | } 53 | 54 | /** Creates a new Output for writing to an OutputStream. */ 55 | public FastOutput (OutputStream outputStream, int bufferSize) { 56 | super(outputStream, bufferSize); 57 | } 58 | 59 | public int writeInt (int value, boolean optimizePositive) throws KryoException { 60 | writeInt(value); 61 | return 4; 62 | } 63 | 64 | public int writeLong (long value, boolean optimizePositive) throws KryoException { 65 | writeLong(value); 66 | return 8; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/InputChunked.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import com.esotericsoftware.kryo.KryoException; 8 | 9 | import static com.esotericsoftware.minlog.Log.*; 10 | 11 | /** An InputStream that reads lengths and chunks of data from another OutputStream, allowing chunks to be skipped. 12 | * @author Nathan Sweet */ 13 | public class InputChunked extends Input { 14 | private int chunkSize = -1; 15 | 16 | /** Creates an uninitialized InputChunked with a buffer size of 2048. The InputStream must be set before it can be used. */ 17 | public InputChunked () { 18 | super(2048); 19 | } 20 | 21 | /** Creates an uninitialized InputChunked. The InputStream must be set before it can be used. */ 22 | public InputChunked (int bufferSize) { 23 | super(bufferSize); 24 | } 25 | 26 | /** Creates an InputChunked with a buffer size of 2048. */ 27 | public InputChunked (InputStream inputStream) { 28 | super(inputStream, 2048); 29 | } 30 | 31 | public InputChunked (InputStream inputStream, int bufferSize) { 32 | super(inputStream, bufferSize); 33 | } 34 | 35 | public void setInputStream (InputStream inputStream) { 36 | super.setInputStream(inputStream); 37 | chunkSize = -1; 38 | } 39 | 40 | public void setBuffer (byte[] bytes, int offset, int count) { 41 | super.setBuffer(bytes, offset, count); 42 | chunkSize = -1; 43 | } 44 | 45 | public void rewind () { 46 | super.rewind(); 47 | chunkSize = -1; 48 | } 49 | 50 | protected int fill (byte[] buffer, int offset, int count) throws KryoException { 51 | if (chunkSize == -1) // No current chunk, expect a new chunk. 52 | readChunkSize(); 53 | else if (chunkSize == 0) // End of chunks. 54 | return -1; 55 | int actual = super.fill(buffer, offset, Math.min(chunkSize, count)); 56 | chunkSize -= actual; 57 | if (chunkSize == 0) readChunkSize(); // Read next chunk size. 58 | return actual; 59 | } 60 | 61 | private void readChunkSize () { 62 | try { 63 | InputStream inputStream = getInputStream(); 64 | for (int offset = 0, result = 0; offset < 32; offset += 7) { 65 | int b = inputStream.read(); 66 | if (b == -1) throw new KryoException("Buffer underflow."); 67 | result |= (b & 0x7F) << offset; 68 | if ((b & 0x80) == 0) { 69 | chunkSize = result; 70 | if (TRACE) trace("kryo", "Read chunk: " + chunkSize); 71 | return; 72 | } 73 | } 74 | } catch (IOException ex) { 75 | throw new KryoException(ex); 76 | } 77 | throw new KryoException("Malformed integer."); 78 | } 79 | 80 | /** Advances the stream to the next set of chunks. InputChunked will appear to hit the end of the data until this method is 81 | * called. */ 82 | public void nextChunks () { 83 | if (chunkSize == -1) readChunkSize(); // No current chunk, expect a new chunk. 84 | while (chunkSize > 0) 85 | skip(chunkSize); 86 | chunkSize = -1; 87 | if (TRACE) trace("kryo", "Next chunks."); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/OutputChunked.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | 7 | import com.esotericsoftware.kryo.KryoException; 8 | 9 | import static com.esotericsoftware.minlog.Log.*; 10 | 11 | /** An OutputStream that buffers data in a byte array and flushes to another OutputStream, writing the length before each flush. 12 | * The length allows the chunks to be skipped when reading. 13 | * @author Nathan Sweet */ 14 | public class OutputChunked extends Output { 15 | /** Creates an uninitialized OutputChunked with a maximum chunk size of 2048. The OutputStream must be set before it can be 16 | * used. */ 17 | public OutputChunked () { 18 | super(2048); 19 | } 20 | 21 | /** Creates an uninitialized OutputChunked. The OutputStream must be set before it can be used. 22 | * @param bufferSize The maximum size of a chunk. */ 23 | public OutputChunked (int bufferSize) { 24 | super(bufferSize); 25 | } 26 | 27 | /** Creates an OutputChunked with a maximum chunk size of 2048. */ 28 | public OutputChunked (OutputStream outputStream) { 29 | super(outputStream, 2048); 30 | } 31 | 32 | /** @param bufferSize The maximum size of a chunk. */ 33 | public OutputChunked (OutputStream outputStream, int bufferSize) { 34 | super(outputStream, bufferSize); 35 | } 36 | 37 | public void flush () throws KryoException { 38 | if (position() > 0) { 39 | try { 40 | writeChunkSize(); 41 | } catch (IOException ex) { 42 | throw new KryoException(ex); 43 | } 44 | } 45 | super.flush(); 46 | } 47 | 48 | private void writeChunkSize () throws IOException { 49 | int size = position(); 50 | if (TRACE) trace("kryo", "Write chunk: " + size); 51 | OutputStream outputStream = getOutputStream(); 52 | if ((size & ~0x7F) == 0) { 53 | outputStream.write(size); 54 | return; 55 | } 56 | outputStream.write((size & 0x7F) | 0x80); 57 | size >>>= 7; 58 | if ((size & ~0x7F) == 0) { 59 | outputStream.write(size); 60 | return; 61 | } 62 | outputStream.write((size & 0x7F) | 0x80); 63 | size >>>= 7; 64 | if ((size & ~0x7F) == 0) { 65 | outputStream.write(size); 66 | return; 67 | } 68 | outputStream.write((size & 0x7F) | 0x80); 69 | size >>>= 7; 70 | if ((size & ~0x7F) == 0) { 71 | outputStream.write(size); 72 | return; 73 | } 74 | outputStream.write((size & 0x7F) | 0x80); 75 | size >>>= 7; 76 | outputStream.write(size); 77 | } 78 | 79 | /** Marks the end of some data that may have been written by any number of chunks. These chunks can then be skipped when 80 | * reading. */ 81 | public void endChunks () { 82 | flush(); // Flush any partial chunk. 83 | if (TRACE) trace("kryo", "End chunks."); 84 | try { 85 | getOutputStream().write(0); // Zero length chunk. 86 | } catch (IOException ex) { 87 | throw new KryoException(ex); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/UnsafeInput.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import com.esotericsoftware.kryo.KryoException; 8 | import com.esotericsoftware.kryo.util.UnsafeUtil; 9 | 10 | import static com.esotericsoftware.kryo.util.UnsafeUtil.*; 11 | 12 | /** An optimized InputStream that reads data from a byte array and optionally fills the byte array from another InputStream as 13 | * needed. Utility methods are provided for efficiently writing primitive types, arrays of primitive types and strings. It uses 14 | * @link{java.misc.Unsafe} to achieve a very good performance. 15 | * 16 | *

17 | * Important notes:
18 | *

  • Bulk operations, e.g. on arrays of primitive types, are always using native byte order.
  • 19 | *
  • Fixed-size int, long, short, float and double elements are always read using native byte order.
  • 20 | *
  • Best performance is achieved if no variable length encoding for integers is used.
  • 21 | *
  • Serialized representation used as input for this class should always be produced using @link{UnsafeOutput}
  • 22 | *

    23 | * @author Roman Levenstein */ 24 | public final class UnsafeInput extends Input { 25 | 26 | private boolean varIntsEnabled = false; 27 | 28 | /** Creates an uninitialized Input. {@link #setBuffer(byte[], int, int)} must be called before the Input is used. */ 29 | public UnsafeInput () { 30 | } 31 | 32 | /** Creates a new Input for reading from a byte array. 33 | * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ 34 | public UnsafeInput (int bufferSize) { 35 | super(bufferSize); 36 | } 37 | 38 | /** Creates a new Input for reading from a byte array. 39 | * @see #setBuffer(byte[]) */ 40 | public UnsafeInput (byte[] buffer) { 41 | super(buffer); 42 | } 43 | 44 | /** Creates a new Input for reading from a byte array. 45 | * @see #setBuffer(byte[], int, int) */ 46 | public UnsafeInput (byte[] buffer, int offset, int count) { 47 | super(buffer, offset, count); 48 | } 49 | 50 | /** Creates a new Input for reading from an InputStream. A buffer size of 4096 is used. */ 51 | public UnsafeInput (InputStream inputStream) { 52 | super(inputStream); 53 | } 54 | 55 | /** Creates a new Input for reading from an InputStream. */ 56 | public UnsafeInput (InputStream inputStream, int bufferSize) { 57 | super(inputStream, bufferSize); 58 | } 59 | 60 | // int 61 | 62 | /** Reads a 4 byte int. */ 63 | public int readInt () throws KryoException { 64 | require(4); 65 | int result = unsafe().getInt(buffer, byteArrayBaseOffset + position); 66 | position += 4; 67 | return result; 68 | } 69 | 70 | // float 71 | 72 | /** Reads a 4 byte float. */ 73 | public float readFloat () throws KryoException { 74 | require(4); 75 | float result = unsafe().getFloat(buffer, byteArrayBaseOffset + position); 76 | position += 4; 77 | return result; 78 | } 79 | 80 | // short 81 | 82 | /** Reads a 2 byte short. */ 83 | public short readShort () throws KryoException { 84 | require(2); 85 | short result = unsafe().getShort(buffer, byteArrayBaseOffset + position); 86 | position += 2; 87 | return result; 88 | } 89 | 90 | // long 91 | 92 | /** Reads an 8 byte long. */ 93 | public long readLong () throws KryoException { 94 | require(8); 95 | long result = unsafe().getLong(buffer, byteArrayBaseOffset + position); 96 | position += 8; 97 | return result; 98 | } 99 | 100 | // double 101 | 102 | /** Writes an 8 byte double. */ 103 | public double readDouble () throws KryoException { 104 | require(8); 105 | double result = unsafe().getDouble(buffer, byteArrayBaseOffset + position); 106 | position += 8; 107 | return result; 108 | } 109 | 110 | public int readInt (boolean optimizePositive) throws KryoException { 111 | if (!varIntsEnabled) 112 | return readInt(); 113 | else 114 | return super.readInt(optimizePositive); 115 | } 116 | 117 | public long readLong (boolean optimizePositive) throws KryoException { 118 | if (!varIntsEnabled) 119 | return readLong(); 120 | else 121 | return super.readLong(optimizePositive); 122 | } 123 | 124 | // Methods implementing bulk operations on arrays of primitive types 125 | 126 | /** {@inheritDoc} */ 127 | final public int[] readInts (int length, boolean optimizePositive) throws KryoException { 128 | if (!varIntsEnabled) { 129 | int bytesToCopy = length << 2; 130 | int[] array = new int[length]; 131 | readBytes(array, intArrayBaseOffset, 0, bytesToCopy); 132 | return array; 133 | } else 134 | return super.readInts(length, optimizePositive); 135 | } 136 | 137 | /** {@inheritDoc} */ 138 | final public long[] readLongs (int length, boolean optimizePositive) throws KryoException { 139 | if (!varIntsEnabled) { 140 | int bytesToCopy = length << 3; 141 | long[] array = new long[length]; 142 | readBytes(array, longArrayBaseOffset, 0, bytesToCopy); 143 | return array; 144 | } else 145 | return super.readLongs(length, optimizePositive); 146 | } 147 | 148 | /** {@inheritDoc} */ 149 | final public int[] readInts (int length) throws KryoException { 150 | int bytesToCopy = length << 2; 151 | int[] array = new int[length]; 152 | readBytes(array, intArrayBaseOffset, 0, bytesToCopy); 153 | return array; 154 | } 155 | 156 | /** {@inheritDoc} */ 157 | final public long[] readLongs (int length) throws KryoException { 158 | int bytesToCopy = length << 3; 159 | long[] array = new long[length]; 160 | readBytes(array, longArrayBaseOffset, 0, bytesToCopy); 161 | return array; 162 | } 163 | 164 | /** {@inheritDoc} */ 165 | final public float[] readFloats (int length) throws KryoException { 166 | int bytesToCopy = length << 2; 167 | float[] array = new float[length]; 168 | readBytes(array, floatArrayBaseOffset, 0, bytesToCopy); 169 | return array; 170 | } 171 | 172 | /** {@inheritDoc} */ 173 | final public short[] readShorts (int length) throws KryoException { 174 | int bytesToCopy = length << 1; 175 | short[] array = new short[length]; 176 | readBytes(array, shortArrayBaseOffset, 0, bytesToCopy); 177 | return array; 178 | } 179 | 180 | /** {@inheritDoc} */ 181 | final public char[] readChars (int length) throws KryoException { 182 | int bytesToCopy = length << 1; 183 | char[] array = new char[length]; 184 | readBytes(array, charArrayBaseOffset, 0, bytesToCopy); 185 | return array; 186 | } 187 | 188 | /** {@inheritDoc} */ 189 | final public double[] readDoubles (int length) throws KryoException { 190 | int bytesToCopy = length << 3; 191 | double[] array = new double[length]; 192 | readBytes(array, doubleArrayBaseOffset, 0, bytesToCopy); 193 | return array; 194 | } 195 | 196 | final public void readBytes (Object dstObj, long offset, long count) throws KryoException { 197 | // Unsafe supports efficient bulk reading into arrays of primitives only because 198 | // of JVM limitations due to GC 199 | if (dstObj.getClass().isArray()) 200 | readBytes(dstObj, 0, offset, (int)count); 201 | else { 202 | throw new KryoException("Only bulk reads of arrays is supported"); 203 | } 204 | } 205 | 206 | final private void readBytes (Object dstArray, long dstArrayTypeOffset, long offset, int count) throws KryoException { 207 | int copyCount = Math.min(limit - position, count); 208 | while (true) { 209 | unsafe().copyMemory(buffer, byteArrayBaseOffset + position, dstArray, dstArrayTypeOffset + offset, copyCount); 210 | position += copyCount; 211 | count -= copyCount; 212 | if (count == 0) break; 213 | offset += copyCount; 214 | copyCount = Math.min(count, capacity); 215 | require(copyCount); 216 | } 217 | } 218 | 219 | /*** Return current setting for variable length encoding of integers 220 | * @return current setting for variable length encoding of integers */ 221 | public boolean getVarIntsEnabled () { 222 | return varIntsEnabled; 223 | } 224 | 225 | /*** Controls if a variable length encoding for integer types should be used when serializers suggest it. 226 | * 227 | * @param varIntsEnabled */ 228 | public void setVarIntsEnabled (boolean varIntsEnabled) { 229 | this.varIntsEnabled = varIntsEnabled; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/io/UnsafeMemoryInput.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.io; 3 | 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.ByteBuffer; 7 | 8 | import sun.nio.ch.DirectBuffer; 9 | 10 | import com.esotericsoftware.kryo.KryoException; 11 | import static com.esotericsoftware.kryo.util.UnsafeUtil.*; 12 | 13 | /** An optimized InputStream that reads data directly from the off-heap memory. Utility methods are provided for efficiently 14 | * reading primitive types, arrays of primitive types and strings. It uses @link{java.misc.Unsafe} to achieve a very good 15 | * performance. 16 | * 17 | *

    18 | * Important notes:
    19 | *

  • Bulk operations, e.g. on arrays of primitive types, are always using native byte order.
  • 20 | *
  • Fixed-size int, long, short, float and double elements are always read using native byte order.
  • 21 | *
  • Best performance is achieved if no variable length encoding for integers is used.
  • 22 | *
  • Serialized representation used as input for this class should always be produced using @link{UnsafeMemoryOutput}
  • 23 | *

    24 | * @author Roman Levenstein */ 25 | public final class UnsafeMemoryInput extends ByteBufferInput { 26 | /** Start address of the memory buffer The memory buffer should be non-movable, which normally means that is is allocated 27 | * off-heap */ 28 | private long bufaddress; 29 | 30 | { 31 | varIntsEnabled = false; 32 | 33 | } 34 | 35 | /** Creates an uninitialized Input. {@link #setBuffer(byte[], int, int)} must be called before the Input is used. */ 36 | public UnsafeMemoryInput () { 37 | } 38 | 39 | /** Creates a new Input for reading from a byte array. 40 | * @param bufferSize The initial and maximum size of the buffer. An exception is thrown if this size is exceeded. */ 41 | public UnsafeMemoryInput (int bufferSize) { 42 | super(bufferSize); 43 | updateBufferAddress(); 44 | } 45 | 46 | /** Creates a new Input for reading from a byte array. 47 | * @see #setBuffer(byte[]) */ 48 | public UnsafeMemoryInput (byte[] buffer) { 49 | super(buffer); 50 | updateBufferAddress(); 51 | } 52 | 53 | public UnsafeMemoryInput (ByteBuffer buffer) { 54 | super(buffer, 0, buffer.position()); 55 | updateBufferAddress(); 56 | } 57 | 58 | public UnsafeMemoryInput (long address, int maxBufferSize) { 59 | super(address, maxBufferSize); 60 | updateBufferAddress(); 61 | } 62 | 63 | /** Creates a new Input for reading from an InputStream. A buffer size of 4096 is used. */ 64 | public UnsafeMemoryInput (InputStream inputStream) { 65 | super(inputStream); 66 | updateBufferAddress(); 67 | } 68 | 69 | /** Creates a new Input for reading from an InputStream. */ 70 | public UnsafeMemoryInput (InputStream inputStream, int bufferSize) { 71 | super(inputStream, bufferSize); 72 | updateBufferAddress(); 73 | } 74 | 75 | public void setBuffer (ByteBuffer buffer, int offset, int count) { 76 | super.setBuffer(buffer, offset, count); 77 | updateBufferAddress(); 78 | } 79 | 80 | private void updateBufferAddress () { 81 | bufaddress = ((DirectBuffer)super.niobuffer).address(); 82 | } 83 | 84 | /** Reads a 4 byte int. */ 85 | public int readInt () throws KryoException { 86 | require(4); 87 | int result = unsafe().getInt(bufaddress + position); 88 | position += 4; 89 | return result; 90 | } 91 | 92 | /** Reads a 4 byte float. */ 93 | public float readFloat () throws KryoException { 94 | require(4); 95 | float result = unsafe().getFloat(bufaddress + position); 96 | position += 4; 97 | return result; 98 | } 99 | 100 | /** Reads a 2 byte short. */ 101 | public short readShort () throws KryoException { 102 | require(2); 103 | short result = unsafe().getShort(bufaddress + position); 104 | position += 2; 105 | return result; 106 | } 107 | 108 | /** Reads an 8 byte long. */ 109 | public long readLong () throws KryoException { 110 | require(8); 111 | long result = unsafe().getLong(bufaddress + position); 112 | position += 8; 113 | return result; 114 | } 115 | 116 | /** Reads a 1 byte boolean. */ 117 | public boolean readBoolean () throws KryoException { 118 | super.niobuffer.position(position); 119 | return super.readBoolean(); 120 | } 121 | 122 | /** Reads a single byte. */ 123 | public byte readByte () throws KryoException { 124 | super.niobuffer.position(position); 125 | return super.readByte(); 126 | } 127 | 128 | /** Reads a 2 byte char. */ 129 | public char readChar () throws KryoException { 130 | super.niobuffer.position(position); 131 | return super.readChar(); 132 | } 133 | 134 | /** Reads an 8 byte double. */ 135 | public double readDouble () throws KryoException { 136 | require(8); 137 | double result = unsafe().getDouble(bufaddress + position); 138 | position += 8; 139 | return result; 140 | } 141 | 142 | public int readInt (boolean optimizePositive) throws KryoException { 143 | if (!varIntsEnabled) 144 | return readInt(); 145 | else 146 | return super.readInt(optimizePositive); 147 | } 148 | 149 | public long readLong (boolean optimizePositive) throws KryoException { 150 | if (!varIntsEnabled) 151 | return readLong(); 152 | else 153 | return super.readLong(optimizePositive); 154 | } 155 | 156 | // Methods implementing bulk operations on arrays of primitive types 157 | 158 | /** {@inheritDoc} */ 159 | final public int[] readInts (int length, boolean optimizePositive) throws KryoException { 160 | if (!varIntsEnabled) { 161 | int bytesToCopy = length << 2; 162 | int[] array = new int[length]; 163 | readBytes(array, intArrayBaseOffset, 0, bytesToCopy); 164 | return array; 165 | } else 166 | return super.readInts(length, optimizePositive); 167 | } 168 | 169 | /** {@inheritDoc} */ 170 | final public long[] readLongs (int length, boolean optimizePositive) throws KryoException { 171 | if (!varIntsEnabled) { 172 | int bytesToCopy = length << 3; 173 | long[] array = new long[length]; 174 | readBytes(array, longArrayBaseOffset, 0, bytesToCopy); 175 | return array; 176 | } else 177 | return super.readLongs(length, optimizePositive); 178 | } 179 | 180 | /** {@inheritDoc} */ 181 | final public float[] readFloats (int length) throws KryoException { 182 | int bytesToCopy = length << 2; 183 | float[] array = new float[length]; 184 | readBytes(array, floatArrayBaseOffset, 0, bytesToCopy); 185 | return array; 186 | } 187 | 188 | /** {@inheritDoc} */ 189 | final public short[] readShorts (int length) throws KryoException { 190 | int bytesToCopy = length << 1; 191 | short[] array = new short[length]; 192 | readBytes(array, shortArrayBaseOffset, 0, bytesToCopy); 193 | return array; 194 | } 195 | 196 | /** {@inheritDoc} */ 197 | final public char[] readChars (int length) throws KryoException { 198 | int bytesToCopy = length << 1; 199 | char[] array = new char[length]; 200 | readBytes(array, charArrayBaseOffset, 0, bytesToCopy); 201 | return array; 202 | } 203 | 204 | /** {@inheritDoc} */ 205 | final public double[] readDoubles (int length) throws KryoException { 206 | int bytesToCopy = length << 2; 207 | double[] array = new double[length]; 208 | readBytes(array, doubleArrayBaseOffset, 0, bytesToCopy); 209 | return array; 210 | } 211 | 212 | final public void readBytes (Object dstObj, long offset, long count) throws KryoException { 213 | /* Unsafe supports efficient bulk reading into arrays of primitives only because of JVM limitations due to GC */ 214 | if (dstObj.getClass().isArray()) 215 | readBytes(dstObj, 0, offset, (int)count); 216 | else { 217 | throw new KryoException("Only bulk reads of arrays is supported"); 218 | } 219 | } 220 | 221 | final private void readBytes (Object dstObj, long dstArrayTypeOffset, long offset, int count) throws KryoException { 222 | int copyCount = Math.min(limit - position, count); 223 | while (true) { 224 | unsafe().copyMemory(null, bufaddress + position, dstObj, dstArrayTypeOffset + offset, copyCount); 225 | position += copyCount; 226 | count -= copyCount; 227 | if (count == 0) break; 228 | offset += copyCount; 229 | copyCount = Math.min(count, capacity); 230 | require(copyCount); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/AsmCacheFields.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.KryoException; 5 | import com.esotericsoftware.kryo.io.Input; 6 | import com.esotericsoftware.kryo.io.Output; 7 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 8 | import com.esotericsoftware.reflectasm.FieldAccess; 9 | 10 | /*** Implementations of ASM-based serializers for fields. 11 | * 12 | * @author Nathan Sweet */ 13 | public class AsmCacheFields { 14 | 15 | abstract static class AsmCachedField extends CachedField { 16 | } 17 | 18 | final static class AsmIntField extends AsmCachedField { 19 | public void write (Output output, Object object) { 20 | if (varIntsEnabled) 21 | output.writeInt(access.getInt(object, accessIndex), false); 22 | else 23 | output.writeInt(access.getInt(object, accessIndex)); 24 | } 25 | 26 | public void read (Input input, Object object) { 27 | if (varIntsEnabled) 28 | access.setInt(object, accessIndex, input.readInt(false)); 29 | else 30 | access.setInt(object, accessIndex, input.readInt()); 31 | } 32 | 33 | public void copy (Object original, Object copy) { 34 | access.setInt(copy, accessIndex, access.getInt(original, accessIndex)); 35 | } 36 | } 37 | 38 | final static class AsmFloatField extends AsmCachedField { 39 | public void write (Output output, Object object) { 40 | output.writeFloat(access.getFloat(object, accessIndex)); 41 | } 42 | 43 | public void read (Input input, Object object) { 44 | access.setFloat(object, accessIndex, input.readFloat()); 45 | } 46 | 47 | public void copy (Object original, Object copy) { 48 | access.setFloat(copy, accessIndex, access.getFloat(original, accessIndex)); 49 | } 50 | } 51 | 52 | final static class AsmShortField extends AsmCachedField { 53 | public void write (Output output, Object object) { 54 | output.writeShort(access.getShort(object, accessIndex)); 55 | } 56 | 57 | public void read (Input input, Object object) { 58 | access.setShort(object, accessIndex, input.readShort()); 59 | } 60 | 61 | public void copy (Object original, Object copy) { 62 | access.setShort(copy, accessIndex, access.getShort(original, accessIndex)); 63 | } 64 | } 65 | 66 | final static class AsmByteField extends AsmCachedField { 67 | public void write (Output output, Object object) { 68 | output.writeByte(access.getByte(object, accessIndex)); 69 | } 70 | 71 | public void read (Input input, Object object) { 72 | access.setByte(object, accessIndex, input.readByte()); 73 | } 74 | 75 | public void copy (Object original, Object copy) { 76 | access.setByte(copy, accessIndex, access.getByte(original, accessIndex)); 77 | } 78 | } 79 | 80 | final static class AsmBooleanField extends AsmCachedField { 81 | public void write (Output output, Object object) { 82 | output.writeBoolean(access.getBoolean(object, accessIndex)); 83 | } 84 | 85 | public void read (Input input, Object object) { 86 | access.setBoolean(object, accessIndex, input.readBoolean()); 87 | } 88 | 89 | public void copy (Object original, Object copy) { 90 | access.setBoolean(copy, accessIndex, access.getBoolean(original, accessIndex)); 91 | } 92 | } 93 | 94 | final static class AsmCharField extends AsmCachedField { 95 | public void write (Output output, Object object) { 96 | output.writeChar(access.getChar(object, accessIndex)); 97 | } 98 | 99 | public void read (Input input, Object object) { 100 | access.setChar(object, accessIndex, input.readChar()); 101 | } 102 | 103 | public void copy (Object original, Object copy) { 104 | access.setChar(copy, accessIndex, access.getChar(original, accessIndex)); 105 | } 106 | } 107 | 108 | final static class AsmLongField extends AsmCachedField { 109 | public void write (Output output, Object object) { 110 | if (varIntsEnabled) 111 | output.writeLong(access.getLong(object, accessIndex), false); 112 | else 113 | output.writeLong(access.getLong(object, accessIndex)); 114 | } 115 | 116 | public void read (Input input, Object object) { 117 | if (varIntsEnabled) 118 | access.setLong(object, accessIndex, input.readLong(false)); 119 | else 120 | access.setLong(object, accessIndex, input.readLong()); 121 | } 122 | 123 | public void copy (Object original, Object copy) { 124 | access.setLong(copy, accessIndex, access.getLong(original, accessIndex)); 125 | } 126 | } 127 | 128 | final static class AsmDoubleField extends AsmCachedField { 129 | public void write (Output output, Object object) { 130 | output.writeDouble(access.getDouble(object, accessIndex)); 131 | } 132 | 133 | public void read (Input input, Object object) { 134 | access.setDouble(object, accessIndex, input.readDouble()); 135 | } 136 | 137 | public void copy (Object original, Object copy) { 138 | access.setDouble(copy, accessIndex, access.getDouble(original, accessIndex)); 139 | } 140 | } 141 | 142 | final static class AsmStringField extends AsmCachedField { 143 | public void write (Output output, Object object) { 144 | output.writeString(access.getString(object, accessIndex)); 145 | } 146 | 147 | public void read (Input input, Object object) { 148 | access.set(object, accessIndex, input.readString()); 149 | } 150 | 151 | public void copy (Object original, Object copy) { 152 | access.set(copy, accessIndex, access.getString(original, accessIndex)); 153 | } 154 | } 155 | 156 | final static class AsmObjectField extends ObjectField { 157 | 158 | public AsmObjectField (FieldSerializer fieldSerializer) { 159 | super(fieldSerializer); 160 | } 161 | 162 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 163 | if (accessIndex != -1) return ((FieldAccess)access).get(object, accessIndex); 164 | throw new KryoException("Unknown acess index"); 165 | } 166 | 167 | public void setField (Object object, Object value) throws IllegalArgumentException, IllegalAccessException { 168 | if (accessIndex != -1) 169 | ((FieldAccess)access).set(object, accessIndex, value); 170 | else 171 | throw new KryoException("Unknown acess index"); 172 | } 173 | 174 | public void copy (Object original, Object copy) { 175 | try { 176 | if (accessIndex != -1) { 177 | access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); 178 | } else 179 | throw new KryoException("Unknown acess index"); 180 | } catch (KryoException ex) { 181 | ex.addTrace(this + " (" + type.getName() + ")"); 182 | throw ex; 183 | } catch (RuntimeException runtimeEx) { 184 | KryoException ex = new KryoException(runtimeEx); 185 | ex.addTrace(this + " (" + type.getName() + ")"); 186 | throw ex; 187 | } 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/AsmCachedFieldFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 6 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedFieldFactory; 7 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmBooleanField; 8 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmByteField; 9 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmCharField; 10 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmDoubleField; 11 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmFloatField; 12 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmIntField; 13 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmLongField; 14 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmShortField; 15 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmStringField; 16 | import com.esotericsoftware.kryo.serializers.AsmCacheFields.AsmObjectField; 17 | 18 | class AsmCachedFieldFactory implements CachedFieldFactory { 19 | public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser) { 20 | CachedField cachedField; 21 | // Use ASM-based serializers 22 | if (fieldClass.isPrimitive()) { 23 | if (fieldClass == boolean.class) 24 | cachedField = new AsmBooleanField(); 25 | else if (fieldClass == byte.class) 26 | cachedField = new AsmByteField(); 27 | else if (fieldClass == char.class) 28 | cachedField = new AsmCharField(); 29 | else if (fieldClass == short.class) 30 | cachedField = new AsmShortField(); 31 | else if (fieldClass == int.class) 32 | cachedField = new AsmIntField(); 33 | else if (fieldClass == long.class) 34 | cachedField = new AsmLongField(); 35 | else if (fieldClass == float.class) 36 | cachedField = new AsmFloatField(); 37 | else if (fieldClass == double.class) 38 | cachedField = new AsmDoubleField(); 39 | else { 40 | cachedField = new AsmObjectField(ser); 41 | } 42 | } else if (fieldClass == String.class 43 | && (!ser.kryo.getReferences() || !ser.kryo.getReferenceResolver().useReferences(String.class))) { 44 | cachedField = new AsmStringField(); 45 | } else { 46 | cachedField = new AsmObjectField(ser); 47 | } 48 | return cachedField; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/BeanSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.beans.BeanInfo; 5 | import java.beans.IntrospectionException; 6 | import java.beans.Introspector; 7 | import java.beans.PropertyDescriptor; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Comparator; 13 | 14 | import com.esotericsoftware.kryo.Kryo; 15 | import com.esotericsoftware.kryo.KryoException; 16 | import com.esotericsoftware.kryo.Serializer; 17 | import com.esotericsoftware.kryo.io.Input; 18 | import com.esotericsoftware.kryo.io.Output; 19 | import com.esotericsoftware.reflectasm.MethodAccess; 20 | 21 | import static com.esotericsoftware.minlog.Log.*; 22 | 23 | /** Serializes Java beans using bean accessor methods. Only bean properties with both a getter and setter are serialized. This 24 | * class is not as fast as {@link FieldSerializer} but is much faster and more efficient than Java serialization. Bytecode 25 | * generation is used to invoke the bean propert methods, if possible. 26 | *

    27 | * BeanSerializer does not write header data, only the object data is stored. If the type of a bean property is not final (note 28 | * primitives are final) then an extra byte is written for that property. 29 | * @see Serializer 30 | * @see Kryo#register(Class, Serializer) 31 | * @author Nathan Sweet */ 32 | public class BeanSerializer extends Serializer { 33 | static final Object[] noArgs = {}; 34 | 35 | private final Kryo kryo; 36 | private CachedProperty[] properties; 37 | Object access; 38 | 39 | public BeanSerializer (Kryo kryo, Class type) { 40 | this.kryo = kryo; 41 | 42 | BeanInfo info; 43 | try { 44 | info = Introspector.getBeanInfo(type); 45 | } catch (IntrospectionException ex) { 46 | throw new KryoException("Error getting bean info.", ex); 47 | } 48 | // Methods are sorted by alpha so the order of the data is known. 49 | PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); 50 | Arrays.sort(descriptors, new Comparator() { 51 | public int compare (PropertyDescriptor o1, PropertyDescriptor o2) { 52 | return o1.getName().compareTo(o2.getName()); 53 | } 54 | }); 55 | ArrayList cachedProperties = new ArrayList(descriptors.length); 56 | for (int i = 0, n = descriptors.length; i < n; i++) { 57 | PropertyDescriptor property = descriptors[i]; 58 | String name = property.getName(); 59 | if (name.equals("class")) continue; 60 | Method getMethod = property.getReadMethod(); 61 | Method setMethod = property.getWriteMethod(); 62 | if (getMethod == null || setMethod == null) continue; // Require both a getter and setter. 63 | 64 | // Always use the same serializer for this property if the properties' class is final. 65 | Serializer serializer = null; 66 | Class returnType = getMethod.getReturnType(); 67 | if (kryo.isFinal(returnType)) serializer = kryo.getRegistration(returnType).getSerializer(); 68 | 69 | CachedProperty cachedProperty = new CachedProperty(); 70 | cachedProperty.name = name; 71 | cachedProperty.getMethod = getMethod; 72 | cachedProperty.setMethod = setMethod; 73 | cachedProperty.serializer = serializer; 74 | cachedProperty.setMethodType = setMethod.getParameterTypes()[0]; 75 | cachedProperties.add(cachedProperty); 76 | } 77 | 78 | properties = cachedProperties.toArray(new CachedProperty[cachedProperties.size()]); 79 | 80 | try { 81 | access = MethodAccess.get(type); 82 | for (int i = 0, n = properties.length; i < n; i++) { 83 | CachedProperty property = properties[i]; 84 | property.getterAccessIndex = ((MethodAccess)access).getIndex(property.getMethod.getName(), 85 | property.getMethod.getParameterTypes()); 86 | property.setterAccessIndex = ((MethodAccess)access).getIndex(property.setMethod.getName(), 87 | property.setMethod.getParameterTypes()); 88 | } 89 | } catch (Throwable ignored) { 90 | // ReflectASM is not available on Android. 91 | } 92 | } 93 | 94 | public void write (Kryo kryo, Output output, T object) { 95 | Class type = object.getClass(); 96 | for (int i = 0, n = properties.length; i < n; i++) { 97 | CachedProperty property = properties[i]; 98 | try { 99 | if (TRACE) trace("kryo", "Write property: " + property + " (" + type.getName() + ")"); 100 | Object value = property.get(object); 101 | Serializer serializer = property.serializer; 102 | if (serializer != null) 103 | kryo.writeObjectOrNull(output, value, serializer); 104 | else 105 | kryo.writeClassAndObject(output, value); 106 | } catch (IllegalAccessException ex) { 107 | throw new KryoException("Error accessing getter method: " + property + " (" + type.getName() + ")", ex); 108 | } catch (InvocationTargetException ex) { 109 | throw new KryoException("Error invoking getter method: " + property + " (" + type.getName() + ")", ex); 110 | } catch (KryoException ex) { 111 | ex.addTrace(property + " (" + type.getName() + ")"); 112 | throw ex; 113 | } catch (RuntimeException runtimeEx) { 114 | KryoException ex = new KryoException(runtimeEx); 115 | ex.addTrace(property + " (" + type.getName() + ")"); 116 | throw ex; 117 | } 118 | } 119 | } 120 | 121 | public T read (Kryo kryo, Input input, Class type) { 122 | T object = kryo.newInstance(type); 123 | kryo.reference(object); 124 | for (int i = 0, n = properties.length; i < n; i++) { 125 | CachedProperty property = properties[i]; 126 | try { 127 | if (TRACE) trace("kryo", "Read property: " + property + " (" + object.getClass() + ")"); 128 | Object value; 129 | Serializer serializer = property.serializer; 130 | if (serializer != null) 131 | value = kryo.readObjectOrNull(input, property.setMethodType, serializer); 132 | else 133 | value = kryo.readClassAndObject(input); 134 | property.set(object, value); 135 | } catch (IllegalAccessException ex) { 136 | throw new KryoException("Error accessing setter method: " + property + " (" + object.getClass().getName() + ")", ex); 137 | } catch (InvocationTargetException ex) { 138 | throw new KryoException("Error invoking setter method: " + property + " (" + object.getClass().getName() + ")", ex); 139 | } catch (KryoException ex) { 140 | ex.addTrace(property + " (" + object.getClass().getName() + ")"); 141 | throw ex; 142 | } catch (RuntimeException runtimeEx) { 143 | KryoException ex = new KryoException(runtimeEx); 144 | ex.addTrace(property + " (" + object.getClass().getName() + ")"); 145 | throw ex; 146 | } 147 | } 148 | return object; 149 | } 150 | 151 | public T copy (Kryo kryo, T original) { 152 | T copy = (T)kryo.newInstance(original.getClass()); 153 | for (int i = 0, n = properties.length; i < n; i++) { 154 | CachedProperty property = properties[i]; 155 | try { 156 | Object value = property.get(original); 157 | property.set(copy, value); 158 | } catch (KryoException ex) { 159 | ex.addTrace(property + " (" + copy.getClass().getName() + ")"); 160 | throw ex; 161 | } catch (RuntimeException runtimeEx) { 162 | KryoException ex = new KryoException(runtimeEx); 163 | ex.addTrace(property + " (" + copy.getClass().getName() + ")"); 164 | throw ex; 165 | } catch (Exception ex) { 166 | throw new KryoException("Error copying bean property: " + property + " (" + copy.getClass().getName() + ")", ex); 167 | } 168 | } 169 | return copy; 170 | } 171 | 172 | class CachedProperty { 173 | String name; 174 | Method getMethod, setMethod; 175 | Class setMethodType; 176 | Serializer serializer; 177 | int getterAccessIndex, setterAccessIndex; 178 | 179 | public String toString () { 180 | return name; 181 | } 182 | 183 | Object get (Object object) throws IllegalAccessException, InvocationTargetException { 184 | if (access != null) return ((MethodAccess)access).invoke(object, getterAccessIndex); 185 | return getMethod.invoke(object, noArgs); 186 | } 187 | 188 | void set (Object object, Object value) throws IllegalAccessException, InvocationTargetException { 189 | if (access != null) { 190 | ((MethodAccess)access).invoke(object, setterAccessIndex, value); 191 | return; 192 | } 193 | setMethod.invoke(object, new Object[] {value}); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/BlowfishSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.io.IOException; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.CipherInputStream; 8 | import javax.crypto.CipherOutputStream; 9 | import javax.crypto.spec.SecretKeySpec; 10 | 11 | import com.esotericsoftware.kryo.Kryo; 12 | import com.esotericsoftware.kryo.KryoException; 13 | import com.esotericsoftware.kryo.Serializer; 14 | import com.esotericsoftware.kryo.io.Input; 15 | import com.esotericsoftware.kryo.io.Output; 16 | 17 | /** Encrypts data using the blowfish cipher. 18 | * @author Nathan Sweet */ 19 | public class BlowfishSerializer extends Serializer { 20 | private final Serializer serializer; 21 | static private SecretKeySpec keySpec; 22 | 23 | public BlowfishSerializer (Serializer serializer, byte[] key) { 24 | this.serializer = serializer; 25 | keySpec = new SecretKeySpec(key, "Blowfish"); 26 | } 27 | 28 | public void write (Kryo kryo, Output output, Object object) { 29 | Cipher cipher = getCipher(Cipher.ENCRYPT_MODE); 30 | CipherOutputStream cipherStream = new CipherOutputStream(output, cipher); 31 | Output cipherOutput = new Output(cipherStream, 256) { 32 | public void close () throws KryoException { 33 | // Don't allow the CipherOutputStream to close the output. 34 | } 35 | }; 36 | kryo.writeObject(cipherOutput, object, serializer); 37 | cipherOutput.flush(); 38 | try { 39 | cipherStream.close(); 40 | } catch (IOException ex) { 41 | throw new KryoException(ex); 42 | } 43 | } 44 | 45 | public Object read (Kryo kryo, Input input, Class type) { 46 | Cipher cipher = getCipher(Cipher.DECRYPT_MODE); 47 | CipherInputStream cipherInput = new CipherInputStream(input, cipher); 48 | return kryo.readObject(new Input(cipherInput, 256), type, serializer); 49 | } 50 | 51 | public Object copy (Kryo kryo, Object original) { 52 | return serializer.copy(kryo, original); 53 | } 54 | 55 | static private Cipher getCipher (int mode) { 56 | try { 57 | Cipher cipher = Cipher.getInstance("Blowfish"); 58 | cipher.init(mode, keySpec); 59 | return cipher; 60 | } catch (Exception ex) { 61 | throw new KryoException(ex); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/CollectionSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | 7 | import com.esotericsoftware.kryo.Kryo; 8 | import com.esotericsoftware.kryo.Serializer; 9 | import com.esotericsoftware.kryo.io.Input; 10 | import com.esotericsoftware.kryo.io.Output; 11 | 12 | /** Serializes objects that implement the {@link Collection} interface. 13 | *

    14 | * 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 15 | * collection. The alternate constructor can be used to improve efficiency to match that of using an array instead of a 16 | * collection. 17 | * @author Nathan Sweet */ 18 | public class CollectionSerializer extends Serializer { 19 | private boolean elementsCanBeNull = true; 20 | private Serializer serializer; 21 | private Class elementClass; 22 | private Class genericType; 23 | 24 | public CollectionSerializer () { 25 | } 26 | 27 | /** @see #setElementClass(Class, Serializer) */ 28 | public CollectionSerializer (Class elementClass, Serializer serializer) { 29 | setElementClass(elementClass, serializer); 30 | } 31 | 32 | /** @see #setElementClass(Class, Serializer) 33 | * @see #setElementsCanBeNull(boolean) */ 34 | public CollectionSerializer (Class elementClass, Serializer serializer, boolean elementsCanBeNull) { 35 | setElementClass(elementClass, serializer); 36 | this.elementsCanBeNull = elementsCanBeNull; 37 | } 38 | 39 | /** @param elementsCanBeNull False if all elements are not null. This saves 1 byte per element if elementClass is set. True if it 40 | * is not known (default). */ 41 | public void setElementsCanBeNull (boolean elementsCanBeNull) { 42 | this.elementsCanBeNull = elementsCanBeNull; 43 | } 44 | 45 | /** @param elementClass The concrete class of each element. This saves 1-2 bytes per element. Set to null if the class is not 46 | * known or varies per element (default). 47 | * @param serializer The serializer to use for each element. */ 48 | public void setElementClass (Class elementClass, Serializer serializer) { 49 | this.elementClass = elementClass; 50 | this.serializer = serializer; 51 | } 52 | 53 | public void setGenerics (Kryo kryo, Class[] generics) { 54 | genericType = null; 55 | if (generics != null && generics.length > 0) { 56 | if (kryo.isFinal(generics[0])) genericType = generics[0]; 57 | } 58 | } 59 | 60 | public void write (Kryo kryo, Output output, Collection collection) { 61 | int length = collection.size(); 62 | output.writeVarInt(length, true); 63 | Serializer serializer = this.serializer; 64 | if (genericType != null) { 65 | if (serializer == null) serializer = kryo.getSerializer(genericType); 66 | genericType = null; 67 | } 68 | if (serializer != null) { 69 | if (elementsCanBeNull) { 70 | for (Object element : collection) 71 | kryo.writeObjectOrNull(output, element, serializer); 72 | } else { 73 | for (Object element : collection) 74 | kryo.writeObject(output, element, serializer); 75 | } 76 | } else { 77 | for (Object element : collection) 78 | kryo.writeClassAndObject(output, element); 79 | } 80 | } 81 | 82 | /** Used by {@link #read(Kryo, Input, Class)} to create the new object. This can be overridden to customize object creation, eg 83 | * to call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ 84 | protected Collection create (Kryo kryo, Input input, Class type) { 85 | return kryo.newInstance(type); 86 | } 87 | 88 | public Collection read (Kryo kryo, Input input, Class type) { 89 | Collection collection = create(kryo, input, type); 90 | kryo.reference(collection); 91 | int length = input.readVarInt(true); 92 | if (collection instanceof ArrayList) ((ArrayList)collection).ensureCapacity(length); 93 | Class elementClass = this.elementClass; 94 | Serializer serializer = this.serializer; 95 | if (genericType != null) { 96 | if (serializer == null) { 97 | elementClass = genericType; 98 | serializer = kryo.getSerializer(genericType); 99 | } 100 | genericType = null; 101 | } 102 | if (serializer != null) { 103 | if (elementsCanBeNull) { 104 | for (int i = 0; i < length; i++) 105 | collection.add(kryo.readObjectOrNull(input, elementClass, serializer)); 106 | } else { 107 | for (int i = 0; i < length; i++) 108 | collection.add(kryo.readObject(input, elementClass, serializer)); 109 | } 110 | } else { 111 | for (int i = 0; i < length; i++) 112 | collection.add(kryo.readClassAndObject(input)); 113 | } 114 | return collection; 115 | } 116 | 117 | /** Used by {@link #copy(Kryo, Collection)} to create the new object. This can be overridden to customize object creation, eg to 118 | * call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ 119 | protected Collection createCopy (Kryo kryo, Collection original) { 120 | return kryo.newInstance(original.getClass()); 121 | } 122 | 123 | public Collection copy (Kryo kryo, Collection original) { 124 | Collection copy = createCopy(kryo, original); 125 | kryo.reference(copy); 126 | for (Object element : original) 127 | copy.add(kryo.copy(element)); 128 | return copy; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import com.esotericsoftware.kryo.Kryo; 5 | import com.esotericsoftware.kryo.io.Input; 6 | import com.esotericsoftware.kryo.io.InputChunked; 7 | import com.esotericsoftware.kryo.io.Output; 8 | import com.esotericsoftware.kryo.io.OutputChunked; 9 | import com.esotericsoftware.kryo.util.ObjectMap; 10 | 11 | import static com.esotericsoftware.minlog.Log.*; 12 | 13 | /** Serializes objects using direct field assignment, with limited support for forward and backward compatibility. Fields can be 14 | * added or removed without invalidating previously serialized bytes. Note that changing the type of a field is not supported. 15 | *

    16 | * There is additional overhead compared to {@link FieldSerializer}. A header is output the first time an object of a given type 17 | * is serialized. The header consists of an int for the number of fields, then a String for each field name. Also, to support 18 | * 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 19 | * bytes. 20 | *

    21 | * 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 22 | * must be avoided. 23 | * @author Nathan Sweet */ 24 | public class CompatibleFieldSerializer extends FieldSerializer { 25 | public CompatibleFieldSerializer (Kryo kryo, Class type) { 26 | super(kryo, type); 27 | } 28 | 29 | public void write (Kryo kryo, Output output, T object) { 30 | CachedField[] fields = getFields(); 31 | ObjectMap context = kryo.getGraphContext(); 32 | if (!context.containsKey(this)) { 33 | context.put(this, null); 34 | if (TRACE) trace("kryo", "Write " + fields.length + " field names."); 35 | output.writeVarInt(fields.length, true); 36 | for (int i = 0, n = fields.length; i < n; i++) 37 | output.writeString(fields[i].field.getName()); 38 | } 39 | 40 | OutputChunked outputChunked = new OutputChunked(output, 1024); 41 | for (int i = 0, n = fields.length; i < n; i++) { 42 | fields[i].write(outputChunked, object); 43 | outputChunked.endChunks(); 44 | } 45 | } 46 | 47 | public T read (Kryo kryo, Input input, Class type) { 48 | T object = create(kryo, input, type); 49 | kryo.reference(object); 50 | ObjectMap context = kryo.getGraphContext(); 51 | CachedField[] fields = (CachedField[])context.get(this); 52 | if (fields == null) { 53 | int length = input.readVarInt(true); 54 | if (TRACE) trace("kryo", "Read " + length + " field names."); 55 | String[] names = new String[length]; 56 | for (int i = 0; i < length; i++) 57 | names[i] = input.readString(); 58 | 59 | fields = new CachedField[length]; 60 | CachedField[] allFields = getFields(); 61 | outer: 62 | for (int i = 0, n = names.length; i < n; i++) { 63 | String schemaName = names[i]; 64 | for (int ii = 0, nn = allFields.length; ii < nn; ii++) { 65 | if (allFields[ii].field.getName().equals(schemaName)) { 66 | fields[i] = allFields[ii]; 67 | continue outer; 68 | } 69 | } 70 | if (TRACE) trace("kryo", "Ignore obsolete field: " + schemaName); 71 | } 72 | context.put(this, fields); 73 | } 74 | 75 | InputChunked inputChunked = new InputChunked(input, 1024); 76 | for (int i = 0, n = fields.length; i < n; i++) { 77 | CachedField cachedField = fields[i]; 78 | if (cachedField == null) { 79 | if (TRACE) trace("kryo", "Skip obsolete field."); 80 | inputChunked.nextChunks(); 81 | continue; 82 | } 83 | cachedField.read(inputChunked, object); 84 | inputChunked.nextChunks(); 85 | } 86 | return object; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/DeflateSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.io.IOException; 5 | import java.util.zip.Deflater; 6 | import java.util.zip.DeflaterOutputStream; 7 | import java.util.zip.Inflater; 8 | import java.util.zip.InflaterInputStream; 9 | 10 | import com.esotericsoftware.kryo.Kryo; 11 | import com.esotericsoftware.kryo.KryoException; 12 | import com.esotericsoftware.kryo.Serializer; 13 | import com.esotericsoftware.kryo.io.Input; 14 | import com.esotericsoftware.kryo.io.InputChunked; 15 | import com.esotericsoftware.kryo.io.Output; 16 | import com.esotericsoftware.kryo.io.OutputChunked; 17 | 18 | public class DeflateSerializer extends Serializer { 19 | private final Serializer serializer; 20 | private boolean noHeaders = true; 21 | private int compressionLevel = 4; 22 | 23 | public DeflateSerializer (Serializer serializer) { 24 | this.serializer = serializer; 25 | } 26 | 27 | public void write (Kryo kryo, Output output, Object object) { 28 | Deflater deflater = new Deflater(compressionLevel, noHeaders); 29 | OutputChunked outputChunked = new OutputChunked(output, 256); 30 | DeflaterOutputStream deflaterStream = new DeflaterOutputStream(outputChunked, deflater); 31 | Output deflaterOutput = new Output(deflaterStream, 256); 32 | kryo.writeObject(deflaterOutput, object, serializer); 33 | deflaterOutput.flush(); 34 | try { 35 | deflaterStream.finish(); 36 | } catch (IOException ex) { 37 | throw new KryoException(ex); 38 | } 39 | outputChunked.endChunks(); 40 | } 41 | 42 | public Object read (Kryo kryo, Input input, Class type) { 43 | // The inflater would read from input beyond the compressed bytes if chunked enoding wasn't used. 44 | InflaterInputStream inflaterStream = new InflaterInputStream(new InputChunked(input, 256), new Inflater(noHeaders)); 45 | return kryo.readObject(new Input(inflaterStream, 256), type, serializer); 46 | } 47 | 48 | public void setNoHeaders (boolean noHeaders) { 49 | this.noHeaders = noHeaders; 50 | } 51 | 52 | /** Default is 4. 53 | * @see Deflater#setLevel(int) */ 54 | public void setCompressionLevel (int compressionLevel) { 55 | this.compressionLevel = compressionLevel; 56 | } 57 | 58 | public Object copy (Kryo kryo, Object original) { 59 | return serializer.copy(kryo, original); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/FieldSerializerUnsafeUtil.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import static com.esotericsoftware.kryo.util.UnsafeUtil.unsafe; 4 | import static com.esotericsoftware.minlog.Log.TRACE; 5 | import static com.esotericsoftware.minlog.Log.trace; 6 | 7 | import java.lang.reflect.Field; 8 | import java.util.List; 9 | 10 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 11 | import com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeRegionField; 12 | import com.esotericsoftware.kryo.util.IntArray; 13 | import com.esotericsoftware.reflectasm.FieldAccess; 14 | 15 | /* Helper class for implementing FieldSerializer using Unsafe-based approach. 16 | * @author Roman Levenstein */ 17 | final class FieldSerializerUnsafeUtil { 18 | private FieldSerializer serializer; 19 | 20 | public FieldSerializerUnsafeUtil (FieldSerializer serializer) { 21 | this.serializer = serializer; 22 | } 23 | 24 | /** Use Unsafe-based information about fields layout in memory to build a list of cached fields and memory regions representing 25 | * consecutive fields in memory */ 26 | public void createUnsafeCacheFieldsAndRegions (List validFields, List cachedFields, int baseIndex, 27 | IntArray useAsm) { 28 | // Find adjacent fields of primitive types 29 | long startPrimitives = 0; 30 | long endPrimitives = 0; 31 | boolean lastWasPrimitive = false; 32 | int primitiveLength = 0; 33 | int lastAccessIndex = -1; 34 | Field lastField = null; 35 | long fieldOffset = -1; 36 | long fieldEndOffset = -1; 37 | long lastFieldEndOffset = -1; 38 | 39 | for (int i = 0, n = validFields.size(); i < n; i++) { 40 | Field field = validFields.get(i); 41 | 42 | int accessIndex = -1; 43 | if (serializer.access != null && useAsm.get(baseIndex + i) == 1) 44 | accessIndex = ((FieldAccess)serializer.access).getIndex(field.getName()); 45 | 46 | fieldOffset = unsafe().objectFieldOffset(field); 47 | fieldEndOffset = fieldOffset + fieldSizeOf(field.getType()); 48 | 49 | if (!field.getType().isPrimitive() && lastWasPrimitive) { 50 | // This is not a primitive field. Therefore, it marks 51 | // the end of a region of primitive fields 52 | endPrimitives = lastFieldEndOffset; 53 | lastWasPrimitive = false; 54 | if (primitiveLength > 1) { 55 | if (TRACE) 56 | trace("kryo", "Class " + serializer.getType().getName() 57 | + ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength 58 | + ". Byte length = " + (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives 59 | + " endOffset=" + endPrimitives); 60 | // TODO: register a region instead of a field 61 | CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives)); 62 | cf.field = lastField; 63 | cachedFields.add(cf); 64 | } else { 65 | if (lastField != null) 66 | cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex)); 67 | } 68 | cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex)); 69 | } else if (!field.getType().isPrimitive()) { 70 | cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex)); 71 | } else if (!lastWasPrimitive) { 72 | // If previous field was non primitive, it marks a start 73 | // of a region of primitive fields 74 | startPrimitives = fieldOffset; 75 | lastWasPrimitive = true; 76 | primitiveLength = 1; 77 | } else { 78 | primitiveLength++; 79 | } 80 | 81 | lastAccessIndex = accessIndex; 82 | lastField = field; 83 | lastFieldEndOffset = fieldEndOffset; 84 | } 85 | 86 | if (!serializer.getUseAsmEnabled() && serializer.getUseMemRegions() && lastWasPrimitive) { 87 | endPrimitives = lastFieldEndOffset; 88 | if (primitiveLength > 1) { 89 | if (TRACE) { 90 | trace("kryo", "Class " + serializer.getType().getName() 91 | + ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength + ". Byte length = " 92 | + (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives + " endOffset=" + endPrimitives); 93 | } 94 | // register a region instead of a field 95 | CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives)); 96 | cf.field = lastField; 97 | cachedFields.add(cf); 98 | } else { 99 | if (lastField != null) cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex)); 100 | } 101 | } 102 | } 103 | 104 | /** Returns the in-memory size of a field which has a given class */ 105 | private int fieldSizeOf (Class clazz) { 106 | if (clazz == int.class || clazz == float.class) return 4; 107 | 108 | if (clazz == long.class || clazz == double.class) return 8; 109 | 110 | if (clazz == byte.class || clazz == boolean.class) return 1; 111 | 112 | if (clazz == short.class || clazz == char.class) return 2; 113 | 114 | // Everything else is a reference to an object, i.e. an address 115 | return unsafe().addressSize(); 116 | } 117 | 118 | long getObjectFieldOffset (Field field) { 119 | return unsafe().objectFieldOffset(field); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/JavaSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | 7 | import com.esotericsoftware.kryo.Kryo; 8 | import com.esotericsoftware.kryo.KryoException; 9 | import com.esotericsoftware.kryo.KryoSerializable; 10 | import com.esotericsoftware.kryo.Serializer; 11 | import com.esotericsoftware.kryo.io.Input; 12 | import com.esotericsoftware.kryo.io.Output; 13 | 14 | /** Serializes objects using Java's built in serialization mechanism. Note that this is very inefficient and should be avoided if 15 | * possible. 16 | * @see Serializer 17 | * @see FieldSerializer 18 | * @see KryoSerializable 19 | * @author Nathan Sweet */ 20 | public class JavaSerializer extends Serializer { 21 | private ObjectOutputStream objectStream; 22 | private Output lastOutput; 23 | 24 | public void write (Kryo kryo, Output output, Object object) { 25 | try { 26 | if (output != lastOutput) { 27 | objectStream = new ObjectOutputStream(output); 28 | lastOutput = output; 29 | } else 30 | objectStream.reset(); 31 | objectStream.writeObject(object); 32 | objectStream.flush(); 33 | } catch (Exception ex) { 34 | throw new KryoException("Error during Java serialization.", ex); 35 | } 36 | } 37 | 38 | public Object read (Kryo kryo, Input input, Class type) { 39 | try { 40 | return new ObjectInputStream(input).readObject(); 41 | } catch (Exception ex) { 42 | throw new KryoException("Error during Java deserialization.", ex); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/MapSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import java.util.Iterator; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | 8 | import com.esotericsoftware.kryo.Kryo; 9 | import com.esotericsoftware.kryo.Serializer; 10 | import com.esotericsoftware.kryo.io.Input; 11 | import com.esotericsoftware.kryo.io.Output; 12 | 13 | /** Serializes objects that implement the {@link Map} interface. 14 | *

    15 | * With the default constructor, a map requires a 1-3 byte header and an extra 4 bytes is written for each key/value pair. 16 | * @author Nathan Sweet */ 17 | public class MapSerializer extends Serializer { 18 | private Class keyClass, valueClass; 19 | private Serializer keySerializer, valueSerializer; 20 | private boolean keysCanBeNull = true, valuesCanBeNull = true; 21 | private Class keyGenericType, valueGenericType; 22 | 23 | /** @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 24 | * (default). */ 25 | public void setKeysCanBeNull (boolean keysCanBeNull) { 26 | this.keysCanBeNull = keysCanBeNull; 27 | } 28 | 29 | /** @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 30 | * per key (default). 31 | * @param keySerializer The serializer to use for each key. */ 32 | public void setKeyClass (Class keyClass, Serializer keySerializer) { 33 | this.keyClass = keyClass; 34 | this.keySerializer = keySerializer; 35 | } 36 | 37 | /** @param valueClass The concrete class of each value. This saves 1 byte per value. Set to null if the class is not known or 38 | * varies per value (default). 39 | * @param valueSerializer The serializer to use for each value. */ 40 | public void setValueClass (Class valueClass, Serializer valueSerializer) { 41 | this.valueClass = valueClass; 42 | this.valueSerializer = valueSerializer; 43 | } 44 | 45 | /** @param valuesCanBeNull True if values are not null. This saves 1 byte per value if keyClass is set. False if it is not known 46 | * (default). */ 47 | public void setValuesCanBeNull (boolean valuesCanBeNull) { 48 | this.valuesCanBeNull = valuesCanBeNull; 49 | } 50 | 51 | public void setGenerics (Kryo kryo, Class[] generics) { 52 | keyGenericType = null; 53 | valueGenericType = null; 54 | 55 | if (generics != null && generics.length > 0) { 56 | if (generics[0] != null && kryo.isFinal(generics[0])) keyGenericType = generics[0]; 57 | if (generics.length > 1 && generics[1] != null && kryo.isFinal(generics[1])) valueGenericType = generics[1]; 58 | } 59 | } 60 | 61 | public void write (Kryo kryo, Output output, Map map) { 62 | int length = map.size(); 63 | output.writeInt(length, true); 64 | 65 | Serializer keySerializer = this.keySerializer; 66 | if (keyGenericType != null) { 67 | if (keySerializer == null) keySerializer = kryo.getSerializer(keyGenericType); 68 | keyGenericType = null; 69 | } 70 | Serializer valueSerializer = this.valueSerializer; 71 | if (valueGenericType != null) { 72 | if (valueSerializer == null) valueSerializer = kryo.getSerializer(valueGenericType); 73 | valueGenericType = null; 74 | } 75 | 76 | for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { 77 | Entry entry = (Entry)iter.next(); 78 | if (keySerializer != null) { 79 | if (keysCanBeNull) 80 | kryo.writeObjectOrNull(output, entry.getKey(), keySerializer); 81 | else 82 | kryo.writeObject(output, entry.getKey(), keySerializer); 83 | } else 84 | kryo.writeClassAndObject(output, entry.getKey()); 85 | if (valueSerializer != null) { 86 | if (valuesCanBeNull) 87 | kryo.writeObjectOrNull(output, entry.getValue(), valueSerializer); 88 | else 89 | kryo.writeObject(output, entry.getValue(), valueSerializer); 90 | } else 91 | kryo.writeClassAndObject(output, entry.getValue()); 92 | } 93 | } 94 | 95 | /** Used by {@link #read(Kryo, Input, Class)} to create the new object. This can be overridden to customize object creation, eg 96 | * to call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */ 97 | protected Map create (Kryo kryo, Input input, Class type) { 98 | return kryo.newInstance(type); 99 | } 100 | 101 | public Map read (Kryo kryo, Input input, Class type) { 102 | Map map = create(kryo, input, type); 103 | int length = input.readInt(true); 104 | 105 | Class keyClass = this.keyClass; 106 | Class valueClass = this.valueClass; 107 | 108 | Serializer keySerializer = this.keySerializer; 109 | if (keyGenericType != null) { 110 | keyClass = keyGenericType; 111 | if (keySerializer == null) keySerializer = kryo.getSerializer(keyClass); 112 | keyGenericType = null; 113 | } 114 | Serializer valueSerializer = this.valueSerializer; 115 | if (valueGenericType != null) { 116 | valueClass = valueGenericType; 117 | if (valueSerializer == null) valueSerializer = kryo.getSerializer(valueClass); 118 | valueGenericType = null; 119 | } 120 | 121 | kryo.reference(map); 122 | 123 | for (int i = 0; i < length; i++) { 124 | Object key; 125 | if (keySerializer != null) { 126 | if (keysCanBeNull) 127 | key = kryo.readObjectOrNull(input, keyClass, keySerializer); 128 | else 129 | key = kryo.readObject(input, keyClass, keySerializer); 130 | } else 131 | key = kryo.readClassAndObject(input); 132 | Object value; 133 | if (valueSerializer != null) { 134 | if (valuesCanBeNull) 135 | value = kryo.readObjectOrNull(input, valueClass, valueSerializer); 136 | else 137 | value = kryo.readObject(input, valueClass, valueSerializer); 138 | } else 139 | value = kryo.readClassAndObject(input); 140 | map.put(key, value); 141 | } 142 | return map; 143 | } 144 | 145 | protected Map createCopy (Kryo kryo, Map original) { 146 | return kryo.newInstance(original.getClass()); 147 | } 148 | 149 | public Map copy (Kryo kryo, Map original) { 150 | Map copy = createCopy(kryo, original); 151 | for (Iterator iter = original.entrySet().iterator(); iter.hasNext();) { 152 | Entry entry = (Entry)iter.next(); 153 | copy.put(kryo.copy(entry.getKey()), kryo.copy(entry.getValue())); 154 | } 155 | return copy; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/ObjectCachedFieldFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 6 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedFieldFactory; 7 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectBooleanField; 8 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectByteField; 9 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectCharField; 10 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectDoubleField; 11 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectFloatField; 12 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectIntField; 13 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectLongField; 14 | import com.esotericsoftware.kryo.serializers.ObjectField.ObjectShortField; 15 | 16 | class ObjectCachedFieldFactory implements CachedFieldFactory { 17 | public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser) { 18 | CachedField cachedField; 19 | if (fieldClass.isPrimitive()) { 20 | if (fieldClass == boolean.class) 21 | cachedField = new ObjectBooleanField(ser); 22 | else if (fieldClass == byte.class) 23 | cachedField = new ObjectByteField(ser); 24 | else if (fieldClass == char.class) 25 | cachedField = new ObjectCharField(ser); 26 | else if (fieldClass == short.class) 27 | cachedField = new ObjectShortField(ser); 28 | else if (fieldClass == int.class) 29 | cachedField = new ObjectIntField(ser); 30 | else if (fieldClass == long.class) 31 | cachedField = new ObjectLongField(ser); 32 | else if (fieldClass == float.class) 33 | cachedField = new ObjectFloatField(ser); 34 | else if (fieldClass == double.class) 35 | cachedField = new ObjectDoubleField(ser); 36 | else { 37 | cachedField = new ObjectField(ser); 38 | } 39 | } else 40 | cachedField = new ObjectField(ser); 41 | return cachedField; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/ObjectField.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import static com.esotericsoftware.minlog.Log.TRACE; 4 | import static com.esotericsoftware.minlog.Log.trace; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.KryoException; 8 | import com.esotericsoftware.kryo.Registration; 9 | import com.esotericsoftware.kryo.Serializer; 10 | import com.esotericsoftware.kryo.io.Input; 11 | import com.esotericsoftware.kryo.io.Output; 12 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 13 | import com.esotericsoftware.reflectasm.FieldAccess; 14 | 15 | /*** Defer generation of serializers until it is really required at run-time. By default, use reflection-based approach. 16 | * @author Nathan Sweet 17 | * @author Roman Levenstein */ 18 | class ObjectField extends CachedField { 19 | public Class[] generics; 20 | final FieldSerializer fieldSerializer; 21 | final Class type; 22 | final Kryo kryo; 23 | 24 | ObjectField (FieldSerializer fieldSerializer) { 25 | this.fieldSerializer = fieldSerializer; 26 | this.kryo = fieldSerializer.kryo; 27 | this.type = fieldSerializer.type; 28 | } 29 | 30 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 31 | return field.get(object); 32 | } 33 | 34 | public void setField (Object object, Object value) throws IllegalArgumentException, IllegalAccessException { 35 | field.set(object, value); 36 | } 37 | 38 | final public void write (Output output, Object object) { 39 | try { 40 | // if(typeVar2concreteClass != null) { 41 | // // Push a new scope for generics 42 | // kryo.pushGenericsScope(type, new Generics(typeVar2concreteClass)); 43 | // } 44 | 45 | if (TRACE) 46 | trace("kryo", "Write field: " + this + " (" + object.getClass().getName() + ")" + " pos=" + output.position()); 47 | 48 | Object value = getField(object); 49 | 50 | Serializer serializer = this.serializer; 51 | if (valueClass == null) { 52 | // The concrete type of the field is unknown, write the class first. 53 | if (value == null) { 54 | kryo.writeClass(output, null); 55 | return; 56 | } 57 | Registration registration = kryo.writeClass(output, value.getClass()); 58 | if (serializer == null) serializer = registration.getSerializer(); 59 | // if (generics != null) 60 | serializer.setGenerics(kryo, generics); 61 | kryo.writeObject(output, value, serializer); 62 | } else { 63 | // The concrete type of the field is known, always use the same serializer. 64 | if (serializer == null) this.serializer = serializer = kryo.getSerializer(valueClass); 65 | // if (generics != null) 66 | serializer.setGenerics(kryo, generics); 67 | if (canBeNull) { 68 | kryo.writeObjectOrNull(output, value, serializer); 69 | } else { 70 | if (value == null) { 71 | throw new KryoException("Field value is null but canBeNull is false: " + this + " (" + object.getClass().getName() + ")"); 72 | } 73 | kryo.writeObject(output, value, serializer); 74 | } 75 | } 76 | } catch (IllegalAccessException ex) { 77 | throw new KryoException("Error accessing field: " + this + " (" + object.getClass().getName() + ")", ex); 78 | } catch (KryoException ex) { 79 | ex.addTrace(this + " (" + object.getClass().getName() + ")"); 80 | throw ex; 81 | } catch (RuntimeException runtimeEx) { 82 | KryoException ex = new KryoException(runtimeEx); 83 | ex.addTrace(this + " (" + object.getClass().getName() + ")"); 84 | throw ex; 85 | } finally { 86 | // if(typeVar2concreteClass != null) 87 | // kryo.popGenericsScope(); 88 | } 89 | } 90 | 91 | final public void read (Input input, Object object) { 92 | try { 93 | if (TRACE) trace("kryo", "Read field: " + this + " (" + type.getName() + ")" + " pos=" + input.position()); 94 | Object value; 95 | 96 | Class concreteType = valueClass; 97 | Serializer serializer = this.serializer; 98 | if (concreteType == null) { 99 | Registration registration = kryo.readClass(input); 100 | if (registration == null) 101 | value = null; 102 | else { 103 | if (serializer == null) serializer = registration.getSerializer(); 104 | // if (generics != null) 105 | serializer.setGenerics(kryo, generics); 106 | value = kryo.readObject(input, registration.getType(), serializer); 107 | } 108 | } else { 109 | if (serializer == null) this.serializer = serializer = kryo.getSerializer(valueClass); 110 | // if (generics != null) 111 | serializer.setGenerics(kryo, generics); 112 | if (canBeNull) 113 | value = kryo.readObjectOrNull(input, concreteType, serializer); 114 | else 115 | value = kryo.readObject(input, concreteType, serializer); 116 | } 117 | 118 | setField(object, value); 119 | } catch (IllegalAccessException ex) { 120 | throw new KryoException("Error accessing field: " + this + " (" + type.getName() + ")", ex); 121 | } catch (KryoException ex) { 122 | ex.addTrace(this + " (" + type.getName() + ")"); 123 | throw ex; 124 | } catch (RuntimeException runtimeEx) { 125 | KryoException ex = new KryoException(runtimeEx); 126 | ex.addTrace(this + " (" + type.getName() + ")"); 127 | throw ex; 128 | } finally { 129 | // if(typeVar2concreteClass != null) 130 | // kryo.popGenericsScope(); 131 | } 132 | } 133 | 134 | public void copy (Object original, Object copy) { 135 | try { 136 | if (accessIndex != -1) { 137 | FieldAccess access = (FieldAccess)fieldSerializer.access; 138 | access.set(copy, accessIndex, kryo.copy(access.get(original, accessIndex))); 139 | } else 140 | setField(copy, kryo.copy(getField(original))); 141 | } catch (IllegalAccessException ex) { 142 | throw new KryoException("Error accessing field: " + this + " (" + type.getName() + ")", ex); 143 | } catch (KryoException ex) { 144 | ex.addTrace(this + " (" + type.getName() + ")"); 145 | throw ex; 146 | } catch (RuntimeException runtimeEx) { 147 | KryoException ex = new KryoException(runtimeEx); 148 | ex.addTrace(this + " (" + type.getName() + ")"); 149 | throw ex; 150 | } 151 | } 152 | 153 | final static class ObjectIntField extends ObjectField { 154 | public ObjectIntField (FieldSerializer fieldSerializer) { 155 | super(fieldSerializer); 156 | } 157 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 158 | return field.getInt(object); 159 | } 160 | } 161 | 162 | final static class ObjectFloatField extends ObjectField { 163 | public ObjectFloatField (FieldSerializer fieldSerializer) { 164 | super(fieldSerializer); 165 | } 166 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 167 | return field.getFloat(object); 168 | } 169 | } 170 | 171 | final static class ObjectShortField extends ObjectField { 172 | public ObjectShortField (FieldSerializer fieldSerializer) { 173 | super(fieldSerializer); 174 | } 175 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 176 | return field.getShort(object); 177 | } 178 | } 179 | 180 | final static class ObjectByteField extends ObjectField { 181 | public ObjectByteField (FieldSerializer fieldSerializer) { 182 | super(fieldSerializer); 183 | } 184 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 185 | return field.getByte(object); 186 | } 187 | } 188 | 189 | final static class ObjectBooleanField extends ObjectField { 190 | public ObjectBooleanField (FieldSerializer fieldSerializer) { 191 | super(fieldSerializer); 192 | } 193 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 194 | return field.getBoolean(object); 195 | } 196 | } 197 | 198 | final static class ObjectCharField extends ObjectField { 199 | public ObjectCharField (FieldSerializer fieldSerializer) { 200 | super(fieldSerializer); 201 | } 202 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 203 | return field.getChar(object); 204 | } 205 | } 206 | 207 | final static class ObjectLongField extends ObjectField { 208 | public ObjectLongField (FieldSerializer fieldSerializer) { 209 | super(fieldSerializer); 210 | } 211 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 212 | return field.getLong(object); 213 | } 214 | } 215 | 216 | final static class ObjectDoubleField extends ObjectField { 217 | public ObjectDoubleField (FieldSerializer fieldSerializer) { 218 | super(fieldSerializer); 219 | } 220 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 221 | return field.getDouble(object); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/TaggedFieldSerializer.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import static com.esotericsoftware.minlog.Log.*; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.KryoException; 8 | import com.esotericsoftware.kryo.io.Input; 9 | import com.esotericsoftware.kryo.io.Output; 10 | 11 | import java.lang.annotation.ElementType; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | import java.lang.annotation.Target; 15 | import java.lang.reflect.Field; 16 | 17 | /** Serializes objects using direct field assignment for fields that have been {@link Tag tagged}. Fields without the {@link Tag} 18 | * annotation are not serialized. New tagged fields can be added without invalidating previously serialized bytes. If any tagged 19 | * field is removed, previously serialized bytes are invalidated. Instead of removing fields, apply the {@link Deprecated} 20 | * annotation and they will still be deserialized but won't be serialized. If fields are public, bytecode generation will be used 21 | * instead of reflection. 22 | * @author Nathan Sweet */ 23 | public class TaggedFieldSerializer extends FieldSerializer { 24 | private int[] tags; 25 | private int writeFieldCount; 26 | private boolean[] deprecated; 27 | 28 | public TaggedFieldSerializer (Kryo kryo, Class type) { 29 | super(kryo, type); 30 | } 31 | 32 | protected void initializeCachedFields () { 33 | CachedField[] fields = getFields(); 34 | // Remove untagged fields. 35 | for (int i = 0, n = fields.length; i < n; i++) { 36 | Field field = fields[i].getField(); 37 | if (field.getAnnotation(Tag.class) == null) { 38 | if (TRACE) trace("kryo", "Ignoring field without tag: " + fields[i]); 39 | super.removeField(field.getName()); 40 | } 41 | } 42 | // Cache tag values. 43 | fields = getFields(); 44 | tags = new int[fields.length]; 45 | deprecated = new boolean[fields.length]; 46 | writeFieldCount = fields.length; 47 | for (int i = 0, n = fields.length; i < n; i++) { 48 | Field field = fields[i].getField(); 49 | tags[i] = field.getAnnotation(Tag.class).value(); 50 | if (field.getAnnotation(Deprecated.class) != null) { 51 | deprecated[i] = true; 52 | writeFieldCount--; 53 | } 54 | } 55 | } 56 | 57 | public void removeField (String fieldName) { 58 | super.removeField(fieldName); 59 | initializeCachedFields(); 60 | } 61 | 62 | public void write (Kryo kryo, Output output, T object) { 63 | CachedField[] fields = getFields(); 64 | output.writeVarInt(writeFieldCount, true); // Can be used for null. 65 | for (int i = 0, n = fields.length; i < n; i++) { 66 | if (deprecated[i]) continue; 67 | output.writeVarInt(tags[i], true); 68 | fields[i].write(output, object); 69 | } 70 | } 71 | 72 | public T read (Kryo kryo, Input input, Class type) { 73 | T object = create(kryo, input, type); 74 | kryo.reference(object); 75 | int fieldCount = input.readVarInt(true); 76 | int[] tags = this.tags; 77 | CachedField[] fields = getFields(); 78 | for (int i = 0, n = fieldCount; i < n; i++) { 79 | int tag = input.readVarInt(true); 80 | 81 | CachedField cachedField = null; 82 | for (int ii = 0, nn = tags.length; ii < nn; ii++) { 83 | if (tags[ii] == tag) { 84 | cachedField = fields[ii]; 85 | break; 86 | } 87 | } 88 | if (cachedField == null) throw new KryoException("Unknown field tag: " + tag + " (" + getType().getName() + ")"); 89 | cachedField.read(input, object); 90 | } 91 | return object; 92 | } 93 | 94 | /** If true, this field will not be serialized. */ 95 | @Retention(RetentionPolicy.RUNTIME) 96 | @Target(ElementType.FIELD) 97 | public @interface Tag { 98 | int value(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/UnsafeCacheFields.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.serializers; 3 | 4 | import static com.esotericsoftware.kryo.util.UnsafeUtil.unsafe; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | import sun.misc.Unsafe; 9 | 10 | import com.esotericsoftware.kryo.Kryo; 11 | import com.esotericsoftware.kryo.KryoException; 12 | import com.esotericsoftware.kryo.io.Input; 13 | import com.esotericsoftware.kryo.io.Output; 14 | import com.esotericsoftware.kryo.io.UnsafeInput; 15 | import com.esotericsoftware.kryo.io.UnsafeMemoryInput; 16 | import com.esotericsoftware.kryo.io.UnsafeMemoryOutput; 17 | import com.esotericsoftware.kryo.io.UnsafeOutput; 18 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 19 | import com.esotericsoftware.reflectasm.FieldAccess; 20 | 21 | /*** Implementations of java.misc.Unsafe-based serializers for fields. 22 | * 23 | * @author Roman Levenstein */ 24 | public class UnsafeCacheFields { 25 | 26 | abstract static class UnsafeCachedField extends CachedField { 27 | UnsafeCachedField (long offset) { 28 | this.offset = offset; 29 | } 30 | } 31 | 32 | final static class UnsafeIntField extends UnsafeCachedField { 33 | public UnsafeIntField (Field f) { 34 | super(unsafe().objectFieldOffset(f)); 35 | } 36 | 37 | public void write (Output output, Object object) { 38 | if (varIntsEnabled) 39 | output.writeInt(unsafe().getInt(object, offset), false); 40 | else 41 | output.writeInt(unsafe().getInt(object, offset)); 42 | } 43 | 44 | public void read (Input input, Object object) { 45 | if (varIntsEnabled) 46 | unsafe().putInt(object, offset, input.readInt(false)); 47 | else 48 | unsafe().putInt(object, offset, input.readInt()); 49 | } 50 | 51 | public void copy (Object original, Object copy) { 52 | unsafe().putInt(copy, offset, unsafe().getInt(original, offset)); 53 | } 54 | } 55 | 56 | final static class UnsafeFloatField extends UnsafeCachedField { 57 | public UnsafeFloatField (Field f) { 58 | super(unsafe().objectFieldOffset(f)); 59 | } 60 | 61 | public void write (Output output, Object object) { 62 | output.writeFloat(unsafe().getFloat(object, offset)); 63 | } 64 | 65 | public void read (Input input, Object object) { 66 | unsafe().putFloat(object, offset, input.readFloat()); 67 | } 68 | 69 | public void copy (Object original, Object copy) { 70 | unsafe().putFloat(copy, offset, unsafe().getFloat(original, offset)); 71 | } 72 | } 73 | 74 | final static class UnsafeShortField extends UnsafeCachedField { 75 | public UnsafeShortField (Field f) { 76 | super(unsafe().objectFieldOffset(f)); 77 | } 78 | 79 | public void write (Output output, Object object) { 80 | output.writeShort(unsafe().getShort(object, offset)); 81 | } 82 | 83 | public void read (Input input, Object object) { 84 | unsafe().putShort(object, offset, input.readShort()); 85 | } 86 | 87 | public void copy (Object original, Object copy) { 88 | unsafe().putShort(copy, offset, unsafe().getShort(original, offset)); 89 | } 90 | } 91 | 92 | final static class UnsafeByteField extends UnsafeCachedField { 93 | public UnsafeByteField (Field f) { 94 | super(unsafe().objectFieldOffset(f)); 95 | } 96 | 97 | public void write (Output output, Object object) { 98 | output.writeByte(unsafe().getByte(object, offset)); 99 | } 100 | 101 | public void read (Input input, Object object) { 102 | unsafe().putByte(object, offset, input.readByte()); 103 | } 104 | 105 | public void copy (Object original, Object copy) { 106 | unsafe().putByte(copy, offset, unsafe().getByte(original, offset)); 107 | } 108 | } 109 | 110 | final static class UnsafeBooleanField extends UnsafeCachedField { 111 | public UnsafeBooleanField (Field f) { 112 | super(unsafe().objectFieldOffset(f)); 113 | } 114 | 115 | public void write (Output output, Object object) { 116 | output.writeBoolean(unsafe().getBoolean(object, offset)); 117 | } 118 | 119 | public void read (Input input, Object object) { 120 | unsafe().putBoolean(object, offset, input.readBoolean()); 121 | } 122 | 123 | public void copy (Object original, Object copy) { 124 | unsafe().putBoolean(copy, offset, unsafe().getBoolean(original, offset)); 125 | } 126 | } 127 | 128 | final static class UnsafeCharField extends UnsafeCachedField { 129 | public UnsafeCharField (Field f) { 130 | super(unsafe().objectFieldOffset(f)); 131 | } 132 | 133 | public void write (Output output, Object object) { 134 | output.writeChar(unsafe().getChar(object, offset)); 135 | } 136 | 137 | public void read (Input input, Object object) { 138 | unsafe().putChar(object, offset, input.readChar()); 139 | } 140 | 141 | public void copy (Object original, Object copy) { 142 | unsafe().putChar(copy, offset, unsafe().getChar(original, offset)); 143 | } 144 | } 145 | 146 | final static class UnsafeLongField extends UnsafeCachedField { 147 | public UnsafeLongField (Field f) { 148 | super(unsafe().objectFieldOffset(f)); 149 | } 150 | 151 | public void write (Output output, Object object) { 152 | if (varIntsEnabled) 153 | output.writeLong(unsafe().getLong(object, offset), false); 154 | else 155 | output.writeLong(unsafe().getLong(object, offset)); 156 | } 157 | 158 | public void read (Input input, Object object) { 159 | if (varIntsEnabled) 160 | unsafe().putLong(object, offset, input.readLong(false)); 161 | else 162 | unsafe().putLong(object, offset, input.readLong()); 163 | } 164 | 165 | public void copy (Object original, Object copy) { 166 | unsafe().putLong(copy, offset, unsafe().getLong(original, offset)); 167 | } 168 | } 169 | 170 | final static class UnsafeDoubleField extends UnsafeCachedField { 171 | public UnsafeDoubleField (Field f) { 172 | super(unsafe().objectFieldOffset(f)); 173 | } 174 | 175 | public void write (Output output, Object object) { 176 | output.writeDouble(unsafe().getDouble(object, offset)); 177 | } 178 | 179 | public void read (Input input, Object object) { 180 | unsafe().putDouble(object, offset, input.readDouble()); 181 | } 182 | 183 | public void copy (Object original, Object copy) { 184 | unsafe().putDouble(copy, offset, unsafe().getDouble(original, offset)); 185 | } 186 | } 187 | 188 | final static class UnsafeStringField extends UnsafeCachedField { 189 | public UnsafeStringField (Field f) { 190 | super(unsafe().objectFieldOffset(f)); 191 | } 192 | 193 | public void write (Output output, Object object) { 194 | output.writeString((String)unsafe().getObject(object, offset)); 195 | } 196 | 197 | public void read (Input input, Object object) { 198 | unsafe().putObject(object, offset, input.readString()); 199 | } 200 | 201 | public void copy (Object original, Object copy) { 202 | unsafe().putObject(copy, offset, unsafe().getObject(original, offset)); 203 | } 204 | } 205 | 206 | /** Helper class for doing bulk copies of memory regions containing adjacent primitive fields. Should be normally used only with 207 | * Unsafe streams to deliver best performance. */ 208 | final static class UnsafeRegionField extends UnsafeCachedField { 209 | final long len; 210 | static final boolean bulkReadsSupported = false; 211 | 212 | public UnsafeRegionField (long offset, long len) { 213 | super(offset); 214 | this.len = len; 215 | } 216 | 217 | final public void write (Output output, Object object) { 218 | if (output instanceof UnsafeOutput) { 219 | UnsafeOutput unsafeOutput = (UnsafeOutput)output; 220 | unsafeOutput.writeBytes(object, offset, len); 221 | } else if (output instanceof UnsafeMemoryOutput) { 222 | UnsafeMemoryOutput unsafeOutput = (UnsafeMemoryOutput)output; 223 | unsafeOutput.writeBytes(object, offset, len); 224 | } else { 225 | long off; 226 | Unsafe unsafe = unsafe(); 227 | for (off = offset; off < offset + len - 8; off += 8) { 228 | output.writeLong(unsafe.getLong(object, off)); 229 | } 230 | 231 | if (off < offset + len) { 232 | for (; off < offset + len; ++off) { 233 | output.write(unsafe.getByte(object, off)); 234 | } 235 | } 236 | } 237 | } 238 | 239 | final public void read (Input input, Object object) { 240 | if (bulkReadsSupported && input instanceof UnsafeInput) { 241 | UnsafeInput unsafeInput = (UnsafeInput)input; 242 | unsafeInput.readBytes(object, offset, len); 243 | } else if (bulkReadsSupported && input instanceof UnsafeMemoryInput) { 244 | UnsafeMemoryInput unsafeInput = (UnsafeMemoryInput)input; 245 | unsafeInput.readBytes(object, offset, len); 246 | } else { 247 | readSlow(input, object); 248 | } 249 | } 250 | 251 | /*** This is a fall-back solution for the case that bulk reading of bytes into object memory is not supported. Unfortunately, 252 | * current Oracle JDKs do not allow for bulk reading in this style due to problems with GC. 253 | * 254 | * @param input 255 | * @param object */ 256 | private void readSlow (Input input, Object object) { 257 | long off; 258 | Unsafe unsafe = unsafe(); 259 | for (off = offset; off < offset + len - 8; off += 8) { 260 | unsafe.putLong(object, off, input.readLong()); 261 | } 262 | 263 | if (off < offset + len) { 264 | for (; off < offset + len; ++off) { 265 | unsafe.putByte(object, off, input.readByte()); 266 | } 267 | } 268 | } 269 | 270 | public void copy (Object original, Object copy) { 271 | unsafe().copyMemory(original, offset, copy, offset, len); 272 | } 273 | } 274 | 275 | final static class UnsafeObjectField extends ObjectField { 276 | public UnsafeObjectField (FieldSerializer fieldSerializer) { 277 | super(fieldSerializer); 278 | } 279 | 280 | public Object getField (Object object) throws IllegalArgumentException, IllegalAccessException { 281 | if (offset >= 0) { 282 | return unsafe().getObject(object, offset); 283 | } else 284 | throw new KryoException("Unknown offset"); 285 | } 286 | 287 | public void setField (Object object, Object value) throws IllegalArgumentException, IllegalAccessException { 288 | if (offset != -1) 289 | unsafe().putObject(object, offset, value); 290 | else 291 | throw new KryoException("Unknown offset"); 292 | } 293 | 294 | public void copy (Object original, Object copy) { 295 | try { 296 | if (offset != -1) { 297 | unsafe().putObject(copy, offset, kryo.copy(unsafe().getObject(original, offset))); 298 | } else 299 | throw new KryoException("Unknown offset"); 300 | } catch (KryoException ex) { 301 | ex.addTrace(this + " (" + type.getName() + ")"); 302 | throw ex; 303 | } catch (RuntimeException runtimeEx) { 304 | KryoException ex = new KryoException(runtimeEx); 305 | ex.addTrace(this + " (" + type.getName() + ")"); 306 | throw ex; 307 | } 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/serializers/UnsafeCachedFieldFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.serializers; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; 6 | import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedFieldFactory; 7 | import com.esotericsoftware.kryo.serializers.UnsafeCacheFields.*; 8 | 9 | class UnsafeCachedFieldFactory implements CachedFieldFactory { 10 | public CachedField createCachedField (Class fieldClass, Field field, FieldSerializer ser) { 11 | CachedField cachedField; 12 | // Use Unsafe-based serializers 13 | if (fieldClass.isPrimitive()) { 14 | if (fieldClass == boolean.class) 15 | cachedField = new UnsafeBooleanField(field); 16 | else if (fieldClass == byte.class) 17 | cachedField = new UnsafeByteField(field); 18 | else if (fieldClass == char.class) 19 | cachedField = new UnsafeCharField(field); 20 | else if (fieldClass == short.class) 21 | cachedField = new UnsafeShortField(field); 22 | else if (fieldClass == int.class) 23 | cachedField = new UnsafeIntField(field); 24 | else if (fieldClass == long.class) 25 | cachedField = new UnsafeLongField(field); 26 | else if (fieldClass == float.class) 27 | cachedField = new UnsafeFloatField(field); 28 | else if (fieldClass == double.class) 29 | cachedField = new UnsafeDoubleField(field); 30 | else { 31 | cachedField = new UnsafeObjectField(ser); 32 | } 33 | } else if (fieldClass == String.class 34 | && (!ser.kryo.getReferences() || !ser.kryo.getReferenceResolver().useReferences(String.class))) { 35 | cachedField = new UnsafeStringField(field); 36 | } else { 37 | cachedField = new UnsafeObjectField(ser); 38 | } 39 | return cachedField; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/DefaultClassResolver.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.util; 3 | 4 | import com.esotericsoftware.kryo.ClassResolver; 5 | import com.esotericsoftware.kryo.Kryo; 6 | import com.esotericsoftware.kryo.KryoException; 7 | import com.esotericsoftware.kryo.Registration; 8 | import com.esotericsoftware.kryo.io.Input; 9 | import com.esotericsoftware.kryo.io.Output; 10 | 11 | import static com.esotericsoftware.kryo.util.Util.*; 12 | import static com.esotericsoftware.minlog.Log.*; 13 | 14 | /** Resolves classes by ID or by fully qualified class name. 15 | * @author Nathan Sweet */ 16 | public class DefaultClassResolver implements ClassResolver { 17 | static public final byte NAME = -1; 18 | 19 | protected Kryo kryo; 20 | 21 | protected final IntMap idToRegistration = new IntMap(); 22 | protected final ObjectMap classToRegistration = new ObjectMap(); 23 | 24 | protected IdentityObjectIntMap classToNameId; 25 | protected IntMap nameIdToClass; 26 | protected ObjectMap nameToClass; 27 | protected int nextNameId; 28 | 29 | private int memoizedClassId = -1; 30 | private Registration memoizedClassIdValue; 31 | private Class memoizedClass; 32 | private Registration memoizedClassValue; 33 | 34 | public void setKryo (Kryo kryo) { 35 | this.kryo = kryo; 36 | } 37 | 38 | public Registration register (Registration registration) { 39 | if (registration == null) throw new IllegalArgumentException("registration cannot be null."); 40 | if (TRACE) { 41 | if (registration.getId() == NAME) { 42 | trace("kryo", "Register class name: " + className(registration.getType()) + " (" 43 | + registration.getSerializer().getClass().getName() + ")"); 44 | } else { 45 | trace("kryo", "Register class ID " + registration.getId() + ": " + className(registration.getType()) + " (" 46 | + registration.getSerializer().getClass().getName() + ")"); 47 | } 48 | } 49 | classToRegistration.put(registration.getType(), registration); 50 | idToRegistration.put(registration.getId(), registration); 51 | if (registration.getType().isPrimitive()) classToRegistration.put(getWrapperClass(registration.getType()), registration); 52 | return registration; 53 | } 54 | 55 | public Registration registerImplicit (Class type) { 56 | return register(new Registration(type, kryo.getDefaultSerializer(type), NAME)); 57 | } 58 | 59 | public Registration getRegistration (Class type) { 60 | if (type == memoizedClass) return memoizedClassValue; 61 | Registration registration = classToRegistration.get(type); 62 | if (registration != null) { 63 | memoizedClass = type; 64 | memoizedClassValue = registration; 65 | } 66 | return registration; 67 | } 68 | 69 | public Registration getRegistration (int classID) { 70 | return idToRegistration.get(classID); 71 | } 72 | 73 | public Registration writeClass (Output output, Class type) { 74 | if (type == null) { 75 | if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Write", null); 76 | output.writeVarInt(Kryo.NULL, true); 77 | return null; 78 | } 79 | Registration registration = kryo.getRegistration(type); 80 | if (registration.getId() == NAME) 81 | writeName(output, type, registration); 82 | else { 83 | if (TRACE) trace("kryo", "Write class " + registration.getId() + ": " + className(type)); 84 | output.writeVarInt(registration.getId() + 2, true); 85 | } 86 | return registration; 87 | } 88 | 89 | protected void writeName (Output output, Class type, Registration registration) { 90 | output.writeVarInt(NAME + 2, true); 91 | if (classToNameId != null) { 92 | int nameId = classToNameId.get(type, -1); 93 | if (nameId != -1) { 94 | if (TRACE) trace("kryo", "Write class name reference " + nameId + ": " + className(type)); 95 | output.writeVarInt(nameId, true); 96 | return; 97 | } 98 | } 99 | // Only write the class name the first time encountered in object graph. 100 | if (TRACE) trace("kryo", "Write class name: " + className(type)); 101 | int nameId = nextNameId++; 102 | if (classToNameId == null) classToNameId = new IdentityObjectIntMap(); 103 | classToNameId.put(type, nameId); 104 | output.writeVarInt(nameId, true); 105 | output.writeString(type.getName()); 106 | } 107 | 108 | public Registration readClass (Input input) { 109 | int classID = input.readVarInt(true); 110 | switch (classID) { 111 | case Kryo.NULL: 112 | if (TRACE || (DEBUG && kryo.getDepth() == 1)) log("Read", null); 113 | return null; 114 | case NAME + 2: // Offset for NAME and NULL. 115 | return readName(input); 116 | } 117 | if (classID == memoizedClassId) return memoizedClassIdValue; 118 | Registration registration = idToRegistration.get(classID - 2); 119 | if (registration == null) throw new KryoException("Encountered unregistered class ID: " + (classID - 2)); 120 | if (TRACE) trace("kryo", "Read class " + (classID - 2) + ": " + className(registration.getType())); 121 | memoizedClassId = classID; 122 | memoizedClassIdValue = registration; 123 | return registration; 124 | } 125 | 126 | protected Registration readName (Input input) { 127 | int nameId = input.readVarInt(true); 128 | if (nameIdToClass == null) nameIdToClass = new IntMap(); 129 | Class type = nameIdToClass.get(nameId); 130 | if (type == null) { 131 | // Only read the class name the first time encountered in object graph. 132 | String className = input.readString(); 133 | if (nameToClass != null) type = nameToClass.get(className); 134 | if (type == null) { 135 | try { 136 | type = Class.forName(className, false, kryo.getClassLoader()); 137 | } catch (ClassNotFoundException ex) { 138 | throw new KryoException("Unable to find class: " + className, ex); 139 | } 140 | if (nameToClass == null) nameToClass = new ObjectMap(); 141 | nameToClass.put(className, type); 142 | } 143 | nameIdToClass.put(nameId, type); 144 | if (TRACE) trace("kryo", "Read class name: " + className); 145 | } else { 146 | if (TRACE) trace("kryo", "Read class name reference " + nameId + ": " + className(type)); 147 | } 148 | return kryo.getRegistration(type); 149 | } 150 | 151 | public void reset () { 152 | if (!kryo.isRegistrationRequired()) { 153 | if (classToNameId != null) classToNameId.clear(); 154 | if (nameIdToClass != null) nameIdToClass.clear(); 155 | nextNameId = 0; 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/DefaultStreamFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.util; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.StreamFactory; 8 | import com.esotericsoftware.kryo.io.Input; 9 | import com.esotericsoftware.kryo.io.Output; 10 | 11 | /** 12 | * StreamFactory which provides usual Input/Output streams, which are 13 | * present in all versions of Kryo. 14 | * 15 | * @author Roman Levenstein 16 | */ 17 | public class DefaultStreamFactory implements StreamFactory { 18 | 19 | private Kryo kryo; 20 | 21 | @Override 22 | public Input getInput() { 23 | return new Input(); 24 | } 25 | 26 | @Override 27 | public Input getInput(int bufferSize) { 28 | return new Input(bufferSize); 29 | } 30 | 31 | @Override 32 | public Input getInput(byte[] buffer) { 33 | return new Input(buffer); 34 | } 35 | 36 | @Override 37 | public Input getInput(byte[] buffer, int offset, int count) { 38 | return new Input(buffer, offset, count); 39 | } 40 | 41 | @Override 42 | public Input getInput(InputStream inputStream) { 43 | return new Input(inputStream); 44 | } 45 | 46 | @Override 47 | public Input getInput(InputStream inputStream, int bufferSize) { 48 | return new Input(inputStream, bufferSize); 49 | } 50 | 51 | @Override 52 | public Output getOutput() { 53 | return new Output(); 54 | } 55 | 56 | @Override 57 | public Output getOutput(int bufferSize) { 58 | return new Output(bufferSize); 59 | } 60 | 61 | @Override 62 | public Output getOutput(int bufferSize, int maxBufferSize) { 63 | return new Output(bufferSize, maxBufferSize); 64 | } 65 | 66 | @Override 67 | public Output getOutput(byte[] buffer) { 68 | return new Output(buffer); 69 | } 70 | 71 | @Override 72 | public Output getOutput(byte[] buffer, int maxBufferSize) { 73 | return new Output(buffer, maxBufferSize); 74 | } 75 | 76 | @Override 77 | public Output getOutput(OutputStream outputStream) { 78 | return new Output(outputStream); 79 | } 80 | 81 | @Override 82 | public Output getOutput(OutputStream outputStream, int bufferSize) { 83 | return new Output(outputStream, bufferSize); 84 | } 85 | 86 | @Override 87 | public void setKryo(Kryo kryo) { 88 | this.kryo = kryo; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/FastestStreamFactory.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.util; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.StreamFactory; 8 | import com.esotericsoftware.kryo.io.Input; 9 | import com.esotericsoftware.kryo.io.Output; 10 | import com.esotericsoftware.kryo.io.UnsafeInput; 11 | import com.esotericsoftware.kryo.io.UnsafeOutput; 12 | 13 | /** 14 | * This StreamFactory tries to provide fastest possible Input/Output streams on a given platform. 15 | * It may return sun.misc.Unsafe based implementations of streams, which are 16 | * very fast, but not portable across platforms. 17 | * 18 | * @author Roman Levenstein 19 | * 20 | */ 21 | public class FastestStreamFactory implements StreamFactory { 22 | 23 | static private boolean isUnsafe = UnsafeUtil.unsafe() != null; 24 | private Kryo kryo; 25 | 26 | @Override 27 | public Input getInput() { 28 | return (isUnsafe)? new UnsafeInput() : new Input(); 29 | } 30 | 31 | @Override 32 | public Input getInput(int bufferSize) { 33 | return (isUnsafe)? new UnsafeInput(bufferSize) : new Input(bufferSize); 34 | } 35 | 36 | @Override 37 | public Input getInput(byte[] buffer) { 38 | return (isUnsafe)? new UnsafeInput(buffer) : new Input(buffer); 39 | } 40 | 41 | @Override 42 | public Input getInput(byte[] buffer, int offset, int count) { 43 | return (isUnsafe)? new UnsafeInput(buffer, offset, count) : new Input(buffer, offset, count); 44 | } 45 | 46 | @Override 47 | public Input getInput(InputStream inputStream) { 48 | return (isUnsafe)? new UnsafeInput(inputStream) : new Input(inputStream); 49 | } 50 | 51 | @Override 52 | public Input getInput(InputStream inputStream, int bufferSize) { 53 | return (isUnsafe)? new UnsafeInput(inputStream, bufferSize) : new Input(inputStream, bufferSize); 54 | } 55 | 56 | @Override 57 | public Output getOutput() { 58 | return (isUnsafe)? new UnsafeOutput() : new Output(); 59 | } 60 | 61 | @Override 62 | public Output getOutput(int bufferSize) { 63 | return (isUnsafe)? new UnsafeOutput(bufferSize) : new Output(bufferSize); 64 | } 65 | 66 | @Override 67 | public Output getOutput(int bufferSize, int maxBufferSize) { 68 | return (isUnsafe)? new UnsafeOutput(bufferSize, maxBufferSize) : new Output(bufferSize, maxBufferSize); 69 | } 70 | 71 | @Override 72 | public Output getOutput(byte[] buffer) { 73 | return (isUnsafe)? new UnsafeOutput(buffer) : new Output(buffer); 74 | } 75 | 76 | @Override 77 | public Output getOutput(byte[] buffer, int maxBufferSize) { 78 | return (isUnsafe)? new UnsafeOutput(buffer, maxBufferSize) : new Output(buffer, maxBufferSize); 79 | } 80 | 81 | @Override 82 | public Output getOutput(OutputStream outputStream) { 83 | return (isUnsafe)? new UnsafeOutput(outputStream) : new Output(outputStream); 84 | } 85 | 86 | @Override 87 | public Output getOutput(OutputStream outputStream, int bufferSize) { 88 | return (isUnsafe)? new UnsafeOutput(outputStream, bufferSize) : new Output(outputStream, bufferSize); 89 | } 90 | 91 | @Override 92 | public void setKryo(Kryo kryo) { 93 | this.kryo = kryo; 94 | // Only use Unsafe-based streams if this Kryo instance supports it 95 | //isUnsafe = UnsafeUtil.unsafe() != null && kryo.getUnsafe(); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/IntArray.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.util; 3 | 4 | import java.util.Arrays; 5 | 6 | /** A resizable, ordered or unordered int array. Avoids the boxing that occurs with ArrayList. If unordered, this class 7 | * avoids a memory copy when removing elements (the last element is moved to the removed element's position). 8 | * @author Nathan Sweet */ 9 | public class IntArray { 10 | public int[] items; 11 | public int size; 12 | public boolean ordered; 13 | 14 | /** Creates an ordered array with a capacity of 16. */ 15 | public IntArray () { 16 | this(true, 16); 17 | } 18 | 19 | /** Creates an ordered array with the specified capacity. */ 20 | public IntArray (int capacity) { 21 | this(true, capacity); 22 | } 23 | 24 | /** @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a 25 | * memory copy. 26 | * @param capacity Any elements added beyond this will cause the backing array to be grown. */ 27 | public IntArray (boolean ordered, int capacity) { 28 | this.ordered = ordered; 29 | items = new int[capacity]; 30 | } 31 | 32 | /** Creates a new array containing the elements in the specific array. The new array will be ordered if the specific array is 33 | * ordered. The capacity is set to the number of elements, so any subsequent elements added will cause the backing array to be 34 | * grown. */ 35 | public IntArray (IntArray array) { 36 | this.ordered = array.ordered; 37 | size = array.size; 38 | items = new int[size]; 39 | System.arraycopy(array.items, 0, items, 0, size); 40 | } 41 | 42 | /** Creates a new ordered array containing the elements in the specified array. The capacity is set to the number of elements, 43 | * so any subsequent elements added will cause the backing array to be grown. */ 44 | public IntArray (int[] array) { 45 | this(true, array); 46 | } 47 | 48 | /** Creates a new array containing the elements in the specified array. The capacity is set to the number of elements, so any 49 | * subsequent elements added will cause the backing array to be grown. 50 | * @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a 51 | * memory copy. */ 52 | public IntArray (boolean ordered, int[] array) { 53 | this(ordered, array.length); 54 | size = array.length; 55 | System.arraycopy(array, 0, items, 0, size); 56 | } 57 | 58 | public void add (int value) { 59 | int[] items = this.items; 60 | if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f))); 61 | items[size++] = value; 62 | } 63 | 64 | public void addAll (IntArray array) { 65 | addAll(array, 0, array.size); 66 | } 67 | 68 | public void addAll (IntArray array, int offset, int length) { 69 | if (offset + length > array.size) 70 | throw new IllegalArgumentException("offset + length must be <= size: " + offset + " + " + length + " <= " + array.size); 71 | addAll(array.items, offset, length); 72 | } 73 | 74 | public void addAll (int[] array) { 75 | addAll(array, 0, array.length); 76 | } 77 | 78 | public void addAll (int[] array, int offset, int length) { 79 | int[] items = this.items; 80 | int sizeNeeded = size + length - offset; 81 | if (sizeNeeded >= items.length) items = resize(Math.max(8, (int)(sizeNeeded * 1.75f))); 82 | System.arraycopy(array, offset, items, size, length); 83 | size += length; 84 | } 85 | 86 | public int get (int index) { 87 | if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); 88 | return items[index]; 89 | } 90 | 91 | public void set (int index, int value) { 92 | if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); 93 | items[index] = value; 94 | } 95 | 96 | public void insert (int index, int value) { 97 | int[] items = this.items; 98 | if (size == items.length) items = resize(Math.max(8, (int)(size * 1.75f))); 99 | if (ordered) 100 | System.arraycopy(items, index, items, index + 1, size - index); 101 | else 102 | items[size] = items[index]; 103 | size++; 104 | items[index] = value; 105 | } 106 | 107 | public void swap (int first, int second) { 108 | if (first >= size) throw new IndexOutOfBoundsException(String.valueOf(first)); 109 | if (second >= size) throw new IndexOutOfBoundsException(String.valueOf(second)); 110 | int[] items = this.items; 111 | int firstValue = items[first]; 112 | items[first] = items[second]; 113 | items[second] = firstValue; 114 | } 115 | 116 | public boolean contains (int value) { 117 | int i = size - 1; 118 | int[] items = this.items; 119 | while (i >= 0) 120 | if (items[i--] == value) return true; 121 | return false; 122 | } 123 | 124 | public int indexOf (int value) { 125 | int[] items = this.items; 126 | for (int i = 0, n = size; i < n; i++) 127 | if (items[i] == value) return i; 128 | return -1; 129 | } 130 | 131 | public boolean removeValue (int value) { 132 | int[] items = this.items; 133 | for (int i = 0, n = size; i < n; i++) { 134 | if (items[i] == value) { 135 | removeIndex(i); 136 | return true; 137 | } 138 | } 139 | return false; 140 | } 141 | 142 | /** Removes and returns the item at the specified index. */ 143 | public int removeIndex (int index) { 144 | if (index >= size) throw new IndexOutOfBoundsException(String.valueOf(index)); 145 | int[] items = this.items; 146 | int value = items[index]; 147 | size--; 148 | if (ordered) 149 | System.arraycopy(items, index + 1, items, index, size - index); 150 | else 151 | items[index] = items[size]; 152 | return value; 153 | } 154 | 155 | /** Removes and returns the last item. */ 156 | public int pop () { 157 | return items[--size]; 158 | } 159 | 160 | /** Returns the last item. */ 161 | public int peek () { 162 | return items[size - 1]; 163 | } 164 | 165 | public void clear () { 166 | size = 0; 167 | } 168 | 169 | /** Reduces the size of the backing array to the size of the actual items. This is useful to release memory when many items have 170 | * been removed, or if it is known that more items will not be added. */ 171 | public void shrink () { 172 | resize(size); 173 | } 174 | 175 | /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many 176 | * items to avoid multiple backing array resizes. 177 | * @return {@link #items} */ 178 | public int[] ensureCapacity (int additionalCapacity) { 179 | int sizeNeeded = size + additionalCapacity; 180 | if (sizeNeeded >= items.length) resize(Math.max(8, sizeNeeded)); 181 | return items; 182 | } 183 | 184 | protected int[] resize (int newSize) { 185 | int[] newItems = new int[newSize]; 186 | int[] items = this.items; 187 | System.arraycopy(items, 0, newItems, 0, Math.min(items.length, newItems.length)); 188 | this.items = newItems; 189 | return newItems; 190 | } 191 | 192 | public void sort () { 193 | Arrays.sort(items, 0, size); 194 | } 195 | 196 | public void reverse () { 197 | for (int i = 0, lastIndex = size - 1, n = size / 2; i < n; i++) { 198 | int ii = lastIndex - i; 199 | int temp = items[i]; 200 | items[i] = items[ii]; 201 | items[ii] = temp; 202 | } 203 | } 204 | 205 | /** Reduces the size of the array to the specified size. If the array is already smaller than the specified size, no action is 206 | * taken. */ 207 | public void truncate (int newSize) { 208 | if (size > newSize) size = newSize; 209 | } 210 | 211 | public int[] toArray () { 212 | int[] array = new int[size]; 213 | System.arraycopy(items, 0, array, 0, size); 214 | return array; 215 | } 216 | 217 | public String toString () { 218 | if (size == 0) return "[]"; 219 | int[] items = this.items; 220 | StringBuilder buffer = new StringBuilder(32); 221 | buffer.append('['); 222 | buffer.append(items[0]); 223 | for (int i = 1; i < size; i++) { 224 | buffer.append(", "); 225 | buffer.append(items[i]); 226 | } 227 | buffer.append(']'); 228 | return buffer.toString(); 229 | } 230 | 231 | public String toString (String separator) { 232 | if (size == 0) return ""; 233 | int[] items = this.items; 234 | StringBuilder buffer = new StringBuilder(32); 235 | buffer.append(items[0]); 236 | for (int i = 1; i < size; i++) { 237 | buffer.append(separator); 238 | buffer.append(items[i]); 239 | } 240 | return buffer.toString(); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/ListReferenceResolver.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.util; 3 | 4 | import java.util.ArrayList; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.ReferenceResolver; 8 | 9 | /** Uses an {@link ArrayList} to track objects that have already been written. This is more efficient than 10 | * {@link MapReferenceResolver} for graphs with few objects, providing an approximate 15% increase in deserialization speed. This 11 | * should not be used for graphs with many objects because it uses a linear look up to find objects that have already been 12 | * written. 13 | * @author Nathan Sweet */ 14 | public class ListReferenceResolver implements ReferenceResolver { 15 | protected Kryo kryo; 16 | protected final ArrayList seenObjects = new ArrayList(); 17 | 18 | public void setKryo (Kryo kryo) { 19 | this.kryo = kryo; 20 | } 21 | 22 | public int addWrittenObject (Object object) { 23 | int id = seenObjects.size(); 24 | seenObjects.add(object); 25 | return id; 26 | } 27 | 28 | public int getWrittenId (Object object) { 29 | for (int i = 0, n = seenObjects.size(); i < n; i++) 30 | if (seenObjects.get(i) == object) return i; 31 | return -1; 32 | } 33 | 34 | public int nextReadId (Class type) { 35 | int id = seenObjects.size(); 36 | seenObjects.add(null); 37 | return id; 38 | } 39 | 40 | public void setReadObject (int id, Object object) { 41 | seenObjects.set(id, object); 42 | } 43 | 44 | public Object getReadObject (Class type, int id) { 45 | return seenObjects.get(id); 46 | } 47 | 48 | public void reset () { 49 | seenObjects.clear(); 50 | } 51 | 52 | /** Returns false for Boolean, Byte, Character, and Short. */ 53 | public boolean useReferences (Class type) { 54 | return !Util.isWrapperClass(type); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/MapReferenceResolver.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.util; 3 | 4 | import java.util.ArrayList; 5 | 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.esotericsoftware.kryo.ReferenceResolver; 8 | 9 | /** Uses an {@link IdentityObjectIntMap} to track objects that have already been written. This can handle graph with any number of 10 | * objects, but is slightly slower than {@link ListReferenceResolver} for graphs with few objects. 11 | * @author Nathan Sweet */ 12 | public class MapReferenceResolver implements ReferenceResolver { 13 | protected Kryo kryo; 14 | protected final IdentityObjectIntMap writtenObjects = new IdentityObjectIntMap(); 15 | protected final ArrayList readObjects = new ArrayList(); 16 | 17 | public void setKryo (Kryo kryo) { 18 | this.kryo = kryo; 19 | } 20 | 21 | public int addWrittenObject (Object object) { 22 | int id = writtenObjects.size; 23 | writtenObjects.put(object, id); 24 | return id; 25 | } 26 | 27 | public int getWrittenId (Object object) { 28 | return writtenObjects.get(object, -1); 29 | } 30 | 31 | public int nextReadId (Class type) { 32 | int id = readObjects.size(); 33 | readObjects.add(null); 34 | return id; 35 | } 36 | 37 | public void setReadObject (int id, Object object) { 38 | readObjects.set(id, object); 39 | } 40 | 41 | public Object getReadObject (Class type, int id) { 42 | return readObjects.get(id); 43 | } 44 | 45 | public void reset () { 46 | readObjects.clear(); 47 | writtenObjects.clear(); 48 | } 49 | 50 | /** Returns false for all primitive wrappers. */ 51 | public boolean useReferences (Class type) { 52 | return !Util.isWrapperClass(type); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/UnsafeUtil.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo.util; 2 | 3 | import static com.esotericsoftware.kryo.util.UnsafeUtil.unsafe; 4 | import static com.esotericsoftware.minlog.Log.*; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Field; 8 | import java.nio.ByteBuffer; 9 | import java.util.Arrays; 10 | import java.util.Comparator; 11 | import java.util.List; 12 | 13 | import sun.misc.Cleaner; 14 | import sun.misc.Unsafe; 15 | import sun.nio.ch.DirectBuffer; 16 | 17 | /** 18 | * A few utility methods for using @link{java.misc.Unsafe}, mostly for private 19 | * use. 20 | * 21 | * Use of Unsafe on Android is forbidden, as Android provides only a very limited 22 | * functionality for this class compared to the JDK version. 23 | * 24 | * @author Roman Levenstein 25 | */ 26 | 27 | public class UnsafeUtil { 28 | final static private Unsafe _unsafe; 29 | final static public long byteArrayBaseOffset; 30 | final static public long floatArrayBaseOffset; 31 | final static public long doubleArrayBaseOffset; 32 | final static public long intArrayBaseOffset; 33 | final static public long longArrayBaseOffset; 34 | final static public long shortArrayBaseOffset; 35 | final static public long charArrayBaseOffset; 36 | 37 | // Constructor to be used for creation of ByteBuffers that use preallocated memory regions 38 | static Constructor directByteBufferConstr; 39 | 40 | 41 | static { 42 | try { 43 | if (!Util.isAndroid) { 44 | java.lang.reflect.Field field = sun.misc.Unsafe.class 45 | .getDeclaredField("theUnsafe"); 46 | field.setAccessible(true); 47 | _unsafe = (sun.misc.Unsafe) field.get(null); 48 | byteArrayBaseOffset = _unsafe.arrayBaseOffset(byte[].class); 49 | charArrayBaseOffset = _unsafe.arrayBaseOffset(char[].class); 50 | shortArrayBaseOffset = _unsafe.arrayBaseOffset(short[].class); 51 | intArrayBaseOffset = _unsafe.arrayBaseOffset(int[].class); 52 | floatArrayBaseOffset = _unsafe.arrayBaseOffset(float[].class); 53 | longArrayBaseOffset = _unsafe.arrayBaseOffset(long[].class); 54 | doubleArrayBaseOffset = _unsafe.arrayBaseOffset(double[].class); 55 | } else { 56 | byteArrayBaseOffset = 0; 57 | charArrayBaseOffset = 0; 58 | shortArrayBaseOffset = 0; 59 | intArrayBaseOffset = 0; 60 | floatArrayBaseOffset = 0; 61 | longArrayBaseOffset = 0; 62 | doubleArrayBaseOffset = 0; 63 | _unsafe = null; 64 | } 65 | } catch (java.lang.Exception e) { 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | static { 71 | ByteBuffer buf = ByteBuffer.allocateDirect(1); 72 | try { 73 | directByteBufferConstr = buf.getClass().getDeclaredConstructor(long.class, int.class, Object.class); 74 | directByteBufferConstr.setAccessible(true); 75 | } catch (Exception e) { 76 | directByteBufferConstr = null; 77 | } 78 | } 79 | 80 | /*** 81 | * 82 | * @return instance of java.misc.Unsafe 83 | */ 84 | final static public Unsafe unsafe() { 85 | return _unsafe; 86 | } 87 | 88 | public static Field[] sortFieldsByOffset (List allFields) { 89 | Field[] allFieldsArray = allFields.toArray(new Field[] {}); 90 | 91 | Comparator fieldOffsetComparator = new Comparator() { 92 | @Override 93 | public int compare (Field f1, Field f2) { 94 | long offset1 = unsafe().objectFieldOffset(f1); 95 | long offset2 = unsafe().objectFieldOffset(f2); 96 | if (offset1 < offset2) return -1; 97 | if (offset1 == offset2) return 0; 98 | return 1; 99 | } 100 | }; 101 | 102 | Arrays.sort(allFieldsArray, fieldOffsetComparator); 103 | 104 | for (Field f : allFields) { 105 | if (TRACE) trace("kryo", "Field '" + f.getName() + "' at offset " + unsafe().objectFieldOffset(f)); 106 | } 107 | 108 | return allFieldsArray; 109 | } 110 | 111 | /*** 112 | * Create a ByteBuffer that uses a provided (off-heap) memory region instead of allocating a new one. 113 | * 114 | * @param address address of the memory region to be used for a ByteBuffer 115 | * @param maxBufferSize size of the memory region 116 | * @return a new ByteBuffer that uses a provided memory region instead of allocating a new one 117 | */ 118 | final static public ByteBuffer getDirectBufferAt(long address, int maxBufferSize) { 119 | if(directByteBufferConstr == null) 120 | return null; 121 | try { 122 | return directByteBufferConstr.newInstance(address, maxBufferSize, null); 123 | } catch (Exception e) { 124 | throw new RuntimeException("Cannot allocate ByteBuffer at a given address: " + address, e); 125 | } 126 | } 127 | 128 | /*** 129 | * Release a direct buffer. 130 | * 131 | * NOTE: If Cleaner is not accessible due to SecurityManager restrictions, reflection could be used 132 | * to obtain the "clean" method and then invoke it. 133 | */ 134 | static public void releaseBuffer(ByteBuffer niobuffer) { 135 | if(niobuffer != null && niobuffer.isDirect()) { 136 | Object cleaner = ((DirectBuffer) niobuffer).cleaner(); 137 | if(cleaner != null) 138 | ((Cleaner)cleaner).clean(); 139 | niobuffer = null; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/kryo/util/Util.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo.util; 3 | 4 | import static com.esotericsoftware.minlog.Log.*; 5 | 6 | /** A few utility methods, mostly for private use. 7 | * @author Nathan Sweet */ 8 | public class Util { 9 | static public boolean isAndroid; 10 | static { 11 | try { 12 | Class.forName("android.os.Process"); 13 | isAndroid = true; 14 | } catch (Exception ignored) { 15 | } 16 | } 17 | 18 | /** Returns the primitive wrapper class for a primitive class. 19 | * @param type Must be a primitive class. */ 20 | static public Class getWrapperClass (Class type) { 21 | if (type == int.class) 22 | return Integer.class; 23 | else if (type == float.class) 24 | return Float.class; 25 | else if (type == boolean.class) 26 | return Boolean.class; 27 | else if (type == long.class) 28 | return Long.class; 29 | else if (type == byte.class) 30 | return Byte.class; 31 | else if (type == char.class) 32 | return Character.class; 33 | else if (type == short.class) // 34 | return Short.class; 35 | else if (type == double.class) 36 | return Double.class; 37 | return Void.class; 38 | } 39 | 40 | /** Returns the primitive class for a primitive wrapper class. Otherwise returns the type parameter. 41 | * @param type Must be a wrapper class. */ 42 | static public Class getPrimitiveClass (Class type) { 43 | if (type == Integer.class) 44 | return int.class; 45 | else if (type == Float.class) 46 | return float.class; 47 | else if (type == Boolean.class) 48 | return boolean.class; 49 | else if (type == Long.class) 50 | return long.class; 51 | else if (type == Byte.class) 52 | return byte.class; 53 | else if (type == Character.class) 54 | return char.class; 55 | else if (type == Short.class) // 56 | return short.class; 57 | else if (type == Double.class) // 58 | return double.class; 59 | else if (type == Void.class) 60 | return void.class; 61 | return type; 62 | } 63 | 64 | static public boolean isWrapperClass (Class type) { 65 | return type == Integer.class || type == Float.class || type == Boolean.class || type == Long.class || type == Byte.class 66 | || type == Character.class || type == Short.class || type == Double.class; 67 | } 68 | 69 | /** Logs a message about an object. The log level and the string format of the object depend on the object type. */ 70 | static public void log (String message, Object object) { 71 | if (object == null) { 72 | if (TRACE) trace("kryo", message + ": null"); 73 | return; 74 | } 75 | Class type = object.getClass(); 76 | if (type.isPrimitive() || type == Boolean.class || type == Byte.class || type == Character.class || type == Short.class 77 | || type == Integer.class || type == Long.class || type == Float.class || type == Double.class || type == String.class) { 78 | if (TRACE) trace("kryo", message + ": " + string(object)); 79 | } else { 80 | debug("kryo", message + ": " + string(object)); 81 | } 82 | } 83 | 84 | /** Returns the object formatted as a string. The format depends on the object's type and whether {@link Object#toString()} has 85 | * been overridden. */ 86 | static public String string (Object object) { 87 | if (object == null) return "null"; 88 | Class type = object.getClass(); 89 | if (type.isArray()) return className(type); 90 | try { 91 | if (type.getMethod("toString", new Class[0]).getDeclaringClass() == Object.class) 92 | return TRACE ? className(type) : type.getSimpleName(); 93 | } catch (Exception ignored) { 94 | } 95 | return String.valueOf(object); 96 | } 97 | 98 | /** Returns the class formatted as a string. The format varies depending on the type. */ 99 | static public String className (Class type) { 100 | if (type.isArray()) { 101 | Class elementClass = getElementClass(type); 102 | StringBuilder buffer = new StringBuilder(16); 103 | for (int i = 0, n = getDimensionCount(type); i < n; i++) 104 | buffer.append("[]"); 105 | return className(elementClass) + buffer; 106 | } 107 | if (type.isPrimitive() || type == Object.class || type == Boolean.class || type == Byte.class || type == Character.class 108 | || type == Short.class || type == Integer.class || type == Long.class || type == Float.class || type == Double.class 109 | || type == String.class) { 110 | return type.getSimpleName(); 111 | } 112 | return type.getName(); 113 | } 114 | 115 | /** Returns the number of dimensions of an array. */ 116 | static public int getDimensionCount (Class arrayClass) { 117 | int depth = 0; 118 | Class nextClass = arrayClass.getComponentType(); 119 | while (nextClass != null) { 120 | depth++; 121 | nextClass = nextClass.getComponentType(); 122 | } 123 | return depth; 124 | } 125 | 126 | /** Returns the base element type of an n-dimensional array class. */ 127 | static public Class getElementClass (Class arrayClass) { 128 | Class elementClass = arrayClass; 129 | while (elementClass.getComponentType() != null) 130 | elementClass = elementClass.getComponentType(); 131 | return elementClass; 132 | } 133 | 134 | /** Converts an "int" value between endian systems. */ 135 | static public int swapInt(int i) { 136 | return ((i & 0xFF) << 24) | 137 | ((i & 0xFF00) << 8) | 138 | ((i & 0xFF0000) >> 8)| 139 | ((i >> 24) & 0xFF); 140 | } 141 | 142 | /** Converts a "long" value between endian systems. */ 143 | static public long swapLong(long value) { 144 | return 145 | ( ( ( value >> 0 ) & 0xff ) << 56 )| 146 | ( ( ( value >> 8 ) & 0xff ) << 48 )| 147 | ( ( ( value >> 16 ) & 0xff ) << 40 )| 148 | ( ( ( value >> 24 ) & 0xff ) << 32 )| 149 | ( ( ( value >> 32 ) & 0xff ) << 24 )| 150 | ( ( ( value >> 40 ) & 0xff ) << 16 )| 151 | ( ( ( value >> 48 ) & 0xff ) << 8 )| 152 | ( ( ( value >> 56 ) & 0xff ) << 0 ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/ArraySerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.ObjectArraySerializer; 5 | 6 | /** @author Nathan Sweet */ 7 | public class ArraySerializerTest extends KryoTestCase { 8 | { 9 | supportsCopy = true; 10 | } 11 | 12 | public void testArrays () { 13 | kryo.register(int[].class); 14 | kryo.register(int[][].class); 15 | kryo.register(int[][][].class); 16 | kryo.register(String[].class); 17 | kryo.register(Object[].class); 18 | roundTrip(4, 4, new Object[] {null, null}); 19 | roundTrip(6, 6, new Object[] {null, "2"}); 20 | roundTrip(6, 18, new int[] {1, 2, 3, 4}); 21 | roundTrip(7, 18, new int[] {1, 2, -100, 4}); 22 | roundTrip(9, 18, new int[] {1, 2, -100, 40000}); 23 | roundTrip(9, 20, new int[][] { {1, 2}, {100, 4}}); 24 | roundTrip(11, 22, new int[][] { {1}, {2}, {100}, {4}}); 25 | roundTrip(13, 24, new int[][][] { { {1}, {2}}, { {100}, {4}}}); 26 | roundTrip(12, 12, new String[] {"11", "2222", "3", "4"}); 27 | roundTrip(11, 11, new String[] {"11", "2222", null, "4"}); 28 | roundTrip(28, 51, 29 | new Object[] {new String[] {"11", "2222", null, "4"}, new int[] {1, 2, 3, 4}, new int[][] { {1, 2}, {100, 4}}}); 30 | 31 | ObjectArraySerializer serializer = new ObjectArraySerializer(kryo, String[].class); 32 | kryo.register(String[].class, serializer); 33 | serializer.setElementsAreSameType(true); 34 | roundTrip(11, 11, new String[] {"11", "2222", null, "4"}); 35 | serializer.setElementsAreSameType(false); 36 | roundTrip(11, 11, new String[] {"11", "2222", null, "4"}); 37 | roundTrip(5, 5, new String[] {null, null, null}); 38 | roundTrip(2, 2, new String[] {}); 39 | serializer.setElementsAreSameType(true); 40 | roundTrip(12, 12, new String[] {"11", "2222", "3", "4"}); 41 | serializer.setElementsCanBeNull(false); 42 | roundTrip(12, 12, new String[] {"11", "2222", "3", "4"}); 43 | 44 | serializer = new ObjectArraySerializer(kryo, Float[].class); 45 | kryo.register(Float[][].class, serializer); 46 | kryo.register(Float[].class, serializer); 47 | Float[][] array = new Float[4][]; 48 | array[0] = new Float[] {0.0f, 1.0f}; 49 | array[1] = null; 50 | array[2] = new Float[] {2.0f, 3.0f}; 51 | array[3] = new Float[] {3.0f}; 52 | roundTrip(31, 31, array); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/BeanSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.serializers.BeanSerializer; 5 | 6 | /** @author Nathan Sweet */ 7 | public class BeanSerializerTest extends KryoTestCase { 8 | { 9 | supportsCopy = true; 10 | } 11 | 12 | public void testBeanSerializer () { 13 | kryo.register(TestClass.class, new BeanSerializer(kryo, TestClass.class)); 14 | 15 | TestClass test = new TestClass(); 16 | test.setOptional(12); 17 | test.setNullField("value"); 18 | test.setText("123"); 19 | test.setChild(new TestClass()); 20 | roundTrip(37, 43, test); 21 | test.setNullField(null); 22 | roundTrip(33, 39, test); 23 | } 24 | 25 | static public class TestClass { 26 | private String text = "something"; 27 | private String nullField; 28 | private TestClass child; 29 | private TestClass child2; 30 | private float abc = 1.2f; 31 | private int optional; 32 | 33 | public String getText () { 34 | return text; 35 | } 36 | 37 | public void setText (String text) { 38 | this.text = text; 39 | } 40 | 41 | public String getNullField () { 42 | return nullField; 43 | } 44 | 45 | public void setNullField (String nullField) { 46 | this.nullField = nullField; 47 | } 48 | 49 | public TestClass getChild () { 50 | return child; 51 | } 52 | 53 | public void setChild (TestClass child) { 54 | this.child = child; 55 | } 56 | 57 | public TestClass getChild2 () { 58 | return child2; 59 | } 60 | 61 | public void setChild2 (TestClass child2) { 62 | this.child2 = child2; 63 | } 64 | 65 | public float getAbc () { 66 | return abc; 67 | } 68 | 69 | public void setAbc (float abc) { 70 | this.abc = abc; 71 | } 72 | 73 | public int getOptional () { 74 | return optional; 75 | } 76 | 77 | public void setOptional (int optional) { 78 | this.optional = optional; 79 | } 80 | 81 | public boolean equals (Object obj) { 82 | if (this == obj) return true; 83 | if (obj == null) return false; 84 | if (getClass() != obj.getClass()) return false; 85 | TestClass other = (TestClass)obj; 86 | if (Float.floatToIntBits(abc) != Float.floatToIntBits(other.abc)) return false; 87 | if (child == null) { 88 | if (other.child != null) return false; 89 | } else if (child != this && !child.equals(other.child)) return false; 90 | if (child2 == null) { 91 | if (other.child2 != null) return false; 92 | } else if (child2 != this && !child2.equals(other.child2)) return false; 93 | if (nullField == null) { 94 | if (other.nullField != null) return false; 95 | } else if (!nullField.equals(other.nullField)) return false; 96 | if (text == null) { 97 | if (other.text != null) return false; 98 | } else if (!text.equals(other.text)) return false; 99 | return true; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/BlowfishSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import javax.crypto.KeyGenerator; 5 | 6 | import com.esotericsoftware.kryo.serializers.BlowfishSerializer; 7 | import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; 8 | 9 | /** @author Nathan Sweet */ 10 | public class BlowfishSerializerTest extends KryoTestCase { 11 | public void testZip () throws Exception { 12 | byte[] key = KeyGenerator.getInstance("Blowfish").generateKey().getEncoded(); 13 | kryo.register(String.class, new BlowfishSerializer(new StringSerializer(), key)); 14 | roundTrip(49, 49, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/ChunkedTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.InputChunked; 6 | import com.esotericsoftware.kryo.io.Output; 7 | import com.esotericsoftware.kryo.io.OutputChunked; 8 | 9 | /** @author Nathan Sweet */ 10 | public class ChunkedTest extends KryoTestCase { 11 | public void testChunks () { 12 | Output output = new Output(512); 13 | output.writeInt(1234); 14 | OutputChunked outputChunked = new OutputChunked(output); 15 | outputChunked.writeInt(1); 16 | outputChunked.endChunks(); 17 | outputChunked.writeInt(2); 18 | outputChunked.endChunks(); 19 | outputChunked.writeInt(3); 20 | outputChunked.endChunks(); 21 | outputChunked.writeInt(4); 22 | outputChunked.endChunks(); 23 | outputChunked.writeInt(5); 24 | outputChunked.endChunks(); 25 | output.writeInt(5678); 26 | output.close(); 27 | 28 | Input input = new Input(output.getBuffer()); 29 | assertEquals(1234, input.readInt()); 30 | InputChunked inputChunked = new InputChunked(input); 31 | assertEquals(1, inputChunked.readInt()); 32 | inputChunked.nextChunks(); 33 | inputChunked.nextChunks(); // skip 3 34 | assertEquals(3, inputChunked.readInt()); 35 | inputChunked.nextChunks(); 36 | inputChunked.nextChunks(); // skip 4 37 | assertEquals(5, inputChunked.readInt()); 38 | assertEquals(5678, input.readInt()); 39 | input.close(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/CollectionSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.util.ArrayList; 5 | import java.util.LinkedList; 6 | import java.util.TreeMap; 7 | import java.util.TreeSet; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | 10 | import com.esotericsoftware.kryo.MapSerializerTest.KeyComparator; 11 | import com.esotericsoftware.kryo.MapSerializerTest.KeyThatIsntComparable; 12 | import com.esotericsoftware.kryo.serializers.CollectionSerializer; 13 | import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; 14 | 15 | /** @author Nathan Sweet */ 16 | public class CollectionSerializerTest extends KryoTestCase { 17 | { 18 | supportsCopy = true; 19 | } 20 | 21 | public void testCollections () { 22 | kryo.register(ArrayList.class); 23 | kryo.register(LinkedList.class); 24 | kryo.register(CopyOnWriteArrayList.class); 25 | roundTrip(11, 11, list("1", "2", "3")); 26 | roundTrip(13, 19, list("1", "2", null, 1, 2)); 27 | roundTrip(15, 24, list("1", "2", null, 1, 2, 5)); 28 | roundTrip(11, 11, list("1", "2", "3")); 29 | roundTrip(11, 11, list("1", "2", "3")); 30 | roundTrip(13, 13, list("1", "2", list("3"))); 31 | roundTrip(13, 13, new LinkedList(list("1", "2", list("3")))); 32 | roundTrip(13, 13, new CopyOnWriteArrayList(list("1", "2", list("3")))); 33 | 34 | CollectionSerializer serializer = new CollectionSerializer(); 35 | kryo.register(ArrayList.class, serializer); 36 | kryo.register(LinkedList.class, serializer); 37 | kryo.register(CopyOnWriteArrayList.class, serializer); 38 | serializer.setElementClass(String.class, kryo.getSerializer(String.class)); 39 | roundTrip(8, 8, list("1", "2", "3")); 40 | serializer.setElementClass(String.class, new StringSerializer()); 41 | roundTrip(8, 8, list("1", "2", "3")); 42 | serializer.setElementsCanBeNull(false); 43 | roundTrip(8, 8, list("1", "2", "3")); 44 | 45 | kryo.register(TreeSet.class); 46 | TreeSet set = new TreeSet(); 47 | set.add("1"); 48 | set.add("2"); 49 | roundTrip(9, 9, set); 50 | 51 | kryo.register(KeyThatIsntComparable.class); 52 | kryo.register(KeyComparator.class); 53 | set = new TreeSet(new KeyComparator()); 54 | set.add(new KeyThatIsntComparable("1")); 55 | set.add(new KeyThatIsntComparable("2")); 56 | roundTrip(9, 9, set); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/CompatibleFieldSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.io.FileNotFoundException; 5 | 6 | import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer; 7 | 8 | /** @author Nathan Sweet */ 9 | public class CompatibleFieldSerializerTest extends KryoTestCase { 10 | { 11 | supportsCopy = true; 12 | } 13 | 14 | public void testCompatibleFieldSerializer () throws FileNotFoundException { 15 | TestClass object1 = new TestClass(); 16 | object1.child = new TestClass(); 17 | object1.other = new AnotherClass(); 18 | object1.other.value = "meow"; 19 | kryo.setDefaultSerializer(CompatibleFieldSerializer.class); 20 | kryo.register(TestClass.class); 21 | kryo.register(AnotherClass.class); 22 | roundTrip(100, 100, object1); 23 | } 24 | 25 | public void testAddedField () throws FileNotFoundException { 26 | TestClass object1 = new TestClass(); 27 | object1.child = new TestClass(); 28 | object1.other = new AnotherClass(); 29 | object1.other.value = "meow"; 30 | 31 | CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); 32 | serializer.removeField("text"); 33 | kryo.register(TestClass.class, serializer); 34 | kryo.register(AnotherClass.class, new CompatibleFieldSerializer(kryo, AnotherClass.class)); 35 | roundTrip(74, 74, object1); 36 | 37 | kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); 38 | Object object2 = kryo.readClassAndObject(input); 39 | assertEquals(object1, object2); 40 | } 41 | 42 | public void testRemovedField () throws FileNotFoundException { 43 | TestClass object1 = new TestClass(); 44 | object1.child = new TestClass(); 45 | 46 | kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); 47 | roundTrip(88, 88, object1); 48 | 49 | CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); 50 | serializer.removeField("text"); 51 | kryo.register(TestClass.class, serializer); 52 | Object object2 = kryo.readClassAndObject(input); 53 | assertEquals(object1, object2); 54 | } 55 | 56 | static public class TestClass { 57 | public String text = "something"; 58 | public int moo = 120; 59 | public long moo2 = 1234120; 60 | public TestClass child; 61 | public int zzz = 123; 62 | public AnotherClass other; 63 | 64 | public boolean equals (Object obj) { 65 | if (this == obj) return true; 66 | if (obj == null) return false; 67 | if (getClass() != obj.getClass()) return false; 68 | TestClass other = (TestClass)obj; 69 | if (child == null) { 70 | if (other.child != null) return false; 71 | } else if (!child.equals(other.child)) return false; 72 | if (moo != other.moo) return false; 73 | if (moo2 != other.moo2) return false; 74 | if (text == null) { 75 | if (other.text != null) return false; 76 | } else if (!text.equals(other.text)) return false; 77 | if (zzz != other.zzz) return false; 78 | return true; 79 | } 80 | } 81 | 82 | static public class AnotherClass { 83 | String value; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/CopyTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.util.ArrayList; 5 | 6 | public class CopyTest extends KryoTestCase { 7 | protected void setUp () throws Exception { 8 | super.setUp(); 9 | kryo.setRegistrationRequired(false); 10 | } 11 | 12 | public void testBasic () { 13 | ArrayList test = new ArrayList(); 14 | test.add("one"); 15 | test.add("two"); 16 | test.add("three"); 17 | 18 | ArrayList copy = kryo.copy(test); 19 | assertTrue(test != copy); 20 | assertEquals(test, copy); 21 | } 22 | 23 | public void testNested () { 24 | ArrayList test = new ArrayList(); 25 | test.add("one"); 26 | test.add("two"); 27 | test.add("three"); 28 | 29 | ArrayList test2 = new ArrayList(); 30 | test2.add(1); 31 | test2.add(2f); 32 | test2.add(3d); 33 | test2.add((byte)4); 34 | test2.add((short)5); 35 | test.add(test2); 36 | 37 | ArrayList copy = kryo.copy(test); 38 | assertTrue(test != copy); 39 | assertTrue(test.get(3) != copy.get(3)); 40 | assertEquals(test, copy); 41 | } 42 | 43 | public void testReferences () { 44 | ArrayList test = new ArrayList(); 45 | test.add("one"); 46 | test.add("two"); 47 | test.add("three"); 48 | 49 | ArrayList test2 = new ArrayList(); 50 | test2.add(1); 51 | test2.add(2f); 52 | test2.add(3d); 53 | test2.add((byte)4); 54 | test2.add((short)5); 55 | test.add(test2); 56 | test.add(test2); 57 | test.add(test2); 58 | 59 | ArrayList copy = kryo.copy(test); 60 | assertTrue(test != copy); 61 | assertEquals(test, copy); 62 | assertTrue(test.get(3) != copy.get(4)); 63 | assertTrue(copy.get(3) == copy.get(4)); 64 | assertTrue(copy.get(3) == copy.get(5)); 65 | } 66 | 67 | public void testCircularReferences () { 68 | ArrayList test = new ArrayList(); 69 | test.add("one"); 70 | test.add("two"); 71 | test.add("three"); 72 | test.add(test); 73 | 74 | ArrayList copy = kryo.copy(test); 75 | assertTrue(test != copy); 76 | assertEquals(copy.get(0), "one"); 77 | assertEquals(copy.get(1), "two"); 78 | assertEquals(copy.get(2), "three"); 79 | assertTrue(copy.get(3) == copy); 80 | 81 | Moo root = new Moo(); 82 | Moo moo1 = new Moo(); 83 | Moo moo2 = new Moo(); 84 | Moo moo3 = new Moo(); 85 | root.moo = moo1; 86 | moo1.moo = moo2; 87 | moo2.moo = moo3; 88 | moo3.moo = root; 89 | Moo root2 = kryo.copy(root); 90 | assertTrue(root != root2); 91 | assertTrue(root.moo != root2.moo); 92 | assertTrue(root.moo.moo != root2.moo.moo); 93 | assertTrue(root.moo.moo.moo != root2.moo.moo.moo); 94 | assertTrue(root.moo.moo.moo.moo != root2.moo.moo.moo.moo); 95 | assertTrue(root.moo.moo.moo.moo == root); 96 | assertTrue(root2.moo.moo.moo.moo == root2); 97 | } 98 | 99 | public void testShallow () { 100 | ArrayList test = new ArrayList(); 101 | test.add("one"); 102 | test.add("two"); 103 | test.add("three"); 104 | 105 | ArrayList test2 = new ArrayList(); 106 | test2.add(1); 107 | test2.add(2f); 108 | test2.add(3d); 109 | test2.add((byte)4); 110 | test2.add((short)5); 111 | test.add(test2); 112 | 113 | ArrayList copy = kryo.copyShallow(test); 114 | assertTrue(test != copy); 115 | assertTrue(test.get(3) == copy.get(3)); 116 | assertEquals(test, copy); 117 | } 118 | 119 | static public class Moo { 120 | Moo moo; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/DefaultSerializersTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.math.BigDecimal; 5 | import java.math.BigInteger; 6 | import java.util.ArrayList; 7 | import java.util.Calendar; 8 | import java.util.Collections; 9 | import java.util.Date; 10 | import java.util.EnumSet; 11 | import java.util.TimeZone; 12 | 13 | import com.esotericsoftware.kryo.io.Input; 14 | import com.esotericsoftware.kryo.io.Output; 15 | 16 | /** @author Nathan Sweet */ 17 | public class DefaultSerializersTest extends KryoTestCase { 18 | { 19 | supportsCopy = true; 20 | } 21 | 22 | public void testBoolean () { 23 | roundTrip(2, 2, true); 24 | roundTrip(2, 2, false); 25 | } 26 | 27 | public void testByte () { 28 | roundTrip(2, 2, (byte)1); 29 | roundTrip(2, 2, (byte)125); 30 | roundTrip(2, 2, (byte)-125); 31 | } 32 | 33 | public void testChar () { 34 | roundTrip(3, 3, 'a'); 35 | roundTrip(3, 3, 'z'); 36 | } 37 | 38 | public void testDouble () { 39 | roundTrip(9, 9, 0d); 40 | roundTrip(9, 9, 1234d); 41 | roundTrip(9, 9, 1234.5678d); 42 | } 43 | 44 | public void testFloat () { 45 | roundTrip(5, 5, 0f); 46 | roundTrip(5, 5, 123f); 47 | roundTrip(5, 5, 123.456f); 48 | } 49 | 50 | public void testInt () { 51 | roundTrip(2, 5, 0); 52 | roundTrip(2, 5, 63); 53 | roundTrip(3, 5, 64); 54 | roundTrip(3, 5, 127); 55 | roundTrip(3, 5, 128); 56 | roundTrip(3, 5, 8191); 57 | roundTrip(4, 5, 8192); 58 | roundTrip(4, 5, 16383); 59 | roundTrip(4, 5, 16384); 60 | roundTrip(5, 5, 2097151); 61 | roundTrip(4, 5, 1048575); 62 | roundTrip(5, 5, 134217727); 63 | roundTrip(6, 5, 268435455); 64 | roundTrip(6, 5, 134217728); 65 | roundTrip(6, 5, 268435456); 66 | roundTrip(2, 5, -64); 67 | roundTrip(3, 5, -65); 68 | roundTrip(3, 5, -8192); 69 | roundTrip(4, 5, -1048576); 70 | roundTrip(5, 5, -134217728); 71 | roundTrip(6, 5, -134217729); 72 | } 73 | 74 | public void testLong () { 75 | roundTrip(2, 9, 0l); 76 | roundTrip(2, 9, 63l); 77 | roundTrip(3, 9, 64l); 78 | roundTrip(3, 9, 127l); 79 | roundTrip(3, 9, 128l); 80 | roundTrip(3, 9, 8191l); 81 | roundTrip(4, 9, 8192l); 82 | roundTrip(4, 9, 16383l); 83 | roundTrip(4, 9, 16384l); 84 | roundTrip(5, 9, 2097151l); 85 | roundTrip(4, 9, 1048575l); 86 | roundTrip(5, 9, 134217727l); 87 | roundTrip(6, 9, 268435455l); 88 | roundTrip(6, 9, 134217728l); 89 | roundTrip(6, 9, 268435456l); 90 | roundTrip(2, 9, -64l); 91 | roundTrip(3, 9, -65l); 92 | roundTrip(3, 9, -8192l); 93 | roundTrip(4, 9, -1048576l); 94 | roundTrip(5, 9, -134217728l); 95 | roundTrip(6, 9, -134217729l); 96 | roundTrip(10, 9, 2368365495612416452l); 97 | roundTrip(10, 9, -2368365495612416452l); 98 | } 99 | 100 | public void testShort () { 101 | roundTrip(3, 3, (short)0); 102 | roundTrip(3, 3, (short)123); 103 | roundTrip(3, 3, (short)123); 104 | roundTrip(3, 3, (short)-123); 105 | roundTrip(3, 3, (short)250); 106 | roundTrip(3, 3, (short)123); 107 | roundTrip(3, 3, (short)400); 108 | } 109 | 110 | public void testString () { 111 | kryo = new Kryo(); 112 | kryo.setRegistrationRequired(true); 113 | roundTrip(6, 6, "meow"); 114 | roundTrip(70, 70, "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"); 115 | 116 | kryo.setReferences(false); 117 | roundTrip(5, 5, "meow"); 118 | 119 | roundTrip(3, 3, "a"); 120 | roundTrip(3, 3, "\n"); 121 | roundTrip(2, 2, ""); 122 | roundTrip(100, 100, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\rabcdefghijklmnopqrstuvwxyz\n1234567890\t\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*"); 123 | 124 | roundTrip(21, 21, "abcdef\u00E1\u00E9\u00ED\u00F3\u00FA\u7C9F"); 125 | } 126 | 127 | public void testVoid () throws InstantiationException, IllegalAccessException { 128 | roundTrip(1, 1, (Void)null); 129 | } 130 | 131 | public void testNull () { 132 | kryo = new Kryo(); 133 | kryo.setRegistrationRequired(true); 134 | kryo.register(ArrayList.class); 135 | roundTrip(1, 1, null); 136 | testNull(Long.class); 137 | testNull(ArrayList.class); 138 | 139 | kryo.setReferences(false); 140 | roundTrip(1, 1, null); 141 | testNull(Long.class); 142 | testNull(ArrayList.class); 143 | } 144 | 145 | private void testNull (Class type) { 146 | kryo.writeObjectOrNull(output, null, type); 147 | input.setBuffer(output.toBytes()); 148 | Object object = kryo.readObjectOrNull(input, type); 149 | assertNull(object); 150 | } 151 | 152 | public void testDateSerializer () { 153 | kryo.register(Date.class); 154 | roundTrip(10, 9, new Date(-1234567)); 155 | roundTrip(2, 9, new Date(0)); 156 | roundTrip(4, 9, new Date(1234567)); 157 | roundTrip(10, 9, new Date(-1234567)); 158 | } 159 | 160 | public void testBigDecimalSerializer () { 161 | kryo.register(BigDecimal.class); 162 | roundTrip(5, 8, BigDecimal.valueOf(12345, 2)); 163 | } 164 | 165 | public void testBigIntegerSerializer () { 166 | kryo.register(BigInteger.class); 167 | roundTrip(8, 8, BigInteger.valueOf(1270507903945L)); 168 | } 169 | 170 | public void testEnumSerializer () { 171 | kryo.register(TestEnum.class); 172 | roundTrip(2, 2, TestEnum.a); 173 | roundTrip(2, 2, TestEnum.b); 174 | roundTrip(2, 2, TestEnum.c); 175 | 176 | kryo = new Kryo(); 177 | kryo.setRegistrationRequired(false); 178 | // 1 byte identifying it's a class name 179 | // 1 byte for the class name id 180 | // 57 bytes for the class name characters 181 | // 1 byte for the reference id 182 | // 1 byte for the enum value 183 | roundTrip(61, 61, TestEnum.c); 184 | } 185 | 186 | public void testEnumSetSerializer () { 187 | kryo.register(EnumSet.class); 188 | kryo.register(TestEnum.class); 189 | roundTrip(5, 8, EnumSet.of(TestEnum.a, TestEnum.c)); 190 | roundTrip(4, 7, EnumSet.of(TestEnum.a)); 191 | roundTrip(6, 9, EnumSet.allOf(TestEnum.class)); 192 | 193 | // Test empty EnumSet 194 | roundTrip(3, 6, EnumSet.noneOf(TestEnum.class)); 195 | 196 | kryo = new Kryo(); 197 | kryo.setRegistrationRequired(false); 198 | roundTrip(89, 92, EnumSet.of(TestEnum.a, TestEnum.c)); 199 | } 200 | 201 | public void testEnumSerializerWithMethods () { 202 | kryo.register(TestEnumWithMethods.class); 203 | roundTrip(2, 2, TestEnumWithMethods.a); 204 | roundTrip(2, 2, TestEnumWithMethods.b); 205 | roundTrip(2, 2, TestEnumWithMethods.c); 206 | 207 | kryo = new Kryo(); 208 | kryo.setRegistrationRequired(false); 209 | roundTrip(76, 76, TestEnumWithMethods.c); 210 | } 211 | 212 | public void testCollectionsMethods () { 213 | kryo.setRegistrationRequired(false); 214 | ArrayList test = new ArrayList(); 215 | test.add(Collections.EMPTY_LIST); 216 | test.add(Collections.EMPTY_MAP); 217 | test.add(Collections.EMPTY_SET); 218 | test.add(Collections.singletonList("meow")); 219 | test.add(Collections.singletonMap("moo", 1234)); 220 | test.add(Collections.singleton(12.34)); 221 | roundTrip(249, 251, test); 222 | } 223 | 224 | public void testCalendar () { 225 | kryo.setRegistrationRequired(false); 226 | Calendar calendar = Calendar.getInstance(); 227 | calendar.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); 228 | calendar.set(1980, 7, 26, 12, 22, 46); 229 | roundTrip(64, 73, calendar); 230 | } 231 | 232 | public void testClassSerializer() { 233 | kryo.register(Class.class); 234 | kryo.register(ArrayList.class); 235 | kryo.setRegistrationRequired(false); 236 | final Output out = new Output(1024); 237 | 238 | kryo.writeObject(out, String.class); 239 | kryo.writeObject(out, Integer.class); 240 | kryo.writeObject(out, Short.class); 241 | kryo.writeObject(out, Long.class); 242 | kryo.writeObject(out, Double.class); 243 | kryo.writeObject(out, Float.class); 244 | kryo.writeObject(out, Boolean.class); 245 | kryo.writeObject(out, Character.class); 246 | kryo.writeObject(out, Void.class); 247 | 248 | kryo.writeObject(out, int.class); 249 | kryo.writeObject(out, short.class); 250 | kryo.writeObject(out, long.class); 251 | kryo.writeObject(out, double.class); 252 | kryo.writeObject(out, float.class); 253 | kryo.writeObject(out, boolean.class); 254 | kryo.writeObject(out, char.class); 255 | kryo.writeObject(out, void.class); 256 | kryo.writeObject(out, ArrayList.class); 257 | kryo.writeObject(out, TestEnum.class); 258 | 259 | final Input in = new Input(out.getBuffer()); 260 | 261 | assertEquals(String.class, kryo.readObject(in, Class.class)); 262 | assertEquals(Integer.class, kryo.readObject(in, Class.class)); 263 | assertEquals(Short.class, kryo.readObject(in, Class.class)); 264 | assertEquals(Long.class, kryo.readObject(in, Class.class)); 265 | assertEquals(Double.class, kryo.readObject(in, Class.class)); 266 | assertEquals(Float.class, kryo.readObject(in, Class.class)); 267 | assertEquals(Boolean.class, kryo.readObject(in, Class.class)); 268 | assertEquals(Character.class, kryo.readObject(in, Class.class)); 269 | assertEquals(Void.class, kryo.readObject(in, Class.class)); 270 | assertEquals(int.class, kryo.readObject(in, Class.class)); 271 | assertEquals(short.class, kryo.readObject(in, Class.class)); 272 | assertEquals(long.class, kryo.readObject(in, Class.class)); 273 | assertEquals(double.class, kryo.readObject(in, Class.class)); 274 | assertEquals(float.class, kryo.readObject(in, Class.class)); 275 | assertEquals(boolean.class, kryo.readObject(in, Class.class)); 276 | assertEquals(char.class, kryo.readObject(in, Class.class)); 277 | assertEquals(void.class, kryo.readObject(in, Class.class)); 278 | assertEquals(ArrayList.class, kryo.readObject(in, Class.class)); 279 | assertEquals(TestEnum.class, kryo.readObject(in, Class.class)); 280 | } 281 | 282 | public enum TestEnum { 283 | a, b, c 284 | } 285 | 286 | public enum TestEnumWithMethods { 287 | a { 288 | }, 289 | b { 290 | }, 291 | c { 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/DeflateSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; 5 | import com.esotericsoftware.kryo.serializers.DeflateSerializer; 6 | 7 | /** @author Nathan Sweet */ 8 | public class DeflateSerializerTest extends KryoTestCase { 9 | public void testString () { 10 | kryo.register(String.class, new DeflateSerializer(new StringSerializer())); 11 | roundTrip(15, 15, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); 12 | } 13 | 14 | public void testGraph () { 15 | kryo.register(Message.class); 16 | kryo.register(MessageType.class); 17 | kryo.register(ServerPhysicsUpdate.class, new DeflateSerializer(kryo.getDefaultSerializer(ServerPhysicsUpdate.class))); 18 | 19 | ServerPhysicsUpdate physicsUpdate = new ServerPhysicsUpdate(); 20 | physicsUpdate.value = 1; 21 | Message message = new Message(); 22 | message.type = MessageType.SERVER_UPDATE; 23 | message.data = physicsUpdate; 24 | 25 | roundTrip(8, 8, message); 26 | } 27 | 28 | public static class ServerPhysicsUpdate { 29 | public int value; 30 | 31 | public ServerPhysicsUpdate () { 32 | } 33 | 34 | public int hashCode () { 35 | final int prime = 31; 36 | int result = 1; 37 | result = prime * result + value; 38 | return result; 39 | } 40 | 41 | public boolean equals (Object obj) { 42 | if (this == obj) return true; 43 | if (obj == null) return false; 44 | if (getClass() != obj.getClass()) return false; 45 | ServerPhysicsUpdate other = (ServerPhysicsUpdate)obj; 46 | if (value != other.value) return false; 47 | return true; 48 | } 49 | } 50 | 51 | public static enum MessageType { 52 | SERVER_UPDATE 53 | } 54 | 55 | public static class Message { 56 | public MessageType type; 57 | public Object data; 58 | 59 | public Message () { 60 | } 61 | 62 | public int hashCode () { 63 | final int prime = 31; 64 | int result = 1; 65 | result = prime * result + ((data == null) ? 0 : data.hashCode()); 66 | result = prime * result + ((type == null) ? 0 : type.hashCode()); 67 | return result; 68 | } 69 | 70 | public boolean equals (Object obj) { 71 | if (this == obj) return true; 72 | if (obj == null) return false; 73 | if (getClass() != obj.getClass()) return false; 74 | Message other = (Message)obj; 75 | if (data == null) { 76 | if (other.data != null) return false; 77 | } else if (!data.equals(other.data)) return false; 78 | if (type != other.type) return false; 79 | return true; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/JavaSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.io.Serializable; 5 | 6 | import com.esotericsoftware.kryo.serializers.JavaSerializer; 7 | 8 | /** @author Nathan Sweet */ 9 | public class JavaSerializerTest extends KryoTestCase { 10 | public void testJavaSerializer () { 11 | kryo.register(String.class, new JavaSerializer()); 12 | roundTrip(50, 50, "abcdefabcdefabcdefabcdefabcdefabcdefabcdef"); 13 | roundTrip(12, 12, "meow"); 14 | 15 | kryo.register(TestClass.class, new JavaSerializer()); 16 | TestClass test = new TestClass(); 17 | test.stringField = "fubar"; 18 | test.intField = 54321; 19 | roundTrip(134, 134, test); 20 | roundTrip(134, 134, test); 21 | roundTrip(134, 134, test); 22 | } 23 | 24 | static public class TestClass implements Serializable { 25 | String stringField; 26 | int intField; 27 | 28 | public boolean equals (Object obj) { 29 | if (this == obj) return true; 30 | if (obj == null) return false; 31 | if (getClass() != obj.getClass()) return false; 32 | TestClass other = (TestClass)obj; 33 | if (intField != other.intField) return false; 34 | if (stringField == null) { 35 | if (other.stringField != null) return false; 36 | } else if (!stringField.equals(other.stringField)) return false; 37 | return true; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/KryoTestCase.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.kryo; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.lang.reflect.Array; 6 | import java.util.ArrayList; 7 | 8 | import junit.framework.TestCase; 9 | 10 | import org.junit.Assert; 11 | 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | 15 | import com.esotericsoftware.kryo.io.FastInput; 16 | import com.esotericsoftware.kryo.io.FastOutput; 17 | import com.esotericsoftware.kryo.io.Input; 18 | import com.esotericsoftware.kryo.io.Output; 19 | import com.esotericsoftware.kryo.io.ByteBufferInput; 20 | import com.esotericsoftware.kryo.io.ByteBufferOutput; 21 | import com.esotericsoftware.kryo.io.UnsafeInput; 22 | import com.esotericsoftware.kryo.io.UnsafeOutput; 23 | import com.esotericsoftware.kryo.io.UnsafeMemoryInput; 24 | import com.esotericsoftware.kryo.io.UnsafeMemoryOutput; 25 | import com.esotericsoftware.minlog.Log; 26 | 27 | /** 28 | * Convenience methods for round tripping objects. 29 | * 30 | * @author Nathan Sweet 31 | */ 32 | abstract public class KryoTestCase extends TestCase { 33 | protected Kryo kryo; 34 | protected Output output; 35 | protected Input input; 36 | protected Object object1, object2; 37 | protected boolean supportsCopy; 38 | 39 | static interface StreamFactory { 40 | public Output createOutput(OutputStream os); 41 | 42 | public Output createOutput(OutputStream os, int size); 43 | 44 | public Output createOutput(int size, int limit); 45 | 46 | public Input createInput(InputStream os, int size); 47 | 48 | public Input createInput(byte[] buffer); 49 | } 50 | 51 | protected void setUp() throws Exception { 52 | Log.TRACE(); 53 | 54 | kryo = new Kryo(); 55 | kryo.setReferences(false); 56 | kryo.setRegistrationRequired(true); 57 | // kryo.useAsmBackend(false); 58 | } 59 | 60 | public T roundTrip(int length, int unsafeLength, T object1) { 61 | 62 | roundTripWithStreamFactory(unsafeLength, object1, new StreamFactory() { 63 | public Output createOutput(OutputStream os) { 64 | return new UnsafeMemoryOutput(os); 65 | } 66 | 67 | public Output createOutput(OutputStream os, int size) { 68 | return new UnsafeMemoryOutput(os, size); 69 | } 70 | 71 | public Output createOutput(int size, int limit) { 72 | return new UnsafeMemoryOutput(size, limit); 73 | } 74 | 75 | public Input createInput(InputStream os, int size) { 76 | return new UnsafeMemoryInput(os, size); 77 | } 78 | 79 | public Input createInput(byte[] buffer) { 80 | return new UnsafeMemoryInput(buffer); 81 | } 82 | }); 83 | 84 | roundTripWithStreamFactory(unsafeLength, object1, new StreamFactory() { 85 | public Output createOutput(OutputStream os) { 86 | return new UnsafeOutput(os); 87 | } 88 | 89 | public Output createOutput(OutputStream os, int size) { 90 | return new UnsafeOutput(os, size); 91 | } 92 | 93 | public Output createOutput(int size, int limit) { 94 | return new UnsafeOutput(size, limit); 95 | } 96 | 97 | public Input createInput(InputStream os, int size) { 98 | return new UnsafeInput(os, size); 99 | } 100 | 101 | public Input createInput(byte[] buffer) { 102 | return new UnsafeInput(buffer); 103 | } 104 | }); 105 | 106 | roundTripWithStreamFactory(length, object1, new StreamFactory() { 107 | public Output createOutput(OutputStream os) { 108 | return new ByteBufferOutput(os); 109 | } 110 | 111 | public Output createOutput(OutputStream os, int size) { 112 | return new ByteBufferOutput(os, size); 113 | } 114 | 115 | public Output createOutput(int size, int limit) { 116 | return new ByteBufferOutput(size, limit); 117 | } 118 | 119 | public Input createInput(InputStream os, int size) { 120 | return new ByteBufferInput(os, size); 121 | } 122 | 123 | public Input createInput(byte[] buffer) { 124 | return new ByteBufferInput(buffer); 125 | } 126 | }); 127 | 128 | roundTripWithStreamFactory(unsafeLength, object1, new StreamFactory() { 129 | public Output createOutput(OutputStream os) { 130 | return new FastOutput(os); 131 | } 132 | 133 | public Output createOutput(OutputStream os, int size) { 134 | return new FastOutput(os, size); 135 | } 136 | 137 | public Output createOutput(int size, int limit) { 138 | return new FastOutput(size, limit); 139 | } 140 | 141 | public Input createInput(InputStream os, int size) { 142 | return new FastInput(os, size); 143 | } 144 | 145 | public Input createInput(byte[] buffer) { 146 | return new FastInput(buffer); 147 | } 148 | }); 149 | 150 | return roundTripWithStreamFactory(length, object1, new StreamFactory() { 151 | public Output createOutput(OutputStream os) { 152 | return new Output(os); 153 | } 154 | 155 | public Output createOutput(OutputStream os, int size) { 156 | return new Output(os, size); 157 | } 158 | 159 | public Output createOutput(int size, int limit) { 160 | return new Output(size, limit); 161 | } 162 | 163 | public Input createInput(InputStream os, int size) { 164 | return new Input(os, size); 165 | } 166 | 167 | public Input createInput(byte[] buffer) { 168 | return new Input(buffer); 169 | } 170 | }); 171 | } 172 | 173 | public T roundTripWithStreamFactory(int length, T object1, 174 | StreamFactory sf) { 175 | this.object1 = object1; 176 | 177 | // Test output to stream, large buffer. 178 | ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 179 | output = sf.createOutput(outStream, 4096); 180 | kryo.writeClassAndObject(output, object1); 181 | output.flush(); 182 | 183 | // Test input from stream, large buffer. 184 | byte[] out = outStream.toByteArray(); 185 | input = sf.createInput( 186 | new ByteArrayInputStream(outStream.toByteArray()), 4096); 187 | object2 = kryo.readClassAndObject(input); 188 | assertEquals("Incorrect number of bytes read.", length, input.total()); 189 | assertEquals("Incorrect number of bytes written.", length, output.total()); 190 | assertEquals(object1, object2); 191 | 192 | // Test output to stream, small buffer. 193 | outStream = new ByteArrayOutputStream(); 194 | output = sf.createOutput(outStream, 10); 195 | kryo.writeClassAndObject(output, object1); 196 | output.flush(); 197 | 198 | // Test input from stream, small buffer. 199 | input = sf.createInput( 200 | new ByteArrayInputStream(outStream.toByteArray()), 10); 201 | object2 = kryo.readClassAndObject(input); 202 | assertEquals("Incorrect number of bytes read.", length, input.total()); 203 | assertEquals(object1, object2); 204 | 205 | if (object1 != null) { 206 | // Test null with serializer. 207 | Serializer serializer = kryo.getRegistration(object1.getClass()) 208 | .getSerializer(); 209 | output.clear(); 210 | outStream.reset(); 211 | kryo.writeObjectOrNull(output, null, serializer); 212 | output.flush(); 213 | 214 | // Test null from byte array with and without serializer. 215 | input = sf.createInput( 216 | new ByteArrayInputStream(outStream.toByteArray()), 10); 217 | assertEquals(null, kryo.readObjectOrNull(input, object1.getClass(), 218 | serializer)); 219 | 220 | input = sf.createInput( 221 | new ByteArrayInputStream(outStream.toByteArray()), 10); 222 | assertEquals(null, kryo.readObjectOrNull(input, object1.getClass())); 223 | } 224 | 225 | // Test output to byte array. 226 | output = sf.createOutput(length * 2, -1); 227 | kryo.writeClassAndObject(output, object1); 228 | output.flush(); 229 | 230 | // Test input from byte array. 231 | input = sf.createInput(output.toBytes()); 232 | object2 = kryo.readClassAndObject(input); 233 | assertEquals(object1, object2); 234 | assertEquals("Incorrect length.", length, output.total()); 235 | assertEquals("Incorrect number of bytes read.", length, input.total()); 236 | input.rewind(); 237 | 238 | if (supportsCopy) { 239 | // Test copy. 240 | T copy = kryo.copy(object1); 241 | assertEquals(object1, copy); 242 | copy = kryo.copyShallow(object1); 243 | assertEquals(object1, copy); 244 | } 245 | 246 | return (T) object2; 247 | } 248 | 249 | static public void assertEquals(Object object1, Object object2) { 250 | Assert.assertEquals(arrayToList(object1), arrayToList(object2)); 251 | } 252 | 253 | static public Object arrayToList(Object array) { 254 | if (array == null || !array.getClass().isArray()) 255 | return array; 256 | ArrayList list = new ArrayList(Array.getLength(array)); 257 | for (int i = 0, n = Array.getLength(array); i < n; i++) 258 | list.add(arrayToList(Array.get(array, i))); 259 | return list; 260 | } 261 | 262 | static public ArrayList list(Object... items) { 263 | ArrayList list = new ArrayList(); 264 | for (Object item : items) 265 | list.add(item); 266 | return list; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/MapSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | import java.util.Random; 9 | import java.util.TreeMap; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | import junit.framework.Assert; 13 | 14 | import com.esotericsoftware.kryo.io.Input; 15 | import com.esotericsoftware.kryo.io.Output; 16 | import com.esotericsoftware.kryo.serializers.MapSerializer; 17 | 18 | /** @author Nathan Sweet */ 19 | public class MapSerializerTest extends KryoTestCase { 20 | { 21 | supportsCopy = true; 22 | } 23 | 24 | public void testMaps () { 25 | kryo.register(HashMap.class); 26 | kryo.register(LinkedHashMap.class); 27 | HashMap map = new HashMap(); 28 | map.put("123", "456"); 29 | map.put("789", "abc"); 30 | roundTrip(18, 21, map); 31 | roundTrip(2, 5, new LinkedHashMap()); 32 | roundTrip(18, 21, new LinkedHashMap(map)); 33 | 34 | MapSerializer serializer = new MapSerializer(); 35 | kryo.register(HashMap.class, serializer); 36 | kryo.register(LinkedHashMap.class, serializer); 37 | serializer.setKeyClass(String.class, kryo.getSerializer(String.class)); 38 | serializer.setKeysCanBeNull(false); 39 | serializer.setValueClass(String.class, kryo.getSerializer(String.class)); 40 | roundTrip(14, 17, map); 41 | serializer.setValuesCanBeNull(false); 42 | roundTrip(14, 17, map); 43 | } 44 | 45 | public void testEmptyHashMap () { 46 | execute(new HashMap(), 0); 47 | } 48 | 49 | public void testNotEmptyHashMap () { 50 | execute(new HashMap(), 1000); 51 | } 52 | 53 | public void testEmptyConcurrentHashMap () { 54 | execute(new ConcurrentHashMap(), 0); 55 | } 56 | 57 | public void testNotEmptyConcurrentHashMap () { 58 | execute(new ConcurrentHashMap(), 1000); 59 | } 60 | 61 | public void testGenerics () { 62 | kryo.register(HasGenerics.class); 63 | kryo.register(Integer[].class); 64 | kryo.register(HashMap.class); 65 | 66 | HasGenerics test = new HasGenerics(); 67 | test.map.put("moo", new Integer[] {1, 2}); 68 | 69 | output = new Output(4096); 70 | kryo.writeClassAndObject(output, test); 71 | output.flush(); 72 | 73 | input = new Input(output.toBytes()); 74 | HasGenerics test2 = (HasGenerics)kryo.readClassAndObject(input); 75 | assertEquals(test.map.get("moo"), test2.map.get("moo")); 76 | } 77 | 78 | private void execute (Map map, int inserts) { 79 | Random random = new Random(); 80 | for (int i = 0; i < inserts; i++) 81 | map.put(random.nextLong(), random.nextBoolean()); 82 | 83 | Kryo kryo = new Kryo(); 84 | kryo.register(HashMap.class, new MapSerializer()); 85 | kryo.register(ConcurrentHashMap.class, new MapSerializer()); 86 | 87 | Output output = new Output(2048, -1); 88 | kryo.writeClassAndObject(output, map); 89 | output.close(); 90 | 91 | Input input = new Input(output.toBytes()); 92 | Object deserialized = kryo.readClassAndObject(input); 93 | input.close(); 94 | 95 | Assert.assertEquals(map, deserialized); 96 | } 97 | 98 | public void testTreeMap () { 99 | kryo.register(TreeMap.class); 100 | TreeMap map = new TreeMap(); 101 | map.put("123", "456"); 102 | map.put("789", "abc"); 103 | roundTrip(19, 22, map); 104 | 105 | kryo.register(KeyThatIsntComparable.class); 106 | kryo.register(KeyComparator.class); 107 | map = new TreeMap(new KeyComparator()); 108 | KeyThatIsntComparable key1 = new KeyThatIsntComparable(); 109 | KeyThatIsntComparable key2 = new KeyThatIsntComparable(); 110 | key1.value = "123"; 111 | map.put(key1, "456"); 112 | key2.value = "1234"; 113 | map.put(key2, "4567"); 114 | roundTrip(21, 24, map); 115 | } 116 | 117 | public void testTreeMapWithReferences () { 118 | kryo.setReferences(true); 119 | kryo.register(TreeMap.class); 120 | TreeMap map = new TreeMap(); 121 | map.put("123", "456"); 122 | map.put("789", "abc"); 123 | roundTrip(24, 27, map); 124 | 125 | kryo.register(KeyThatIsntComparable.class); 126 | kryo.register(KeyComparator.class); 127 | map = new TreeMap(new KeyComparator()); 128 | KeyThatIsntComparable key1 = new KeyThatIsntComparable(); 129 | KeyThatIsntComparable key2 = new KeyThatIsntComparable(); 130 | key1.value = "123"; 131 | map.put(key1, "456"); 132 | key2.value = "1234"; 133 | map.put(key2, "4567"); 134 | roundTrip(29, 32, map); 135 | } 136 | 137 | static public class HasGenerics { 138 | public HashMap map = new HashMap(); 139 | public HashMap map2 = new HashMap(); 140 | } 141 | 142 | static public class KeyComparator implements Comparator { 143 | public int compare (KeyThatIsntComparable o1, KeyThatIsntComparable o2) { 144 | return o1.value.compareTo(o2.value); 145 | } 146 | } 147 | 148 | static public class KeyThatIsntComparable { 149 | public String value; 150 | public KeyThatIsntComparable () { 151 | } 152 | public KeyThatIsntComparable (String value) { 153 | this.value = value; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/ReferenceTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.esotericsoftware.kryo.serializers.MapSerializer; 7 | 8 | import java.lang.reflect.Field; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.TreeMap; 13 | 14 | public class ReferenceTest extends KryoTestCase { 15 | static public class Ordering { 16 | public String order; 17 | } 18 | 19 | static public class Stuff extends TreeMap { 20 | public Ordering ordering; 21 | 22 | public Stuff (Ordering ordering) { 23 | this.ordering = ordering; 24 | } 25 | } 26 | 27 | public void testChildObjectBeforeReference () { 28 | Ordering ordering = new Ordering(); 29 | ordering.order = "assbackwards"; 30 | Stuff stuff = new Stuff(ordering); 31 | stuff.put("key", "value"); 32 | stuff.put("something", 456); 33 | stuff.put("self", stuff); 34 | 35 | Kryo kryo = new Kryo(); 36 | kryo.addDefaultSerializer(Stuff.class, new MapSerializer() { 37 | public void write (Kryo kryo, Output output, Map object) { 38 | kryo.writeObjectOrNull(output, ((Stuff)object).ordering, Ordering.class); 39 | super.write(kryo, output, object); 40 | } 41 | 42 | protected Map create (Kryo kryo, Input input, Class type) { 43 | Ordering ordering = kryo.readObjectOrNull(input, Ordering.class); 44 | return new Stuff(ordering); 45 | } 46 | }); 47 | 48 | Output output = new Output(512, -1); 49 | kryo.writeObject(output, stuff); 50 | 51 | Input input = new Input(output.getBuffer(), 0, output.position()); 52 | Stuff stuff2 = kryo.readObject(input, Stuff.class); 53 | 54 | assertEquals(stuff.ordering.order, stuff2.ordering.order); 55 | assertEquals(stuff.get("key"), stuff2.get("key")); 56 | assertEquals(stuff.get("something"), stuff2.get("something")); 57 | assertTrue(stuff.get("self") == stuff); 58 | assertTrue(stuff2.get("self") == stuff2); 59 | } 60 | 61 | public void testReadingNestedObjectsFirst () { 62 | ArrayList list = new ArrayList(); 63 | list.add("1"); 64 | list.add("1"); 65 | list.add("2"); 66 | list.add("1"); 67 | list.add("1"); 68 | List subList = list.subList(0, 5); 69 | 70 | kryo.setRegistrationRequired(false); 71 | kryo.register(ArrayList.class); 72 | Class subListClass = (Class)subList.getClass(); 73 | if(subListClass.getName().equals("java.util.ArrayList$SubList")) { 74 | // This is JDK > = 1.7 75 | kryo.register(subList.getClass(), new ArraySubListSerializer()); 76 | } else { 77 | kryo.register(subList.getClass(), new SubListSerializer()); 78 | } 79 | roundTrip(26, 26, subList); 80 | } 81 | 82 | static public class SubListSerializer extends Serializer { 83 | private Field listField, offsetField, sizeField; 84 | 85 | public SubListSerializer () { 86 | try { 87 | Class sublistClass = Class.forName("java.util.SubList"); 88 | listField = sublistClass.getDeclaredField("l"); 89 | offsetField = sublistClass.getDeclaredField("offset"); 90 | sizeField = sublistClass.getDeclaredField("size"); 91 | listField.setAccessible(true); 92 | offsetField.setAccessible(true); 93 | sizeField.setAccessible(true); 94 | } catch (Exception ex) { 95 | throw new RuntimeException(ex); 96 | } 97 | } 98 | 99 | public void write (Kryo kryo, Output output, List list) { 100 | try { 101 | kryo.writeClassAndObject(output, listField.get(list)); 102 | int fromIndex = offsetField.getInt(list); 103 | int count = sizeField.getInt(list); 104 | output.writeInt(fromIndex); 105 | output.writeInt(count); 106 | } catch (Exception ex) { 107 | throw new RuntimeException(ex); 108 | } 109 | } 110 | 111 | public List read (Kryo kryo, Input input, Class type) { 112 | List list = (List)kryo.readClassAndObject(input); 113 | int fromIndex = input.readInt(); 114 | int count = input.readInt(); 115 | return list.subList(fromIndex, fromIndex + count); 116 | } 117 | } 118 | 119 | static public class ArraySubListSerializer extends Serializer { 120 | private Field parentField, offsetField, sizeField; 121 | 122 | public ArraySubListSerializer () { 123 | try { 124 | Class sublistClass = Class.forName("java.util.ArrayList$SubList"); 125 | parentField = sublistClass.getDeclaredField("parent"); 126 | offsetField = sublistClass.getDeclaredField("offset"); 127 | sizeField = sublistClass.getDeclaredField("size"); 128 | parentField.setAccessible(true); 129 | offsetField.setAccessible(true); 130 | sizeField.setAccessible(true); 131 | } catch (Exception ex) { 132 | throw new RuntimeException(ex); 133 | } 134 | } 135 | 136 | public void write (Kryo kryo, Output output, List list) { 137 | try { 138 | kryo.writeClassAndObject(output, parentField.get(list)); 139 | int offset = offsetField.getInt(list); 140 | int size = sizeField.getInt(list); 141 | output.writeInt(offset); 142 | output.writeInt(size); 143 | } catch (Exception ex) { 144 | throw new RuntimeException(ex); 145 | } 146 | } 147 | 148 | public List read (Kryo kryo, Input input, Class type) { 149 | List list = (List)kryo.readClassAndObject(input); 150 | int offset = input.readInt(); 151 | int size = input.readInt(); 152 | return list.subList(offset, offset + size); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/kryo/TaggedFieldSerializerTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.esotericsoftware.kryo; 3 | 4 | import java.io.FileNotFoundException; 5 | 6 | import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer; 7 | import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer.Tag; 8 | 9 | public class TaggedFieldSerializerTest extends KryoTestCase { 10 | { 11 | supportsCopy = true; 12 | } 13 | 14 | public void testTaggedFieldSerializer () throws FileNotFoundException { 15 | TestClass object1 = new TestClass(); 16 | object1.moo = 2; 17 | object1.child = new TestClass(); 18 | object1.child.moo = 5; 19 | object1.other = new AnotherClass(); 20 | object1.other.value = "meow"; 21 | object1.ignored = 32; 22 | kryo.setDefaultSerializer(TaggedFieldSerializer.class); 23 | kryo.register(TestClass.class); 24 | kryo.register(AnotherClass.class); 25 | TestClass object2 = roundTrip(57, 75, object1); 26 | assertTrue(object2.ignored == 0); 27 | } 28 | 29 | public void testAddedField () throws FileNotFoundException { 30 | TestClass object1 = new TestClass(); 31 | object1.child = new TestClass(); 32 | object1.other = new AnotherClass(); 33 | object1.other.value = "meow"; 34 | 35 | TaggedFieldSerializer serializer = new TaggedFieldSerializer(kryo, TestClass.class); 36 | serializer.removeField("text"); 37 | kryo.register(TestClass.class, serializer); 38 | kryo.register(AnotherClass.class, new TaggedFieldSerializer(kryo, AnotherClass.class)); 39 | roundTrip(39, 55, object1); 40 | 41 | kryo.register(TestClass.class, new TaggedFieldSerializer(kryo, TestClass.class)); 42 | Object object2 = kryo.readClassAndObject(input); 43 | assertEquals(object1, object2); 44 | } 45 | 46 | static public class TestClass { 47 | @Tag(0) public String text = "something"; 48 | @Tag(1) public int moo = 120; 49 | @Tag(2) public long moo2 = 1234120; 50 | @Tag(3) public TestClass child; 51 | @Tag(4) public int zzz = 123; 52 | @Tag(5) public AnotherClass other; 53 | @Tag(6) @Deprecated public int ignored; 54 | 55 | public boolean equals (Object obj) { 56 | if (this == obj) return true; 57 | if (obj == null) return false; 58 | if (getClass() != obj.getClass()) return false; 59 | TestClass other = (TestClass)obj; 60 | if (child == null) { 61 | if (other.child != null) return false; 62 | } else if (!child.equals(other.child)) return false; 63 | if (moo != other.moo) return false; 64 | if (moo2 != other.moo2) return false; 65 | if (text == null) { 66 | if (other.text != null) return false; 67 | } else if (!text.equals(other.text)) return false; 68 | if (zzz != other.zzz) return false; 69 | return true; 70 | } 71 | } 72 | 73 | static public class AnotherClass { 74 | @Tag(1) String value; 75 | } 76 | } 77 | --------------------------------------------------------------------------------