///|
/// Size-prefix framing only: returned frames still require decode().
pub struct SizeFramer {
priv mut pending : Array[Byte]
priv max_frame : Int32
priv max_feed : Int32
priv max_frames : Int32
}
///|
pub fn SizeFramer::new(
max_frame~ : Int32 = 1048576,
max_feed~ : Int32 = 16777216,
max_frames~ : Int32 = 4096,
) -> SizeFramer raise OscError {
if max_frame < 4 ||
max_frame > 16777216 ||
max_feed < 1 ||
max_feed > 67108864 ||
max_frames < 1 ||
max_frames > 65536 {
raise Limit("invalid stream budgets")
}
{ pending: [], max_frame, max_feed, max_frames, }
}
///|
/// Entire feed is transactional, including completed frames before a bad header.
/// On failure no input is accepted: retry corrected bytes or explicitly reset().
pub fn SizeFramer::feed(
self : SizeFramer,
chunk : Bytes,
) -> Array[Bytes] raise OscError {
if chunk.length() > self.max_feed {
raise Limit("stream feed bytes")
}
let data = self.pending.copy()
for b in chunk {
data.push(b)
}
let out : Array[Bytes] = []
let mut offset = 0
while data.length() - offset >= 4 {
let mut size = 0U
for i in 0..<4 {
size = (size << 8) | data[offset + i].to_uint()
}
if size == 0U || size % 4U != 0U {
raise Invalid("unaligned or empty stream frame", offset)
}
if size > self.max_frame.reinterpret_as_uint() {
raise Limit("stream frame bytes")
}
let n = size.reinterpret_as_int()
if data.length() - offset - 4 < n {
break
}
if out.length() >= self.max_frames {
raise Limit("stream output frames")
}
let frame : Array[Byte] = []
for i in (offset + 4)..<(offset + 4 + n) {
frame.push(data[i])
}
out.push(Bytes::from_array(frame))
offset += 4 + n
}
let tail : Array[Byte] = []
for i in offset.. Unit raise OscError {
if !self.pending.is_empty() {
raise Invalid("truncated size-prefix stream", self.pending.length())
}
}
///|
pub fn SizeFramer::buffered(self : SizeFramer) -> Int32 {
self.pending.length()
}
///|
pub fn SizeFramer::reset(self : SizeFramer) -> Unit {
self.pending = []
}
///|
pub fn size_frame(
bytes : Bytes,
max_frame~ : Int32 = 1048576,
) -> Bytes raise OscError {
if max_frame < 4 || max_frame > 16777216 {
raise Limit("invalid frame budget")
}
if bytes.is_empty() || bytes.length() % 4 != 0 {
raise Invalid("unaligned or empty stream frame", 0)
}
if bytes.length() > max_frame {
raise Limit("stream frame bytes")
}
let n = bytes.length().reinterpret_as_uint()
let out : Array[Byte] = []
for shift in [24, 16, 8, 0] {
out.push((n >> shift).to_byte())
}
for b in bytes {
out.push(b)
}
Bytes::from_array(out)
}