LUCENE-7936: Improve serialization by using a shortcut way of describing classes.

This commit is contained in:
Karl Wright 2017-08-30 10:40:38 -04:00
parent 1cc3d8050c
commit faad8ae6a8
3 changed files with 173 additions and 40 deletions

View File

@ -17,15 +17,15 @@
package org.apache.lucene.spatial3d.geom;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.lang.reflect.Constructor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;
import java.util.List;
/**
* Indicates that a geo3d object can be serialized and deserialized.
@ -47,7 +47,7 @@ public interface SerializableObject {
object.getPlanetModel().write(outputStream);
writeObject(outputStream, object);
}
/** Read a PlanetObject from a stream.
* @param inputStream is the input stream.
* @return the PlanetObject.
@ -60,30 +60,28 @@ public interface SerializableObject {
}
return (PlanetObject)so;
}
/** Write an object to a stream.
* @param outputStream is the output stream.
* @param object is the object to write.
*/
public static void writeObject(final OutputStream outputStream, final SerializableObject object) throws IOException {
writeString(outputStream, object.getClass().getName());
writeClass(outputStream, object.getClass());
object.write(outputStream);
}
/** Read an object from a stream (for objects that need a PlanetModel).
* @param planetModel is the planet model to use to deserialize the object.
* @param inputStream is the input stream.
* @return the deserialized object.
*/
public static SerializableObject readObject(final PlanetModel planetModel, final InputStream inputStream) throws IOException {
// Read the class name
final String className = readString(inputStream);
try {
// Look for the class
final Class<?> clazz = Class.forName(className);
// Read the class
final Class<?> clazz = readClass(inputStream);
return readObject(planetModel, inputStream, clazz);
} catch (ClassNotFoundException e) {
throw new IOException("Can't find class "+className+" for deserialization: "+e.getMessage(), e);
throw new IOException("Can't find class for deserialization: "+e.getMessage(), e);
}
}
@ -92,17 +90,15 @@ public interface SerializableObject {
* @return the deserialized object.
*/
public static SerializableObject readObject(final InputStream inputStream) throws IOException {
// Read the class name
final String className = readString(inputStream);
try {
// Look for the class
final Class<?> clazz = Class.forName(className);
// read the class
final Class<?> clazz = readClass(inputStream);
return readObject(inputStream, clazz);
} catch (ClassNotFoundException e) {
throw new IOException("Can't find class "+className+" for deserialization: "+e.getMessage(), e);
throw new IOException("Can't find class for deserialization: "+e.getMessage(), e);
}
}
/** Instantiate a serializable object from a stream.
* @param planetModel is the planet model.
* @param inputStream is the input stream.
@ -130,7 +126,7 @@ public interface SerializableObject {
}
}
/** Instantiate a serializable object from a stream without a planet model.
* @param inputStream is the input stream.
* @param clazz is the class to instantiate.
@ -158,6 +154,39 @@ public interface SerializableObject {
}
/** Write a class to a stream.
* @param outputStream is the output stream.
* @param clazz is the class to write.
*/
static void writeClass(final OutputStream outputStream, final Class<?> clazz) throws IOException {
Integer index = StandardObjects.classRegsitry.get(clazz);
if (index == null){
writeBoolean(outputStream, false);
writeString(outputStream, clazz.getName());
}
else {
writeBoolean(outputStream, true);
outputStream.write(index);
}
}
/**
* Read the class from the stream
* @param inputStream is the stream to read from.
* @return is the class read
*/
static Class<?> readClass(final InputStream inputStream) throws IOException, ClassNotFoundException {
boolean standard = readBoolean(inputStream);
if (standard) {
int index = inputStream.read();
return StandardObjects.codeRegsitry.get(index);
}
else {
String className = readString(inputStream);
return Class.forName(className);
}
}
/** Write a string to a stream.
* @param outputStream is the output stream.
* @param value is the string to write.
@ -165,7 +194,7 @@ public interface SerializableObject {
static void writeString(final OutputStream outputStream, final String value) throws IOException {
writeByteArray(outputStream, value.getBytes(StandardCharsets.UTF_8));
}
/** Read a string from a stream.
* @param inputStream is the stream to read from.
* @return the string that was read.
@ -173,7 +202,7 @@ public interface SerializableObject {
static String readString(final InputStream inputStream) throws IOException {
return new String(readByteArray(inputStream), StandardCharsets.UTF_8);
}
/** Write a point array.
* @param outputStream is the output stream.
* @param values is the array of points to write.
@ -189,7 +218,7 @@ public interface SerializableObject {
static void writePointArray(final OutputStream outputStream, final List<GeoPoint> values) throws IOException {
writeHomogeneousArray(outputStream, values);
}
/** Read a point array.
* @param planetModel is the planet model.
* @param inputStream is the input stream.
@ -214,7 +243,7 @@ public interface SerializableObject {
static void writePolygonArray(final OutputStream outputStream, final List<GeoPolygon> values) throws IOException {
writeHeterogeneousArray(outputStream, values);
}
/** Read a polygon array.
* @param planetModel is the planet model.
* @param inputStream is the input stream.
@ -253,7 +282,7 @@ public interface SerializableObject {
}
}
}
/** Read an array.
* @param planetModel is the planet model.
* @param inputStream is the input stream.
@ -299,7 +328,7 @@ public interface SerializableObject {
}
}
}
/** Read an array.
* @param planetModel is the planet model.
* @param inputStream is the input stream.
@ -322,7 +351,7 @@ public interface SerializableObject {
static void writeBitSet(final OutputStream outputStream, final BitSet bitSet) throws IOException {
writeByteArray(outputStream, bitSet.toByteArray());
}
/** Read a bitset from a stream.
* @param inputStream is the input stream.
* @return the bitset read from the stream.
@ -339,7 +368,7 @@ public interface SerializableObject {
writeInt(outputStream, bytes.length);
outputStream.write(bytes);
}
/** Read byte array.
* @param inputStream is the input stream.
* @return the byte array.
@ -366,7 +395,7 @@ public interface SerializableObject {
static void writeDouble(final OutputStream outputStream, final double value) throws IOException {
writeLong(outputStream, Double.doubleToLongBits(value));
}
/** Read a double from a stream.
* @param inputStream is the input stream.
* @return the double value read from the stream.
@ -374,7 +403,7 @@ public interface SerializableObject {
static double readDouble(final InputStream inputStream) throws IOException {
return Double.longBitsToDouble(readLong(inputStream));
}
/** Write a long to a stream.
* @param outputStream is the output stream.
* @param value is the value to write.
@ -383,7 +412,7 @@ public interface SerializableObject {
writeInt(outputStream, (int)value);
writeInt(outputStream, (int)(value >> 32));
}
/** Read a long from a stream.
* @param inputStream is the input stream.
* @return the long value read from the stream.
@ -393,7 +422,7 @@ public interface SerializableObject {
final long upper = (((long)(readInt(inputStream))) << 32) & 0xffffffff00000000L;
return lower + upper;
}
/** Write an int to a stream.
* @param outputStream is the output stream.
* @param value is the value to write.
@ -404,7 +433,7 @@ public interface SerializableObject {
outputStream.write(value >> 16);
outputStream.write(value >> 24);
}
/** Read an int from a stream.
* @param inputStream is the input stream.
* @return the value read from the stream.
@ -424,7 +453,7 @@ public interface SerializableObject {
static void writeBoolean(final OutputStream outputStream, final boolean value) throws IOException {
outputStream.write(value?1:0);
}
/** Read a boolean from a stream.
* @param inputStream is the input stream.
* @return the boolean value.
@ -436,5 +465,6 @@ public interface SerializableObject {
}
return (valueRead == 0)?false:true;
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import java.util.HashMap;
import java.util.Map;
/**
* Lookup tables for classes that can be serialized using a code.
*
* @lucene.internal
*/
class StandardObjects {
/**
* Registry of standard classes to corresponding code
*/
static Map<Class<?>, Integer> classRegsitry = new HashMap<>();
/**
* Registry of codes to corresponding classes
*/
static Map<Integer, Class<?>> codeRegsitry = new HashMap<>();
static {
classRegsitry.put(GeoPoint.class, 0);
classRegsitry.put(GeoRectangle.class, 1);
classRegsitry.put(GeoStandardCircle.class, 2);
classRegsitry.put(GeoStandardPath.class, 3);
classRegsitry.put(GeoConvexPolygon.class, 4);
classRegsitry.put(GeoConcavePolygon.class, 5);
classRegsitry.put(GeoComplexPolygon.class, 6);
classRegsitry.put(GeoCompositePolygon.class, 7);
classRegsitry.put(GeoCompositeMembershipShape.class, 8);
classRegsitry.put(GeoCompositeAreaShape.class, 9);
classRegsitry.put(GeoDegeneratePoint.class, 10);
classRegsitry.put(GeoDegenerateHorizontalLine.class, 11);
classRegsitry.put(GeoDegenerateLatitudeZone.class, 12);
classRegsitry.put(GeoDegenerateLongitudeSlice.class, 13);
classRegsitry.put(GeoDegenerateVerticalLine.class, 14);
classRegsitry.put(GeoLatitudeZone.class, 15);
classRegsitry.put(GeoLongitudeSlice.class, 16);
classRegsitry.put(GeoNorthLatitudeZone.class, 17);
classRegsitry.put(GeoNorthRectangle.class, 18);
classRegsitry.put(GeoSouthLatitudeZone.class, 19);
classRegsitry.put(GeoSouthRectangle.class, 20);
classRegsitry.put(GeoWideDegenerateHorizontalLine.class, 21);
classRegsitry.put(GeoWideLongitudeSlice.class, 22);
classRegsitry.put(GeoWideNorthRectangle.class, 23);
classRegsitry.put(GeoWideRectangle.class, 24);
classRegsitry.put(GeoWideSouthRectangle.class, 25);
classRegsitry.put(GeoWorld.class, 26);
classRegsitry.put(dXdYdZSolid.class, 27);
classRegsitry.put(dXdYZSolid.class, 28);
classRegsitry.put(dXYdZSolid.class, 29);
classRegsitry.put(dXYZSolid.class, 30);
classRegsitry.put(XdYdZSolid.class, 31);
classRegsitry.put(XdYZSolid.class, 32);
classRegsitry.put(XYdZSolid.class, 33);
classRegsitry.put(StandardXYZSolid.class, 34);
classRegsitry.put(PlanetModel.class, 35);
for (Class<?> clazz : classRegsitry.keySet()){
codeRegsitry.put(classRegsitry.get(clazz), clazz);
}
}
}

View File

@ -30,12 +30,10 @@ import org.junit.Test;
public class RandomBinaryCodecTest extends RandomGeoShapeGenerator{
@Test
@Repeat(iterations = 100)
public void testRandomShapeCodec() throws IOException{
@Repeat(iterations = 10)
public void testRandomPointCodec() throws IOException{
PlanetModel planetModel = randomPlanetModel();
int type = randomShapeType();
GeoShape shape = randomGeoShape(type, planetModel);
GeoPoint shape = randomGeoPoint(planetModel, getEmptyConstraint());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SerializableObject.writeObject(outputStream, shape);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
@ -43,5 +41,29 @@ public class RandomBinaryCodecTest extends RandomGeoShapeGenerator{
assertEquals(shape, shapeCopy);
}
@Test
@Repeat(iterations = 100)
public void testRandomPlanetObjectCodec() throws IOException{
PlanetModel planetModel = randomPlanetModel();
int type = randomShapeType();
GeoShape shape = randomGeoShape(type, planetModel);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SerializableObject.writePlanetObject(outputStream, shape);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
SerializableObject shapeCopy = SerializableObject.readPlanetObject(inputStream);
assertEquals(shape, shapeCopy);
}
@Test
@Repeat(iterations = 100)
public void testRandomShapeCodec() throws IOException{
PlanetModel planetModel = randomPlanetModel();
int type = randomShapeType();
GeoShape shape = randomGeoShape(type, planetModel);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SerializableObject.writeObject(outputStream, shape);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
SerializableObject shapeCopy = SerializableObject.readObject(planetModel, inputStream);
assertEquals(shape, shapeCopy);
}
}