From e5924b591453fde5e502b9fbcef83c058afe6fdf Mon Sep 17 00:00:00 2001 From: Jacob Middag Date: Thu, 2 Apr 2026 15:58:08 +0200 Subject: [PATCH] Fix writing half code points to Android logger --- src/platform_log_writer.rs | 65 +++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/platform_log_writer.rs b/src/platform_log_writer.rs index 2a7b53a..7c558df 100644 --- a/src/platform_log_writer.rs +++ b/src/platform_log_writer.rs @@ -140,16 +140,18 @@ impl PlatformLogWriter<'_> { impl fmt::Write for PlatformLogWriter<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { - let mut incoming_bytes = s.as_bytes(); + let mut incoming = s; - while !incoming_bytes.is_empty() { + loop { let len = self.len; - // write everything possible to buffer and mark last \n - let new_len = len + incoming_bytes.len(); - let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN] + // write everything possible to buffer considering char boundary and mark last \n + let (to_buffer, remaining) = + floor_at_char_boundary_and_split(incoming, LOGGING_MSG_MAX_LEN - len); + let new_len = len + to_buffer.len(); + let last_newline = self.buffer[len..new_len] .iter_mut() - .zip(incoming_bytes) + .zip(to_buffer.as_bytes()) .enumerate() .fold(None, |acc, (i, (output, input))| { if *input == b'\0' { @@ -166,27 +168,34 @@ impl fmt::Write for PlatformLogWriter<'_> { if let Some(newline) = last_newline { self.last_newline_index = len + newline; } + // update len + self.len = new_len; - // calculate how many bytes were written - let written_len = if new_len <= LOGGING_MSG_MAX_LEN { - // if the len was not exceeded - self.len = new_len; - new_len - len // written len - } else { - // if new length was exceeded - self.len = LOGGING_MSG_MAX_LEN; - self.temporal_flush(); - - LOGGING_MSG_MAX_LEN - len // written len - }; + // if remaining is empty stop + if remaining.is_empty() { + break; + } - incoming_bytes = &incoming_bytes[written_len..]; + // flush to make room and write remaining + self.temporal_flush(); + incoming = remaining; } Ok(()) } } +/// Combination of `[str::floor_char_boundary]` and `[str::split_at]` +#[inline] +fn floor_at_char_boundary_and_split(text: &str, mut index: usize) -> (&str, &str) { + loop { + if let Some(split) = text.split_at_checked(index) { + return split; + } + index -= 1; + } +} + #[cfg(test)] pub mod tests { use crate::arrays::slice_assume_init_ref; @@ -317,6 +326,24 @@ pub mod tests { ); } + #[test] + fn writer_splits_on_utf8_correctly() { + let mut test_string = Vec::from([b' '; 3999]); + test_string.extend_from_slice("\u{00DF}".as_bytes()); + let test_string = + String::from_utf8(test_string).expect("Unable to convert bytes to String"); + + let mut writer = get_tag_writer(); + writer + .write_str(&test_string) + .expect("Unable to write to PlatformLogWriter"); + + assert_eq!( + unsafe { slice_assume_init_ref(&writer.buffer[..2]) }, + "ß".as_bytes() + ); + } + fn get_tag_writer() -> PlatformLogWriter<'static> { PlatformLogWriter::new( None,