From e1075671f18d2046ca4361d115aa18fae80adaa3 Mon Sep 17 00:00:00 2001 From: Titouan Vervack Date: Mon, 24 Jun 2019 16:45:40 +0200 Subject: [PATCH] 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 --- .../serialization/SerializationUtilTest.java | 19 +++- .../nodemodel/impl/SerializableNodeModel.java | 5 +- .../DeserializationConversionContext.java | 25 ++++- .../SerializationConversionContext.java | 27 +++--- .../serialization/SerializationUtil.java | 91 +++++++++++++------ 5 files changed, 112 insertions(+), 55 deletions(-) diff --git a/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java b/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java index 4793815cc..b1cd57f29 100644 --- a/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java +++ b/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java @@ -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 map = new ArrayList<>(); + List list = new ArrayList<>(); + SerializationUtil.fillListWithAllEObjects(rootObject, list); + assertTrue(list.contains(rootObject)); + assertTrue(list.contains(someTypeObject1)); + assertFalse(list.contains(someTypeObject2)); + assertEquals(2, list.size()); + + Map map = new HashMap<>(); SerializationUtil.fillIdToEObjectMap(rootObject, map); - assertTrue(map.contains(rootObject)); - assertTrue(map.contains(someTypeObject1)); - assertFalse(map.contains(someTypeObject2)); + Set keys = map.keySet(); + assertTrue(keys.contains(rootObject)); + assertTrue(keys.contains(someTypeObject1)); + assertFalse(keys.contains(someTypeObject2)); assertEquals(2, map.size()); } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java index 3e6ef8e9c..ba8a42d90 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java @@ -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 grammarIdToURIMap = scc.getGrammarIdToURIMap(); - out.writeInt(grammarIdToURIMap.length); + out.writeInt(grammarIdToURIMap.size()); for (String string : grammarIdToURIMap) { out.writeUTF(string); } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java index 278db1535..487756cb1 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java @@ -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> CACHE = Maps.newHashMap(); + private EObject[] grammarIdToGrammarElementMap; final private List 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 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) { diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java index a34baff51..83aab173d 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java @@ -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 eObjectToIdMap; public SerializationConversionContext(XtextResource resource) { - grammarElementToIdMap = new IdentityHashMap(); - grammarIdToURIMap = new ArrayList(); - eObjectToIdMap = new IdentityHashMap(); + grammarElementToIdMap = new IdentityHashMap(100); + grammarIdToURIMap = new ArrayList(100); + eObjectToIdMap = new IdentityHashMap(1000); fillEObjectToIdMap(resource); fillGrammarElementToIdMap(resource); @@ -47,13 +49,7 @@ public class SerializationConversionContext { } protected void fillEObjectToIdMap(Resource resource) { - ArrayList idToEObjectMap = new ArrayList(); - - 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 getGrammarIdToURIMap() { + return Collections.unmodifiableList(grammarIdToURIMap); } } diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java index 52c548ef4..a1b39486b 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java @@ -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 map) { - TreeIterator allContents = EcoreUtil.getAllContents(resource, false); - + + public static void fillListWithAllEObjects(Resource resource, List map) { + TreeIterator allContents = EcoreUtil.getAllContents(resource, false); + + if (allContents.hasNext()) { + EObject root = allContents.next(); + fillListWithAllEObjects(root, map); + } + } + + public static void fillListWithAllEObjects(EObject eObject, List map) { + if (eObject.eContainingFeature() == null || !eObject.eContainingFeature().isTransient()) { + map.add(eObject); + EList 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 map) { + TreeIterator allContents = EcoreUtil.getAllContents(resource, false); + if (allContents.hasNext()) { EObject root = allContents.next(); fillIdToEObjectMap(root, map); } } - public static void fillIdToEObjectMap(EObject eObject, List 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 map) { + EStructuralFeature containingFeature = eObject.eContainingFeature(); + if (containingFeature == null || !containingFeature.isTransient()) { + map.put(eObject, map.size()); EList 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 {