aboutsummaryrefslogtreecommitdiff
path: root/src/reader.rs
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2021-07-06 11:56:17 -0700
committeriximeow <me@iximeow.net>2021-07-06 15:18:06 -0700
commitca5e4d5d2bdecaebc7da7657cbda2f70a57fdad0 (patch)
tree10d34571ec1416c7b951ae44cff40ce3084a54d2 /src/reader.rs
parent4755f79f91fa4ee27e2626c1202e0ff8fc66f323 (diff)
add ReaderBuilder to generically construct arch-required Readers0.2.2
also revise an `unsafe` that might be unsafe un extremely unlikely circumstances - no one should be passing yaxpeax a `&[u8]` larger than `isize::MAX`, but on 32-bit architectures we can't necessarily guarantee that it won't happen
Diffstat (limited to 'src/reader.rs')
-rw-r--r--src/reader.rs49
1 files changed, 48 insertions, 1 deletions
diff --git a/src/reader.rs b/src/reader.rs
index b263f22..afadea0 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -11,14 +11,42 @@ pub enum ReadError {
IOError(&'static str),
}
+/// a trait defining how `Item`-sized words are read at `Address`-positioned offsets into some
+/// stream of data. for *most* uses, [`yaxpeax_arch::U8Reader`] probably is sufficient. when
+/// reading from data sources that aren't `&[u8]`, `Address` isn't a multiple of `u8`, or `Item`
+/// isn't a multiple of 8 bits, `U8Reader` won't be sufficient.
pub trait Reader<Address, Item> {
fn next(&mut self) -> Result<Item, ReadError>;
+ /// read `buf`-many items from this reader in bulk. if `Reader` cannot read `buf`-many items,
+ /// return `ReadError::ExhaustedInput`.
fn next_n(&mut self, buf: &mut [Item]) -> Result<(), ReadError>;
+ /// mark the current position as where to measure `offset` against.
fn mark(&mut self);
+ /// the difference, in `Address`, between the current `Reader` position and its last `mark`.
+ /// when created, a `Reader`'s initial position is `mark`ed, so creating a `Reader` and
+ /// immediately calling `offset()` must return `Address::zero()`.
fn offset(&mut self) -> Address;
+ /// the difference, in `Address`, between the current `Reader` position and the initial offset
+ /// when constructed.
fn total_offset(&mut self) -> Address;
}
+/// a trait defining how to build a `Reader<Address, Item>` from some data source (`Self`).
+/// definitions of `ReaderBuilder` are provided for `U8Reader` on `Address` and `Word` types that
+/// `yaxpeax_arch` provides - external decoder implementations should also provide `ReaderBuilder`
+/// impls if they use custom `Reader` types.
+pub trait ReaderBuilder<Address: crate::AddressBase, Item> where Self: Sized {
+ type Result: Reader<Address, Item>;
+
+ /// construct a reader from `data` beginning at `addr` from its beginning.
+ fn read_at(data: Self, addr: Address) -> Self::Result;
+ /// construct a reader from `data` beginning at the start of `data`.
+ fn read_from(data: Self) -> Self::Result {
+ Self::read_at(data, Address::zero())
+ }
+}
+
+/// a struct for `Reader` impls that can operate on units of `u8`.
pub struct U8Reader<'a> {
start: *const u8,
data: *const u8,
@@ -29,10 +57,22 @@ pub struct U8Reader<'a> {
impl<'a> U8Reader<'a> {
pub fn new(data: &'a [u8]) -> U8Reader<'a> {
+
+ // WHY: either on <64b systems we panic on `data.len() > isize::MAX`, or we compute end
+ // without `offset` (which would be UB for such huge slices)
+ #[cfg(not(target_pointer_width = "64"))]
+ let end = data.as_ptr().wrapping_add(data.len());
+
+ // SAFETY: the slice was valid, so data + data.len() does not overflow. at the moment,
+ // there aren't 64-bit systems with 63 bits of virtual address space, so it's not possible
+ // to have a slice length larger than 64-bit isize::MAX.
+ #[cfg(target_pointer_width = "64")]
+ let end = unsafe { data.as_ptr().offset(data.len() as isize) };
+
U8Reader {
start: data.as_ptr(),
data: data.as_ptr(),
- end: unsafe { data.as_ptr().offset(data.len() as isize) },
+ end,
mark: data.as_ptr(),
_lifetime: core::marker::PhantomData,
}
@@ -135,6 +175,13 @@ macro_rules! u8reader_reader_impl {
}
}
+ impl<'data> ReaderBuilder<$addr_size, $word> for &'data [u8] {
+ type Result = U8Reader<'data>;
+
+ fn read_at(data: Self, addr: $addr_size) -> Self::Result {
+ U8Reader::new(&data[(addr as usize)..])
+ }
+ }
}
}