///|
/// Incremental decoder for chunked VCD input.
pub struct StreamDecoder {
mut pending : String
mut record_buffer : String
mut records : Int
mut lines : Int
mut finished : Bool
} derive(Debug, Eq)
///|
/// Create an empty chunk decoder.
pub fn new_stream_decoder() -> StreamDecoder {
{ pending: "", record_buffer: "", records: 0, lines: 0, finished: false }
}
///|
/// Number of complete records emitted by this decoder.
pub fn StreamDecoder::record_count(self : StreamDecoder) -> Int {
self.records
}
///|
/// Number of lines received by this decoder.
pub fn StreamDecoder::line_count(self : StreamDecoder) -> Int {
self.lines
}
///|
/// Feed a chunk and emit all complete VCD documents ending in `//`.
pub fn StreamDecoder::feed(
self : StreamDecoder,
chunk : String,
) -> Result[Array[Trace], ParseError] {
if self.finished {
return Err(InvalidDirective("stream already finished"))
}
let combined = self.pending + chunk
let ready : Array[Trace] = []
let lines = combined.split("\n").map(fn(line) { line.to_owned() }).to_array()
if combined.has_suffix("\n") {
self.pending = ""
} else {
self.pending = match lines.pop() {
Some(tail) => tail
None => ""
}
}
for line in lines {
self.lines += 1
self.record_buffer = self.record_buffer + line + "\n"
if line.trim().to_owned() == "//" {
let record_text = self.record_buffer
self.record_buffer = ""
if record_text.trim().to_owned() != "//" {
match parse(record_text) {
Ok(trace) => ready.push(trace)
Err(error) => return Err(error)
}
self.records += 1
}
}
}
Ok(ready)
}
///|
/// Finish a stream and parse its final record when present.
pub fn StreamDecoder::finish(
self : StreamDecoder,
) -> Result[Array[Trace], ParseError] {
if self.finished {
return Ok([])
}
self.finished = true
if self.pending.trim().to_owned() != "" {
self.record_buffer = self.record_buffer + self.pending
self.pending = ""
}
if self.record_buffer.trim().to_owned() == "" {
Ok([])
} else {
self.records += 1
match parse(self.record_buffer) {
Ok(trace) => Ok([trace])
Err(error) => Err(error)
}
}
}
///|
/// Parse a complete stream in chunks of a fixed size.
pub fn parse_chunked(
input : String,
chunk_size : Int,
) -> Result[Array[Trace], ParseError] {
let decoder = new_stream_decoder()
let safe = Int::max(1, chunk_size)
let result : Array[Trace] = []
let mut start = 0
while start < input.length() {
let end = Int::min(input.length(), start + safe)
let chunk = input.unsafe_substring(start~, end~)
match decoder.feed(chunk) {
Ok(traces) => result.append(traces)
Err(error) => return Err(error)
}
start = end
}
match decoder.finish() {
Ok(traces) => {
result.append(traces)
Ok(result)
}
Err(error) => Err(error)
}
}