Skip to content

Instantly share code, notes, and snippets.

@aroemers
Created April 4, 2023 10:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aroemers/9e3d50e358a7fe0f87bce0a27d9e4f07 to your computer and use it in GitHub Desktop.
Save aroemers/9e3d50e358a7fe0f87bce0a27d9e4f07 to your computer and use it in GitHub Desktop.
A buffering Reader wrapper, implementing CharSequence, suitable for regex.Pattern matching
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReaderBuffer implements CharSequence {
private StringBuilder buffer;
private Reader reader;
private int chunkSize;
private boolean hitEnd;
private int cutAt;
public ReaderBuffer(Reader reader, int chunkSize) {
this.buffer = new StringBuilder(chunkSize);
this.reader = reader;
this.chunkSize = chunkSize;
this.hitEnd = false;
this.cutAt = 0;
}
@Override
public int length() {
return buffer.length() + this.cutAt;
}
@Override
public char charAt(int index) {
try {
this.fillBufferToIndex(index);
return buffer.charAt(index - this.cutAt);
} catch (IOException e) {
throw new RuntimeException("IOException while reading from source", e);
}
}
@Override
public CharSequence subSequence(int start, int end) {
StringBuilder subBuffer = new StringBuilder();
for (int i = start; i < end; i++) {
char c = this.charAt(i);
subBuffer.append(c);
}
return subBuffer.toString();
}
@Override
public String toString() {
return buffer.toString();
}
public List<Integer> match(int index, String literal) throws IOException {
int end = index + literal.length();
this.fillBufferToIndex(end - 1);
if (end > this.length()) return null;
if (this.subSequence(index, end).equals(literal)) {
return Arrays.asList(index, end);
} else {
return null;
}
}
public List<Integer> match(int index, Pattern pattern) throws IOException {
this.fillBufferToIndex(index);
if (index >= this.length()) return null;
Matcher matcher = pattern.matcher(this);
while (true) {
matcher.region(index, this.length());
if (matcher.lookingAt()) {
if (matcher.hitEnd() && !this.hitEnd) {
this.readChunk();
} else {
return Arrays.asList(matcher.start(), matcher.end());
}
} else {
if (matcher.hitEnd() && !this.hitEnd) {
this.readChunk();
} else {
return null;
}
}
}
}
public void cut(int index) {
buffer.delete(0, index - cutAt);
this.cutAt = index;
}
private void readChunk() throws IOException {
if (this.hitEnd) return;
char[] charBuffer = new char[chunkSize];
int charsRead = reader.read(charBuffer);
if (charsRead == -1) {
this.hitEnd = true;
reader.close();
return;
}
buffer.append(charBuffer, 0, charsRead);
}
private void fillBufferToIndex(int index) throws IOException {
while (this.length() <= index && !this.hitEnd) {
this.readChunk();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment