net.amygdalum.util.io.ReaderBufferCharProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compilerutils Show documentation
Show all versions of compilerutils Show documentation
Utility classes needed for search and compiler applications
The newest version!
package net.amygdalum.util.io;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
public class ReaderBufferCharProvider implements CharProvider {
private static final long NO_MARK = Long.MIN_VALUE;
private Reader input;
private int chunk;
private int capacity;
private CharBuffer buffer;
private long absolutePos;
private long mark;
private int pos;
private int state;
/**
* provides an reader as char sequence. Uses a {@link CharBuffer} for encoding/decoding.
*
* A reader could possibly contain more chars than main memory can hold, so chars that where already read
* are discarded after some time. You can specify the size of one buffer of the reader and the number of buffers
* that should remain in main memory.
*
* Since many algorithms search backwards from the current position it may be essential that already proceeded
* chunks are preserved a while. In general a search word should fit into the current buffer + the reverse buffers.
* Yet the least problems occur if the chunk size is chosen large enough to hold any search word.
*
* @param input the source reader
* @param start the position to start from
* @param buffer the size of the buffer
* @param reverseBuffers the number of buffers for looking back
*/
public ReaderBufferCharProvider(Reader input, long start, int buffer, int reverseBuffers) {
this.input = input;
this.chunk = buffer;
this.capacity = buffer * (reverseBuffers + 1);
this.buffer = CharBuffer.allocate(capacity);
this.mark = NO_MARK;
init(start);
}
public final void init(long start) {
long shift = ((start / chunk) + 1) * chunk;
long skip = 0;
while (shift > capacity) {
shift -= chunk;
skip += chunk;
}
if (skip > 0) {
skip(skip);
}
read();
pos = (int) (start - skip);
absolutePos = start;
}
public void read() {
buffer.position(0);
buffer.limit(capacity);
try {
state = input.read(buffer);
} catch (IOException e) {
throw new IORuntimeException(e);
}
buffer.flip();
}
public void shift(int shift) {
buffer.position(shift);
buffer.compact();
try {
state = input.read(buffer);
} catch (IOException e) {
throw new IORuntimeException(e);
}
buffer.flip();
pos -= shift;
}
public void skip(long skip) {
try {
input.skip(skip);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
@Override
public char next() {
if (pos >= buffer.limit()) {
shift(chunk);
}
char c = buffer.charAt(pos);
pos++;
absolutePos++;
return c;
}
@Override
public char lookahead() {
return buffer.charAt(pos);
}
@Override
public char lookahead(int i) {
return buffer.charAt(pos + i);
}
@Override
public char prev() {
pos--;
absolutePos--;
if (pos < 0) {
throw new OutOfBufferException();
}
return buffer.charAt(pos);
}
@Override
public char lookbehind() {
return buffer.charAt(pos - 1);
}
@Override
public char lookbehind(int i) {
return buffer.charAt(pos - 1 - i);
}
@Override
public long current() {
return absolutePos;
}
@Override
public void move(long i) {
long rel = i - absolutePos;
if (pos + rel < 0) {
int rev = pos + 1;
pos = -1;
absolutePos -= rev;
throw new OutOfBufferException();
}
int shift = 0;
while (pos - shift + rel > buffer.limit()) {
shift += chunk;
}
int skip = 0;
while (shift > capacity) {
shift -= chunk;
skip += chunk;
}
if (skip > 0) {
skip(skip);
}
shift(shift);
pos += rel;
absolutePos += rel;
}
@Override
public void forward(int i) {
move(absolutePos + i);
}
@Override
public void finish() {
try {
pos = buffer.limit();
state = -1;
input.close();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
@Override
public boolean finished() {
if (buffer.limit() - pos > 0) {
return false;
} else if (buffer.limit() - pos == 0 && state == -1) {
return true;
}
shift(chunk);
return buffer.limit() - pos <= 0 && state == -1;
}
@Override
public boolean finished(int i) {
if (buffer.limit() - pos > i) {
return false;
} else if (buffer.limit() - pos <= i && state == -1) {
return true;
}
if ((pos % chunk) + i >= capacity) {
throw new OutOfBufferException();
}
int shift = 0;
while (pos - shift + i >= capacity) {
shift += chunk;
}
if (shift > 0 || buffer.limit() < capacity) {
shift(shift);
}
return buffer.limit() - pos <= i && state == -1;
}
@Override
public char at(long i) {
long rel = i - absolutePos;
if (pos + rel < 0) {
int rev = pos + 1;
pos = -1;
absolutePos -= rev;
throw new OutOfBufferException();
}
if ((pos % chunk) + rel >= capacity) {
throw new OutOfBufferException();
}
int shift = 0;
while (pos - shift + rel >= capacity) {
shift += chunk;
}
if (shift > 0 || buffer.limit() < capacity) {
shift(shift);
}
return buffer.charAt((int) (pos + rel));
}
@Override
public char[] between(long start, long end) {
if (end - start + (start % chunk) > capacity) {
throw new OutOfBufferException();
}
//capacity is sufficient
long startRel = start - absolutePos;
long endRel = end - absolutePos;
if (pos + startRel < 0) {
int rev = pos + 1;
pos = -1;
absolutePos -= rev;
throw new OutOfBufferException();
}
//startRel is reachable
int shift = 0;
while (pos - shift + endRel >= buffer.limit()) {
shift += chunk;
}
if (shift > pos) {
throw new OutOfBufferException();
}
if (shift > 0 || buffer.limit() < capacity) {
shift(shift);
}
//endRel is reachable
int startSeq = (int) (pos + startRel);
int endSeq = (int) (pos + endRel);
char[] cs = new char[endSeq - startSeq];
buffer.subSequence(startSeq, endSeq).get(cs);
return cs;
}
@Override
public String slice(long start, long end) {
return new String(between(start, end));
}
@Override
public void mark() {
mark = absolutePos;
}
@Override
public boolean changed() {
boolean changed = mark != NO_MARK && mark != absolutePos;
mark = NO_MARK;
return changed;
}
@Override
public String toString() {
return "..." + buffer.subSequence(0, pos) + '|' + buffer.subSequence(pos, buffer.limit());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy