상태 머신 모호성 해결
LAST_LINE 상태 모호성 발생.
LAST_LINE의 의미의 경우 코드를 읽는 입장에서 마지막 CSV Record로 인식할 수 있음.
하지만 실질적으로 buf 내에 row ≥ 1이 될 수 있음.
그렇기에 LAST_LINE의 상태 보다는 (LAST_DATA, LAST_LINE | LAST_RECORD)로 세분화가 좋다고 판단됨.
LAST_DATA의 경우 현 LAST_LINE의 기능을 그대로 사용.
LAST_LINE | LAST_RECORD 의 경우 delimiter 유무에 따라 LAST_DATA 에서 전이됨.
실질적으로 마지막에 false 반환은 EOF로 끝날 예정.
package parserTest3;
import lombok.AccessLevel;
import lombok.Getter;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
@Getter
public class InternalBuffer2 {
@Getter(AccessLevel.NONE)
private final Reader reader;
private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final int READ_SIZE = DEFAULT_BUFFER_SIZE;
private BufferState state = BufferState.INITIAL;
private char[] buf = new char[DEFAULT_BUFFER_SIZE];
private int pos, begin, limit;
private enum BufferState {
INITIAL,
PROGRESSING,
EXTEND,
RELOCATE,
LAST_DATA,
LAST_LINE,
EOF;
}
public InternalBuffer2(Reader reader) {
this.reader = reader;
}
/**
* TODO isLastLine 추가
* RecordParser내에서 마지막 줄 여부 판단을 위해서 필요함
* <p>
* 상태 전이 [] 반복, () 선택, {} 종료
* INITIAL -> [PROGRESSING <-> (EXTEND | RELOCATE)] -> LAST_DATA -> LAST_LINE -> EOF {종료}
* -> EOF {종료}
*/
boolean fill() {
switch (state) {
case INITIAL:
int cnt = read(buf, pos, READ_SIZE);
if (cnt == -1) {
return false;
}
state = BufferState.PROGRESSING;
limit += cnt;
return true;
case PROGRESSING:
/**
* if -> begin >= limit 일 경우 즉, 커서가 끝까지 갔는데 아무 변화 없을때
* state = (pos == 0) ? EXTEND : RELOCATE;
* continue;
* retrun true;
*/
if (begin >= limit) {
state = pos == 0 ? BufferState.EXTEND : BufferState.RELOCATE;
}
return true;
case EXTEND:
/**
* 확장 및 read
* extend() 확장
* cnt = read(begin) -> READ_SIZE 만큼 읽기
* if -> cnt == -1 LAST_LINE
* else -> PROGRESSING and limit += cnt
* continue;
*/
extendAndLoad();
return true;
case RELOCATE:
/**
* 재배치 및 read
* relocate()
* begin = limit - pos;
* pos = 0;
* cnt = read(begin) // READ_SIZE 만큼 데이터 로드
* if -> cnt == -1 LAST_LINE
* else -> PROGRESSING
*/
relocateAndLoad();
return true;
case LAST_DATA:
if (pos < limit) {
state = BufferState.LAST_LINE;
return true;
}
state = BufferState.EOF;
return true;
case LAST_LINE:
state = BufferState.EOF;
return true;
case EOF:
default:
return false;
}
}
boolean isLastLine() {
return state == BufferState.LAST_LINE;
}
static <T, R> Optional<Function<T, R>> ifLastRowOrElse(InternalBuffer2 buffer,
Function<T, R> action,
Function<T, R> elseAction) {
if (buffer.getPos() == buffer.getLimit()) {
return Optional.empty();
}
boolean isLastLine = buffer.getState() == BufferState.LAST_LINE && buffer.getPos() < buffer.getLimit();
return Optional.of(isLastLine ? action : elseAction);
}
char getBeginAndIncrement() {
return buf[begin++];
}
void setPos(int newPos) {
this.pos = newPos;
}
boolean notBeginAtLimit() {
return begin < limit;
}
private int read(char[] buf, int off, int len) {
try {
return reader.read(buf, off, len);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void extendAndLoad() {
// TODO max size 예외처리
char[] newBuf = new char[buf.length * 2];
System.arraycopy(buf, 0, newBuf, 0, buf.length);
buf = newBuf;
int cnt = read(buf, begin, READ_SIZE);
stateAndLimitUpdate(cnt, limit + cnt);
}
private void relocateAndLoad() {
final int copyLength = begin - pos;
System.arraycopy(buf, pos, buf, 0, copyLength);
int cnt = read(buf, copyLength, READ_SIZE - copyLength);
pos = 0;
begin = copyLength;
stateAndLimitUpdate(cnt, copyLength + cnt);
}
private void stateAndLimitUpdate(int readSize, int newLimit) {
state = readSize == -1 ? BufferState.LAST_DATA : BufferState.PROGRESSING;
limit = readSize == -1 ? begin : newLimit;
}
}
JavaScript
복사