///|
/// Count ASCII output code units for a value without constructing the encoded
/// string. The temporary UTF-8 byte array is at most the input's byte length.
fn measure_value_encoding(
  input : String,
  allow_reserved : Bool,
  limit : Int,
) -> Result[Int, ExpandError] {
  if limit < 0 {
    return Err(OutputTooLong)
  }
  let bytes = @utf8.encode(input)
  let mut i = 0
  let mut used = 0
  while i < bytes.length() {
    let code = bytes[i].to_int()
    if is_unreserved(code) || (allow_reserved && is_reserved(code)) {
      if used >= limit {
        return Err(OutputTooLong)
      }
      used += 1
    } else if allow_reserved &&
      code == 37 &&
      i + 2 < bytes.length() &&
      is_hex(bytes[i + 1].to_int()) &&
      is_hex(bytes[i + 2].to_int()) {
      if used > limit - 3 {
        return Err(OutputTooLong)
      }
      used += 3
      i += 2
    } else {
      if used > limit - 3 {
        return Err(OutputTooLong)
      }
      used += 3
    }
    i += 1
  }
  Ok(used)
}

///|
fn measure_literal_encoding(
  input : String,
  limit : Int,
) -> Result[Int, ExpandError] {
  if limit < 0 {
    return Err(OutputTooLong)
  }
  let bytes = @utf8.encode(input)
  let mut used = 0
  for byte in bytes {
    let code = byte.to_int()
    let extra = if is_unreserved(code) || is_reserved(code) || code == 37 {
      1
    } else {
      3
    }
    if used > limit - extra {
      return Err(OutputTooLong)
    }
    used += extra
  }
  Ok(used)
}