package fr.adrienbrault.idea.symfony2plugin.profiler.reader;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
/**
* Rebuild ReversedLinesFileReader
*
* @link http://stackoverflow.com/questions/6011345/read-a-file-line-by-line-in-reverse-order
*/
public class ReverseFileLineReader {
private static final int BUFFER_SIZE = 8192;
private int limit;
private FileChannel channel;
private final String encoding;
private long filePos;
private MappedByteBuffer buf;
private int bufPos;
private byte lastLineBreak = '\n';
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private final RandomAccessFile raf;
public ReverseFileLineReader(@NotNull File file, @NotNull String encoding, int limit) throws IOException {
this.limit = limit;
raf = new RandomAccessFile(file, "r");
channel = raf.getChannel();
filePos = raf.length();
this.encoding = encoding;
}
public String[] readLines() throws IOException {
List<String> lines = new ArrayList<>();
String line;
while ((line = readLine()) != null && limit-- > 0) {
lines.add(line);
}
channel.close();
raf.close();
return lines.toArray(new String[lines.size()]);
}
private String readLine() throws IOException {
while (true) {
if (bufPos < 0) {
if (filePos == 0) {
if (baos == null) {
return null;
}
String line = bufToString();
baos = null;
return line;
}
long start = Math.max(filePos - BUFFER_SIZE, 0);
long end = filePos;
long len = end - start;
buf = channel.map(FileChannel.MapMode.READ_ONLY, start, len);
bufPos = (int) len;
filePos = start;
}
while (bufPos-- > 0) {
byte c = buf.get(bufPos);
if (c == '\r' || c == '\n') {
if (c != lastLineBreak) {
lastLineBreak = c;
continue;
}
lastLineBreak = c;
return bufToString();
}
baos.write(c);
}
}
}
private String bufToString() throws UnsupportedEncodingException {
if (baos.size() == 0) {
return "";
}
byte[] bytes = baos.toByteArray();
for (int i = 0; i < bytes.length / 2; i++) {
byte t = bytes[i];
bytes[i] = bytes[bytes.length - i - 1];
bytes[bytes.length - i - 1] = t;
}
baos.reset();
return new String(bytes, encoding);
}
}