You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2022/05/18 14:57:21 UTC

[felix-dev] 01/01: FELIX-6529: deduplicate strings and dirs/attrs of reqs and caps

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch FELIX-6529
in repository https://gitbox.apache.org/repos/asf/felix-dev.git

commit b2d225c380da17cd82868eb81969ba73bf63f686
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Wed May 18 16:57:08 2022 +0200

    FELIX-6529: deduplicate strings and dirs/attrs of reqs and caps
---
 .../apache/felix/framework/cache/BundleCache.java  |  3 ++
 .../framework/capabilityset/SimpleFilter.java      | 18 +++++++
 .../java/org/apache/felix/framework/util/Util.java |  4 +-
 .../util/manifestparser/ManifestParser.java        | 56 +++++++++++++++++-----
 .../framework/wiring/BundleCapabilityImpl.java     | 28 ++++++++++-
 .../framework/wiring/BundleRequirementImpl.java    | 23 +++++++++
 6 files changed, 117 insertions(+), 15 deletions(-)

diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index ef544d07dc..d11533c5b8 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -336,6 +336,9 @@ public class BundleCache
                 // Otherwise, parse the value and add it to the map (we throw an
                 // exception if we don't have a key or the key already exist.
                 String value = new String(bytes, last, (current - last), "UTF-8");
+                if (value.length() < 100) {
+                    value = value.intern();
+                }
                 if (key == null)
                 {
                     throw new Exception("Manifest error: Missing attribute name - " + value);
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
index 6e56485292..b3d85a1811 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 
 public class SimpleFilter
 {
@@ -49,6 +50,23 @@ public class SimpleFilter
         m_op = op;
     }
 
+    public boolean equals(Object o)
+    {
+        if (o instanceof SimpleFilter)
+        {
+            SimpleFilter other = (SimpleFilter) o;
+            return m_op == other.m_op &&
+                    Objects.equals(m_name,other.m_name) &&
+                    Objects.equals(m_value, other.m_value);
+        }
+        return false;
+    }
+
+    public int hashCode()
+    {
+        return m_op + Objects.hashCode(m_name) + Objects.hashCode(m_value);
+    }
+
     public String getName()
     {
         return m_name;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index ee2545286e..1745efe121 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -1090,8 +1090,8 @@ public class Util
         return manifest;
     }
 
-    private static final List EMPTY_LIST = Collections.unmodifiableList(Collections.EMPTY_LIST);
-    private static final Map EMPTY_MAP = Collections.unmodifiableMap(Collections.EMPTY_MAP);
+    private static final List EMPTY_LIST = Collections.EMPTY_LIST;
+    private static final Map EMPTY_MAP = Collections.EMPTY_MAP;
 
     public static <T> List<T> newImmutableList(List<T> list)
     {
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index 09e0572774..013300b1ea 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -38,6 +38,7 @@ import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -47,7 +48,12 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 public class ManifestParser
 {
@@ -67,6 +73,32 @@ public class ManifestParser
     private volatile List<NativeLibraryClause> m_libraryClauses;
     private volatile boolean m_libraryHeadersOptional = false;
 
+    private static final Map<Object, WeakReference<Object>> objectCache = new WeakHashMap<>();
+    private static final Function<Object, Object> cache = (foo) ->
+    {
+        if (foo instanceof String)
+        {
+            return ((String) foo).intern();
+        }
+        else if (foo != null)
+        {
+            synchronized (objectCache)
+            {
+                WeakReference<Object> ref = objectCache.get(foo);
+                if (ref != null)
+                {
+                    Object refValue = ref.get();
+                    if (refValue != null)
+                    {
+                        return refValue;
+                    }
+                }
+                objectCache.put(foo, new WeakReference<>(foo));
+            }
+        }
+        return foo;
+    };
+
     public ManifestParser(Logger logger, Map<String, Object> configMap, BundleRevision owner, Map<String, Object> headerMap)
         throws BundleException
     {
@@ -108,6 +140,8 @@ public class ManifestParser
             }
         }
 
+        m_bundleVersion = (Version) cache.apply(m_bundleVersion);
+
         //
         // Parse bundle symbolic name.
         //
@@ -269,25 +303,25 @@ public class ManifestParser
         }
         
         List<BundleRequirement> nativeCodeReqs = convertNativeCode(owner, m_libraryClauses, m_libraryHeadersOptional);
-        
+
         // Combine all requirements.
         m_requirements = new ArrayList<BundleRequirement>(
             hostReqs.size() + importReqs.size() + rbReqs.size()
             + requireReqs.size() + dynamicReqs.size() + breeReqs.size());
-        m_requirements.addAll(hostReqs);
-        m_requirements.addAll(importReqs);
-        m_requirements.addAll(rbReqs);
-        m_requirements.addAll(requireReqs);
-        m_requirements.addAll(dynamicReqs);
-        m_requirements.addAll(breeReqs);
-        m_requirements.addAll(nativeCodeReqs);
+        m_requirements.addAll(hostReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(importReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(rbReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(requireReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(dynamicReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(breeReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
+        m_requirements.addAll(nativeCodeReqs.stream().map(req -> BundleRequirementImpl.createFrom((BundleRequirementImpl) req, cache)).collect(Collectors.toList()));
         
         // Combine all capabilities.
         m_capabilities = new ArrayList<BundleCapability>(
              capList.size() + exportCaps.size() + provideCaps.size());
-        m_capabilities.addAll(capList);
-        m_capabilities.addAll(exportCaps);
-        m_capabilities.addAll(provideCaps);
+        m_capabilities.addAll(capList.stream().map(cap -> BundleCapabilityImpl.createFrom((BundleCapabilityImpl) cap, cache)).collect(Collectors.toList()));
+        m_capabilities.addAll(exportCaps.stream().map(cap -> BundleCapabilityImpl.createFrom((BundleCapabilityImpl) cap, cache)).collect(Collectors.toList()));
+        m_capabilities.addAll(provideCaps.stream().map(cap -> BundleCapabilityImpl.createFrom((BundleCapabilityImpl) cap, cache)).collect(Collectors.toList()));
 
         //
         // Parse activation policy.
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index f426ad12ff..ddc6a4169c 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -20,11 +20,15 @@ package org.apache.felix.framework.wiring;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.function.Function;
+
+import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
 import org.osgi.framework.Constants;
@@ -42,6 +46,26 @@ public class BundleCapabilityImpl implements BundleCapability
     private final List<String> m_uses;
     private final Set<String> m_mandatory;
 
+    public static BundleCapabilityImpl createFrom(BundleCapabilityImpl capability, Function<Object, Object> cache)
+    {
+        String namespaceI = (String) cache.apply(capability.m_namespace);
+        Map<String, String> dirsI = new HashMap<>();
+        for (Map.Entry<String, String> entry : capability.m_dirs.entrySet())
+        {
+            dirsI.put((String) cache.apply(entry.getKey()), (String) cache.apply(entry.getValue()));
+        }
+        dirsI = (Map<String, String>) cache.apply(dirsI);
+
+        Map<String, Object> attrsI = new HashMap<>();
+        for (Map.Entry<String, Object> entry : capability.m_attrs.entrySet())
+        {
+            attrsI.put((String) cache.apply(entry.getKey()), cache.apply(entry.getValue()));
+        }
+        attrsI = (Map<String, Object>) cache.apply(attrsI);
+
+        return new BundleCapabilityImpl(capability.m_revision, namespaceI, dirsI, attrsI);
+    }
+
     public BundleCapabilityImpl(BundleRevision revision, String namespace,
         Map<String, String> dirs, Map<String, Object> attrs)
     {
@@ -61,7 +85,7 @@ public class BundleCapabilityImpl implements BundleCapability
             uses = new ArrayList<>(tok.countTokens());
             while (tok.hasMoreTokens())
             {
-                uses.add(tok.nextToken().trim());
+                uses.add(tok.nextToken().trim().intern());
             }
         }
         m_uses = uses;
@@ -77,7 +101,7 @@ public class BundleCapabilityImpl implements BundleCapability
                 // If attribute exists, then record it as mandatory.
                 if (m_attrs.containsKey(name))
                 {
-                    mandatory.add(name);
+                    mandatory.add(name.intern());
                 }
                 // Otherwise, report an error.
                 else
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
index 881d366c6b..a8a111913a 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
@@ -19,7 +19,10 @@
 package org.apache.felix.framework.wiring;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Function;
+
 import org.apache.felix.framework.capabilityset.CapabilitySet;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.util.Util;
@@ -37,6 +40,26 @@ public class BundleRequirementImpl implements BundleRequirement
     private final Map<String, String> m_dirs;
     private final Map<String, Object> m_attrs;
 
+    public static BundleRequirementImpl createFrom(BundleRequirementImpl requirement, Function<Object, Object> cache)
+    {
+        String namespaceI = (String) cache.apply(requirement.m_namespace);
+        Map<String, String> dirsI = new HashMap<>();
+        for (Map.Entry<String, String> entry : requirement.m_dirs.entrySet())
+        {
+            dirsI.put((String) cache.apply(entry.getKey()), (String) cache.apply(entry.getValue()));
+        }
+        dirsI = (Map<String, String>) cache.apply(dirsI);
+
+        Map<String, Object> attrsI = new HashMap<>();
+        for (Map.Entry<String, Object> entry : requirement.m_attrs.entrySet())
+        {
+            attrsI.put((String) cache.apply(entry.getKey()), cache.apply(entry.getValue()));
+        }
+        attrsI = (Map<String, Object>) cache.apply(attrsI);
+        SimpleFilter filterI = (SimpleFilter) cache.apply(requirement.m_filter);
+        return new BundleRequirementImpl(requirement.m_revision, namespaceI, dirsI, attrsI, filterI);
+    }
+
     public BundleRequirementImpl(
         BundleRevision revision, String namespace,
         Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter)