summaryrefslogtreecommitdiffstats
path: root/third_party/rust/minidump-writer/src/mem_writer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/minidump-writer/src/mem_writer.rs')
-rw-r--r--third_party/rust/minidump-writer/src/mem_writer.rs272
1 files changed, 272 insertions, 0 deletions
diff --git a/third_party/rust/minidump-writer/src/mem_writer.rs b/third_party/rust/minidump-writer/src/mem_writer.rs
new file mode 100644
index 0000000000..a703d2b11e
--- /dev/null
+++ b/third_party/rust/minidump-writer/src/mem_writer.rs
@@ -0,0 +1,272 @@
+use crate::minidump_format::{MDLocationDescriptor, MDRVA};
+use scroll::ctx::{SizeWith, TryIntoCtx};
+
+#[derive(Debug, thiserror::Error)]
+pub enum MemoryWriterError {
+ #[error("IO error when writing to DumpBuf")]
+ IOError(#[from] std::io::Error),
+ #[error("Failed integer conversion")]
+ TryFromIntError(#[from] std::num::TryFromIntError),
+ #[error("Failed to write to buffer")]
+ Scroll(#[from] scroll::Error),
+}
+
+type WriteResult<T> = std::result::Result<T, MemoryWriterError>;
+
+macro_rules! size {
+ ($t:ty) => {
+ <$t>::size_with(&scroll::Endian::Little)
+ };
+}
+
+pub struct Buffer {
+ inner: Vec<u8>,
+}
+
+impl Buffer {
+ pub fn with_capacity(cap: usize) -> Self {
+ Self {
+ inner: Vec::with_capacity(cap),
+ }
+ }
+
+ #[inline]
+ pub fn position(&self) -> u64 {
+ self.inner.len() as u64
+ }
+
+ #[inline]
+ #[must_use]
+ fn reserve(&mut self, len: usize) -> usize {
+ let mark = self.inner.len();
+ self.inner.resize(self.inner.len() + len, 0);
+ mark
+ }
+
+ #[inline]
+ fn write<N, E>(&mut self, val: N) -> Result<usize, E>
+ where
+ N: TryIntoCtx<scroll::Endian, Error = E> + SizeWith<scroll::Endian>,
+ E: From<scroll::Error>,
+ {
+ self.write_at(self.inner.len(), val)
+ }
+
+ fn write_at<N, E>(&mut self, offset: usize, val: N) -> Result<usize, E>
+ where
+ N: TryIntoCtx<scroll::Endian, Error = E> + SizeWith<scroll::Endian>,
+ E: From<scroll::Error>,
+ {
+ let to_write = size!(N);
+ let remainder = self.inner.len() - offset;
+ if remainder < to_write {
+ self.inner
+ .resize(self.inner.len() + to_write - remainder, 0);
+ }
+
+ let dst = &mut self.inner[offset..offset + to_write];
+ val.try_into_ctx(dst, scroll::Endian::Little)
+ }
+
+ #[inline]
+ pub fn write_all(&mut self, buffer: &[u8]) {
+ self.inner.extend_from_slice(buffer);
+ }
+}
+
+impl From<Buffer> for Vec<u8> {
+ fn from(b: Buffer) -> Self {
+ b.inner
+ }
+}
+
+impl std::ops::Deref for Buffer {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+#[derive(Debug)]
+pub struct MemoryWriter<T> {
+ pub position: MDRVA,
+ pub size: usize,
+ phantom: std::marker::PhantomData<T>,
+}
+
+impl<T> MemoryWriter<T>
+where
+ T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian>,
+{
+ /// Create a slot for a type T in the buffer, we can fill right now with real values.
+ pub fn alloc_with_val(buffer: &mut Buffer, val: T) -> WriteResult<Self> {
+ // Mark the position as we may overwrite later
+ let position = buffer.position();
+ let size = buffer.write(val)?;
+
+ Ok(Self {
+ position: position as u32,
+ size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Create a slot for a type T in the buffer, we can fill later with real values.
+ pub fn alloc(buffer: &mut Buffer) -> WriteResult<Self> {
+ let size = size!(T);
+ let position = buffer.reserve(size) as u32;
+
+ Ok(Self {
+ position,
+ size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Write actual values in the buffer-slot we got during `alloc()`
+ #[inline]
+ pub fn set_value(&mut self, buffer: &mut Buffer, val: T) -> WriteResult<()> {
+ Ok(buffer.write_at(self.position as usize, val).map(|_sz| ())?)
+ }
+
+ #[inline]
+ pub fn location(&self) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: size!(T) as u32,
+ rva: self.position,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct MemoryArrayWriter<T> {
+ pub position: MDRVA,
+ array_size: usize,
+ phantom: std::marker::PhantomData<T>,
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+impl MemoryArrayWriter<u8> {
+ #[inline]
+ pub fn write_bytes(buffer: &mut Buffer, slice: &[u8]) -> Self {
+ let position = buffer.position();
+ buffer.write_all(slice);
+
+ Self {
+ position: position as u32,
+ array_size: slice.len(),
+ phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+impl<T> MemoryArrayWriter<T>
+where
+ T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian> + Copy,
+{
+ pub fn alloc_from_array(buffer: &mut Buffer, array: &[T]) -> WriteResult<Self> {
+ let array_size = array.len();
+ let position = buffer.reserve(array_size * size!(T));
+
+ for (idx, val) in array.iter().enumerate() {
+ buffer.write_at(position + idx * size!(T), *val)?;
+ }
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+}
+
+impl<T> MemoryArrayWriter<T>
+where
+ T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian>,
+{
+ /// Create a slot for a type T in the buffer, we can fill in the values in one go.
+ pub fn alloc_from_iter<I>(
+ buffer: &mut Buffer,
+ iter: impl IntoIterator<Item = T, IntoIter = I>,
+ ) -> WriteResult<Self>
+ where
+ I: std::iter::ExactSizeIterator<Item = T>,
+ {
+ let iter = iter.into_iter();
+ let array_size = iter.len();
+ let size = size!(T);
+ let position = buffer.reserve(array_size * size);
+
+ for (idx, val) in iter.enumerate() {
+ buffer.write_at(position + idx * size, val)?;
+ }
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Create a slot for a type T in the buffer, we can fill later with real values.
+ /// This function fills it with `Default::default()`, which is less performant than
+ /// using uninitialized memory, but safe.
+ pub fn alloc_array(buffer: &mut Buffer, array_size: usize) -> WriteResult<Self> {
+ let position = buffer.reserve(array_size * size!(T));
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Write actual values in the buffer-slot we got during `alloc()`
+ #[inline]
+ pub fn set_value_at(&mut self, buffer: &mut Buffer, val: T, index: usize) -> WriteResult<()> {
+ Ok(buffer
+ .write_at(self.position as usize + size!(T) * index, val)
+ .map(|_sz| ())?)
+ }
+
+ #[inline]
+ pub fn location(&self) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: (self.array_size * size!(T)) as u32,
+ rva: self.position,
+ }
+ }
+
+ #[inline]
+ pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: size!(T) as u32,
+ rva: self.position + (size!(T) * idx) as u32,
+ }
+ }
+}
+
+pub fn write_string_to_location(
+ buffer: &mut Buffer,
+ text: &str,
+) -> WriteResult<MDLocationDescriptor> {
+ let letters: Vec<u16> = text.encode_utf16().collect();
+
+ // First write size of the string (x letters in u16, times the size of u16)
+ let text_header = MemoryWriter::<u32>::alloc_with_val(
+ buffer,
+ (letters.len() * std::mem::size_of::<u16>()).try_into()?,
+ )?;
+
+ // Then write utf-16 letters after that
+ let mut text_section = MemoryArrayWriter::<u16>::alloc_array(buffer, letters.len())?;
+ for (index, letter) in letters.iter().enumerate() {
+ text_section.set_value_at(buffer, *letter, index)?;
+ }
+
+ let mut location = text_header.location();
+ location.data_size += text_section.location().data_size;
+
+ Ok(location)
+}