///|
/// Feed arbitrary byte chunks, including splits inside a UTF-8 sequence or BOM.
/// Only the unfinished physical line is buffered, within configured limits.
pub struct ByteParser {
  priv parser : Parser
  priv pending : @buffer.Buffer
  priv mut after_cr : Bool
  priv mut closed : Bool
  priv mut failed : Bool
  priv mut total_bytes : Int64
}

///|
pub fn ByteParser::new(limits? : Limits) -> ByteParser raise GedError {
  {
    parser: Parser::new(limits?),
    pending: @buffer.Buffer(),
    after_cr: false,
    closed: false,
    failed: false,
    total_bytes: 0L,
  }
}

///|
fn ByteParser::line(
  self : ByteParser,
  eol_units : Int32,
) -> Unit raise GedError {
  let line = @utf8.decode(self.pending.to_bytes(), ignore_bom=false) catch {
    _ => raise Syntax(self.parser.line + 1, "invalid UTF-8")
  }
  if line.length().to_int64() + eol_units.to_int64() >
    (self.parser.limits.max_total_units - self.parser.total).to_int64() {
    raise Limit("decoded input size exceeded")
  }
  self.parser.total = self.parser.total + line.length() + eol_units
  self.pending.reset()
  if line.length() > self.parser.limits.max_line_units {
    raise Limit("line size exceeded")
  }
  self.parser.take_line(line)
}

///|
pub fn ByteParser::feed(
  self : ByteParser,
  chunk : BytesView,
) -> Unit raise GedError {
  if self.closed || self.failed {
    raise InvalidState("byte parser is closed or failed")
  }
  errdefer {
    self.failed = true
  }
  self.total_bytes = self.total_bytes + chunk.length().to_int64()
  if self.total_bytes > self.parser.limits.max_total_units.to_int64() * 4L {
    raise Limit("byte input size exceeded")
  }
  for b in chunk {
    if self.after_cr {
      self.after_cr = false
      if b == b'\n' {
        if self.parser.total >= self.parser.limits.max_total_units {
          raise Limit("decoded input size exceeded")
        }
        self.parser.total = self.parser.total + 1
        continue
      }
    }
    if b == b'\n' || b == b'\r' {
      self.line(1)
      self.after_cr = b == b'\r'
    } else {
      if self.pending.length().to_int64() >=
        self.parser.limits.max_line_units.to_int64() * 4L {
        raise Limit("unfinished UTF-8 line size exceeded")
      }
      self.pending.write_byte(b)
    }
  }
}

///|
pub fn ByteParser::finish(self : ByteParser) -> Document raise GedError {
  if self.closed || self.failed {
    raise InvalidState("byte parser is closed or failed")
  }
  self.closed = true
  if self.pending.length() > 0 {
    self.line(0)
  }
  self.parser.finish()
}