From 931ad9b84e75faf734ddff19b692481013260f6e Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 23 Jun 2024 10:44:24 -0700 Subject: InstructionTextBuffer is only present with alloc (new crate flag) --- Cargo.toml | 8 ++- src/long_mode/display.rs | 177 +++++++++++++++++++++++++---------------------- src/long_mode/mod.rs | 4 +- test/long_mode/mod.rs | 3 + 4 files changed, 106 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34f8af7..ecc56be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,13 @@ lto = true default = ["std", "colors", "use-serde", "fmt"] # opt-in for some apis that are really much nicer with String -std = ["yaxpeax-arch/std"] +std = ["alloc", "yaxpeax-arch/std"] + +# opt-in for some formatting-related helpers that require performing allocation +# +# this should only be useful with `fmt` currently, but in the future there could +# be other `fmt`-independent code gated on `alloc`. +alloc = ["yaxpeax-arch/alloc"] # feature for formatting instructions and their components fmt = [] diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index e8000ed..9d9b7bb 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -4273,97 +4273,106 @@ impl<'a, F: DisplaySink> crate::long_mode::OperandVisitor for RelativeBranchPrin } } -/// helper to format `amd64` instructions with highest throughput and least configuration. this is -/// functionally a buffer for one x86 instruction's text. -/// -/// ### when to use this over `fmt::Display`? -/// -/// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may -/// support formatting options that may be difficult to configure for a `Display` impl. -/// additionally, `InstructionTextBuffer` may be able to specialize more effectively where -/// `fmt::Display`, writing to a generic `fmt::Write`, may not. -/// -/// if your use case for `yaxpeax-x86` involves being bounded on the speed of disassembling and -/// formatting instructions, [`InstructionTextBuffer::format_inst`] has been measured as up to 11% -/// faster than an equivalent `write!(buf, "{}", inst)`. -/// -/// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-x86` -/// requires allocations never occurring, it is not an appropriate tool. -/// -/// ### example -/// -/// ``` -/// use yaxpeax_x86::long_mode::InstDecoder; -/// use yaxpeax_x86::long_mode::InstructionTextBuffer; -/// use yaxpeax_x86::long_mode::DisplayStyle; -/// -/// let bytes = &[0x33, 0xc0]; -/// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); -/// let mut text_buf = InstructionTextBuffer::new(); -/// assert_eq!( -/// text_buf.format_inst(&inst.display_with(DisplayStyle::Intel)).expect("can format"), -/// "xor eax, eax" -/// ); -/// -/// // or, getting the formatted instruction with `text_str`: -/// assert_eq!( -/// text_buf.text_str(), -/// "xor eax, eax" -/// ); -/// ``` -pub struct InstructionTextBuffer { - content: alloc::string::String, -} - -impl InstructionTextBuffer { - /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default - /// settings format instructions identically to their corresponding `fmt::Display`. - pub fn new() -> Self { - let mut buf = alloc::string::String::new(); - // TODO: move 512 out to a MAX_INSTRUCTION_LEN const and appropriate justification (and - // fuzzing and ..) - buf.reserve(512); - Self { - content: buf, - } - } +#[cfg(feature="alloc")] +mod buffer_sink { + use core::fmt; + use super::super::{DisplayStyle, InstructionDisplayer}; + use super::{contextualize_c, contextualize_intel}; - /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. + /// helper to format `amd64` instructions with highest throughput and least configuration. this is + /// functionally a buffer for one x86 instruction's text. + /// + /// ### when to use this over `fmt::Display`? + /// + /// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may + /// support formatting options that may be difficult to configure for a `Display` impl. + /// additionally, `InstructionTextBuffer` may be able to specialize more effectively where + /// `fmt::Display`, writing to a generic `fmt::Write`, may not. + /// + /// if your use case for `yaxpeax-x86` involves being bounded on the speed of disassembling and + /// formatting instructions, [`InstructionTextBuffer::format_inst`] has been measured as up to 11% + /// faster than an equivalent `write!(buf, "{}", inst)`. /// - /// this clears and reuses an internal buffer; if an instruction had been previously formatted - /// through this buffer, it will be overwritten. - pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { - // Safety: this sink is used to format exactly one instruction and then dropped. it can - // never escape `format_inst`. - let mut handle = unsafe { self.write_handle() }; + /// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-x86` + /// requires allocations never occurring, it is not an appropriate tool. + /// + /// ### example + /// + /// ``` + /// use yaxpeax_x86::long_mode::InstDecoder; + /// use yaxpeax_x86::long_mode::InstructionTextBuffer; + /// use yaxpeax_x86::long_mode::DisplayStyle; + /// + /// let bytes = &[0x33, 0xc0]; + /// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); + /// let mut text_buf = InstructionTextBuffer::new(); + /// assert_eq!( + /// text_buf.format_inst(&inst.display_with(DisplayStyle::Intel)).expect("can format"), + /// "xor eax, eax" + /// ); + /// + /// // or, getting the formatted instruction with `text_str`: + /// assert_eq!( + /// text_buf.text_str(), + /// "xor eax, eax" + /// ); + /// ``` + pub struct InstructionTextBuffer { + content: alloc::string::String, + } - match display.style { - DisplayStyle::Intel => { - contextualize_intel(&display.instr, &mut handle)?; - } - DisplayStyle::C => { - contextualize_c(&display.instr, &mut handle)?; + impl InstructionTextBuffer { + /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default + /// settings format instructions identically to their corresponding `fmt::Display`. + pub fn new() -> Self { + let mut buf = alloc::string::String::new(); + // TODO: move 512 out to a MAX_INSTRUCTION_LEN const and appropriate justification (and + // fuzzing and ..) + buf.reserve(512); + Self { + content: buf, } } - Ok(self.text_str()) - } + /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. + /// + /// this clears and reuses an internal buffer; if an instruction had been previously formatted + /// through this buffer, it will be overwritten. + pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { + // Safety: this sink is used to format exactly one instruction and then dropped. it can + // never escape `format_inst`. + let mut handle = unsafe { self.write_handle() }; - /// return a borrow of the internal buffer. if an instruction has been formatted, the - /// returned `&str` contains that instruction's buffered text. - pub fn text_str(&self) -> &str { - self.content.as_str() - } + match display.style { + DisplayStyle::Intel => { + contextualize_intel(&display.instr, &mut handle)?; + } + DisplayStyle::C => { + contextualize_c(&display.instr, &mut handle)?; + } + } - /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction - /// into. - /// - /// SAFETY: callers must print at most one instruction into this handle. - unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { - self.content.clear(); - // Safety: `content` was just cleared, so writing begins at the start of the buffer. - // `content`is large enough to hold a fully-formatted instruction (see - // `InstructionTextBuffer::new`). - yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) + Ok(self.text_str()) + } + + /// return a borrow of the internal buffer. if an instruction has been formatted, the + /// returned `&str` contains that instruction's buffered text. + pub fn text_str(&self) -> &str { + self.content.as_str() + } + + /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction + /// into. + /// + /// SAFETY: callers must print at most one instruction into this handle. + unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { + self.content.clear(); + // Safety: `content` was just cleared, so writing begins at the start of the buffer. + // `content`is large enough to hold a fully-formatted instruction (see + // `InstructionTextBuffer::new`). + yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) + } } } +#[cfg(feature="alloc")] +pub use buffer_sink::InstructionTextBuffer; diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index d66f59a..2ccbc22 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -7,7 +7,9 @@ pub mod uarch; pub use crate::MemoryAccessSize; #[cfg(feature = "fmt")] -pub use self::display::{DisplayStyle, InstructionDisplayer, InstructionTextBuffer}; +pub use self::display::{DisplayStyle, InstructionDisplayer}; +#[cfg(all(feature = "fmt", feature = "alloc"))] +pub use self::display::InstructionTextBuffer; use core::cmp::PartialEq; use crate::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index 6c666e1..d8cb0ef 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -92,9 +92,12 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str text, ); + #[cfg(feature="alloc")] let mut formatter = yaxpeax_x86::long_mode::InstructionTextBuffer::new(); + #[cfg(feature="alloc")] let text3 = formatter.format_inst(&instr.display_with(yaxpeax_x86::long_mode::DisplayStyle::Intel)).expect("printing succeeds"); + #[cfg(feature="alloc")] assert!( text3 == text, "display error through InstructionTextBuffer for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", -- cgit v1.1