blob: 9e569b73a8443315b8f95fcd5581d2aab5402b51 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 University of Illinois at Urbana-Champaign and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.internal.core.vpg;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import org.eclipse.photran.internal.core.analysis.binding.Definition;
import org.eclipse.photran.internal.core.analysis.binding.ImplicitSpec;
import org.eclipse.photran.internal.core.analysis.binding.VariableAccess;
import org.eclipse.photran.internal.core.analysis.types.ArraySpec;
import org.eclipse.photran.internal.core.analysis.types.Dimension;
import org.eclipse.photran.internal.core.analysis.types.Type;
/**
* Serializes {@link IPhotranSerializable} objects for storage as annotations in the VPG.
*
* @author Jeff Overbey
*
* @see IPhotranSerializable
* @see PhotranVPGDB#setAnnotation(PhotranTokenRef, int, Serializable)
*/
/*
* The VPG originally used Java's built-in serialization mechanism (see commented-out
* code in PhotranVPGDB#serialize(Serializable)}, but according to a profile, that was
* consuming significant time in the POP and LAPACK test applications; using this custom
* serialization mechanism improved indexing time by 28% and reduced the database size by
* about half (in POP's case).
*/
public class PhotranVPGSerializer
{
private PhotranVPGSerializer() {;}
// EACH SERIALIZABLE CLASS MUST HAVE A UNIQUE LETTER
public static final byte CLASS_NULL = '0';
public static final byte CLASS_STRING = 's';
public static final byte CLASS_INT = 'i';
public static final byte CLASS_BOOLEAN = 'b';
public static final byte CLASS_TOKENREF = 'T';
public static final byte CLASS_DEFINITION = 'D';
public static final byte CLASS_TYPE = 'Y';
public static final byte CLASS_ARRAYSPEC = 'A';
public static final byte CLASS_DIMENSION = 'M';
public static final byte CLASS_IMPLICITSPEC = 'I';
public static final byte CLASS_VARIABLEACCESS = 'V';
protected static IOException readFailure()
{
return new IOException(Messages.PhotranVPGSerializer_AnnotationCorrupted);
}
////////////////////////////////////////////////////////////////////////////////
// Serialization
////////////////////////////////////////////////////////////////////////////////
/*
* WHEN OBJECTS ARE SERIALIZED...
*
* (1) The first byte written is the CLASS_* character, as defined above,
* indicating what the class of the object is
*
* (2) The #writeTo method is called on the object, which then calls back to
* this class to serialize its sub-objects (i.e., the values of its fields).
*
* Since null, String, int, and boolean don't have #readFrom methods, they are
* handled using custom serialization code below.
*/
public static void serialize(int annotation, OutputStream out) throws IOException
{
out.write(CLASS_INT);
writeInt(annotation, out);
}
public static void serialize(boolean annotation, OutputStream out) throws IOException
{
out.write(CLASS_BOOLEAN);
writeBoolean(annotation, out);
}
public static void serialize(String annotation, OutputStream out) throws IOException
{
if (annotation == null) { serializeNull(out); return; }
out.write(CLASS_STRING);
writeString((String)annotation, out);
}
public static void serialize(IPhotranSerializable annotation, OutputStream out) throws IOException
{
if (annotation == null) { serializeNull(out); return; }
IPhotranSerializable ann = (IPhotranSerializable)annotation;
out.write(ann.getSerializationCode());
//serialize(annotation.getClass().getSimpleName(), out);
ann.writeTo(out);
}
private static void serializeNull(OutputStream out) throws IOException
{
out.write(CLASS_NULL);
}
////////////////////////////////////////////////////////////////////////////////
// Deserialization
////////////////////////////////////////////////////////////////////////////////
/*
* WHEN OBJECTS ARE DESERIALIZED...
*
* (1) The first byte read is matched against the CLASS_* constants
* above to determine the class of the serialized object
*
* (2) The #readFrom method is called on the class, which then calls back to
* this class to deserialize its sub-objects (i.e., the values of its fields).
*
* Since null, String, int, and boolean don't have #readFrom methods, they are
* handled using custom serialization code below.
*/
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deserialize(InputStream in)
{
try
{
int code = in.read();
switch (code)
{
case CLASS_NULL: return null;
case CLASS_STRING: return (T)readString(in);
case CLASS_INT: return (T)Integer.valueOf(readInt(in));
case CLASS_BOOLEAN: return (T)Boolean.valueOf(readBoolean(in));
case CLASS_TOKENREF: return (T)PhotranTokenRef.readFrom(in);
case CLASS_DEFINITION: return (T)Definition.readFrom(in);
case CLASS_TYPE: return (T)Type.readFrom(in);
case CLASS_ARRAYSPEC: return (T)ArraySpec.readFrom(in);
case CLASS_DIMENSION: return (T)Dimension.readFrom(in);
case CLASS_IMPLICITSPEC: return (T)ImplicitSpec.readFrom(in);
case CLASS_VARIABLEACCESS: return (T)VariableAccess.readFrom(in);
default: throw new Error("Unknown class code in deserialization: " + Integer.toString(code)); //$NON-NLS-1$
}
}
catch (IOException e)
{
throw new Error(e);
}
}
////////////////////////////////////////////////////////////////////////////////
// Methods for (de)serializing primitives and Strings
////////////////////////////////////////////////////////////////////////////////
private static void writeString(String string, OutputStream out) throws IOException
{
if (string == null)
{
writeInt(-1, out);
}
else
{
byte[] stringBytes = string.getBytes();
writeInt(stringBytes.length, out);
out.write(stringBytes);
}
}
private static String readString(InputStream in) throws IOException
{
int numBytesInString = readInt(in);
if (numBytesInString < 0)
{
return null;
}
else
{
byte[] stringBytes = new byte[numBytesInString];
in.read(stringBytes);
return new String(stringBytes);
}
}
private static void writeInt(int value, OutputStream out) throws IOException
{
out.write((value & 0xFF000000) >>> 24);
out.write((value & 0x00FF0000) >>> 16);
out.write((value & 0x0000FF00) >>> 8);
out.write((value & 0x000000FF) >>> 0);
}
private static int readInt(InputStream in) throws IOException
{
byte bytes[] = new byte[4];
in.read(bytes);
return
((bytes[0] & 0xFF) << 24)
| ((bytes[1] & 0xFF) << 16)
| ((bytes[2] & 0xFF) << 8)
| ((bytes[3] & 0xFF) << 0);
}
private static void writeBoolean(boolean value, OutputStream out) throws IOException
{
out.write(value ? 'T' : 'F');
}
private static boolean readBoolean(InputStream in) throws IOException
{
switch (in.read())
{
case 'T': return true;
case 'F': return false;
default: throw readFailure();
}
}
////////////////////////////////////////////////////////////////////////////////
// Serializes arbitrary objects, including boxed primitives
////////////////////////////////////////////////////////////////////////////////
public static void serialize(Serializable object, OutputStream out) throws IOException
{
// We could use reflection to find the right overload...
// PhotranVPGSerializer.class.getMethod("serialize",
// object.getClass(),
// OutputStream.class).invoke(object, out);
// ...but it's ridiculously slow
if (object == null)
serialize((String)null, out);
else if (object instanceof String)
serialize((String)object, out);
else if (object instanceof Integer)
serialize(((Integer)object).intValue(), out);
else if (object instanceof Boolean)
serialize(((Boolean)object).booleanValue(), out);
else if (object instanceof IPhotranSerializable)
serialize((IPhotranSerializable)object, out);
else
throw new IllegalArgumentException("Cannot serialize " + object.getClass().getSimpleName()); //$NON-NLS-1$
}
}