mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 06:21:16 +07:00
memory optimization: store byte array instead of String in FilePartNode
This commit is contained in:
@@ -27,6 +27,7 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.ByteArrayCharSequence;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -37,7 +38,7 @@ import java.util.List;
|
||||
// all file pointers we store in the tree with nodes corresponding to the file structure on disk
|
||||
class FilePointerPartNode {
|
||||
private static final FilePointerPartNode[] EMPTY_ARRAY = new FilePointerPartNode[0];
|
||||
@NotNull String part; // common prefix of all file pointers beneath
|
||||
@NotNull CharSequence part; // common prefix of all file pointers beneath
|
||||
@NotNull FilePointerPartNode[] children;
|
||||
FilePointerPartNode parent;
|
||||
// file pointers for this exact path (e.g. concatenation of all "part" fields down from the root).
|
||||
@@ -52,8 +53,8 @@ class FilePointerPartNode {
|
||||
int pointersUnder; // number of alive pointers in this node plus all nodes beneath
|
||||
private static final VirtualFileManager ourFileManager = VirtualFileManager.getInstance();
|
||||
|
||||
FilePointerPartNode(@NotNull String part, FilePointerPartNode parent, Pair<VirtualFile,String> fileAndUrl, int pointersToStore) {
|
||||
this.part = part;
|
||||
FilePointerPartNode(@NotNull CharSequence part, FilePointerPartNode parent, Pair<VirtualFile,String> fileAndUrl, int pointersToStore) {
|
||||
this.part = ByteArrayCharSequence.convertToBytesIfAsciiString(part);
|
||||
this.parent = parent;
|
||||
children = EMPTY_ARRAY;
|
||||
myFileAndUrl = fileAndUrl;
|
||||
@@ -172,11 +173,11 @@ class FilePointerPartNode {
|
||||
}
|
||||
|
||||
private void doCheckConsistency(boolean dotDotOccurred) {
|
||||
int ddi = part.indexOf("..");
|
||||
if (ddi != -1) {
|
||||
int dotDotIndex = StringUtil.indexOf(part, "..");
|
||||
if (dotDotIndex != -1) {
|
||||
// part must not contain "/.." nor "../" nor be just ".."
|
||||
// (except when the pointer was created from URL of non-existing file with ".." inside)
|
||||
dotDotOccurred |= part.equals("..") || ddi != 0 && part.charAt(ddi-1) == '/' || ddi < part.length() - 2 && part.charAt(ddi+2) == '/';
|
||||
dotDotOccurred |= part.equals("..") || dotDotIndex != 0 && part.charAt(dotDotIndex-1) == '/' || dotDotIndex < part.length() - 2 && part.charAt(dotDotIndex+2) == '/';
|
||||
}
|
||||
int childSum = 0;
|
||||
for (FilePointerPartNode child : children) {
|
||||
@@ -237,8 +238,8 @@ class FilePointerPartNode {
|
||||
if (index > start + 1 && index != path.length() && path.charAt(index - 1) == '/') index--;
|
||||
String pathRest = path.substring(index);
|
||||
FilePointerPartNode newNode = pathRest.isEmpty() ? this : new FilePointerPartNode(pathRest, this, fileAndUrl, pointersToStore);
|
||||
String commonPredecessor = StringUtil.first(part, index - start, false);
|
||||
FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl, pointersUnder);
|
||||
CharSequence commonPredecessor = StringUtil.first(part, index - start, false);
|
||||
FilePointerPartNode splittedAway = new FilePointerPartNode(part.subSequence(index - start, part.length()), this, myFileAndUrl, pointersUnder);
|
||||
splittedAway.children = children;
|
||||
for (FilePointerPartNode child : children) {
|
||||
child.parent = splittedAway;
|
||||
@@ -275,7 +276,7 @@ class FilePointerPartNode {
|
||||
return indexOfFirstDifferentChar(path, start, part, 0);
|
||||
}
|
||||
|
||||
private static boolean endsWith(@NotNull String string, @NotNull String end) {
|
||||
private static boolean endsWith(@NotNull String string, @NotNull CharSequence end) {
|
||||
return indexOfFirstDifferentChar(string, string.length() - end.length(), end, 0) == string.length();
|
||||
}
|
||||
|
||||
@@ -335,7 +336,7 @@ class FilePointerPartNode {
|
||||
}
|
||||
|
||||
// return an index in s1 of the first different char of strings s1[start1..) and s2[start2..)
|
||||
private static int indexOfFirstDifferentChar(@NotNull CharSequence s1, int start1, @NotNull String s2, int start2) {
|
||||
private static int indexOfFirstDifferentChar(@NotNull CharSequence s1, int start1, @NotNull CharSequence s2, int start2) {
|
||||
boolean ignoreCase = !SystemInfo.isFileSystemCaseSensitive;
|
||||
int len1 = s1.length();
|
||||
int len2 = s2.length();
|
||||
@@ -381,8 +382,9 @@ class FilePointerPartNode {
|
||||
return leaves == null ? null : leaves instanceof VirtualFilePointerImpl ? (VirtualFilePointerImpl)leaves : ((VirtualFilePointerImpl[])leaves)[0];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getUrl() {
|
||||
return parent == null ? part : parent.getUrl() + part;
|
||||
return parent == null ? String.valueOf(part) : parent.getUrl() + part;
|
||||
}
|
||||
|
||||
private int leavesNumber() {
|
||||
|
||||
+1
-1
@@ -458,7 +458,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
Pair<VirtualFile,String> after = node.update();
|
||||
assert after != null : "can't invalidate inside modification";
|
||||
String urlAfter = after.second;
|
||||
if (URL_COMPARATOR.compare(urlBefore, urlAfter) != 0 || !urlAfter.endsWith(node.part)) {
|
||||
if (URL_COMPARATOR.compare(urlBefore, urlAfter) != 0 || !StringUtil.endsWith(urlAfter, node.part)) {
|
||||
List<VirtualFilePointerImpl> myPointers = new SmartList<>();
|
||||
node.addAllPointersTo(myPointers);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.CharSequenceWithStringHash;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -79,22 +80,41 @@ public class ByteArrayCharSequence implements CharSequenceWithStringHash {
|
||||
public static CharSequence convertToBytesIfAsciiString(@NotNull CharSequence name) {
|
||||
int length = name.length();
|
||||
if (length == 0) return "";
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = name.charAt(i);
|
||||
if (c >= 128) {
|
||||
//noinspection RedundantStringConstructorCall
|
||||
return new String(name.toString()); // So we don't hold whole char[] buffer of a lengthy path on JDK 6
|
||||
}
|
||||
|
||||
bytes[i] = (byte)c;
|
||||
}
|
||||
return new ByteArrayCharSequence(bytes);
|
||||
if (name instanceof ByteArrayCharSequence) return name;
|
||||
byte[] bytes = toBytesIfPossible(name);
|
||||
return bytes == null ? name : new ByteArrayCharSequence(bytes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
byte[] getBytes() {
|
||||
return myStart == 0 && myEnd == myChars.length ? myChars : Arrays.copyOfRange(myChars, myStart , myEnd);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static byte[] toBytesIfPossible(CharSequence seq) {
|
||||
if (seq instanceof ByteArrayCharSequence) {
|
||||
return ((ByteArrayCharSequence)seq).getBytes();
|
||||
}
|
||||
byte[] bytes = new byte[seq.length()];
|
||||
char[] chars = CharArrayUtil.fromSequenceWithoutCopying(seq);
|
||||
if (chars == null) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
char c = seq.charAt(i);
|
||||
if ((c & 0xff00) != 0) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte)c;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
char c = chars[i];
|
||||
if ((c & 0xff00) != 0) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte)c;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.intellij.openapi.util.text.CharSequenceWithStringHash;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A pruned and optimized version of javolution.text.Text
|
||||
@@ -89,7 +88,7 @@ final class ImmutableText extends ImmutableCharSequence implements CharArrayExte
|
||||
}
|
||||
|
||||
private static LeafNode createLeafNode(@NotNull CharSequence str) {
|
||||
byte[] bytes = toBytesIfPossible(str);
|
||||
byte[] bytes = ByteArrayCharSequence.toBytesIfPossible(str);
|
||||
if (bytes != null) {
|
||||
return new Leaf8BitNode(bytes);
|
||||
}
|
||||
@@ -98,34 +97,6 @@ final class ImmutableText extends ImmutableCharSequence implements CharArrayExte
|
||||
return new WideLeafNode(chars);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] toBytesIfPossible(CharSequence seq) {
|
||||
if (seq instanceof ByteArrayCharSequence) {
|
||||
return ((ByteArrayCharSequence)seq).getBytes();
|
||||
}
|
||||
byte[] bytes = new byte[seq.length()];
|
||||
char[] chars = CharArrayUtil.fromSequenceWithoutCopying(seq);
|
||||
if (chars == null) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
char c = seq.charAt(i);
|
||||
if ((c & 0xff00) != 0) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte)c;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
char c = chars[i];
|
||||
if ((c & 0xff00) != 0) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte)c;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* When first loaded, ImmutableText contents are stored as a single large array. This saves memory but isn't
|
||||
* modification-friendly as it disallows slightly changed texts to retain most of the internal structure of the
|
||||
|
||||
Reference in New Issue
Block a user