Skip to content

Commit

Permalink
New feature: deep and shallow copy.
Browse files Browse the repository at this point in the history
Updated ReflectASM to 1.03, added support for creating objects via ReflectASM.
Less logging at debug level (only messages for root of object graph).
  • Loading branch information
NathanSweet committed Apr 28, 2012
1 parent afc812c commit fb22a95
Show file tree
Hide file tree
Showing 27 changed files with 1,238 additions and 54 deletions.
4 changes: 2 additions & 2 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<classpathentry excluding="**/.svn/*" kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="build/junit-4.6.jar"/>
<classpathentry exported="true" kind="lib" path="lib/minlog-1.2.jar"/>
<classpathentry exported="true" kind="lib" path="lib/asm-4.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/reflectasm-1.02.jar"/>
<classpathentry kind="lib" path="lib/objenesis-1.2.jar"/>
<classpathentry kind="lib" path="lib/reflectasm-1.03.jar"/>
<classpathentry kind="lib" path="lib/minlog-1.2.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Binary file removed lib/reflectasm-1.02.jar
Binary file not shown.
Binary file added lib/reflectasm-1.03.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>2.05</version>
<version>2.07</version>
<packaging>jar</packaging>
<name>Kryo</name>
<description>Fast, efficient Java serialization</description>
Expand Down
2 changes: 1 addition & 1 deletion project.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 2.05
version: 2.07
---
Build.build(project);
Build.oneJAR(project);
156 changes: 133 additions & 23 deletions src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@
import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer;
import com.esotericsoftware.kryo.serializers.FieldSerializer;
import com.esotericsoftware.kryo.serializers.MapSerializer;
import com.esotericsoftware.kryo.util.IdentityMap;
import com.esotericsoftware.kryo.util.IdentityObjectIntMap;
import com.esotericsoftware.kryo.util.IntMap;
import com.esotericsoftware.kryo.util.ObjectMap;
import com.esotericsoftware.reflectasm.ConstructorAccess;

import static com.esotericsoftware.kryo.Util.*;
import static com.esotericsoftware.minlog.Log.*;
Expand Down Expand Up @@ -89,6 +91,9 @@ public class Kryo {
private final IdentityObjectIntMap objectToInstanceId = new IdentityObjectIntMap();
private final ObjectMap<InstanceId, Object> instanceIdToObject = new ObjectMap();

private boolean copyShallow;
private IdentityMap originalToCopy;

public Kryo () {
addDefaultSerializer(byte[].class, ByteArraySerializer.class);
addDefaultSerializer(BigInteger.class, BigIntegerSerializer.class);
Expand Down Expand Up @@ -394,7 +399,7 @@ public Registration writeClass (Output output, Class type) {
if (output == null) throw new IllegalArgumentException("output cannot be null.");
try {
if (type == null) {
if (DEBUG) log("Write", null);
if (TRACE || (DEBUG && depth == 1)) log("Write", null);
output.writeByte(NULL);
return null;
}
Expand Down Expand Up @@ -430,7 +435,7 @@ public void writeObject (Output output, Object object) {
depth++;
try {
if (references && writeReferenceOrNull(output, object, false)) return;
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
getRegistration(object.getClass()).getSerializer().write(this, output, object);
} finally {
if (--depth == 0) reset();
Expand All @@ -445,7 +450,7 @@ public void writeObject (Output output, Object object, Serializer serializer) {
depth++;
try {
if (references && writeReferenceOrNull(output, object, false)) return;
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
serializer.write(this, output, object);
} finally {
if (--depth == 0) reset();
Expand All @@ -463,13 +468,13 @@ public void writeObjectOrNull (Output output, Object object) {
if (writeReferenceOrNull(output, object, true)) return;
} else if (!serializer.getAcceptsNull()) {
if (object == null) {
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
output.writeByte(NULL);
return;
}
output.writeByte(Kryo.NOT_NULL);
}
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
serializer.write(this, output, object);
} finally {
if (--depth == 0) reset();
Expand All @@ -487,13 +492,13 @@ public void writeObjectOrNull (Output output, Object object, Serializer serializ
if (writeReferenceOrNull(output, object, true)) return;
} else if (!serializer.getAcceptsNull()) {
if (object == null) {
if (DEBUG) log("Write", null);
if (TRACE || (DEBUG && depth == 1)) log("Write", null);
output.writeByte(NULL);
return;
}
output.writeByte(Kryo.NOT_NULL);
}
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
serializer.write(this, output, object);
} finally {
if (--depth == 0) reset();
Expand All @@ -512,7 +517,7 @@ public void writeClassAndObject (Output output, Object object) {
}
Registration registration = writeClass(output, object.getClass());
if (references && writeReferenceOrNull(output, object, false)) return;
if (DEBUG) log("Write", object);
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
registration.getSerializer().write(this, output, object);
} finally {
if (--depth == 0) reset();
Expand All @@ -522,7 +527,7 @@ public void writeClassAndObject (Output output, Object object) {
/** @param object May be null if mayBeNull is true. */
private boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) {
if (object == null) {
if (DEBUG) log("Write", null);
if (TRACE || (DEBUG && depth == 1)) log("Write", null);
output.writeByte(NULL);
return true;
}
Expand Down Expand Up @@ -553,7 +558,7 @@ public Registration readClass (Input input) {
int classID = input.readInt(true);
switch (classID) {
case NULL:
if (DEBUG) log("Read", null);
if (TRACE || (DEBUG && depth == 1)) log("Read", null);
return null;
case NAME + 2: // Offset for NAME and NULL.
int nameId = input.readInt(true);
Expand Down Expand Up @@ -597,8 +602,8 @@ public <T> T readObject (Input input, Class<T> type) {
Serializer serializer = getRegistration(type).getSerializer();
T object = (T)serializer.create(this, input, type);
if (instanceId != null) instanceIdToObject.put(instanceId, object);
serializer.read(this, input, object);
if (DEBUG) log("Read", object);
if (object != null) serializer.read(this, input, object);
if (TRACE || (DEBUG && depth == 1)) log("Read", object);
return object;
} finally {
if (--depth == 0) reset();
Expand All @@ -620,8 +625,8 @@ public <T> T readObject (Input input, Class<T> type, Serializer serializer) {

T object = (T)serializer.create(this, input, type);
if (instanceId != null) instanceIdToObject.put(instanceId, object);
serializer.read(this, input, object);
if (DEBUG) log("Read", object);
if (object != null) serializer.read(this, input, object);
if (TRACE || (DEBUG && depth == 1)) log("Read", object);
return object;
} finally {
if (--depth == 0) reset();
Expand All @@ -643,15 +648,15 @@ public <T> T readObjectOrNull (Input input, Class<T> type) {
if (instanceId == this.instanceId) return (T)instanceId.object;
} else if (!serializer.getAcceptsNull()) {
if (input.readByte() == NULL) {
if (DEBUG) log("Read", null);
if (TRACE || (DEBUG && depth == 1)) log("Read", null);
return null;
}
}

T object = (T)serializer.create(this, input, type);
if (instanceId != null) instanceIdToObject.put(instanceId, object);
serializer.read(this, input, object);
if (DEBUG) log("Read", object);
if (object != null) serializer.read(this, input, object);
if (TRACE || (DEBUG && depth == 1)) log("Read", object);
return object;
} finally {
if (--depth == 0) reset();
Expand All @@ -672,15 +677,15 @@ public <T> T readObjectOrNull (Input input, Class<T> type, Serializer serializer
if (instanceId == this.instanceId) return (T)instanceId.object;
} else if (!serializer.getAcceptsNull()) {
if (input.readByte() == NULL) {
if (DEBUG) log("Read", null);
if (TRACE || (DEBUG && depth == 1)) log("Read", null);
return null;
}
}

T object = (T)serializer.create(this, input, type);
if (instanceId != null) instanceIdToObject.put(instanceId, object);
serializer.read(this, input, object);
if (DEBUG) log("Read", object);
if (object != null) serializer.read(this, input, object);
if (TRACE || (DEBUG && depth == 1)) log("Read", object);
return object;
} finally {
if (--depth == 0) reset();
Expand All @@ -706,8 +711,8 @@ public Object readClassAndObject (Input input) {
Serializer serializer = registration.getSerializer();
Object object = serializer.create(this, input, type);
if (instanceId != null) instanceIdToObject.put(instanceId, object);
serializer.read(this, input, object);
if (DEBUG) log("Read", object);
if (object != null) serializer.read(this, input, object);
if (TRACE || (DEBUG && depth == 1)) log("Read", object);
return object;
} finally {
if (--depth == 0) reset();
Expand All @@ -723,7 +728,7 @@ private InstanceId readReferenceOrNull (Input input, Class type, boolean mayBeNu
if (mayBeNull) {
id = input.readInt(true);
if (id == NULL) {
if (DEBUG) log("Read", null);
if (TRACE || (DEBUG && depth == 1)) log("Read", null);
instanceId.object = null;
return instanceId;
}
Expand Down Expand Up @@ -759,9 +764,98 @@ protected void reset () {
instanceIdToObject.clear();
classToNextInstanceId.clear();
}
if (originalToCopy != null) originalToCopy.clear();
if (TRACE) trace("kryo", "Object graph complete.");
}

/** Returns a deep copy of the object. Serializers for the classes involved must support
* {@link Serializer#createCopy(Kryo, Object)}.
* @param object May be null. */
public <T> T copy (T object) {
if (object == null) return null;
if (copyShallow) return object;
depth++;
try {
if (originalToCopy == null) originalToCopy = new IdentityMap();
Object existingCopy = originalToCopy.get(object);
if (existingCopy != null) return (T)existingCopy;
Serializer serializer = getRegistration(object.getClass()).getSerializer();
Object copy = serializer.createCopy(this, object);
originalToCopy.put(object, copy);
serializer.copy(this, object, copy);
if (TRACE || (DEBUG && depth == 1)) log("Copy", copy);
return (T)copy;
} finally {
if (--depth == 0) reset();
}
}

/** Returns a deep copy of the object using the specified serializer. Serializers for the classes involved must support
* {@link Serializer#createCopy(Kryo, Object)}.
* @param object May be null. */
public <T> T copy (T object, Serializer serializer) {
if (object == null) return null;
if (copyShallow) return object;
depth++;
try {
if (originalToCopy == null) originalToCopy = new IdentityMap();
Object existingCopy = originalToCopy.get(object);
if (existingCopy != null) return (T)existingCopy;
Object copy = serializer.createCopy(this, object);
originalToCopy.put(object, copy);
serializer.copy(this, object, copy);
if (TRACE || (DEBUG && depth == 1)) log("Copy", copy);
return (T)copy;
} finally {
if (--depth == 0) reset();
}
}

/** Returns a shallow copy of the object. Serializers for the classes involved must support
* {@link Serializer#createCopy(Kryo, Object)}.
* @param object May be null. */
public <T> T copyShallow (T object) {
if (object == null) return null;
depth++;
copyShallow = true;
try {
if (originalToCopy == null) originalToCopy = new IdentityMap();
Object existingCopy = originalToCopy.get(object);
if (existingCopy != null) return (T)existingCopy;
Serializer serializer = getRegistration(object.getClass()).getSerializer();
Object copy = serializer.createCopy(this, object);
originalToCopy.put(object, copy);
serializer.copy(this, object, copy);
if (TRACE || (DEBUG && depth == 1)) log("Shallow copy", copy);
return (T)copy;
} finally {
copyShallow = false;
if (--depth == 0) reset();
}
}

/** Returns a shallow copy of the object using the specified serializer. Serializers for the classes involved must support
* {@link Serializer#createCopy(Kryo, Object)}.
* @param object May be null. */
public <T> T copyShallow (T object, Serializer serializer) {
if (object == null) return null;
depth++;
copyShallow = true;
try {
if (originalToCopy == null) originalToCopy = new IdentityMap();
Object existingCopy = originalToCopy.get(object);
if (existingCopy != null) return (T)existingCopy;
Object copy = serializer.createCopy(this, object);
originalToCopy.put(object, copy);
serializer.copy(this, object, copy);
if (TRACE || (DEBUG && depth == 1)) log("Shallow copy", copy);
return (T)copy;
} finally {
copyShallow = false;
if (--depth == 0) reset();
}
}

/** Sets the classloader to resolve unregistered class names to classes. */
public void setClassLoader (ClassLoader classLoader) {
if (classLoader == null) throw new IllegalArgumentException("classLoader cannot be null.");
Expand Down Expand Up @@ -818,6 +912,21 @@ public void setInstantiatorStrategy (InstantiatorStrategy strategy) {
* uses reflection if the class has a zero argument constructor, an exception is thrown. If a
* {@link #setInstantiatorStrategy(InstantiatorStrategy) strategy} is set, it will be used instead of throwing an exception. */
protected ObjectInstantiator newInstantiator (final Class type) {
// ReflectASM.
try {
final ConstructorAccess access = ConstructorAccess.get(type);
return new ObjectInstantiator() {
public Object newInstance () {
try {
return access.newInstance();
} catch (Exception ex) {
throw new KryoException("Error constructing instance of class: " + className(type), ex);
}
}
};
} catch (Exception ignored) {
}
// Reflection.
try {
Constructor ctor;
try {
Expand All @@ -844,6 +953,7 @@ public Object newInstance () {
else
throw new KryoException("Class cannot be created (missing no-arg constructor): " + className(type));
}
// InstantiatorStrategy.
return strategy.newInstantiatorOf(type);
}

Expand Down
Loading

0 comments on commit fb22a95

Please sign in to comment.