compressed OptionDescriptors (3mb down to 1mb)

This commit is contained in:
Alexey Kudravtsev
2015-05-15 16:16:07 +03:00
parent f089d9afe4
commit de250482df
3 changed files with 177 additions and 55 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -24,27 +24,31 @@ import org.jetbrains.annotations.Nullable;
* Date: 17-Mar-2006
*/
public class OptionDescription implements Comparable{
private String myOption;
private final String myOption;
private final String myHit;
private String myPath;
private String myConfigurableId;
public OptionDescription(final String option, final String hit, final String path) {
myOption = option;
myHit = hit;
myPath = path;
}
private final String myPath;
private final String myConfigurableId;
private final String myGroupName;
public OptionDescription(String hit) {
myHit = hit;
this(null, hit, null);
}
public OptionDescription(final String option, final String hit, final String path) {
this(option, null, hit, path);
}
public OptionDescription(final String option, final String configurableId, final String hit, final String path) {
this(option, configurableId, hit, path, null);
}
public OptionDescription(final String option, final String configurableId, final String hit, final String path, String groupName) {
myOption = option;
myHit = hit;
myPath = path;
myConfigurableId = configurableId;
myGroupName = groupName;
}
public String getOption() {
@@ -66,6 +70,10 @@ public class OptionDescription implements Comparable{
return myConfigurableId;
}
public String getGroupName() {
return myGroupName;
}
public String toString() {
return myHit;
}
@@ -0,0 +1,77 @@
/*
* 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.ide.ui.search;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.OpenTHashSet;
import com.intellij.util.text.ByteArrayCharSequence;
import com.intellij.util.text.CharSequenceHashingStrategy;
import org.jetbrains.annotations.NotNull;
/**
* Converts the String to the int id and back.
* It guarantees to return the same id for the equals Strings.
* Used for compression: instead of keeping many (maybe equal) String references it sometimes cheaper to keep int ids in a packed array.
*/
class IndexedCharsInterner {
private int[] idToIndex = new int[10];
private final OpenTHashSet<CharSequence> mySet = new OpenTHashSet<CharSequence>(10, 0.9f, CharSequenceHashingStrategy.CASE_SENSITIVE) {
@Override
protected void rehash(int newCapacity) {
Object[] oldSet = _set;
int oldSize = mySet.size();
super.rehash(newCapacity);
for (int i = 0; i < oldSize-1; i++) {
int oldIndex = idToIndex[i];
CharSequence oldString = (CharSequence)oldSet[oldIndex];
int newIndex = index(oldString);
assert newIndex != -1;
idToIndex[i] = newIndex;
}
}
};
public int toId(@NotNull String name) {
CharSequence seq = ByteArrayCharSequence.convertToBytesIfAsciiString(name);
int index = mySet.index(seq);
int id;
if (index == -1) {
id = mySet.size();
mySet.add(seq);
index = mySet.index(seq);
assert index != -1;
if (id >= idToIndex.length) {
idToIndex = ArrayUtil.realloc(idToIndex, idToIndex.length * 3 / 2);
}
idToIndex[id] = index;
assert ArrayUtil.indexOf(idToIndex, index) == id;
assert toId(name) == id;
}
else {
id = ArrayUtil.indexOf(idToIndex, index);
}
assert StringUtil.equals(fromId(id), name);
return id;
}
@NotNull
public CharSequence fromId(int id) {
assert id >=0 && id < mySet.size();
return mySet.get(idToIndex[id]);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* 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.
@@ -18,8 +18,8 @@ package com.intellij.ide.ui.search;
import com.intellij.codeStyle.CodeStyleFacade;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerConfigurable;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,10 +31,11 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ResourceUtil;
import com.intellij.util.SingletonSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.StringInterner;
import com.intellij.util.text.ByteArrayCharSequence;
import com.intellij.util.text.CharSequenceHashingStrategy;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jdom.Document;
@@ -54,24 +55,29 @@ import java.util.regex.Pattern;
* Date: 07-Feb-2006
*/
public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
private final Map<String, Set<OptionDescription>> myStorage = Collections.synchronizedMap(new THashMap<String, Set<OptionDescription>>(20, 0.9f));
private final Map<String, String> myId2Name = Collections.synchronizedMap(new THashMap<String, String>(20, 0.9f));
// option => array of packed OptionDescriptor
private final Map<CharSequence, long[]> myStorage = Collections.synchronizedMap(new THashMap<CharSequence, long[]>(20, 0.9f, CharSequenceHashingStrategy.CASE_SENSITIVE));
private final Set<String> myStopWords = Collections.synchronizedSet(new HashSet<String>());
private final Map<Couple<String>, Set<String>> myHighlightOption2Synonym = Collections.synchronizedMap(new THashMap<Couple<String>, Set<String>>());
private final Set<String> myStopWords = Collections.synchronizedSet(new THashSet<String>());
private final Map<Couple<String>, Set<String>> myHighlightOption2Synonym = Collections.synchronizedMap(
new THashMap<Couple<String>, Set<String>>());
private volatile boolean allTheseHugeFilesAreLoaded;
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
private final StringInterner myIdentifierTable = new StringInterner() {
private final IndexedCharsInterner myIdentifierTable = new IndexedCharsInterner() {
@Override
public synchronized int toId(@NotNull String name) {
return super.toId(name);
}
@NotNull
public synchronized String intern(@NotNull final String name) {
return super.intern(name);
@Override
public synchronized CharSequence fromId(int id) {
return super.fromId(id);
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.ui.search.SearchableOptionsRegistrarImpl");
public static final int LOAD_FACTOR = 20;
private static final int LOAD_FACTOR = 20;
@NonNls
private static final Pattern REG_EXP = Pattern.compile("[\\W&&[^-]]+");
@@ -198,7 +204,7 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
LOG.error(e);
}
for (IdeaPluginDescriptor plugin : PluginManager.getPlugins()) {
for (IdeaPluginDescriptor plugin : PluginManagerCore.getPlugins()) {
final Set<String> words = getProcessedWordsWithoutStemming(plugin.getName());
final String description = plugin.getDescription();
if (description != null) {
@@ -210,31 +216,61 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
}
}
private synchronized void putOptionWithHelpId(@NotNull String option, @NotNull final String id, @Nullable final String groupName, @Nullable String hit, @Nullable final String path) {
/**
* @return XYZT:64 bits where X:16 bits - id of the interned groupName
* Y:16 bits - id of the interned id
* Z:16 bits - id of the interned hit
* T:16 bits - id of the interned path
*/
private long pack(@NotNull final String id, @Nullable String hit, @Nullable final String path, @Nullable String groupName) {
long _id = myIdentifierTable.toId(id.trim());
long _hit = hit == null ? Short.MAX_VALUE : myIdentifierTable.toId(hit.trim());
long _path = path == null ? Short.MAX_VALUE : myIdentifierTable.toId(path.trim());
long _groupName = groupName == null ? Short.MAX_VALUE : myIdentifierTable.toId(groupName.trim());
assert _id >= 0 && _id < Short.MAX_VALUE;
assert _hit >= 0 && _hit <= Short.MAX_VALUE;
assert _path >= 0 && _path <= Short.MAX_VALUE;
assert _groupName >= 0 && _groupName <= Short.MAX_VALUE;
return _groupName << 48 | _id << 32 | _hit << 16 | _path << 0;
}
private OptionDescription unpack(long data) {
int _groupName = (int)(data >> 48 & 0xffff);
int _id = (int)(data >> 32 & 0xffff);
int _hit = (int)(data >> 16 & 0xffff);
int _path = (int)(data & 0xffff);
assert _id >= 0 && _id < Short.MAX_VALUE;
assert _hit >= 0 && _hit <= Short.MAX_VALUE;
assert _path >= 0 && _path <= Short.MAX_VALUE;
assert _groupName >= 0 && _groupName <= Short.MAX_VALUE;
String groupName = _groupName == Short.MAX_VALUE ? null : myIdentifierTable.fromId(_groupName).toString();
String configurableId = myIdentifierTable.fromId(_id).toString();
String hit = _hit == Short.MAX_VALUE ? null : myIdentifierTable.fromId(_hit).toString();
String path = _path == Short.MAX_VALUE ? null : myIdentifierTable.fromId(_path).toString();
return new OptionDescription(null, configurableId, hit, path, groupName);
}
private synchronized void putOptionWithHelpId(@NotNull String option,
@NotNull final String id,
@Nullable final String groupName,
@Nullable String hit,
@Nullable final String path) {
if (isStopWord(option)) return;
String stopWord = PorterStemmerUtil.stem(option);
if (stopWord == null) return;
if (isStopWord(stopWord)) return;
if (!myId2Name.containsKey(id) && groupName != null) {
myId2Name.put(myIdentifierTable.intern(id), myIdentifierTable.intern(groupName));
}
OptionDescription description =
new OptionDescription(null, myIdentifierTable.intern(id).trim(), hit != null ? myIdentifierTable.intern(hit).trim() : null,
path != null ? myIdentifierTable.intern(path).trim() : null);
Set<OptionDescription> configs = myStorage.get(option);
long[] configs = myStorage.get(option);
long packed = pack(id, hit, path, groupName);
if (configs == null) {
configs = new SingletonSet<OptionDescription>(description);
myStorage.put(new String(option), configs);
}
else if (configs instanceof SingletonSet){
configs = new THashSet<OptionDescription>(configs);
configs.add(description);
myStorage.put(new String(option), configs);
configs = new long[] {packed};
}
else {
configs.add(description);
configs = ArrayUtil.indexOf(configs, packed) == -1 ? ArrayUtil.append(configs, packed) : configs;
}
myStorage.put(ByteArrayCharSequence.convertToBytesIfAsciiString(option), configs);
}
@Override
@@ -331,20 +367,21 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
if (StringUtil.isEmptyOrSpaces(stemmedPrefix)) return null;
loadHugeFilesIfNecessary();
Set<OptionDescription> result = null;
for (Map.Entry<String, Set<OptionDescription>> entry : myStorage.entrySet()) {
final Set<OptionDescription> descriptions = entry.getValue();
if (descriptions != null) {
final String option = entry.getKey();
if (!option.startsWith(prefix) && !option.startsWith(stemmedPrefix)) {
final String stemmedOption = PorterStemmerUtil.stem(option);
if (stemmedOption != null && !stemmedOption.startsWith(prefix) && !stemmedOption.startsWith(stemmedPrefix)) {
continue;
}
for (Map.Entry<CharSequence, long[]> entry : myStorage.entrySet()) {
final long[] descriptions = entry.getValue();
final CharSequence option = entry.getKey();
if (!StringUtil.startsWith(option, prefix) && !StringUtil.startsWith(option, stemmedPrefix)) {
final String stemmedOption = PorterStemmerUtil.stem(option.toString());
if (stemmedOption != null && !stemmedOption.startsWith(prefix) && !stemmedOption.startsWith(stemmedPrefix)) {
continue;
}
if (result == null) {
result = new THashSet<OptionDescription>();
}
result.addAll(descriptions);
}
if (result == null) {
result = new THashSet<OptionDescription>();
}
for (long description : descriptions) {
OptionDescription desc = unpack(description);
result.add(desc);
}
}
return result;
@@ -414,7 +451,7 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
Set<OptionDescription> configs = getAcceptableDescriptions(opt);
if (configs == null) continue;
for (OptionDescription description : configs) {
String groupName = myId2Name.get(description.getConfigurableId());
String groupName = description.getGroupName();
if (perProject) {
if (Comparing.strEqual(groupName, ApplicationBundle.message("title.global.code.style"))) {
groupName = ApplicationBundle.message("title.project.code.style");