Skip to content

Commit

Permalink
Merge pull request EsotericSoftware#414 from magro/fieldserializerconfig
Browse files Browse the repository at this point in the history
Introduce FieldSerializerConfig to encapsulate config in Kryo
  • Loading branch information
magro committed Mar 30, 2016
2 parents 45e8cd8 + 4a47981 commit b4dfe28
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 83 deletions.
38 changes: 16 additions & 22 deletions src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.TreeSet;

import com.esotericsoftware.kryo.serializers.DefaultSerializers.URLSerializer;
import com.esotericsoftware.kryo.serializers.FieldSerializerConfig;
import com.esotericsoftware.kryo.serializers.OptionalSerializers;
import com.esotericsoftware.kryo.serializers.GenericsResolver;
import com.esotericsoftware.kryo.serializers.TimeSerializers;
Expand Down Expand Up @@ -143,12 +144,11 @@ public class Kryo {

private int copyDepth;
private boolean copyShallow;
private boolean copyTransient = true;
private IdentityMap originalToCopy;
private Object needsCopyReference;
private GenericsResolver genericsResolver = new GenericsResolver();
/** Tells if ASM-based backend should be used by new serializer instances created using this Kryo instance. */
private boolean asmEnabled = false;

private FieldSerializerConfig fieldSerializerConfig = new FieldSerializerConfig();

private StreamFactory streamFactory;

Expand Down Expand Up @@ -1058,22 +1058,11 @@ public void setCopyReferences (boolean copyReferences) {
this.copyReferences = copyReferences;
}

/**
* If false, when {@link #copy(Object)} is called all transient fields that are accessible will be ignored from
* being copied. This has to be set before registering classes with kryo for it to be used by all field
* serializers. If transient fields has to be copied for specific classes then use {@link FieldSerializer#setCopyTransient(boolean)}.
* Default is true.
*/
public void setCopyTransient(boolean copyTransient) {
this.copyTransient = copyTransient;
}

/**
* Returns true if copying of transient fields is enabled for {@link #copy(Object)}.
* @return true if transient field copy is enable
*/
public boolean getCopyTransient() {
return copyTransient;
/** The default configuration for {@link FieldSerializer} instances. Already existing serializer instances (e.g.
* implicitely created for already registered classes) are not affected by this configuration. You can override
* the configuration for a single {@link FieldSerializer}. */
public FieldSerializerConfig getFieldSerializerConfig() {
return fieldSerializerConfig;
}

/** Sets the reference resolver and enables references. */
Expand Down Expand Up @@ -1209,13 +1198,18 @@ public void setStreamFactory (StreamFactory streamFactory) {
* </p>
*
* @param flag if true, ASM-based backend will be used. Otherwise Unsafe-based backend could be used by some serializers, e.g.
* FieldSerializer */
* FieldSerializer
*
* @deprecated Use {@link #getFieldSerializerConfig()} to change the default {@link FieldSerializer} configuration. */
@Deprecated
public void setAsmEnabled (boolean flag) {
this.asmEnabled = flag;
fieldSerializerConfig.setUseAsm(flag);
}

/** @deprecated Use {@link #getFieldSerializerConfig()} to change the default {@link FieldSerializer} configuration. */
@Deprecated
public boolean getAsmEnabled () {
return asmEnabled;
return fieldSerializerConfig.isUseAsm();
}

static public class DefaultInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy {
Expand Down
82 changes: 22 additions & 60 deletions src/com/esotericsoftware/kryo/serializers/FieldSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,17 @@
* @author Nathan Sweet <[email protected]>
* @author Roman Levenstein <[email protected]> */
public class FieldSerializer<T> extends Serializer<T> implements Comparator<FieldSerializer.CachedField> {

final Kryo kryo;
final Class type;
/** type variables declared for this type */
final TypeVariable[] typeParameters;
final Class componentType;
private final FieldSerializerConfig config;
private CachedField[] fields = new CachedField[0];
private CachedField[] transientFields = new CachedField[0];
protected HashSet<CachedField> removedFields = new HashSet();
Object access;
private boolean fieldsCanBeNull = true, setFieldsAsAccessible = true;
private boolean ignoreSyntheticFields = true;
private boolean fixedFieldTypes;
/** If set, ASM-backend is used. Otherwise Unsafe-based backend or reflection is used */
private boolean useAsmEnabled;
private FieldSerializerUnsafeUtil unsafeUtil;

private FieldSerializerGenericsUtil genericsUtil;
Expand All @@ -101,9 +98,6 @@ public class FieldSerializer<T> extends Serializer<T> implements Comparator<Fiel
* </p> */
private boolean useMemRegions = false;

/** If set, transient fields will be copied */
private boolean copyTransient = true;

/** If set, transient fields will be serialized */
private final boolean serializeTransient = false;

Expand All @@ -130,29 +124,12 @@ public class FieldSerializer<T> extends Serializer<T> implements Comparator<Fiel
}

{
useAsmEnabled = !unsafeAvailable;
varIntsEnabled = true;
if (TRACE) trace("kryo", "Optimize ints: " + varIntsEnabled);
}

public FieldSerializer (Kryo kryo, Class type) {
this.kryo = kryo;
this.type = type;
this.typeParameters = type.getTypeParameters();
if (this.typeParameters == null || this.typeParameters.length == 0)
this.componentType = type.getComponentType();
else
this.componentType = null;
this.useAsmEnabled = kryo.getAsmEnabled();
if (!this.useAsmEnabled && !unsafeAvailable) {
this.useAsmEnabled = true;
if (TRACE) trace("kryo", "sun.misc.Unsafe is unavailable, using ASM.");
}
this.genericsUtil = new FieldSerializerGenericsUtil(this);
this.unsafeUtil = FieldSerializerUnsafeUtil.Factory.getInstance(this);
this.annotationsUtil = new FieldSerializerAnnotationsUtil(this);
this.copyTransient = kryo.getCopyTransient();
rebuildCachedFields();
this(kryo, type, null);
}

public FieldSerializer (Kryo kryo, Class type, Class[] generics) {
Expand All @@ -164,15 +141,10 @@ public FieldSerializer (Kryo kryo, Class type, Class[] generics) {
this.componentType = type.getComponentType();
else
this.componentType = null;
this.useAsmEnabled = kryo.getAsmEnabled();
if (!this.useAsmEnabled && !unsafeAvailable) {
this.useAsmEnabled = true;
if (TRACE) trace("kryo", "sun.misc.Unsafe is unavailable, using ASM.");
}
this.config = kryo.getFieldSerializerConfig().clone();
this.genericsUtil = new FieldSerializerGenericsUtil(this);
this.unsafeUtil = FieldSerializerUnsafeUtil.Factory.getInstance(this);
this.annotationsUtil = new FieldSerializerAnnotationsUtil(this);
this.copyTransient = kryo.getCopyTransient();
rebuildCachedFields();
}

Expand Down Expand Up @@ -226,7 +198,7 @@ protected void rebuildCachedFields (boolean minorRebuild) {
ObjectMap context = kryo.getContext();

// Sort fields by their offsets
if (useMemRegions && !useAsmEnabled && unsafeAvailable) {
if (useMemRegions && !config.isUseAsm() && unsafeAvailable) {
try {
Field[] allFieldsArray = (Field[])sortFieldsByOffsetMethod.invoke(null, allFields);
allFields = Arrays.asList(allFieldsArray);
Expand All @@ -242,7 +214,7 @@ protected void rebuildCachedFields (boolean minorRebuild) {
validTransientFields = buildValidFields(true, allFields, context, useAsm);

// Use ReflectASM for any public fields.
if (useAsmEnabled && !Util.isAndroid && Modifier.isPublic(type.getModifiers()) && useAsm.indexOf(1) != -1) {
if (config.isUseAsm() && !Util.isAndroid && Modifier.isPublic(type.getModifiers()) && useAsm.indexOf(1) != -1) {
try {
access = FieldAccess.get(type);
} catch (RuntimeException ignored) {
Expand Down Expand Up @@ -299,10 +271,10 @@ private List<Field> buildValidFields (boolean transientFields, List<Field> allFi
int modifiers = field.getModifiers();
if (Modifier.isTransient(modifiers) != transientFields) continue;
if (Modifier.isStatic(modifiers)) continue;
if (field.isSynthetic() && ignoreSyntheticFields) continue;
if (field.isSynthetic() && config.isIgnoreSyntheticFields()) continue;

if (!field.isAccessible()) {
if (!setFieldsAsAccessible) continue;
if (!config.isSetFieldsAsAccessible()) continue;
try {
field.setAccessible(true);
} catch (AccessControlException ex) {
Expand All @@ -324,7 +296,7 @@ private List<Field> buildValidFields (boolean transientFields, List<Field> allFi

private void createCachedFields (IntArray useAsm, List<Field> validFields, List<CachedField> cachedFields, int baseIndex) {

if (useAsmEnabled || !useMemRegions) {
if (config.isUseAsm() || !useMemRegions) {
for (int i = 0, n = validFields.size(); i < n; i++) {
Field field = validFields.get(i);
int accessIndex = -1;
Expand Down Expand Up @@ -376,16 +348,16 @@ CachedField newCachedField (Field field, int fieldIndex, int accessIndex) {
cachedField.field = field;
cachedField.varIntsEnabled = varIntsEnabled;

if (!useAsmEnabled) {
if (!config.isUseAsm()) {
cachedField.offset = unsafeUtil.getObjectFieldOffset(field);
}

cachedField.access = (FieldAccess)access;
cachedField.accessIndex = accessIndex;
cachedField.canBeNull = fieldsCanBeNull && !fieldClass[0].isPrimitive() && !field.isAnnotationPresent(NotNull.class);
cachedField.canBeNull = config.isFieldsCanBeNull() && !fieldClass[0].isPrimitive() && !field.isAnnotationPresent(NotNull.class);

// Always use the same serializer for this field if the field's class is final.
if (kryo.isFinal(fieldClass[0]) || fixedFieldTypes) cachedField.valueClass = fieldClass[0];
if (kryo.isFinal(fieldClass[0]) || config.isFixedFieldTypes()) cachedField.valueClass = fieldClass[0];

return cachedField;
}
Expand All @@ -395,7 +367,7 @@ CachedField newMatchingCachedField (Field field, int accessIndex, Class fieldCla
CachedField cachedField;
if (accessIndex != -1) {
cachedField = getAsmFieldFactory().createCachedField(fieldClass, field, this);
} else if (!useAsmEnabled) {
} else if (!config.isUseAsm()) {
cachedField = getUnsafeFieldFactory().createCachedField(fieldClass, field, this);
} else {
cachedField = getObjectFieldFactory().createCachedField(fieldClass, field, this);
Expand Down Expand Up @@ -444,8 +416,7 @@ public int compare (CachedField o1, CachedField o2) {
* cached fields}.
* @param fieldsCanBeNull False if none of the fields are null. Saves 0-1 byte per field. True if it is not known (default). */
public void setFieldsCanBeNull (boolean fieldsCanBeNull) {
this.fieldsCanBeNull = fieldsCanBeNull;
if (TRACE) trace("kryo", "setFieldsCanBeNull: " + fieldsCanBeNull);
config.setFieldsCanBeNull(fieldsCanBeNull);
rebuildCachedFields();
}

Expand All @@ -454,45 +425,36 @@ public void setFieldsCanBeNull (boolean fieldsCanBeNull) {
* {@link Field#setAccessible(boolean) set as accessible} if necessary (default). If false, only fields in the public
* API will be serialized. */
public void setFieldsAsAccessible (boolean setFieldsAsAccessible) {
this.setFieldsAsAccessible = setFieldsAsAccessible;
if (TRACE) trace("kryo", "setFieldsAsAccessible: " + setFieldsAsAccessible);
config.setFieldsAsAccessible(setFieldsAsAccessible);
rebuildCachedFields();
}

/** Controls if synthetic fields are serialized. Default is true. Calling this method resets the {@link #getFields() cached
* fields}.
* @param ignoreSyntheticFields If true, only non-synthetic fields will be serialized. */
public void setIgnoreSyntheticFields (boolean ignoreSyntheticFields) {
this.ignoreSyntheticFields = ignoreSyntheticFields;
if (TRACE) trace("kryo", "setIgnoreSyntheticFields: " + ignoreSyntheticFields);
config.setIgnoreSyntheticFields(ignoreSyntheticFields);
rebuildCachedFields();
}

/** Sets the default value for {@link CachedField#setClass(Class)} to the field's declared type. This allows FieldSerializer to
* be more efficient, since it knows field values will not be a subclass of their declared type. Default is false. Calling this
* method resets the {@link #getFields() cached fields}. */
public void setFixedFieldTypes (boolean fixedFieldTypes) {
this.fixedFieldTypes = fixedFieldTypes;
if (TRACE) trace("kryo", "setFixedFieldTypes: " + fixedFieldTypes);
config.setFixedFieldTypes(fixedFieldTypes);
rebuildCachedFields();
}

/** Controls whether ASM should be used. Calling this method resets the {@link #getFields() cached fields}.
* @param setUseAsm If true, ASM will be used for fast serialization. If false, Unsafe will be used (default) */
public void setUseAsm (boolean setUseAsm) {
useAsmEnabled = setUseAsm;
if (!useAsmEnabled && !unsafeAvailable) {
useAsmEnabled = true;
if (TRACE) trace("kryo", "sun.misc.Unsafe is unavailable, using ASM.");
}
// optimizeInts = useAsmBackend;
if (TRACE) trace("kryo", "setUseAsm: " + setUseAsm);
config.setUseAsm(setUseAsm);
rebuildCachedFields();
}

// Enable/disable copying of transient fields
public void setCopyTransient (boolean setCopyTransient) {
copyTransient = setCopyTransient;
config.setCopyTransient(setCopyTransient);
}

/** This method can be called for different fields having the same type. Even though the raw type is the same, if the type is
Expand Down Expand Up @@ -649,15 +611,15 @@ public Kryo getKryo () {
}

public boolean getUseAsmEnabled () {
return useAsmEnabled;
return config.isUseAsm();
}

public boolean getUseMemRegions () {
return useMemRegions;
}

public boolean getCopyTransient () {
return copyTransient;
return config.isCopyTransient();
}

/** Used by {@link #copy(Kryo, Object)} to create the new object. This can be overridden to customize object creation, eg to
Expand All @@ -671,7 +633,7 @@ public T copy (Kryo kryo, T original) {
kryo.reference(copy);

// Copy transient fields
if (copyTransient) {
if (config.isCopyTransient()) {
for (int i = 0, n = transientFields.length; i < n; i++)
transientFields[i].copy(original, copy);
}
Expand Down
Loading

0 comments on commit b4dfe28

Please sign in to comment.