mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-09-08 11:03:28 +02:00
Bug 292908: Implementation of AbstractCharArray for large files.
This commit is contained in:
parent
4934483c60
commit
7a66e99871
3 changed files with 359 additions and 5 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2003, 2009 IBM Corporation and others.
|
* Copyright (c) 2003, 2010 IBM Corporation and others.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -20,7 +20,8 @@ import org.eclipse.cdt.core.CCorePlugin;
|
||||||
import org.eclipse.cdt.core.index.IIndexFileLocation;
|
import org.eclipse.cdt.core.index.IIndexFileLocation;
|
||||||
import org.eclipse.cdt.core.parser.FileContent;
|
import org.eclipse.cdt.core.parser.FileContent;
|
||||||
import org.eclipse.cdt.core.parser.ParserFactory;
|
import org.eclipse.cdt.core.parser.ParserFactory;
|
||||||
import org.eclipse.cdt.internal.core.parser.scanner.CharArray;
|
import org.eclipse.cdt.internal.core.parser.scanner.AbstractCharArray;
|
||||||
|
import org.eclipse.cdt.internal.core.parser.scanner.FileCharArray;
|
||||||
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
|
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
|
||||||
import org.eclipse.cdt.internal.core.resources.PathCanonicalizationStrategy;
|
import org.eclipse.cdt.internal.core.resources.PathCanonicalizationStrategy;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
|
@ -197,9 +198,8 @@ public class InternalParserUtil extends ParserFactory {
|
||||||
|
|
||||||
private static InternalFileContent createFileContent(String path, String charset, InputStream in) {
|
private static InternalFileContent createFileContent(String path, String charset, InputStream in) {
|
||||||
try {
|
try {
|
||||||
// replace with a better implementation
|
AbstractCharArray chars= FileCharArray.create(path, charset, in);
|
||||||
org.eclipse.cdt.core.parser.CodeReader reader= new org.eclipse.cdt.core.parser.CodeReader(path, charset, in);
|
return new InternalFileContent(path, chars);
|
||||||
return new InternalFileContent(path, new CharArray(reader.buffer));
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
CCorePlugin.log(e);
|
CCorePlugin.log(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2010 Wind River Systems, Inc. and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Markus Schorn - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.internal.core.parser.scanner;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of char array for a file referencing content via
|
||||||
|
* soft references.
|
||||||
|
*/
|
||||||
|
public class FileCharArray extends LazyCharArray {
|
||||||
|
|
||||||
|
public static AbstractCharArray create(String fileName, String charSet, InputStream in) throws IOException {
|
||||||
|
// no support for non-local files
|
||||||
|
if (!(in instanceof FileInputStream)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
FileInputStream fis= (FileInputStream) in;
|
||||||
|
if (!Charset.isSupported(charSet)) {
|
||||||
|
charSet= System.getProperty("file.encoding"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
FileChannel channel = fis.getChannel();
|
||||||
|
final long lsize = channel.size();
|
||||||
|
if (lsize < CHUNK_SIZE) {
|
||||||
|
return decodeSmallFile(channel, (int) lsize, charSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileCharArray(fileName, charSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AbstractCharArray decodeSmallFile(FileChannel channel, int lsize, String charSet) throws IOException {
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(lsize);
|
||||||
|
channel.read(byteBuffer);
|
||||||
|
byteBuffer.flip();
|
||||||
|
|
||||||
|
CharBuffer charBuffer = Charset.forName(charSet).decode(byteBuffer);
|
||||||
|
char[] buf= extractChars(charBuffer);
|
||||||
|
return new CharArray(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fFileName;
|
||||||
|
private String fCharSet;
|
||||||
|
private FileChannel fChannel;
|
||||||
|
|
||||||
|
private FileCharArray(String fileName, String charSet) {
|
||||||
|
fFileName= fileName;
|
||||||
|
fCharSet= charSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Chunk createChunk(int chunkOffset) {
|
||||||
|
FileInputStream fis;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(fFileName);
|
||||||
|
} catch (FileNotFoundException e1) {
|
||||||
|
// File has been deleted in the meantime
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
fChannel= fis.getChannel();
|
||||||
|
try {
|
||||||
|
return super.createChunk(chunkOffset);
|
||||||
|
} finally {
|
||||||
|
fChannel= null;
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long readChunkData(long fileOffset, CharBuffer dest) throws IOException {
|
||||||
|
assert fChannel != null;
|
||||||
|
final Charset charset = Charset.forName(fCharSet);
|
||||||
|
final CharsetDecoder decoder = charset.newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
|
||||||
|
int needBytes = (int) (CHUNK_SIZE * (double) decoder.averageCharsPerByte()); // avoid rounding errors.
|
||||||
|
final ByteBuffer in = ByteBuffer.allocate(needBytes);
|
||||||
|
|
||||||
|
int total= 0;
|
||||||
|
boolean endOfInput= false;
|
||||||
|
while(total < CHUNK_SIZE && !endOfInput) {
|
||||||
|
fChannel.position(fileOffset);
|
||||||
|
in.clear();
|
||||||
|
int count= fChannel.read(in);
|
||||||
|
if (count == -1) {
|
||||||
|
return fileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
endOfInput= count < in.capacity();
|
||||||
|
total+= count;
|
||||||
|
in.flip();
|
||||||
|
decoder.decode(in, dest, endOfInput);
|
||||||
|
fileOffset+= in.position();
|
||||||
|
}
|
||||||
|
return fileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void rereadChunkData(long fileOffset, long fileEndOffset, CharBuffer dest) {
|
||||||
|
FileInputStream fis;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(fFileName);
|
||||||
|
} catch (FileNotFoundException e1) {
|
||||||
|
// File has been deleted in the meantime
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
FileChannel channel = fis.getChannel();
|
||||||
|
decode(channel, fileOffset, fileEndOffset, dest);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// File cannot be read
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decode(FileChannel channel, long fileOffset, long fileEndOffset, CharBuffer dest) throws IOException {
|
||||||
|
final Charset charset = Charset.forName(fCharSet);
|
||||||
|
final CharsetDecoder decoder = charset.newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
|
||||||
|
int needBytes = (int) (fileEndOffset-fileOffset);
|
||||||
|
final ByteBuffer in = ByteBuffer.allocate(needBytes);
|
||||||
|
|
||||||
|
channel.position(fileOffset);
|
||||||
|
in.clear();
|
||||||
|
channel.read(in);
|
||||||
|
in.flip();
|
||||||
|
decoder.decode(in, dest, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2010 Wind River Systems, Inc. and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Markus Schorn - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.internal.core.parser.scanner;
|
||||||
|
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of char array for a file referencing content via
|
||||||
|
* soft references.
|
||||||
|
*/
|
||||||
|
public abstract class LazyCharArray extends AbstractCharArray {
|
||||||
|
private final static int CHUNK_BITS= 16; // 2^16 == 64K
|
||||||
|
protected final static int CHUNK_SIZE= 1 << CHUNK_BITS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to extract char[] out of CharBuffer.
|
||||||
|
*/
|
||||||
|
protected static char[] extractChars(CharBuffer charBuffer) {
|
||||||
|
if (charBuffer.hasArray() && charBuffer.arrayOffset() == 0) {
|
||||||
|
char[] buf = charBuffer.array();
|
||||||
|
if (buf.length == charBuffer.remaining())
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
char[] buf = new char[charBuffer.remaining()];
|
||||||
|
charBuffer.get(buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class Chunk {
|
||||||
|
final int fDataLength;
|
||||||
|
final long fFileOffset;
|
||||||
|
final long fFileEndOffset;
|
||||||
|
private SoftReference<char[]> fData;
|
||||||
|
|
||||||
|
private Chunk(long fileOffset, long fileEndOffset, char[] data) {
|
||||||
|
fDataLength= data.length;
|
||||||
|
fFileOffset= fileOffset;
|
||||||
|
fFileEndOffset= fileEndOffset;
|
||||||
|
fData= new SoftReference<char[]>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fLength= -1;
|
||||||
|
private List<Chunk> fChunks= new ArrayList<Chunk>();
|
||||||
|
|
||||||
|
protected LazyCharArray() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int tryGetLength() {
|
||||||
|
return fLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getLength() {
|
||||||
|
readUpTo(Integer.MAX_VALUE);
|
||||||
|
return fLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isValidOffset(int offset) {
|
||||||
|
if (offset < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
readUpTo(offset);
|
||||||
|
if (fLength >= 0)
|
||||||
|
return offset < fLength;
|
||||||
|
|
||||||
|
return offset < fChunks.size() << CHUNK_BITS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readUpTo(int offset) {
|
||||||
|
if (fLength >=0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final int chunkOffset= offset >> CHUNK_BITS;
|
||||||
|
getChunkData(chunkOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final char get(int offset) {
|
||||||
|
int chunkOffset= offset >> CHUNK_BITS;
|
||||||
|
char[] data= getChunkData(chunkOffset);
|
||||||
|
return data[offset & (CHUNK_SIZE-1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void arraycopy(int offset, char[] destination, int destinationPos, int length) {
|
||||||
|
int chunkOffset= offset >> CHUNK_BITS;
|
||||||
|
int loffset= offset & (CHUNK_SIZE-1);
|
||||||
|
char[] data= getChunkData(chunkOffset);
|
||||||
|
final int canCopy = data.length-loffset;
|
||||||
|
if (length <= canCopy) {
|
||||||
|
System.arraycopy(data, loffset, destination, destinationPos, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.arraycopy(data, loffset, destination, destinationPos, canCopy);
|
||||||
|
arraycopy(offset+canCopy, destination, destinationPos+canCopy, length-canCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private char[] getChunkData(int chunkOffset) {
|
||||||
|
Chunk chunk= getChunk(chunkOffset);
|
||||||
|
if (chunk != null) {
|
||||||
|
char[] data= chunk.fData.get();
|
||||||
|
if (data != null)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
return loadChunkData(chunk);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Chunk getChunk(int chunkOffset) {
|
||||||
|
final int chunkCount = fChunks.size();
|
||||||
|
if (chunkOffset < chunkCount)
|
||||||
|
return fChunks.get(chunkOffset);
|
||||||
|
|
||||||
|
if (fLength >=0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return createChunk(chunkOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a chunk is requested for the first time. There is no
|
||||||
|
* need to override this method.
|
||||||
|
*/
|
||||||
|
protected Chunk createChunk(int chunkOffset) {
|
||||||
|
final int chunkCount = fChunks.size();
|
||||||
|
long fileOffset= chunkCount == 0 ? 0 : fChunks.get(chunkCount-1).fFileEndOffset;
|
||||||
|
try {
|
||||||
|
CharBuffer dest= CharBuffer.allocate(CHUNK_SIZE);
|
||||||
|
for (int i = chunkCount; i <= chunkOffset; i++) {
|
||||||
|
dest.clear();
|
||||||
|
long fileEndOffset= readChunkData(fileOffset, dest);
|
||||||
|
dest.flip();
|
||||||
|
final int charCount= dest.remaining();
|
||||||
|
if (charCount == 0) {
|
||||||
|
fLength= fChunks.size() * CHUNK_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// New chunk
|
||||||
|
Chunk chunk= new Chunk(fileOffset, fileEndOffset, extractChars(dest));
|
||||||
|
fChunks.add(chunk);
|
||||||
|
if (charCount < CHUNK_SIZE) {
|
||||||
|
fLength= (fChunks.size()-1) * CHUNK_SIZE + charCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// File cannot be read
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunkOffset < fChunks.size())
|
||||||
|
return fChunks.get(chunkOffset);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char[] loadChunkData(Chunk chunk) {
|
||||||
|
CharBuffer dest= CharBuffer.allocate(chunk.fDataLength);
|
||||||
|
rereadChunkData(chunk.fFileOffset, chunk.fFileEndOffset, dest);
|
||||||
|
dest.flip();
|
||||||
|
char[] result= extractChars(dest);
|
||||||
|
if (result.length != chunk.fDataLength) {
|
||||||
|
// In case the file changed
|
||||||
|
char[] copy= new char[chunk.fDataLength];
|
||||||
|
System.arraycopy(result, 0, copy, 0, Math.min(result.length, copy.length));
|
||||||
|
result= copy;
|
||||||
|
}
|
||||||
|
chunk.fData= new SoftReference<char[]>(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the chunk data at the given source offset and provide the end-offset in the
|
||||||
|
* source.
|
||||||
|
*/
|
||||||
|
protected abstract long readChunkData(long sourceOffset, CharBuffer dest) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the chunk data at the given source range. In case the source range no longer (fully) exists,
|
||||||
|
* read as much as possible.
|
||||||
|
*/
|
||||||
|
protected abstract void rereadChunkData(long fileOffset, long fileEndOffset, CharBuffer dest);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue