const LINE_FEED: u8 = 0x0a;
const CARRIAGE_RETURN: u8 = 0x0d;
#[derive(Debug, thiserror::Error)]
#[error("agent sent a record longer than {limit} bytes without a line feed")]
pub struct RecordTooLargeError {
pub limit: usize,
}
pub struct LineFramer {
buffer: Vec<u8>,
max_record_bytes: usize,
}
impl Default for LineFramer {
fn default() -> Self {
Self::new(8 * 1024 * 1024)
}
}
impl LineFramer {
pub fn new(max_record_bytes: usize) -> Self {
Self {
buffer: Vec::new(),
max_record_bytes,
}
}
pub fn push(&mut self, chunk: &[u8]) -> Result<Vec<String>, RecordTooLargeError> {
let mut combined = std::mem::take(&mut self.buffer);
combined.extend_from_slice(chunk);
let mut records = Vec::new();
let mut start = 0_usize;
for (index, byte) in combined.iter().enumerate() {
if *byte != LINE_FEED {
continue;
}
let mut end = index;
if end > start && combined[end - 1] == CARRIAGE_RETURN {
end -= 1;
}
records.push(String::from_utf8_lossy(&combined[start..end]).into_owned());
start = index + 1;
}
self.buffer = combined.split_off(start);
if self.buffer.len() > self.max_record_bytes {
return Err(RecordTooLargeError {
limit: self.max_record_bytes,
});
}
Ok(records)
}
#[allow(
dead_code,
reason = "read by this module's tests, which assert on state the daemon never asks for"
)]
pub fn pending_bytes(&self) -> usize {
self.buffer.len()
}
}
#[cfg(test)]
mod tests;