Optimized (de)serialization code

* Added cache to grammarIdToURIMap for deserialization
* Prevent conversion from list to map in serialization
* Prevent conversion from map to string array in serialization

Signed-off-by: Titouan Vervack <titouan.vervack@sigasi.com>
This commit is contained in:
Titouan Vervack 2019-06-24 16:45:40 +02:00
parent 09c9adf666
commit e1075671f1
5 changed files with 112 additions and 55 deletions
org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization
org.eclipse.xtext/src/org/eclipse/xtext/nodemodel

View file

@ -13,7 +13,10 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EFactory;
@ -48,11 +51,19 @@ public class SerializationUtilTest extends Assert {
rootObject.eSet(ref1, someTypeObject1);
rootObject.eSet(ref2, someTypeObject2);
List<EObject> map = new ArrayList<>();
List<EObject> list = new ArrayList<>();
SerializationUtil.fillListWithAllEObjects(rootObject, list);
assertTrue(list.contains(rootObject));
assertTrue(list.contains(someTypeObject1));
assertFalse(list.contains(someTypeObject2));
assertEquals(2, list.size());
Map<EObject, Integer> map = new HashMap<>();
SerializationUtil.fillIdToEObjectMap(rootObject, map);
assertTrue(map.contains(rootObject));
assertTrue(map.contains(someTypeObject1));
assertFalse(map.contains(someTypeObject2));
Set<EObject> keys = map.keySet();
assertTrue(keys.contains(rootObject));
assertTrue(keys.contains(someTypeObject1));
assertFalse(keys.contains(someTypeObject2));
assertEquals(2, map.size());
}

View file

@ -10,6 +10,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
@ -46,9 +47,9 @@ public class SerializableNodeModel {
* @noreference This method is not intended to be referenced by clients.
*/
public void writeObjectData(DataOutputStream out, SerializationConversionContext scc) throws IOException {
String[] grammarIdToURIMap = scc.getGrammarIdToURIMap();
List<String> grammarIdToURIMap = scc.getGrammarIdToURIMap();
out.writeInt(grammarIdToURIMap.length);
out.writeInt(grammarIdToURIMap.size());
for (String string : grammarIdToURIMap) {
out.writeUTF(string);
}

View file

@ -7,7 +7,9 @@
package org.eclipse.xtext.nodemodel.serialization;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
@ -17,13 +19,17 @@ import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.resource.XtextResource;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* @author mark.christiaens - Initial contribution and API
*
*
* @since 2.3
*/
public class DeserializationConversionContext {
private static final Map<URI, Map<URI, EObject>> CACHE = Maps.newHashMap();
private EObject[] grammarIdToGrammarElementMap;
final private List<EObject> idToEObjectMap;
@ -43,12 +49,23 @@ public class DeserializationConversionContext {
}
public void setGrammarIdToURIMap(String[] grammarIdToURIMap) {
grammarIdToGrammarElementMap = new EObject[grammarIdToURIMap.length];
URI grammarUri = grammarAccess.getGrammar().eResource().getURI();
Map<URI, EObject> grammarCache = CACHE.get(grammarUri);
if (grammarCache == null) {
grammarCache = new HashMap<>();
CACHE.put(grammarUri, grammarCache);
}
grammarIdToGrammarElementMap = new EObject[grammarIdToURIMap.length];
ResourceSet grammarResourceSet = grammarAccess.getGrammar().eResource().getResourceSet();
for (int grammarId = 0; grammarId < grammarIdToURIMap.length; ++grammarId) {
URI uri = URI.createURI(grammarIdToURIMap[grammarId], true);
EObject grammarElement = grammarResourceSet.getEObject(uri, true);
EObject grammarElement = grammarCache.get(uri);
if (grammarElement == null) {
grammarElement = grammarResourceSet.getEObject(uri, true);
grammarCache.put(uri, grammarElement);
}
if (grammarElement == null) {
throw new IllegalStateException(
@ -73,7 +90,7 @@ public class DeserializationConversionContext {
}
public void fillIdToEObjectMap(Resource resource) {
SerializationUtil.fillIdToEObjectMap(resource, idToEObjectMap);
SerializationUtil.fillListWithAllEObjects(resource, idToEObjectMap);
}
public EObject getSemanticObject(int id) {

View file

@ -7,7 +7,9 @@
package org.eclipse.xtext.nodemodel.serialization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
@ -27,9 +29,9 @@ public class SerializationConversionContext {
final private Map<EObject, Integer> eObjectToIdMap;
public SerializationConversionContext(XtextResource resource) {
grammarElementToIdMap = new IdentityHashMap<EObject, Integer>();
grammarIdToURIMap = new ArrayList<String>();
eObjectToIdMap = new IdentityHashMap<EObject, Integer>();
grammarElementToIdMap = new IdentityHashMap<EObject, Integer>(100);
grammarIdToURIMap = new ArrayList<String>(100);
eObjectToIdMap = new IdentityHashMap<EObject, Integer>(1000);
fillEObjectToIdMap(resource);
fillGrammarElementToIdMap(resource);
@ -47,13 +49,7 @@ public class SerializationConversionContext {
}
protected void fillEObjectToIdMap(Resource resource) {
ArrayList<EObject> idToEObjectMap = new ArrayList<EObject>();
SerializationUtil.fillIdToEObjectMap(resource, idToEObjectMap);
for (int id = 0; id < idToEObjectMap.size(); ++id) {
eObjectToIdMap.put(idToEObjectMap.get(id), id);
}
SerializationUtil.fillIdToEObjectMap(resource, eObjectToIdMap);
}
protected void fillGrammarElementToIdMap(XtextResource r) {
@ -68,16 +64,17 @@ public class SerializationConversionContext {
Integer id = eObjectToIdMap.get(eObject);
if (id == null) {
throw new IllegalArgumentException("Tryin to fetch an EMF object that does not exist (no longer) with id: "
throw new IllegalArgumentException("Trying to fetch an EMF object that does not exist (no longer) with id: "
+ id);
}
return id;
}
public String[] getGrammarIdToURIMap() {
String[] map = grammarIdToURIMap.toArray(new String[grammarIdToURIMap.size()]);
return map;
/**
* Returns an unmodifiable view on the grammarIdToURIMap
*/
public List<String> getGrammarIdToURIMap() {
return Collections.unmodifiableList(grammarIdToURIMap);
}
}

View file

@ -15,12 +15,14 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.util.EcoreUtil;
@ -30,26 +32,55 @@ import org.eclipse.xtext.resource.XtextResourceSet;
/**
* @author mark.christiaens - Initial contribution and API
*
*
* @since 2.3
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public class SerializationUtil {
public static final int KIB = 1024;
public static void fillIdToEObjectMap(Resource resource, List<EObject> map) {
TreeIterator<EObject> allContents = EcoreUtil.getAllContents(resource, false);
public static void fillListWithAllEObjects(Resource resource, List<EObject> map) {
TreeIterator<EObject> allContents = EcoreUtil.getAllContents(resource, false);
if (allContents.hasNext()) {
EObject root = allContents.next();
fillListWithAllEObjects(root, map);
}
}
public static void fillListWithAllEObjects(EObject eObject, List<EObject> map) {
if (eObject.eContainingFeature() == null || !eObject.eContainingFeature().isTransient()) {
map.add(eObject);
EList<EObject> eContents = eObject.eContents();
for (EObject child : eContents) {
fillListWithAllEObjects(child, map);
}
}
}
/**
* Basically a copy of {@link #fillListWithAllEObjects(Resource, List)}, but for efficiency we need an
* implementation for Map and List
*/
public static void fillIdToEObjectMap(Resource resource, Map<EObject, Integer> map) {
TreeIterator<EObject> allContents = EcoreUtil.getAllContents(resource, false);
if (allContents.hasNext()) {
EObject root = allContents.next();
fillIdToEObjectMap(root, map);
}
}
public static void fillIdToEObjectMap(EObject eObject, List<EObject> map) {
if (eObject.eContainingFeature() == null || !eObject.eContainingFeature().isTransient()) {
map.add(eObject);
/**
* Basically a copy of {@link #fillListWithAllEObjects(EObject, List)}, but for efficiency we need an implementation
* for Map and List
*/
public static void fillIdToEObjectMap(EObject eObject, Map<EObject, Integer> map) {
EStructuralFeature containingFeature = eObject.eContainingFeature();
if (containingFeature == null || !containingFeature.isTransient()) {
map.put(eObject, map.size());
EList<EObject> eContents = eObject.eContents();
for (EObject child : eContents) {
@ -83,7 +114,7 @@ public class SerializationUtil {
throws UnsupportedEncodingException, IOException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, encoding);
StringBuilder sb = new StringBuilder ();
StringBuilder sb = new StringBuilder ();
char[] buffer = new char[128 * KIB];
int n = inputStreamReader.read(buffer);
@ -95,23 +126,23 @@ public class SerializationUtil {
return sb.toString();
}
public static byte [] getCompleteContent(InputStream inputStream)
throws IOException {
byte[] buffer = new byte[128 * KIB];
int nextFreePos = 0;
int nextFreePos = 0;
int n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
while (n != -1) {
nextFreePos += n;
nextFreePos += n;
if (nextFreePos >= buffer.length) {
buffer = copyOf(buffer, buffer.length*2);
buffer = copyOf(buffer, buffer.length*2);
}
n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
}
return copyOf(buffer, nextFreePos);
return copyOf(buffer, nextFreePos);
}
static byte[] copyOf(byte[] original, int newLength) {
byte[] copy = new byte[newLength];
System.arraycopy(original, 0, copy, 0,
@ -130,7 +161,7 @@ public class SerializationUtil {
SerializationUtil.writeStringArray(out, syntaxErrorMessage.getIssueData());
}
}
public static SyntaxErrorMessage readSyntaxErrorMessage(DataInputStream in, DeserializationConversionContext context)
throws IOException {
boolean isNull = in.readBoolean();
@ -188,10 +219,10 @@ public class SerializationUtil {
}
public static final void writeString(DataOutputStream out, String s) throws IOException {
boolean isNull = s == null;
out.writeBoolean(isNull);
boolean isNull = s == null;
out.writeBoolean(isNull);
if (!isNull) {
out.writeUTF(s);
}
@ -199,20 +230,20 @@ public class SerializationUtil {
public static final String readString(DataInputStream in) throws IOException {
boolean isNull = in.readBoolean();
if (isNull) {
return null;
}
String string = in.readUTF();
return string;
}
public static void writeStringArray(DataOutputStream out, String[] ss) throws IOException {
out.writeBoolean(ss == null);
if (ss != null) {
writeInt(out, ss.length, true);
writeInt(out, ss.length, true);
for (String data : ss) {
writeString(out, data);
}
@ -222,19 +253,19 @@ public class SerializationUtil {
public static String[] readStringArray(DataInputStream in) throws IOException {
boolean isIssueDataNull = in.readBoolean();
String[] result = null;
if (!isIssueDataNull) {
int issueDataLength = readInt(in, true);
int issueDataLength = readInt(in, true);
result = new String[issueDataLength];
for (int i = 0; i < issueDataLength; ++i) {
result[i] = readString(in);
}
}
return result;
}
public static void tryClose(Closeable stream, Logger logger) throws IOException {
if (stream != null) {
try {