///|
/// RFC 1055 SLIP byte-stuffing, constrained to nonempty aligned OSC frames.
pub struct SlipFramer {
priv mut pending : Array[Byte]
priv mut escaped : Bool
priv max_frame : Int32
priv max_feed : Int32
priv max_frames : Int32
}
///|
pub fn SlipFramer::new(
max_frame~ : Int32 = 1048576,
max_feed~ : Int32 = 16777216,
max_frames~ : Int32 = 4096,
) -> SlipFramer raise OscError {
if max_frame < 4 ||
max_frame > 16777216 ||
max_feed < 1 ||
max_feed > 67108864 ||
max_frames < 1 ||
max_frames > 65536 {
raise Limit("invalid SLIP budgets")
}
{ pending: [], escaped: false, max_frame, max_feed, max_frames, }
}
///|
/// Empty ENDs are synchronization markers. Invalid escapes roll back the whole feed.
pub fn SlipFramer::feed(
self : SlipFramer,
chunk : Bytes,
) -> Array[Bytes] raise OscError {
if chunk.length() > self.max_feed {
raise Limit("SLIP feed bytes")
}
let data = self.pending.copy()
let mut escaped = self.escaped
let out : Array[Bytes] = []
for i in 0.. data.push(0xc0)
0xdd => data.push(0xdb)
_ => raise Invalid("invalid SLIP escape", i)
}
escaped = false
} else if b == 0xdb {
escaped = true
} else if b == 0xc0 {
if !data.is_empty() {
if data.length() % 4 != 0 {
raise Invalid("unaligned SLIP OSC frame", i)
}
if out.length() >= self.max_frames {
raise Limit("SLIP output frames")
}
out.push(Bytes::from_array(data))
data.clear()
}
} else {
data.push(b)
}
if data.length() > self.max_frame {
raise Limit("SLIP frame bytes")
}
}
self.pending = data
self.escaped = escaped
out
}
///|
pub fn SlipFramer::finish(self : SlipFramer) -> Unit raise OscError {
if self.escaped || !self.pending.is_empty() {
raise Invalid("truncated SLIP stream", self.pending.length())
}
}
///|
pub fn SlipFramer::buffered(self : SlipFramer) -> Int32 {
self.pending.length()
}
///|
pub fn SlipFramer::reset(self : SlipFramer) -> Unit {
self.pending = []
self.escaped = false
}
///|
/// Adds leading and trailing END for synchronization; worst case 2*n+2 bytes.
pub fn slip_frame(
bytes : Bytes,
max_frame~ : Int32 = 1048576,
) -> Bytes raise OscError {
if max_frame < 4 || max_frame > 16777216 || bytes.length() > max_frame {
raise Limit("SLIP frame bytes")
}
if bytes.is_empty() || bytes.length() % 4 != 0 {
raise Invalid("unaligned or empty SLIP OSC frame", 0)
}
let out : Array[Byte] = [0xc0]
for b in bytes {
match b {
0xc0 => {
out.push(0xdb)
out.push(0xdc)
}
0xdb => {
out.push(0xdb)
out.push(0xdd)
}
_ => out.push(b)
}
}
out.push(0xc0)
Bytes::from_array(out)
}