From b4066052a3d77d8d13d67d8301cca29072ef47b6 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Tue, 5 May 2015 11:17:08 +0200 Subject: [PATCH] Remove trove and logging dependency --- .../com/intellij/util/UrlClassLoaderTest.java | 49 +++++ .../intellij/util/lang/ClasspathCache.java | 184 +----------------- .../com/intellij/util/lang/FileLoader.java | 20 -- .../intellij/util/lang/IntObjectHashMap.java | 89 +++++++++ .../intellij/util/lang/JarMemoryLoader.java | 5 +- .../intellij/util/lang/UrlClassLoader.java | 3 +- 6 files changed, 149 insertions(+), 201 deletions(-) create mode 100644 platform/util/src/com/intellij/util/lang/IntObjectHashMap.java diff --git a/platform/platform-tests/testSrc/com/intellij/util/UrlClassLoaderTest.java b/platform/platform-tests/testSrc/com/intellij/util/UrlClassLoaderTest.java index 046e153d45a7..7f56a7680b96 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/UrlClassLoaderTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/UrlClassLoaderTest.java @@ -17,7 +17,10 @@ package com.intellij.util; import com.intellij.openapi.application.PathManager; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.lang.IntObjectHashMap; import com.intellij.util.lang.UrlClassLoader; +import gnu.trove.TIntObjectHashMap; +import gnu.trove.TIntObjectProcedure; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; @@ -44,6 +47,52 @@ public class UrlClassLoaderTest extends TestCase { assertNotNull(UrlClassLoader.build().allowBootstrapResources().get().getResourceAsStream(name)); } + public void testIntObjectHashMap() { + final IntObjectHashMap map = new IntObjectHashMap(); + final TIntObjectHashMap checkMap = new TIntObjectHashMap(); + final TIntObjectHashMap dupesMap = new TIntObjectHashMap(); + Random random = new Random(); + for(int i = 0; i < 1000000; ++i) { + int key = random.nextInt(); + String value = String.valueOf(random.nextInt()); + + if (!checkMap.contains(key)) { + map.put(key, value); + checkMap.put(key, value); + assertEquals(map.size(), checkMap.size()); + assertEquals(value, map.get(key)); + } else { + dupesMap.put(key, value); + } + } + + dupesMap.put(0, "random string"); + + dupesMap.forEachEntry(new TIntObjectProcedure() { + @Override + public boolean execute(int key, Object value) { + checkMap.put(key, value); + map.put(key, value); + assertEquals(map.size(), checkMap.size()); + assertEquals(value, map.get(key)); + return true; + } + }); + + String value = "random string2"; + checkMap.put(0, value); + map.put(0, value); + + checkMap.forEachEntry(new TIntObjectProcedure() { + @Override + public boolean execute(int key, Object value) { + assertEquals(value, map.get(key)); + return true; + } + }); + assertEquals(map.size(), checkMap.size()); + } + public void testConcurrentResourceLoading() throws Exception { final List resourceNames = ContainerUtil.newArrayList(); List urls = ContainerUtil.newArrayList(); diff --git a/platform/util/src/com/intellij/util/lang/ClasspathCache.java b/platform/util/src/com/intellij/util/lang/ClasspathCache.java index bfe39ed936ce..19f31630c894 100644 --- a/platform/util/src/com/intellij/util/lang/ClasspathCache.java +++ b/platform/util/src/com/intellij/util/lang/ClasspathCache.java @@ -15,20 +15,14 @@ */ package com.intellij.util.lang; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringHash; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.BloomFilterBase; -import com.intellij.util.SmartList; -import com.intellij.util.containers.HashMap; -import gnu.trove.THashMap; -import gnu.trove.TIntHashSet; -import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.Nullable; -import sun.misc.Resource; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; @@ -38,22 +32,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * @author max */ public class ClasspathCache { - static final Logger LOG = Logger.getInstance(ClasspathCache.class); - static final boolean doDebug = LOG.isDebugEnabled(); + private final IntObjectHashMap myResourcePackagesCache = new IntObjectHashMap(); + private final IntObjectHashMap myClassPackagesCache = new IntObjectHashMap(); - private final DebugInfo myDebugInfo; - - private final TIntObjectHashMap myResourcePackagesCache = new TIntObjectHashMap(); - private final TIntObjectHashMap myClassPackagesCache = new TIntObjectHashMap(); - - private THashMap myResources2LoadersTempMap = new THashMap(); + private Map myResources2LoadersTempMap = new HashMap(); private static final double PROBABILITY = 0.005d; private Name2LoaderFilter myNameFilter; - public ClasspathCache() { - myDebugInfo = doDebug ? new DebugInfo() : new NullDebugInfo(); - } - static class LoaderData { private final List myResourcePaths = new ArrayList(); private final List myNames = new ArrayList(); @@ -102,13 +87,12 @@ public class ClasspathCache { ParameterType2 parameter2) { myLock.readLock().lock(); try { - TIntObjectHashMap map = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION) ? + IntObjectHashMap map = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION) ? myClassPackagesCache : myResourcePackagesCache; String packageName = getPackageName(resourcePath); int hash = packageName.hashCode(); Object o = map.get(hash); - myDebugInfo.checkLoadersCount(resourcePath, o); if (o == null) return null; if (o instanceof Loader) return iterator.process((Loader)o, parameter, parameter2); @@ -130,10 +114,8 @@ public class ClasspathCache { } private void addResourceEntry(String resourcePath, Loader loader) { - myDebugInfo.addResourceEntry(resourcePath, loader); - String packageName = getPackageName(resourcePath); - TIntObjectHashMap map = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION) ? + IntObjectHashMap map = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION) ? myClassPackagesCache : myResourcePackagesCache; int hash = packageName.hashCode(); Object o = map.get(hash); @@ -151,18 +133,14 @@ public class ClasspathCache { private void addNameEntry(String name, Loader loader) { name = transformName(name); - myDebugInfo.addNameEntry(name, loader); if (myNameFilter == null) { Object loaders = myResources2LoadersTempMap.get(name); - boolean added = false; if (loaders == null) { myResources2LoadersTempMap.put(name, loader); - added = true; } else if (loaders instanceof Loader && loaders != loader) { myResources2LoadersTempMap.put(name, new Loader[] {(Loader)loaders, loader}); - added = true; } else if (loaders instanceof Loader[]) { boolean weHaveThisLoader = false; @@ -175,16 +153,9 @@ public class ClasspathCache { if (!weHaveThisLoader) { myResources2LoadersTempMap.put(name, ArrayUtil.append((Loader[])loaders, loader)); - added = true; } } - - if (doDebug && added) ++registeredBeforeClose; } else { - if (doDebug) { - if (!myNameFilter.maybeContains(name, loader)) ++registeredAfterClose; - } - myNameFilter.add(name, loader); } } @@ -200,53 +171,9 @@ public class ClasspathCache { if (myNameFilter == null) { Object loaders = myResources2LoadersTempMap.get(shortName); result = contains(loader, loaders); - - if (doDebug) { - ++requestsWithoutNameFilter; - if (!result) ++hits; - boolean result2 = myDebugInfo.loaderHasName(shortName, loader); - if (result2 != result) { - ++diffs3; - } - Resource resource = loader.getResource(name, true); - if (resource != null && !result || resource == null && result) { - ++falseHits; - } - - if (requestsWithoutNameFilter % 1000 == 0) { - LOG.debug("Avoided disk hits: " + hits + " from " + requestsWithoutNameFilter + ", false hits:" + falseHits + ", bitmap diffs:" + diffs3); - } - } } else { result = myNameFilter.maybeContains(shortName, loader); - - if (doDebug) { - ++requestsWithNameFilter; - if (!result) ++avoidedDiskHits2; - boolean result2 = myDebugInfo.loaderHasName(shortName, loader); - if (result2 != result) { - ++diffs2; - } - - Object loaders = myResources2LoadersTempMap.get(shortName); - if (result != contains(loader, loaders)) { - ++diffs; - } - - Resource resource = loader.getResource(name, true); - if (resource == null && result) { - ++falseHits2; - } - if (resource != null && !result) { - ++falseHits2; - } - - if (requestsWithNameFilter % 1000 == 0) { - LOG.debug("Avoided disk hits2: " + avoidedDiskHits2 + " from " + - requestsWithNameFilter + "," + diffs + ", false hits:" + falseHits2 + ", bitmap diffs:" + diffs2); - } - } } return result; @@ -292,33 +219,14 @@ public class ClasspathCache { return name; } - private int registeredBeforeClose; - private int registeredAfterClose; - private int hits; - private int requestsWithoutNameFilter; - private int falseHits; - private int requestsWithNameFilter; - private int avoidedDiskHits2; - private int falseHits2; - private int diffs; - private int diffs2; - private int diffs3; - void nameSymbolsLoaded() { myLock.writeLock().lock(); try { if (myNameFilter != null) { - if (doDebug && registeredAfterClose > 0) { - LOG.debug("Registered number of classes after close " + registeredAfterClose + " " + toString()); - } return; } - if (doDebug) { - LOG.debug("Registered number of classes before classes " + registeredBeforeClose + " " + toString()); - } - int nBits = 0; //noinspection UnusedDeclaration int uniques = 0; @@ -348,9 +256,7 @@ public class ClasspathCache { } myNameFilter = name2LoaderFilter; - if (!doDebug) { - myResources2LoadersTempMap = null; - } + myResources2LoadersTempMap = null; } finally { myLock.writeLock().unlock(); @@ -388,80 +294,4 @@ public class ClasspathCache { return hash; } } - - private static class DebugInfo { - private final HashMap> myClassPackagesCache = new HashMap>(); - private final HashMap> myResourcePackagesCache = new HashMap>(); - private final TIntHashSet myResourceIndex = new TIntHashSet(); - - private List getLoaders(String resourcePath) { - boolean isClassFile = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION); - final int idx = resourcePath.lastIndexOf('/'); - String packageName = idx > 0 ? resourcePath.substring(0, idx) : ""; - - Map> map = isClassFile ? myClassPackagesCache : myResourcePackagesCache; - List list = map.get(packageName); - if (list == null) { - list = new SmartList(); - map.put(packageName, list); - } - - return list; - } - - protected void addResourceEntry(String resourcePath, Loader loader) { - final List loaders = getLoaders(resourcePath); - if (!loaders.contains(loader)) { // TODO Make linked hash set instead? - loaders.add(loader); - } - } - - protected void addNameEntry(String name, Loader loader) { - int hash = hashFromNameAndLoader(name, loader); - myResourceIndex.add(hash); - } - - protected static int hashFromNameAndLoader(String name, Loader loader) { - int hash = name.hashCode(); - int i = loader.getIndex(); - while(i > 0) { - hash = hash * 31 + ((i % 10) + '0'); - i /= 10; - } - return hash; - } - - public void checkLoadersCount(String resourcePath, Object o) { - List loaders1 = getLoaders(resourcePath); - if (o == null && !loaders1.isEmpty() || - o instanceof Loader && loaders1.size() != 1 || - o instanceof Loader[] && loaders1.size() != ((Loader[])o).length - ) { - assert false; - } - } - - protected boolean loaderHasName(String name, Loader loader) { - return myResourceIndex.contains(hashFromNameAndLoader(name, loader)); - } - } - - private static class NullDebugInfo extends DebugInfo { - @Override - public void checkLoadersCount(String resourcePath, Object o) { - } - - @Override - protected void addResourceEntry(String resourcePath, Loader loader) { - } - - @Override - protected void addNameEntry(String name, Loader loader) { - } - - @Override - protected boolean loaderHasName(String name, Loader loader) { - return false; - } - } } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/lang/FileLoader.java b/platform/util/src/com/intellij/util/lang/FileLoader.java index 25912f5f7af0..15fa00efd855 100644 --- a/platform/util/src/com/intellij/util/lang/FileLoader.java +++ b/platform/util/src/com/intellij/util/lang/FileLoader.java @@ -16,7 +16,6 @@ package com.intellij.util.lang; import com.intellij.openapi.util.io.FileUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; @@ -30,8 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; class FileLoader extends Loader { private final File myRootDir; private final String myRootDirAbsolutePath; - private static int misses; - private static int hits; private final boolean myCanHavePersistentIndex; FileLoader(URL url, int index, boolean canHavePersistentIndex) throws IOException { @@ -89,26 +86,10 @@ class FileLoader extends Loader { file = new File(myRootDir, name.replace('/', File.separatorChar)); if (!check || file.exists()) { // check means we load or process resource so we check its existence via old way - if (check) { - ++misses; - if (misses % 1000 == 0 && ClasspathCache.doDebug) { - ClasspathCache.LOG.debug("[Sample of] missed resource " + name + " from " + myRootDir); - } - } - - ++hits; - if (hits % 1000 == 0 && ClasspathCache.doDebug) { - ClasspathCache.LOG.debug("Exists file loader: misses:" + misses + ", hits:" + hits); - } - return new MyResource(name, url, file, !check); } } catch (Exception exception) { - ++misses; - if (misses % 1000 == 0 && ClasspathCache.doDebug) { - ClasspathCache.LOG.debug("Missed " + name + " from " + myRootDir); - } if (!check && file != null && file.exists()) { try { // we can not open the file if it is directory, Resource still can be created return new MyResource(name, url, file, false); @@ -314,7 +295,6 @@ class FileLoader extends Loader { } } - @NonNls public String toString() { return "FileLoader [" + myRootDir + "]"; } diff --git a/platform/util/src/com/intellij/util/lang/IntObjectHashMap.java b/platform/util/src/com/intellij/util/lang/IntObjectHashMap.java new file mode 100644 index 000000000000..77c19c30e811 --- /dev/null +++ b/platform/util/src/com/intellij/util/lang/IntObjectHashMap.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.lang; + +// Nongeneral purpose memory saving map implementation for UrlClassLoader to avoid extra dependencies +// made public for tests +public final class IntObjectHashMap { + private int size; + private int[] keys; + private Object[] values; + private Object specialZeroValue; + private boolean hasZeroValue; + + public IntObjectHashMap() { + keys = new int[4]; + values = new Object[keys.length]; + } + + public int size() { + return size + (hasZeroValue ? 1 : 0); + } + + public void put(int key, Object value) { + if (key == 0) { + specialZeroValue = value; + hasZeroValue = true; + return; + } + + if (size >= (2 * values.length) / 3) rehash(); + Object previousValue = doPut(keys, values, key, value); + if (previousValue == null) ++size; + } + + private static Object doPut(int[] keys, Object[] values, int key, Object value) { + int index = hashIndex(keys, key); + Object obj = values[index]; + values[index] = value; + if (keys[index] == 0) keys[index] = key; + return obj; + } + + private static int hashIndex(int[] keys, int key) { + int hash = (int)((key * 0x9E3779B9L) & 0x7fffffff); + int index = hash & (keys.length - 1); + int candidate; + + while ((candidate = keys[index]) != 0) { + if (candidate == key) return index; + if (index == 0) index = keys.length; + index--; + } + + return index; + } + + private void rehash() { + int[] newKeys = new int[keys.length << 1]; + Object[] newValues = new Object[newKeys.length]; + + for (int i = keys.length; --i >= 0; ) { + int key = keys[i]; + if (key != 0) doPut(newKeys, newValues, key, values[i]); + } + + keys = newKeys; + values = newValues; + } + + public Object get(int key) { + if (key == 0) { + return specialZeroValue; + } + return values[hashIndex(keys, key)]; + } +} diff --git a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java index 03b41cd0d493..f012f1fc6526 100644 --- a/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java +++ b/platform/util/src/com/intellij/util/lang/JarMemoryLoader.java @@ -16,14 +16,15 @@ package com.intellij.util.lang; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.hash.HashMap; import com.intellij.util.io.zip.ZipShort; -import gnu.trove.THashMap; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.zip.ZipEntry; @@ -36,7 +37,7 @@ import java.util.zip.ZipFile; public class JarMemoryLoader { public static final String SIZE_ENTRY = "META-INF/jb/$$size$$"; - private final Map myResources = new THashMap(); + private final Map myResources = Collections.synchronizedMap(new HashMap()); // todo do we need it ? private JarMemoryLoader() { } diff --git a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java index 20b5a5fe4706..d71066e1eacb 100644 --- a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java +++ b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java @@ -22,7 +22,6 @@ import com.intellij.openapi.util.io.win32.IdeaWin32; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.WeakStringInterner; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.misc.Resource; @@ -46,7 +45,7 @@ import java.util.List; public class UrlClassLoader extends ClassLoader { // Feature enabling flag for saving / restoring file system information for local class directories, see Builder#usePersistentClasspathIndexForLocalClassDirectories private static final boolean INDEX_PERSISTENCE_ENABLED = Boolean.parseBoolean(System.getProperty("idea.classpath.index.enabled", "true")); - @NonNls static final String CLASS_EXTENSION = ".class"; + static final String CLASS_EXTENSION = ".class"; private static boolean ourParallel = false; static {