Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java63
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/LongString.java10
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ShortString.java12
3 files changed, 80 insertions, 5 deletions
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
index 8177c6b4b94..5c473cc974d 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
@@ -18,11 +18,16 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.core.runtime.CoreException;
@@ -110,6 +115,22 @@ public class Database {
private long cacheHits;
private long cacheMisses;
+ /** Soft reference wrapper to keep track of the record for disposed strings. */
+ private static class SoftStringRef extends SoftReference<IString> {
+ private final long record;
+ public SoftStringRef(IString referent, ReferenceQueue<? super IString> q) {
+ super(referent, q);
+ record = referent.getRecord();
+ }
+ public long getRecord() {
+ return record;
+ }
+ }
+
+ // a cache for strings which is used for btree lookups; soft refs ensure garbage collection
+ private final Map<Long, Reference<IString>> stringCache = new HashMap<>();
+ private final ReferenceQueue<IString> stringDisposal = new ReferenceQueue<>();
+
/**
* Construct a new Database object, creating a backing file if necessary.
* @param location the local file path for the database
@@ -243,6 +264,8 @@ public class Database {
createNewChunks((int) setasideChunks);
flush();
}
+ // clear cache for strings which are used for btree searches
+ clearStringCache();
}
private void removeChunksFromCache() {
@@ -463,6 +486,7 @@ public class Database {
}
addBlock(chunk, blocksize, block);
freed += blocksize;
+ stringCache.remove(offset); // also remove record from string cache (if it exists)
}
public void putByte(long offset, byte value) throws CoreException {
@@ -564,9 +588,9 @@ public class Database {
}
if (bytelen > ShortString.MAX_BYTE_LENGTH) {
- return new LongString(this, chars, useBytes);
+ return addStringToCache(new LongString(this, chars, useBytes));
} else {
- return new ShortString(this, chars, useBytes);
+ return addStringToCache(new ShortString(this, chars, useBytes));
}
}
@@ -579,12 +603,33 @@ public class Database {
}
public IString getString(long offset) throws CoreException {
+ final Reference<IString> cachedStringReference = stringCache.get(offset);
+ if (cachedStringReference != null) {
+ final IString cachedString = cachedStringReference.get();
+ if (cachedString != null) {
+ return cachedString; // string already cached, no need to re-retrieve it :-)
+ }
+ }
final int l = getInt(offset);
int bytelen= l < 0 ? -l : 2 * l;
if (bytelen > ShortString.MAX_BYTE_LENGTH) {
- return new LongString(this, offset);
+ return addStringToCache(new LongString(this, offset));
}
- return new ShortString(this, offset);
+ return addStringToCache(new ShortString(this, offset));
+ }
+
+ private IString addStringToCache(IString string) {
+ // add string to cache
+ stringCache.put(string.getRecord(), new SoftStringRef(string, stringDisposal));
+ // also remove keys from cache list upon garbage collection
+ if (stringDisposal != null) {
+ Reference<? extends IString> disposedRef = stringDisposal.poll();
+ while (disposedRef instanceof SoftStringRef) {
+ stringCache.remove(((SoftStringRef) disposedRef).getRecord());
+ disposedRef = stringDisposal.poll();
+ }
+ }
+ return string;
}
/**
@@ -629,6 +674,7 @@ public class Database {
} catch (IOException e) {
throw new CoreException(new DBStatus(e));
}
+ clearStringCache();
}
/**
@@ -731,6 +777,15 @@ public class Database {
// Also handles header chunk.
flushAndUnlockChunks(dirtyChunks, true);
+
+ // And clear string cache
+ clearStringCache();
+ }
+
+ private void clearStringCache() {
+ stringCache.clear();
+ while (stringDisposal.poll() != null) {
+ }
}
private void flushAndUnlockChunks(final ArrayList<Chunk> dirtyChunks, boolean isComplete) throws CoreException {
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/LongString.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/LongString.java
index 996d5ee93af..596fbc92b94 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/LongString.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/LongString.java
@@ -27,6 +27,9 @@ public class LongString implements IString {
private final long record;
private int hash;
+ // this string is immutable, so we can cache the actual char array
+ private char[] cachedChars;
+
// Additional fields of first record.
private static final int LENGTH = 0; // Must be first to match ShortString.
private static final int NEXT1 = 4;
@@ -89,6 +92,9 @@ public class LongString implements IString {
} else {
chunk.putChars(nextRecord + CHARSN, chars, start, remaining);
}
+
+ // There is currently no need to store char[] in cachedChars because all
+ // callers are currently only interested in the record.
}
@Override
@@ -98,6 +104,9 @@ public class LongString implements IString {
@Override
public char[] getChars() throws CoreException {
+ if (cachedChars != null) {
+ return cachedChars; // no need to re-retrieve array if it is already cached
+ }
int length = db.getInt(record + LENGTH);
final boolean useBytes = length < 0;
int numChars1 = NUM_CHARS1;
@@ -135,6 +144,7 @@ public class LongString implements IString {
start += partLen;
p= p + NEXTN;
}
+ cachedChars = chars; // cache the array
return chars;
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ShortString.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ShortString.java
index f8d7626a1bf..45778bcd4eb 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ShortString.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ShortString.java
@@ -25,7 +25,10 @@ public class ShortString implements IString {
private final Database db;
private final long record;
private int hash;
-
+
+ // this string is immutable, so we can cache the actual char array
+ private char[] cachedChars;
+
private static final int LENGTH = 0;
private static final int CHARS = 4;
@@ -49,6 +52,9 @@ public class ShortString implements IString {
} else {
chunk.putChars(p, chars, 0, n);
}
+
+ // There is currently no need to store char[] in cachedChars because all
+ // callers are currently only interested in the record.
}
@Override
@@ -63,6 +69,9 @@ public class ShortString implements IString {
@Override
public char[] getChars() throws CoreException {
+ if (cachedChars != null) {
+ return cachedChars; // no need to re-retrieve array if it is already cached
+ }
final Chunk chunk = db.getChunk(record);
final int l = chunk.getInt(record + LENGTH);
final int length = Math.abs(l);
@@ -72,6 +81,7 @@ public class ShortString implements IString {
} else {
chunk.getChars(record + CHARS, chars, 0, length);
}
+ cachedChars = chars; // cache the array
return chars;
}

Back to the top