
net.oneandone.mork.scanner.Buffer Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.oneandone.mork.scanner;
import java.io.IOException;
import java.io.Reader;
/**
* Connection between Scanner and Reader. This class is kind of a StringBuilder optimized for
* deleting at the beginning and appending at the end. In addition, appending is done buffered
* from a Reader. The buffer behaves as if the reader is filled in completely at the beginning,
* but this is done in steps.
*
* Buffer has a start and an end, that are used to select a possible token.
* Moving the start forward removes characters from the beginning;
* moving the end forward reads characters from the underlying stream (if they have not been
* read before -- the end is not necessarily that last character read).
*
* Buffer storage is devided into pages.
*/
public class Buffer {
/**
* True if src.read() has returned -1. Does not necessarily meant that this buffer
* is EOF as well.
*/
private boolean eof;
/**
* Offset in the first page where the current selection starts.
*/
private int start;
/** start position */
private Position position;
private final Pages pages;
/** reduncant, but more efficient */
private final int pageSize;
/** Index of the end page. endPage == pages.get(endPageIdx), pageIdx < pages.size() */
private int endPageIdx;
/** Current page. pages.get(pageNo) */
private char[] endPage;
/** Offset in the end page. */
private int end;
/** Last valid index in end page that has beend filled from the underlying stream. endPage[end] is valid if end < endFilled */
private int endFilled;
public Buffer() {
this(8192);
}
public Buffer(int pageSize) {
if (pageSize == 0) {
throw new IllegalArgumentException();
}
this.pageSize = pageSize;
this.pages = new Pages(pageSize);
}
public void open(Position position, Reader src) {
this.position = position;
this.eof = false;
this.start = 0;
this.pages.open(src);
this.endPageIdx = 0;
this.endPage = pages.get(0);
this.end = 0;
this.endFilled = pages.getFilled(0);
}
//--
public void assertInvariant() {
if (start > pages.getSize()) {
throw new IllegalStateException();
}
if (start > pageSize) {
throw new IllegalStateException();
}
if (start > getEndOfs()) {
throw new IllegalStateException();
}
if (end > endFilled) {
throw new IllegalStateException();
}
if (endPage != pages.get(endPageIdx)) {
throw new IllegalStateException();
}
}
public int getEndOfs() {
return endPageIdx * pageSize + end;
}
/**
* Sets the current end ofs by to the specified value
* @param ofs < getEndOfs()
*/
public void resetEndOfs(int ofs) {
if (endPageIdx == 0) {
// because a precondition is that ofs is left of the
// end
end = ofs;
} else {
endPageIdx = ofs / pageSize;
end = ofs % pageSize;
if (end == 0 && pages.getLastNo() == endPageIdx) {
// this happens if getOfs() was called after the last character of a page was read
end += pageSize;
endPageIdx--;
}
endPage = pages.get(endPageIdx);
endFilled = pages.getFilled(endPageIdx);
}
}
/**
* Returns true if the end of file has been seen and the buffer is at it's end.
* Does *not* try to read in order to check for an end-of-file.
*/
public boolean wasEof() {
return eof && (getEndOfs() == pages.getSize());
}
/**
* Advances the end and returns the character at this positio.
* @return character or Scanner.EOF
*/
public int read() throws IOException {
if (end == endFilled) {
switch (pages.read(endPageIdx, endFilled)) {
case -1:
eof = true;
return Scanner.EOF;
case 0:
endFilled = pages.getFilled(endPageIdx);
break;
case 1:
endPageIdx++;
end = 0;
endPage = pages.get(endPageIdx);
endFilled = pages.getFilled(endPageIdx);
break;
default:
throw new RuntimeException();
}
}
return endPage[end++];
}
//--
/**
* Move start forward to the current position.
*/
public void eat() {
int i;
if (endPageIdx == 0) {
position.update(endPage, start, end);
start = end;
} else {
position.update(pages.get(0), start, pageSize);
for (i = 1; i < endPageIdx; i++) {
position.update(pages.get(i), 0, pageSize);
}
pages.shrink(endPageIdx);
endPageIdx = 0;
endPage = pages.get(0);
start = end;
position.update(endPage, 0, start);
}
}
/**
* Returns the string between start and the current position.
*/
public String createString() {
int i;
int count;
if (endPageIdx == 0) {
// speedup the most frequent situation
return new String(endPage, start, end - start);
} else {
char[] buffer;
buffer = new char[endPageIdx * pageSize + end - start];
count = pageSize - start;
System.arraycopy(pages.get(0), start, buffer, 0, count);
for (i = 1; i < endPageIdx; i++) {
System.arraycopy(pages.get(i), 0, buffer, count, pageSize);
count += pageSize;
}
System.arraycopy(pages.get(endPageIdx), 0, buffer, count, end);
return new String(buffer);
}
}
public void getPosition(Position result) {
result.set(position);
}
//--
@Override
public String toString() {
StringBuilder buf;
buf = new StringBuilder();
buf.append("buffer {");
buf.append("\n srcEof = ").append(eof);
buf.append("\n start = ").append(start);
buf.append("\n endPageIdx = ").append(endPageIdx);
buf.append("\n end = ").append(end);
buf.append("\n endUsed = ").append(endFilled);
buf.append(pages.toString());
buf.append("\n}");
return buf.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy