///|
pub fn Writer::write_fixed(
  self : Writer,
  bytes : Array[Byte],
) -> Unit raise XdrError {
  let padding = (4 - bytes.length() % 4) % 4
  if bytes.length() > self.limits.max_bytes - self.length() - padding {
    raise Invalid(self.length(), "opaque byte limit")
  }
  self.write_raw(bytes)
  self.write_raw(Array::make(padding, 0))
}

///|
pub fn Reader::read_fixed(
  self : Reader,
  length : Int,
) -> Array[Byte] raise XdrError {
  if length < 0 || length > self.limits.max_bytes {
    raise Invalid(self.pos, "opaque length limit")
  }
  let padding = (4 - length % 4) % 4
  if length > self.remaining() - padding {
    raise Invalid(self.pos, "truncated opaque")
  }
  let out = self.read_raw(length)
  for b in self.read_raw(padding) {
    if b != 0 {
      raise Invalid(self.pos - padding, "nonzero XDR padding")
    }
  }
  out
}

///|
pub fn Writer::write_opaque(
  self : Writer,
  bytes : Array[Byte],
  bound? : Int = 1048576,
) -> Unit raise XdrError {
  if bound < 0 || bytes.length() > bound {
    raise Invalid(self.length(), "opaque bound exceeded")
  }
  self.write_uint(bytes.length().reinterpret_as_uint())
  self.write_fixed(bytes)
}

///|
pub fn Reader::read_length(self : Reader, bound : Int) -> Int raise XdrError {
  let length = self.read_uint()
  if bound < 0 || length > bound.reinterpret_as_uint() {
    raise Invalid(self.pos - 4, "length bound exceeded")
  }
  length.reinterpret_as_int()
}

///|
pub fn Reader::read_opaque(
  self : Reader,
  bound? : Int = 1048576,
) -> Array[Byte] raise XdrError {
  let n = self.read_length(bound)
  self.read_fixed(n)
}

///|
/// RFC 4506 strings are ASCII; arbitrary bytes use opaque instead.
pub fn Writer::write_string(
  self : Writer,
  text : String,
  bound? : Int = 1048576,
) -> Unit raise XdrError {
  let bytes : Array[Byte] = []
  for c in text.iter() {
    if c.to_int() > 127 {
      raise Invalid(self.length(), "XDR string must be ASCII")
    }
    bytes.push(c.to_int().to_byte())
  }
  for b in bytes {
    if b > 127 {
      raise Invalid(self.length(), "XDR string must be ASCII")
    }
  }
  self.write_opaque(bytes, bound~)
}

///|
pub fn Reader::read_string(
  self : Reader,
  bound? : Int = 1048576,
) -> String raise XdrError {
  let bytes = self.read_opaque(bound~)
  let out = StringBuilder()
  for b in bytes {
    if b > 127 {
      raise Invalid(self.pos - bytes.length(), "XDR string must be ASCII")
    }
    out.write_char(b.to_char())
  }
  out.to_string()
}