Skip to content

Commit 91ab670

Browse files
committed
rs: add DisasmBuffer api to reuse the cs_insn allocation further
Closes #185 partially.
1 parent 9b78802 commit 91ab670

File tree

1 file changed

+104
-22
lines changed

1 file changed

+104
-22
lines changed

capstone-rs/src/capstone.rs

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -227,18 +227,72 @@ impl Capstone {
227227
code: &'b [u8],
228228
addr: u64,
229229
) -> CsResult<DisasmIter<'a, 'b>> {
230+
let buffer = self.create_buffer()?;
231+
Ok(self.disasm_iter_with_buffer(code, addr, buffer))
232+
}
233+
234+
/// Disassemble and iterate instructions from user-provided buffer `code` using `cs_disasm_iter`.
235+
/// The disassembled address of the buffer is assumed to be `addr`.
236+
/// The provided `buffer` is used to store the disassembled instruction.
237+
///
238+
/// # Examples
239+
///
240+
/// ```
241+
/// # use capstone::prelude::*;
242+
/// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap();
243+
/// let buffer = cs.create_buffer().unwrap();
244+
/// let mut iter = cs.disasm_iter_with_buffer(b"\x90", 0x1000, buffer);
245+
/// assert_eq!(iter.next().unwrap().mnemonic(), Some("nop"));
246+
/// assert!(iter.next().is_none());
247+
/// ```
248+
pub fn disasm_iter_with_buffer<'cs, 'buf>(
249+
&'cs self,
250+
code: &'buf [u8],
251+
addr: u64,
252+
buffer: DisasmBuffer<'cs>,
253+
) -> DisasmIter<'cs, 'buf> {
254+
DisasmIter {
255+
buffer,
256+
code: code.as_ptr(),
257+
size: code.len(),
258+
addr,
259+
_data: PhantomData,
260+
}
261+
}
262+
263+
/// Create a `DisasmBuffer`[DisasmBuffer] instance for future
264+
/// [`disasm_iter_with_buffer`](Capstone::disasm_iter_with_buffer)
265+
/// to reduce memory allocations.
266+
///
267+
/// # Examples
268+
///
269+
/// ```
270+
/// # use capstone::prelude::*;
271+
/// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap();
272+
/// let buffer = cs.create_buffer().unwrap();
273+
/// let mut iter = cs.disasm_iter_with_buffer(b"\x90", 0x1000, buffer);
274+
/// assert_eq!(iter.next().unwrap().mnemonic(), Some("nop"));
275+
/// assert!(iter.next().is_none());
276+
/// // Reuse the undelying buffer
277+
/// let buffer2 = iter.into_buffer();
278+
/// let mut iter2 = cs.disasm_iter_with_buffer(b"\xc3", 0x1000, buffer2);
279+
/// assert_eq!(iter2.next().unwrap().mnemonic(), Some("ret"));
280+
/// assert!(iter2.next().is_none());
281+
/// ```
282+
///
283+
/// # Errors
284+
///
285+
/// If `cs_malloc` failed due to OOM, [`Err(Error::OutOfMemory)`](Error::OutOfMemory) is returned.
286+
///
287+
pub fn create_buffer<'cs>(&'cs self) -> CsResult<DisasmBuffer<'cs>> {
230288
let insn = unsafe { cs_malloc(self.csh()) };
231289
if insn.is_null() {
232290
return Err(Error::OutOfMemory);
233291
}
234-
Ok(DisasmIter {
292+
Ok(DisasmBuffer {
235293
insn,
236294
csh: self.csh,
237-
code: code.as_ptr(),
238-
size: code.len(),
239-
addr,
240-
_data1: PhantomData,
241-
_data2: PhantomData,
295+
_data: PhantomData,
242296
})
243297
}
244298

@@ -628,31 +682,43 @@ impl Drop for Capstone {
628682
}
629683
}
630684

631-
/// Structure to handle iterative disassembly.
685+
/// Structure to hold the memory buffer for a disassembled instruction.
632686
///
633-
/// Create with a [`Capstone`](Capstone) instance: [`Capstone::disasm_iter()`](Capstone::disasm_iter).
687+
/// Create with a [`Capstone`](Capstone) instance: [`Capstone::create_buffer()`](Capstone::create_buffer).
688+
///
689+
/// It deallocates memory via `cs_free` when dropped.
634690
///
635691
/// # Lifetimes
636692
///
637693
/// `'cs` is the lifetime of the [`Capstone`](Capstone) instance.
638-
/// `'buf` is the lifetime of the user provided code buffer in [`Capstone::disasm_iter()`](Capstone::disasm_iter).
639-
///
640-
pub struct DisasmIter<'cs, 'buf> {
641-
insn: *mut cs_insn, // space for current instruction to be processed
642-
csh: *mut c_void, // reference to the the capstone handle required by disasm_iter
643-
code: *const u8, // pointer to the code buffer
644-
size: usize, // size of the code buffer
645-
addr: u64, // current address
646-
_data1: PhantomData<&'cs ()>, // used to make sure DisasmIter lifetime doesn't exceed Capstone's lifetime
647-
_data2: PhantomData<&'buf ()>, // used to make sure code lifetime doesn't exceed user provided array
694+
pub struct DisasmBuffer<'cs> {
695+
insn: *mut cs_insn, // space for disassembled instruction
696+
csh: *mut c_void, // reference to the the capstone handle
697+
_data: PhantomData<&'cs ()>, // used to make sure DisasmBuffer lifetime doesn't exceed Capstone's lifetime
648698
}
649699

650-
impl<'cs, 'buf> Drop for DisasmIter<'cs, 'buf> {
700+
impl<'cs> Drop for DisasmBuffer<'cs> {
651701
fn drop(&mut self) {
652702
unsafe { cs_free(self.insn, 1) };
653703
}
654704
}
655705

706+
/// Structure to handle iterative disassembly.
707+
///
708+
/// Create with a [`Capstone`](Capstone) instance: [`Capstone::disasm_iter()`](Capstone::disasm_iter).
709+
///
710+
/// # Lifetimes
711+
///
712+
/// `'cs` is the lifetime of the [`Capstone`](Capstone) instance.
713+
/// `'buf` is the lifetime of the user provided code buffer in [`Capstone::disasm_iter()`](Capstone::disasm_iter).
714+
pub struct DisasmIter<'cs, 'buf> {
715+
buffer: DisasmBuffer<'cs>, // buffer for current instruction to be processed
716+
code: *const u8, // pointer to the code buffer
717+
size: usize, // size of the code buffer
718+
addr: u64, // current address
719+
_data: PhantomData<&'buf ()>, // used to make sure code lifetime doesn't exceed user provided array
720+
}
721+
656722
impl<'cs, 'buf> DisasmIter<'cs, 'buf> {
657723
/// Get next instruction if available.
658724
///
@@ -685,13 +751,13 @@ impl<'cs, 'buf> DisasmIter<'cs, 'buf> {
685751
pub fn next<'iter>(&'iter mut self) -> Option<Insn<'iter>> {
686752
unsafe {
687753
if cs_disasm_iter(
688-
self.csh as csh,
754+
self.buffer.csh as csh,
689755
&mut self.code,
690756
&mut self.size,
691757
&mut self.addr,
692-
self.insn,
758+
self.buffer.insn,
693759
) {
694-
return Some(Insn::from_raw(self.insn));
760+
return Some(Insn::from_raw(self.buffer.insn));
695761
}
696762
}
697763

@@ -750,4 +816,20 @@ impl<'cs, 'buf> DisasmIter<'cs, 'buf> {
750816
self.size = code.len();
751817
self.addr = addr;
752818
}
819+
820+
/// Extract the underlying buffer for reuse.
821+
/// ```
822+
/// # use capstone::prelude::*;
823+
/// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap();
824+
/// let code = b"\x90";
825+
/// let mut iter = cs.disasm_iter(code, 0x1000).unwrap();
826+
/// assert!(iter.next().is_some());
827+
/// // reuse buffer
828+
/// let buffer = iter.into_buffer();
829+
/// let mut iter2 = cs.disasm_iter_with_buffer(code, 0x2000, buffer);
830+
/// assert!(iter2.next().is_some());
831+
/// ```
832+
pub fn into_buffer(self) -> DisasmBuffer<'cs> {
833+
self.buffer
834+
}
753835
}

0 commit comments

Comments
 (0)