Remove trove and logging dependency

This commit is contained in:
Maxim.Mossienko
2015-05-05 11:20:39 +02:00
parent ecd7fb4374
commit b4066052a3
6 changed files with 149 additions and 201 deletions
@@ -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<Object> checkMap = new TIntObjectHashMap<Object>();
final TIntObjectHashMap<Object> dupesMap = new TIntObjectHashMap<Object>();
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<Object>() {
@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<Object>() {
@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<String> resourceNames = ContainerUtil.newArrayList();
List<URL> urls = ContainerUtil.newArrayList();
@@ -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<Object> myResourcePackagesCache = new TIntObjectHashMap<Object>();
private final TIntObjectHashMap<Object> myClassPackagesCache = new TIntObjectHashMap<Object>();
private THashMap<String, Object> myResources2LoadersTempMap = new THashMap<String, Object>();
private Map<String, Object> myResources2LoadersTempMap = new HashMap<String, Object>();
private static final double PROBABILITY = 0.005d;
private Name2LoaderFilter myNameFilter;
public ClasspathCache() {
myDebugInfo = doDebug ? new DebugInfo() : new NullDebugInfo();
}
static class LoaderData {
private final List<String> myResourcePaths = new ArrayList<String>();
private final List<String> myNames = new ArrayList<String>();
@@ -102,13 +87,12 @@ public class ClasspathCache {
ParameterType2 parameter2) {
myLock.readLock().lock();
try {
TIntObjectHashMap<Object> 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<Object> 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<String, List<Loader>> myClassPackagesCache = new HashMap<String, List<Loader>>();
private final HashMap<String, List<Loader>> myResourcePackagesCache = new HashMap<String, List<Loader>>();
private final TIntHashSet myResourceIndex = new TIntHashSet();
private List<Loader> getLoaders(String resourcePath) {
boolean isClassFile = resourcePath.endsWith(UrlClassLoader.CLASS_EXTENSION);
final int idx = resourcePath.lastIndexOf('/');
String packageName = idx > 0 ? resourcePath.substring(0, idx) : "";
Map<String, List<Loader>> map = isClassFile ? myClassPackagesCache : myResourcePackagesCache;
List<Loader> list = map.get(packageName);
if (list == null) {
list = new SmartList<Loader>();
map.put(packageName, list);
}
return list;
}
protected void addResourceEntry(String resourcePath, Loader loader) {
final List<Loader> 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<Loader> 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;
}
}
}
@@ -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 + "]";
}
@@ -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)];
}
}
@@ -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<String, Resource> myResources = new THashMap<String, Resource>();
private final Map<String, Resource> myResources = Collections.synchronizedMap(new HashMap<String, Resource>()); // todo do we need it ?
private JarMemoryLoader() { }
@@ -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 {