use std::fmt;
use crate::text::log_escape::encode_log_safe_character;
use super::markers;
pub(in crate::http) struct BoundedLogWriter {
output: String,
marker_boundary: usize,
max_bytes: usize,
truncated: bool,
output_truncated: bool,
}
impl BoundedLogWriter {
pub(in crate::http) fn new(
max_bytes: usize,
source_truncated: bool,
) -> Self {
Self {
output: String::new(),
marker_boundary: 0,
max_bytes,
truncated: source_truncated,
output_truncated: false,
}
}
pub(in crate::http) fn write_str(&mut self, value: &str) -> fmt::Result {
if self.is_full() {
return Ok(());
}
for character in value.chars() {
let mut encoded = [0_u8; 12];
let piece = encode_log_safe_character(character, &mut encoded)?;
if !self.append_piece(piece) {
break;
}
}
Ok(())
}
#[inline(always)]
pub(in crate::http) fn is_full(&self) -> bool {
self.output_truncated
|| (self.truncated && self.output.len() >= self.payload_limit())
}
#[inline(always)]
pub(in crate::http) fn remaining_bytes(&self) -> usize {
self.payload_limit().saturating_sub(self.output.len())
}
pub(in crate::http) fn finish(mut self) -> (String, bool) {
if self.truncated {
self.truncate_to_payload_limit();
self.output.push_str(markers::TRUNCATED);
}
(self.output, self.truncated)
}
fn append_piece(&mut self, piece: &str) -> bool {
let limit = self.payload_limit();
if self.output.len().saturating_add(piece.len()) <= limit {
self.output.push_str(piece);
let marker_payload_limit =
self.max_bytes - markers::TRUNCATED.len();
if self.output.len() <= marker_payload_limit {
self.marker_boundary = self.output.len();
}
return true;
}
self.truncated = true;
self.output_truncated = true;
self.truncate_to_payload_limit();
false
}
#[inline(always)]
fn payload_limit(&self) -> usize {
if self.truncated {
self.max_bytes - markers::TRUNCATED.len()
} else {
self.max_bytes
}
}
fn truncate_to_payload_limit(&mut self) {
self.output.truncate(self.marker_boundary);
}
}