class CsvParser {
constructor(encoding) {
this._decoder = new TextDecoder(encoding);
this._readOffset = 0;
this._readLength = 0;
this._row = null;
this._state = STATE_FIELD;
this._lineEmpty = true;
this._tokenOffset = 0;
this._tokenLength = tokenDefaultLength;
this._token = new Uint8Array(this._tokenLength);
}
*parse(buffer) {
let token;
this._readOffset = 0;
this._readLength = buffer.length;
while ((token = this._readToken(buffer)) != null) {
if (this._row === null) this._row = [];
this._row.push(this._decoder.decode(token));
if (this._state === STATE_END_OF_LINE) {
if (!this._lineEmpty) yield this._row;
this._row = null;
this._state = STATE_FIELD;
this._lineEmpty = true;
}
}
}
ended() {
return this._row === null;
}
*end() {
if (this._row !== null) {
this._row.push(this._decoder.decode(this._readDelimiter()));
yield this._row;
this._row = null;
}
}
// TODO Optimize special case of non-quoted, not-fragmented field.
_readToken(buffer) {
while (this._readOffset < this._readLength) {
const code = buffer[this._readOffset++];
if (this._state === STATE_FIELD) {
if (code === CODE_QUOTE) { // entered a quoted value
this._state = STATE_QUOTE;
this._lineEmpty = false;
} else if (code === CODE_CARRIAGE_RETURN) { // possible CRLF
this._state = STATE_AFTER_CARRIAGE_RETURN;
} else if (code === CODE_LINE_FEED) { // ended an unquoted field & row
this._state = STATE_END_OF_LINE;
return this._readDelimiter();
} else if (code === delimiterCode) { // ended an unquoted field
this._lineEmpty = false;
return this._readDelimiter();
} else { // read an unquoted character
this._lineEmpty = false;
this._readCharacter(code);
continue;
}
} else if (this._state === STATE_QUOTE) {
if (code === CODE_QUOTE) { // read a quote within a quoted value
this._state = STATE_AFTER_QUOTE_IN_QUOTE;
} else { // read a quoted character
this._readCharacter(code);
continue;
}
} else if (this._state === STATE_AFTER_QUOTE_IN_QUOTE) {
if (code === CODE_QUOTE) { // read a quoted quote
this._state = STATE_QUOTE;
this._readCharacter(CODE_QUOTE);
} else { // exited a quoted value
this._state = STATE_FIELD;
--this._readOffset;
}
} else if (this._state === STATE_AFTER_CARRIAGE_RETURN) {
this._state = STATE_END_OF_LINE;
if (code === CODE_LINE_FEED) {
return this._readDelimiter();
} else {
--this._readOffset;
}
}
}
return null;
}
_readCharacter(code) {
if (this._tokenOffset >= this._tokenLength) {
const token = this._token;
this._token = new Uint8Array(this._tokenLength <<= 1);
this._token.set(token);
}
this._token[this._tokenOffset++] = code;
}
_readDelimiter() {
const token = this._token.subarray(0, this._tokenOffset);
this._token = new Uint8Array(this._tokenLength = tokenDefaultLength);
this._tokenOffset = 0;
return token;
}
}