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

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


The following commit(s) were added to refs/heads/master by this push:
     new f8f0def4fa FELIX-6529: deduplicate strings and dirs/attrs of reqs and 
caps (#151)
f8f0def4fa is described below

commit f8f0def4fa863e3de7bf49522340956da1a367d9
Author: Karl Pauls <karlpa...@gmail.com>
AuthorDate: Fri May 20 00:08:45 2022 +0200

    FELIX-6529: deduplicate strings and dirs/attrs of reqs and caps (#151)
---
 .../apache/felix/framework/cache/BundleCache.java  |  1 +
 .../framework/capabilityset/SimpleFilter.java      | 18 +++++++
 .../util/manifestparser/ManifestParser.java        | 56 +++++++++++++++++-----
 .../framework/wiring/BundleCapabilityImpl.java     | 28 ++++++++++-
 .../framework/wiring/BundleRequirementImpl.java    | 23 +++++++++
 5 files changed, 113 insertions(+), 13 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..27e59da68e 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,7 @@ 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 (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/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)

Reply via email to