aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG17
-rw-r--r--Cargo.toml5
-rw-r--r--README.md2
-rw-r--r--src/address/mod.rs218
-rw-r--r--src/lib.rs8
-rw-r--r--tests/lib.rs12
6 files changed, 252 insertions, 10 deletions
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..741cf88
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,17 @@
+## 0.0.4
+
+add `AddressDiff`. `LengthedInstruction::len` now return `AddressDiff`. the length of an instruction is the difference between two addresses, not itself an address.
+
+## 0.0.3
+
+`ColorSettings` gets a default impl
+
+## 0.0.2
+
+add `AddressDisplay` to provide a usable interface to display `Address` implementors.
+
+at the same time, remove `Address::stringy()`. it was a very bad interface, and will not be missed.
+
+## 0.0.1
+
+history starts here
diff --git a/Cargo.toml b/Cargo.toml
index 442a9fa..f72f8ad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,12 +7,13 @@ keywords = ["disassembly", "disassembler"]
license = "0BSD"
name = "yaxpeax-arch"
repository = "https://git.iximeow.net/yaxpeax-arch/"
-version = "0.0.3"
+version = "0.0.4"
[dependencies]
"num-traits" = { version = "0.2", default-features = false }
"termion" = { version = "1.4.0", optional = true }
"serde" = { version = "1.0", optional = true }
+"serde_derive" = { version = "1.0", optional = true }
[profile.release]
lto = true
@@ -22,7 +23,7 @@ default = ["use-serde", "colors", "address-parse"]
# enables the (optional) use of Serde for bounds on
# Arch and Arch::Address
-use-serde = ["serde"]
+use-serde = ["serde", "serde_derive"]
colors = ["termion"]
diff --git a/README.md b/README.md
index b3c4cb1..260263b 100644
--- a/README.md
+++ b/README.md
@@ -35,4 +35,4 @@ the canonical copy of `yaxpeax-arch` is at [https://git.iximeow.net/yaxpeax-arch
`yaxpeax-arch` is also mirrored on GitHub at [https://www.github.com/iximeow/yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-arch).
### ! user beware !
-these interfaces will almost certainly move and change. the version number is `0.0.1` and i mean it with every fiber of my being.
+these interfaces will almost certainly move and change. the version number is `0.0.4` and i mean it with every fiber of my being.
diff --git a/src/address/mod.rs b/src/address/mod.rs
index edda85d..d3f5113 100644
--- a/src/address/mod.rs
+++ b/src/address/mod.rs
@@ -10,17 +10,227 @@ use num_traits::{Bounded, WrappingAdd, WrappingSub, CheckedAdd, CheckedSub};
#[cfg(feature="use-serde")]
use serde::{Deserialize, Serialize};
+#[cfg(feature="use-serde")]
+pub trait AddressDiffAmount: Copy + Clone + PartialEq + PartialOrd + Eq + Ord + identities::Zero + identities::One + Serialize + for<'de> Deserialize<'de> {}
+#[cfg(not(feature="use-serde"))]
+pub trait AddressDiffAmount: Copy + Clone + PartialEq + PartialOrd + Eq + Ord + identities::Zero + identities::One {}
+
+impl AddressDiffAmount for u64 {}
+impl AddressDiffAmount for u32 {}
+impl AddressDiffAmount for u16 {}
+impl AddressDiffAmount for usize {}
+
+/// a struct describing the differece between some pair of `A: Address`. this is primarily useful
+/// in describing the size of an instruction, or the relative offset of a branch.
+///
+/// for any address type `A`, the following must hold:
+/// ```rust
+/// use yaxpeax_arch::AddressBase;
+/// fn diff_check<A: AddressBase + core::fmt::Debug>(left: A, right: A) {
+/// let diff = left.diff(&right);
+/// if let Some(offset) = diff {
+/// assert_eq!(left.wrapping_offset(offset), right);
+/// }
+/// }
+/// ```
+///
+/// which is to say, `yaxpeax` assumes associativity holds when `diff` yields a `Some`.
+#[cfg(feature="use-serde")]
+#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
+pub struct AddressDiff<T: AddressDiffAmount> {
+ // the AddressDiffAmount trait fools `Deserialize`'s proc macro, so we have to explicitly write
+ // the bound serde should use.
+ #[serde(bound(deserialize = "T: AddressDiffAmount"))]
+ amount: T,
+}
+/// a struct describing the differece between some pair of `A: Address`. this is primarily useful
+/// in describing the size of an instruction, or the relative offset of a branch.
+///
+/// for any address type `A`, the following must hold:
+/// ```rust
+/// use yaxpeax_arch::AddressBase;
+/// fn diff_check<A: AddressBase + core::fmt::Debug>(left: A, right: A) {
+/// let diff = left.diff(&right);
+/// if let Some(offset) = diff {
+/// assert_eq!(left.wrapping_offset(offset), right);
+/// }
+/// }
+/// ```
+///
+/// which is to say, `yaxpeax` assumes associativity holds when `diff` yields a `Some`.
+#[cfg(not(feature="use-serde"))]
+#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
+pub struct AddressDiff<T: AddressDiffAmount> {
+ amount: T,
+}
+
+impl<T: AddressDiffAmount> AddressDiff<T> {
+ pub fn from_const(amount: T) -> Self {
+ AddressDiff { amount }
+ }
+}
+
+impl<T: AddressDiffAmount + fmt::Debug> fmt::Debug for AddressDiff<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "AddressDiff({:?})", self.amount)
+ }
+}
+
+impl<T: AddressDiffAmount> AddressDiff<T> {
+ pub fn one() -> Self {
+ AddressDiff {
+ amount: T::one(),
+ }
+ }
+
+ pub fn zero() -> Self {
+ AddressDiff {
+ amount: T::zero(),
+ }
+ }
+}
+
+impl Sub<AddressDiff<u16>> for u16 {
+ type Output = Self;
+
+ fn sub(self, other: AddressDiff<Self>) -> Self::Output {
+ self - other.amount
+ }
+}
+
+impl Sub<AddressDiff<u32>> for u32 {
+ type Output = Self;
+
+ fn sub(self, other: AddressDiff<Self>) -> Self::Output {
+ self - other.amount
+ }
+}
+
+impl Sub<AddressDiff<u64>> for u64 {
+ type Output = Self;
+
+ fn sub(self, other: AddressDiff<Self>) -> Self::Output {
+ self - other.amount
+ }
+}
+
+impl Sub<AddressDiff<usize>> for usize {
+ type Output = Self;
+
+ fn sub(self, other: AddressDiff<Self>) -> Self::Output {
+ self - other.amount
+ }
+}
+
+impl Add<AddressDiff<u16>> for u16 {
+ type Output = Self;
+
+ fn add(self, other: AddressDiff<Self>) -> Self::Output {
+ self + other.amount
+ }
+}
+
+impl Add<AddressDiff<u32>> for u32 {
+ type Output = Self;
+
+ fn add(self, other: AddressDiff<Self>) -> Self::Output {
+ self + other.amount
+ }
+}
+
+impl Add<AddressDiff<u64>> for u64 {
+ type Output = Self;
+
+ fn add(self, other: AddressDiff<Self>) -> Self::Output {
+ self + other.amount
+ }
+}
+
+impl Add<AddressDiff<usize>> for usize {
+ type Output = Self;
+
+ fn add(self, other: AddressDiff<Self>) -> Self::Output {
+ self + other.amount
+ }
+}
+
+impl SubAssign<AddressDiff<u16>> for u16 {
+ fn sub_assign(&mut self, other: AddressDiff<Self>) {
+ *self -= other.amount;
+ }
+}
+
+impl SubAssign<AddressDiff<u32>> for u32 {
+ fn sub_assign(&mut self, other: AddressDiff<Self>) {
+ *self -= other.amount;
+ }
+}
+
+impl SubAssign<AddressDiff<u64>> for u64 {
+ fn sub_assign(&mut self, other: AddressDiff<Self>) {
+ *self -= other.amount;
+ }
+}
+
+impl SubAssign<AddressDiff<usize>> for usize {
+ fn sub_assign(&mut self, other: AddressDiff<Self>) {
+ *self -= other.amount;
+ }
+}
+
+impl AddAssign<AddressDiff<u16>> for u16 {
+ fn add_assign(&mut self, other: AddressDiff<Self>) {
+ *self += other.amount;
+ }
+}
+
+impl AddAssign<AddressDiff<u32>> for u32 {
+ fn add_assign(&mut self, other: AddressDiff<Self>) {
+ *self += other.amount;
+ }
+}
+
+impl AddAssign<AddressDiff<u64>> for u64 {
+ fn add_assign(&mut self, other: AddressDiff<Self>) {
+ *self += other.amount;
+ }
+}
+
+impl AddAssign<AddressDiff<usize>> for usize {
+ fn add_assign(&mut self, other: AddressDiff<Self>) {
+ *self += other.amount;
+ }
+}
+
pub trait AddressBase where Self:
AddressDisplay +
Copy + Clone + Sized + Hash +
Ord + Eq + PartialEq + Bounded +
- Add<Output=Self> + Sub<Output=Self> +
- AddAssign + SubAssign +
+ Add<AddressDiff<Self>, Output=Self> + Sub<AddressDiff<Self>, Output=Self> +
+ AddAssign<AddressDiff<Self>> + SubAssign<AddressDiff<Self>> +
WrappingAdd + WrappingSub +
CheckedAdd + CheckedSub +
- Hash +
- identities::One + identities::Zero {
+ identities::Zero +
+ AddressDiffAmount +
+ Hash {
fn to_linear(&self) -> usize;
+
+ /// compute the `AddressDiff` beetween `self` and `other`.
+ ///
+ /// may return `None` if the two addresses aren't comparable. for example, if a pair of
+ /// addresses are a data-space address and code-space address, there may be no scalar that can
+ /// describe the difference between them.
+ fn diff(&self, other: &Self) -> Option<AddressDiff<Self>> {
+ Some(AddressDiff { amount: self.wrapping_sub(other) })
+ }
+
+ fn wrapping_offset(&self, other: AddressDiff<Self>) -> Self {
+ self.wrapping_add(&other.amount)
+ }
+
+ fn checked_offset(&self, other: AddressDiff<Self>) -> Option<Self> {
+ self.checked_add(&other.amount)
+ }
}
#[cfg(all(feature="use-serde", feature="address-parse"))]
diff --git a/src/lib.rs b/src/lib.rs
index 17115af..76de58c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,8 @@ use core::hash::Hash;
extern crate num_traits;
#[cfg(feature="use-serde")]
extern crate serde;
+#[cfg(feature="use-serde")]
+#[macro_use] extern crate serde_derive;
#[cfg(feature="colors")]
extern crate termion;
@@ -17,7 +19,7 @@ extern crate termion;
use serde::{Serialize, Deserialize};
mod address;
-pub use address::{Address, AddressBase, AddressDisplay};
+pub use address::{Address, AddressBase, AddressDiff, AddressDiffAmount, AddressDisplay};
pub use address::{AddressDisplayUsize, AddressDisplayU64, AddressDisplayU32, AddressDisplayU16};
#[cfg(feature="address-parse")]
pub use address::AddrParse;
@@ -50,7 +52,7 @@ pub trait Decoder<Inst> where Inst: Sized + Default {
#[cfg(feature="use-serde")]
pub trait Arch {
type Address: Address + Debug + Hash + PartialEq + Eq + Serialize + for<'de> Deserialize<'de>;
- type Instruction: Instruction + LengthedInstruction<Unit=Self::Address> + Debug + Default;
+ type Instruction: Instruction + LengthedInstruction<Unit=AddressDiff<Self::Address>> + Debug + Default;
type DecodeError: DecodeError + Debug + Display;
type Decoder: Decoder<Self::Instruction, Error=Self::DecodeError> + Default;
type Operand;
@@ -59,7 +61,7 @@ pub trait Arch {
#[cfg(not(feature="use-serde"))]
pub trait Arch {
type Address: Address + Debug + Hash + PartialEq + Eq;
- type Instruction: Instruction + LengthedInstruction<Unit=Self::Address> + Debug + Default;
+ type Instruction: Instruction + LengthedInstruction<Unit=AddressDiff<Self::Address>> + Debug + Default;
type DecodeError: DecodeError + Debug + Display;
type Decoder: Decoder<Self::Instruction, Error=Self::DecodeError> + Default;
type Operand;
diff --git a/tests/lib.rs b/tests/lib.rs
new file mode 100644
index 0000000..15477f7
--- /dev/null
+++ b/tests/lib.rs
@@ -0,0 +1,12 @@
+#![no_std]
+
+use yaxpeax_arch::AddressBase;
+
+#[test]
+fn test_u16() {
+ for l in 0..100 {
+ for r in 0..=core::u16::MAX {
+ assert_eq!(r.wrapping_offset(l.diff(&r).expect("u16 addresses always have valid diffs")), l);
+ }
+ }
+}