Skip to content

Commit

Permalink
[performance] QuickTextSearcher avoid streaming
Browse files Browse the repository at this point in the history
For small files read and decode the whole file content at once.
* Avoid exceptions.

Quick Search (crtl alt shift L) the whole platform workspace (any String
that can not be found): 4s -> 2sec
  • Loading branch information
EcljpseB0T authored and jukzi committed Sep 17, 2024
1 parent e9f49ae commit a43889d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
package org.eclipse.text.quicksearch.internal.core;

import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -23,6 +26,7 @@

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
Expand All @@ -36,6 +40,7 @@
import org.eclipse.text.quicksearch.internal.util.LineReader;

public class QuickTextSearcher {
private static int MAX_BUFFER_LENGTH = 999_999; // read max 1MB bytes => max 2MB chars.
private final QuickTextSearchRequestor requestor;
private QuickTextQuery query;

Expand Down Expand Up @@ -155,7 +160,7 @@ private static boolean search(IFile f, BooleanSupplier canceled,
if (canceled.getAsBoolean()) {
return false;
}
try (LineReader lr = new LineReader(new InputStreamReader(f.getContents(true), f.getCharset()),
try (LineReader lr = new LineReader(getReader(f),
maxLineLength)) {
String line;
int lineIndex = 1;
Expand All @@ -179,6 +184,15 @@ private static boolean search(IFile f, BooleanSupplier canceled,
return true;
}

private static Reader getReader(IFile f) throws UnsupportedEncodingException, CoreException {
String shortString = toShortString(f);
if (shortString != null) {
return new StringReader(shortString);
} else {
return new InputStreamReader(f.getContents(true), f.getCharset());
}
}

@Override
public void resume() {
//Only resume if we don't already exceed the maxResult limit.
Expand All @@ -188,7 +202,26 @@ public void resume() {
}

}

/**
* Try to get a content as String. Avoids Streaming.
*/
private static String toShortString(IFile file) {
/**
* Just any number such that the most source files will fit in. And not too
* big to avoid out of memory.
**/
try {
byte[] content = file.readNBytes(MAX_BUFFER_LENGTH);
int length = content.length;
if (length >= MAX_BUFFER_LENGTH) {
return null;
}
String charset = file.getCharset();
return new String(content, charset);
} catch (Exception e) {
return null;
}
}
/**
* This job updates already found matches when the query is changed.
* Both the walker job and this job share the same scheduling rule so
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

/**
* Provides a helper to efficiently split a file into
Expand All @@ -30,7 +31,7 @@ public class LineReader implements AutoCloseable{
private static final int EXPECTED_LINE_LENGTH = 160;
public static final int DEFAULT_MAX_LINE_LENGTH = 1000;

private BufferedReader input;
private Reader input;

//This simple implementation just wraps a BufferedReader and StringBuilder
//to do the buffering and String building.
Expand All @@ -43,21 +44,23 @@ public LineReader(Reader reader) {

public LineReader(Reader reader, int maxLineLength) {
input = buffered(reader);
MAX_LINE_LENGTH = maxLineLength;
this.maxLineLength = maxLineLength;
}


private StringBuilder line = new StringBuilder(EXPECTED_LINE_LENGTH);

private final int MAX_LINE_LENGTH;
private final int maxLineLength;
private int lineOffset = -1; //Start pos of last line read.
private int offset = 0; //position of next char in input.
private int mark = 0; //mark offset in underlying stream

private BufferedReader buffered(Reader reader) {
private Reader buffered(Reader reader) {
//If already buffered don't wrap it again.
if (reader instanceof BufferedReader) {
return (BufferedReader) reader;
if (reader instanceof StringReader sr) {
return sr;
} else if (reader instanceof BufferedReader br) {
return br;
} else {
return new BufferedReader(reader);
}
Expand All @@ -68,24 +71,19 @@ private BufferedReader buffered(Reader reader) {
*/
@Override
public void close() {
BufferedReader toClose = null;
synchronized (input) {
if (input==null) {
return;
}
toClose = input;
Reader toClose = input;
try (toClose) {
input = null;
}
try {
toClose.close();
} catch (IOException e) {
} catch (IOException closeException) {
//Ignore.
} finally {
input = null;
}
}

public String readLine() throws IOException {
lineOffset = offset; //remember start of line
int maxOffset = offset + MAX_LINE_LENGTH;
int maxOffset = offset + maxLineLength;
//Read text until we see either a CR, CR LF or LF.
int c = read();
if (c==-1) {
Expand All @@ -96,7 +94,8 @@ public String readLine() throws IOException {
line.append((char)c);
c = read();
if (offset>maxOffset) {
throw new IOException("Very long lines of text. Minified file?"); //$NON-NLS-1$
// Very long lines of text. Minified file?
return null;
}
}
//Last char read was some kind of line terminator. But only read first char of it.
Expand Down

0 comments on commit a43889d

Please sign in to comment.