///|
priv struct WireReader {
data : Bytes
mut pos : Int32
end : Int32
limits : Limits
mut nodes : Int32
}
///|
fn wire_reader(data : Bytes, limits : Limits) -> WireReader raise AmqpError {
limits.validate()
if data.length() > limits.max_value_bytes {
raise Limit("wire input bytes")
}
{ data, pos: 0, end: data.length(), limits, nodes: 0, }
}
///|
fn WireReader::u8(self : WireReader) -> Int32 raise AmqpError {
if self.pos >= self.end {
raise Invalid("truncated byte", self.pos)
}
let value = self.data[self.pos].to_int()
self.pos += 1
value
}
///|
fn WireReader::take(self : WireReader, size : Int32) -> Bytes raise AmqpError {
if size < 0 || size > self.end - self.pos {
raise Invalid("truncated bytes", self.pos)
}
let result = self.data[self.pos:self.pos + size].to_owned()
self.pos += size
result
}
///|
fn WireReader::number(
self : WireReader,
size : Int32,
) -> UInt64 raise AmqpError {
if size < 0 || size > 8 {
raise Invalid("integer width", self.pos)
}
let mut result = 0UL
for i = 0; i < size; i = i + 1 {
result = (result << 8) | self.u8().to_uint64()
}
result
}
///|
fn WireReader::length(
self : WireReader,
width : Int32,
) -> Int32 raise AmqpError {
let value = self.number(width)
if value > 2147483647UL {
raise Limit("wire length")
}
value.to_int()
}
///|
fn WireReader::visit(self : WireReader, depth : Int32) -> Unit raise AmqpError {
if depth > self.limits.max_depth || self.nodes >= self.limits.max_items {
raise Limit("value nesting or node budget")
}
self.nodes += 1
}
///|
priv struct WireWriter {
bytes : Array[Byte]
maximum : Int32
}
///|
fn wire_writer(maximum : Int32) -> WireWriter {
{ bytes: [], maximum, }
}
///|
fn WireWriter::u8(self : WireWriter, value : Int32) -> Unit raise AmqpError {
if self.bytes.length() >= self.maximum {
raise Limit("encoded bytes")
}
self.bytes.push(value.to_byte())
}
///|
fn WireWriter::number(
self : WireWriter,
value : UInt64,
width : Int32,
) -> Unit raise AmqpError {
for i = width - 1; i >= 0; i = i - 1 {
self.u8(((value >> (i * 8)) & 255UL).to_int())
}
}
///|
fn WireWriter::put(self : WireWriter, bytes : Bytes) -> Unit raise AmqpError {
if bytes.length() > self.maximum - self.bytes.length() {
raise Limit("encoded bytes")
}
for byte in bytes {
self.bytes.push(byte)
}
}
///|
fn WireWriter::finish(self : WireWriter) -> Bytes {
Bytes::from_array(self.bytes)
}
///|
fn text_bytes(
value : String,
maximum~ : Int32 = 2147483647,
) -> Bytes raise AmqpError {
let units = value.code_units()
let mut index = 0
let mut size = 0
while index < units.length() {
let unit = units[index].to_int()
if unit >= 0xd800 && unit <= 0xdbff {
if index + 1 >= units.length() ||
units[index + 1].to_int() < 0xdc00 ||
units[index + 1].to_int() > 0xdfff {
raise Invalid("unpaired UTF-16 leading surrogate", index)
}
size = checked_add(size, 4)
index += 2
} else {
if unit >= 0xdc00 && unit <= 0xdfff {
raise Invalid("unpaired UTF-16 trailing surrogate", index)
}
size = checked_add(
size,
if unit < 0x80 {
1
} else if unit < 0x800 {
2
} else {
3
},
)
index += 1
}
if size > maximum {
raise Limit("UTF-8 encoded bytes")
}
}
@utf8.encode(value)
}
///|
fn WireReader::text(
self : WireReader,
size : Int32,
ascii : Bool,
) -> String raise AmqpError {
let start = self.pos
let bytes = self.take(size)
if ascii {
for byte in bytes {
if byte.to_int() > 127 {
raise Invalid("non-ASCII symbol", start)
}
}
}
@utf8.decode(bytes[:]) catch {
_ => raise Invalid("invalid UTF-8", start)
}
}
///|
test "network integers are big endian and truncated input is an error" {
let writer = wire_writer(20)
writer.number(0x12345678UL, 4)
assert_eq(writer.finish(), b"\x12\x34\x56\x78")
let reader = wire_reader(writer.finish(), Limits::default())
assert_eq(reader.number(4), 0x12345678UL)
assert_true(
try {
ignore(reader.u8())
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(checked_add(2147483647, 1))
false
} catch {
_ => true
},
)
}