///|
pub suberror XdrError {
Invalid(Int, String)
}
///|
/// All limits are per operation, never trusted from wire lengths.
pub(all) struct Limits {
max_bytes : Int
max_items : Int
max_depth : Int
max_schema_tokens : Int
}
///|
pub fn Limits::default() -> Limits {
{
max_bytes: 1048576,
max_items: 65536,
max_depth: 64,
max_schema_tokens: 65536,
}
}
///|
pub fn Limits::validate(self : Limits) -> Unit raise XdrError {
if self.max_bytes < 4 ||
self.max_bytes > 67108864 ||
self.max_items < 1 ||
self.max_items > 1048576 ||
self.max_depth < 1 ||
self.max_depth > 128 ||
self.max_schema_tokens < 1 ||
self.max_schema_tokens > 1048576 {
raise Invalid(0, "invalid operation limits")
}
}
///|
pub struct Reader {
data : Array[Byte]
mut pos : Int
limits : Limits
}
///|
pub fn Reader::new(
data : Array[Byte],
limits? : Limits = Limits::default(),
) -> Reader raise XdrError {
limits.validate()
if data.length() > limits.max_bytes {
raise Invalid(0, "input byte limit")
}
{ data: data.copy(), pos: 0, limits, }
}
///|
pub fn Reader::position(self : Reader) -> Int {
self.pos
}
///|
pub fn Reader::remaining(self : Reader) -> Int {
self.data.length() - self.pos
}
///|
pub fn Reader::read_raw(
self : Reader,
count : Int,
) -> Array[Byte] raise XdrError {
if count < 0 || count > self.remaining() {
raise Invalid(self.pos, "truncated input")
}
let out = Array::makei(count, i => self.data[self.pos + i])
self.pos += count
out
}
///|
pub fn Reader::read_uint(self : Reader) -> UInt raise XdrError {
let b = self.read_raw(4)
(b[0].to_uint() << 24) |
(b[1].to_uint() << 16) |
(b[2].to_uint() << 8) |
b[3].to_uint()
}
///|
pub fn Reader::read_int(self : Reader) -> Int raise XdrError {
self.read_uint().reinterpret_as_int()
}
///|
pub fn Reader::finish(self : Reader) -> Unit raise XdrError {
if self.remaining() != 0 {
raise Invalid(self.pos, "trailing bytes")
}
}