///|
/// Read a null-terminated string from the front of a `BytesView`.
///
/// Returns the decoded string and the remaining bytes after the null.
fn read_cstring(bytes : BytesView) -> (String, BytesView) raise WireError {
  let null_idx = match bytes.find(b"\x00") {
    Some(i) => i
    None => raise WireError::InvalidMessage("read_cstring: unterminated string")
  }
  let s = @utf8.decode(bytes[0:null_idx]) catch {
    _ => bytes[0:null_idx].to_owned().to_unchecked_string()
  }
  (s, bytes[null_idx + 1:])
}

///|
/// Read a sequence of null-terminated strings, terminated by an
/// empty string (i.e. an immediate null byte: `str\\0str\\0\\0`).
/// Consumes the entire input.
fn read_cstrings(bytes : BytesView) -> Array[String] raise WireError {
  let result : Array[String] = []
  for rest = bytes {
    match rest {
      [b'\x00', ..] => break
      _ => {
        let (s, next) = read_cstring(rest[:])
        result.push(s)
        continue next
      }
    }
  }
  result
}