/*! Provides convenience routines for escaping raw bytes. Since this crate tends to deal with `&[u8]` everywhere and the default `Debug` implementation just shows decimal integers, it makes debugging those representations quite difficult. This module provides types that show `&[u8]` as if it were a string, with invalid UTF-8 escaped into its byte-by-byte hex representation. */ use crate::util::utf8; /// Provides a convenient `Debug` implementation for a `u8`. /// /// The `Debug` impl treats the byte as an ASCII, and emits a human readable /// representation of it. If the byte isn't ASCII, then it's emitted as a hex /// escape sequence. #[derive(Clone, Copy)] pub struct DebugByte(pub u8); impl core::fmt::Debug for DebugByte { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { // Special case ASCII space. It's too hard to read otherwise, so // put quotes around it. I sometimes wonder whether just '\x20' would // be better... if self.0 == b' ' { return write!(f, "' '"); } // 10 bytes is enough to cover any output from ascii::escape_default. let mut bytes = [0u8; 10]; let mut len = 0; for (i, mut b) in core::ascii::escape_default(self.0).enumerate() { // capitalize \xab to \xAB if i >= 2 && b'a' <= b && b <= b'f' { b -= 32; } bytes[len] = b; len += 1; } write!(f, "{}", core::str::from_utf8(&bytes[..len]).unwrap()) } } /// Provides a convenient `Debug` implementation for `&[u8]`. /// /// This generally works best when the bytes are presumed to be mostly UTF-8, /// but will work for anything. For any bytes that aren't UTF-8, they are /// emitted as hex escape sequences. pub struct DebugHaystack<'a>(pub &'a [u8]); impl<'a> core::fmt::Debug for DebugHaystack<'a> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "\"")?; // This is a sad re-implementation of a similar impl found in bstr. let mut bytes = self.0; while let Some(result) = utf8::decode(bytes) { let ch = match result { Ok(ch) => ch, Err(byte) => { write!(f, r"\x{:02x}", byte)?; bytes = &bytes[1..]; continue; } }; bytes = &bytes[ch.len_utf8()..]; match ch { '\0' => write!(f, "\\0")?, // ASCII control characters except \0, \n, \r, \t '\x01'..='\x08' | '\x0b' | '\x0c' | '\x0e'..='\x19' | '\x7f' => { write!(f, "\\x{:02x}", u32::from(ch))?; } '\n' | '\r' | '\t' | _ => { write!(f, "{}", ch.escape_debug())?; } } } write!(f, "\"")?; Ok(()) } }