Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Add MinimalGenerics and allow users to set a GenericsStrategy #879

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -75,6 +75,7 @@ public void custom (CustomState state) {
@State(Scope.Thread)
static public abstract class BenchmarkState {
@Param({"true", "false"}) public boolean references;
@Param({"DEFAULT", "MINIMAL", "NONE"}) public Kryo.GenericsStrategy generics;
@Param() public ObjectType objectType;

final Kryo kryo = new Kryo();
Expand Down Expand Up @@ -108,6 +109,7 @@ public void setup () {
break;
}

kryo.setGenericsStrategy(generics);
kryo.setReferences(references);
}

Expand Down
32 changes: 31 additions & 1 deletion src/com/esotericsoftware/kryo/Kryo.java
Expand Up @@ -92,6 +92,7 @@
import com.esotericsoftware.kryo.util.IdentityMap;
import com.esotericsoftware.kryo.util.IntArray;
import com.esotericsoftware.kryo.util.MapReferenceResolver;
import com.esotericsoftware.kryo.util.MinimalGenerics;
import com.esotericsoftware.kryo.util.NoGenerics;
import com.esotericsoftware.kryo.util.ObjectMap;
import com.esotericsoftware.kryo.util.Util;
Expand Down Expand Up @@ -1301,7 +1302,36 @@ public Generics getGenerics () {
* setting in order to be compatible.
* @param optimizedGenerics whether to optimize generics (default is true) */
public void setOptimizedGenerics (boolean optimizedGenerics) {
generics = optimizedGenerics ? new DefaultGenerics(this) : NoGenerics.INSTANCE;
setGenericsStrategy(optimizedGenerics ? GenericsStrategy.DEFAULT : GenericsStrategy.NONE);
}

/** Sets a {@link GenericsStrategy}.
*
* TODO JavaDoc
*
* @param strategy the strategy for processing generics information */
public void setGenericsStrategy (GenericsStrategy strategy) {
this.generics = strategy.createInstance(this);
}

public enum GenericsStrategy {
DEFAULT {
public Generics createInstance (Kryo kryo) {
return new DefaultGenerics(kryo);
}
},
MINIMAL {
public Generics createInstance (Kryo kryo) {
return new MinimalGenerics(kryo);
}
},
NONE {
public Generics createInstance (Kryo kryo) {
return NoGenerics.INSTANCE;
}
};

public abstract Generics createInstance (Kryo kryo);
}

static final class DefaultSerializerEntry {
Expand Down
93 changes: 93 additions & 0 deletions src/com/esotericsoftware/kryo/util/BaseGenerics.java
@@ -0,0 +1,93 @@
/* Copyright (c) 2008-2022, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

package com.esotericsoftware.kryo.util;

import com.esotericsoftware.kryo.Kryo;

/** Base class for implementations of {@link Generics} that stores generic type information.
* @author Nathan Sweet */
abstract class BaseGenerics implements Generics {
final Kryo kryo;

private int genericTypesSize;
private GenericType[] genericTypes = new GenericType[16];
private int[] depths = new int[16];

protected BaseGenerics (Kryo kryo) {
this.kryo = kryo;
}

@Override
public void pushGenericType (GenericType fieldType) {
// Ensure genericTypes and depths capacity.
int size = genericTypesSize;
if (size + 1 == genericTypes.length) {
GenericType[] genericTypesNew = new GenericType[genericTypes.length << 1];
System.arraycopy(genericTypes, 0, genericTypesNew, 0, size);
genericTypes = genericTypesNew;
int[] depthsNew = new int[depths.length << 1];
System.arraycopy(depths, 0, depthsNew, 0, size);
depths = depthsNew;
}

genericTypesSize = size + 1;
genericTypes[size] = fieldType;
depths[size] = kryo.getDepth();
}

@Override
public void popGenericType () {
int size = genericTypesSize;
if (size == 0) return;
size--;
if (depths[size] < kryo.getDepth()) return;
genericTypes[size] = null;
genericTypesSize = size;
}

@Override
public GenericType[] nextGenericTypes () {
int index = genericTypesSize;
if (index > 0) {
index--;
GenericType genericType = genericTypes[index];
if (genericType.arguments == null) return null;
// The depth must match to prevent the types being wrong if a serializer doesn't call nextGenericTypes.
if (depths[index] == kryo.getDepth() - 1) {
pushGenericType(genericType.arguments[genericType.arguments.length - 1]);
return genericType.arguments;
}
}
return null;
}

@Override
public Class nextGenericClass () {
GenericType[] arguments = nextGenericTypes();
if (arguments == null) return null;
return arguments[0].resolve(this);
}

@Override
public int getGenericTypesSize () {
return genericTypesSize;
}

}
65 changes: 2 additions & 63 deletions src/com/esotericsoftware/kryo/util/DefaultGenerics.java
Expand Up @@ -26,69 +26,13 @@

/** Stores the generic type arguments and actual classes for type variables in the current location in the object graph.
* @author Nathan Sweet */
public final class DefaultGenerics implements Generics {
private final Kryo kryo;

private int genericTypesSize;
private GenericType[] genericTypes = new GenericType[16];
private int[] depths = new int[16];
public final class DefaultGenerics extends BaseGenerics {

private int argumentsSize;
private Type[] arguments = new Type[16];

public DefaultGenerics (Kryo kryo) {
this.kryo = kryo;
}

@Override
public void pushGenericType (GenericType fieldType) {
// Ensure genericTypes and depths capacity.
int size = genericTypesSize;
if (size + 1 == genericTypes.length) {
GenericType[] genericTypesNew = new GenericType[genericTypes.length << 1];
System.arraycopy(genericTypes, 0, genericTypesNew, 0, size);
genericTypes = genericTypesNew;
int[] depthsNew = new int[depths.length << 1];
System.arraycopy(depths, 0, depthsNew, 0, size);
depths = depthsNew;
}

genericTypesSize = size + 1;
genericTypes[size] = fieldType;
depths[size] = kryo.getDepth();
}

@Override
public void popGenericType () {
int size = genericTypesSize;
if (size == 0) return;
size--;
if (depths[size] < kryo.getDepth()) return;
genericTypes[size] = null;
genericTypesSize = size;
}

@Override
public GenericType[] nextGenericTypes () {
int index = genericTypesSize;
if (index > 0) {
index--;
GenericType genericType = genericTypes[index];
if (genericType.arguments == null) return null;
// The depth must match to prevent the types being wrong if a serializer doesn't call nextGenericTypes.
if (depths[index] == kryo.getDepth() - 1) {
pushGenericType(genericType.arguments[genericType.arguments.length - 1]);
return genericType.arguments;
}
}
return null;
}

@Override
public Class nextGenericClass () {
GenericType[] arguments = nextGenericTypes();
if (arguments == null) return null;
return arguments[0].resolve(this);
super(kryo);
}

@Override
Expand Down Expand Up @@ -146,11 +90,6 @@ public Class resolveTypeVariable (TypeVariable typeVariable) {
return null;
}

@Override
public int getGenericTypesSize () {
return genericTypesSize;
}

public String toString () {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < argumentsSize; i += 2) {
Expand Down
44 changes: 44 additions & 0 deletions src/com/esotericsoftware/kryo/util/MinimalGenerics.java
@@ -0,0 +1,44 @@
/* Copyright (c) 2008-2022, Nathan Sweet
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

package com.esotericsoftware.kryo.util;

import com.esotericsoftware.kryo.Kryo;

import java.lang.reflect.TypeVariable;

/** Implementation of {@link Generics} that stores generic type information, but does not attempt to resolve type variables. */
public final class MinimalGenerics extends BaseGenerics {

public MinimalGenerics(Kryo kryo) {
super(kryo);
}

public int pushTypeVariables(GenericsHierarchy hierarchy, GenericType[] args) {
return 0;
}

public void popTypeVariables(int count) {

}

public Class resolveTypeVariable(TypeVariable typeVariable) {
return null;
}
}
Expand Up @@ -32,6 +32,7 @@
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;

Expand Down Expand Up @@ -112,6 +113,17 @@ void testCopy () {
assertNotSame(objects1.get(0), objects2.get(0));
}

@Test
void testGenerics() {
kryo.register(HasGenerics.class);
kryo.register(ArrayList.class);

final HasGenerics test = new HasGenerics();
test.list.add("moo");

roundTrip(6, test);
}

public static class TreeSetSubclass<E> extends TreeSet<E> {
public TreeSetSubclass () {
}
Expand All @@ -120,4 +132,15 @@ public TreeSetSubclass (Comparator<? super E> comparator) {
super(comparator);
}
}

public static class HasGenerics {
public List<String> list = new ArrayList<>();

public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HasGenerics that = (HasGenerics) o;
return Objects.equals(list, that.list);
}
}
}
Expand Up @@ -117,7 +117,10 @@ void testGenerics () {
kryo.writeClassAndObject(output, test);
output.flush();

input = new Input(output.toBytes());
final byte[] bytes = output.toBytes();
assertEquals(bytes.length, 13);

input = new Input(bytes);
HasGenerics test2 = (HasGenerics)kryo.readClassAndObject(input);
assertArrayEquals(test.map.get("moo"), test2.map.get("moo"));
}
Expand Down