///|
fn tokens(text : StringView) -> Array[String] {
  let result : Array[String] = []
  let word = StringBuilder()
  for ch in text {
    if ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' {
      if !word.is_empty() {
        result.push(word.to_string())
        word.reset()
      }
    } else {
      word.write_char(ch)
    }
  }
  if !word.is_empty() {
    result.push(word.to_string())
  }
  result
}

///|
fn bounded(text : String) -> Unit raise ParseError {
  guard text.length() <= 65536 else { raise Invalid("input exceeds 64 KiB") }
}

///|
/// Strict unsigned decimal in signed Int64 range; rejects signs and overflow.
pub fn parse_counter(text : String) -> Int64 raise ParseError {
  bounded(text)
  let words = tokens(text[:])
  guard words.length() == 1 else { raise Invalid("expected one counter") }
  let word = words[0]
  let mut value = 0L
  for c in word {
    guard c >= '0' && c <= '9' else { raise Invalid("non-decimal counter") }
    let digit = (c.to_int() - 48).to_int64()
    guard value <= (9223372036854775807L - digit) / 10L else {
      raise Invalid("counter overflow")
    }
    value = value * 10L + digit
  }
  value
}

///|
pub fn parse_limit(text : String) -> Limit raise ParseError {
  bounded(text)
  let words = tokens(text[:])
  guard words.length() == 1 else { raise Invalid("expected limit or max") }
  if words[0] == "max" {
    Unlimited
  } else {
    Finite(parse_counter(words[0]))
  }
}

///|
pub fn parse_cpu_max(text : String) -> CpuMax raise ParseError {
  bounded(text)
  let words = tokens(text[:])
  guard words.length() == 2 else { raise Invalid("cpu.max needs two fields") }
  let quota = parse_limit(words[0])
  let period_us = parse_counter(words[1])
  guard period_us > 0 && quota != Finite(0L) else {
    raise Invalid("zero CPU period or quota")
  }
  { quota, period_us, }
}

///|
fn percent(text : String) -> Double raise ParseError {
  guard text.length() > 0 else { raise Invalid("empty percent") }
  let mut dots = 0
  let mut digits = 0
  let mut value = 0.0
  let mut scale = 0.1
  for c in text {
    if c == '.' {
      dots += 1
      guard dots <= 1 else { raise Invalid("invalid percent") }
    } else if c >= '0' && c <= '9' {
      digits += 1
      let digit = (c.to_int() - 48).to_double()
      if dots == 0 {
        value = value * 10.0 + digit
      } else {
        value += digit * scale
        scale *= 0.1
      }
      guard value <= 100.0 else { raise Invalid("percent outside 0..100") }
    } else {
      raise Invalid("invalid percent")
    }
  }
  guard digits > 0 else { raise Invalid("invalid percent") }
  value
}

///|
fn required(
  fields : Map[String, String],
  key : String,
) -> String raise ParseError {
  match fields.get(key) {
    Some(value) => value
    None => raise Invalid("missing \{key}")
  }
}

///|
fn pressure_line(words : Array[String]) -> PressureLine raise ParseError {
  let fields : Map[String, String] = Map([])
  for i in 1.. Pressure raise ParseError {
  bounded(text)
  let mut some : PressureLine? = None
  let mut full : PressureLine? = None
  for line in text.split("\n") {
    let words = tokens(line)
    if words.is_empty() {
      continue
    }
    match words[0] {
      "some" => {
        guard some is None else { raise Invalid("duplicate some line") }
        some = Some(pressure_line(words))
      }
      "full" => {
        guard full is None else { raise Invalid("duplicate full line") }
        full = Some(pressure_line(words))
      }
      _ => raise Invalid("unknown PSI line")
    }
  }
  guard some is Some(some) else { raise Invalid("missing some line") }
  { some, full, }
}

///|
fn counters(text : String) -> Map[String, Int64] raise ParseError {
  bounded(text)
  let fields : Map[String, Int64] = Map([])
  for line in text.split("\n") {
    let words = tokens(line)
    if words.is_empty() {
      continue
    }
    guard words.length() == 2 else { raise Invalid("expected key value line") }
    let key = words[0]
    guard !fields.contains(key) else {
      raise Invalid("duplicate counter \{key}")
    }
    fields[key] = parse_counter(words[1])
  }
  fields
}

///|
fn counter(fields : Map[String, Int64], key : String) -> Int64 raise ParseError {
  match fields.get(key) {
    Some(value) => value
    None => raise Invalid("missing \{key}")
  }
}

///|
pub fn parse_cpu_stat(text : String) -> CpuStat raise ParseError {
  let fields = counters(text)
  let present = fields.contains("nr_periods") ||
    fields.contains("nr_throttled") ||
    fields.contains("throttled_usec")
  let throttle = if present {
    let periods = counter(fields, "nr_periods")
    let throttled_periods = counter(fields, "nr_throttled")
    guard throttled_periods <= periods else {
      raise Invalid("throttled periods exceed periods")
    }
    Some({
      periods,
      throttled_periods,
      throttled_us: counter(fields, "throttled_usec"),
    })
  } else {
    None
  }
  { usage_us: counter(fields, "usage_usec"), throttle, }
}

///|
pub fn parse_memory_events(text : String) -> MemoryEvents raise ParseError {
  let fields = counters(text)
  {
    high: counter(fields, "high"),
    max: counter(fields, "max"),
    oom: counter(fields, "oom"),
    oom_kill: counter(fields, "oom_kill"),
  }
}