use std::fmt::{self, Display, Write as _}; use std::slice; use std::str; #[allow(non_camel_case_types)] type c_char = i8; pub struct CStr { ptr: *const u8, } impl CStr { pub unsafe fn from_ptr(ptr: *const c_char) -> Self { CStr { ptr: ptr.cast() } } } impl Display for CStr { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let len = unsafe { strlen(self.ptr) }; let mut bytes = unsafe { slice::from_raw_parts(self.ptr, len) }; loop { match str::from_utf8(bytes) { Ok(valid) => return formatter.write_str(valid), Err(utf8_error) => { let valid_up_to = utf8_error.valid_up_to(); let valid = unsafe { str::from_utf8_unchecked(&bytes[..valid_up_to]) }; formatter.write_str(valid)?; formatter.write_char(char::REPLACEMENT_CHARACTER)?; if let Some(error_len) = utf8_error.error_len() { bytes = &bytes[valid_up_to + error_len..]; } else { return Ok(()); } } } } } } unsafe fn strlen(s: *const u8) -> usize { let mut end = s; while *end != 0 { end = end.add(1); } end.offset_from(s) as usize }