diff --git a/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java b/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java index 435908b9e0f..31a1431c75f 100644 --- a/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java +++ b/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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. @@ -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 diff --git a/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java b/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java index c72b00bde68..b74a174982e 100644 --- a/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java +++ b/bundles/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java @@ -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 @@ -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. @@ -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); } @@ -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) { @@ -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.