use crate::cglue::ReprCString;
use crate::dataview::Pod;
use crate::error::{PartialError, PartialResult};
use crate::mem::MemoryView;
use crate::types::{imem, umem, Address, ByteSwap, PrimitiveAddress};
use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem::size_of;
use std::{cmp, fmt, hash, ops};
pub type Pointer32<T> = Pointer<u32, T>;
pub type Pointer64<T> = Pointer<u64, T>;
const _: [(); std::mem::size_of::<Pointer32<()>>()] = [(); std::mem::size_of::<u32>()];
const _: [(); std::mem::size_of::<Pointer64<()>>()] = [(); std::mem::size_of::<u64>()];
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
pub struct Pointer<U: Sized, T: ?Sized = ()> {
pub inner: U,
phantom_data: PhantomData<fn() -> T>,
}
unsafe impl<U: Pod, T: ?Sized + 'static> Pod for Pointer<U, T> {}
impl<U: PrimitiveAddress, T: ?Sized> Pointer<U, T> {
const PHANTOM_DATA: PhantomData<fn() -> T> = PhantomData;
#[inline]
pub fn null() -> Self {
Pointer {
inner: U::null(),
phantom_data: PhantomData,
}
}
#[inline]
pub fn is_null(self) -> bool {
self.inner.is_null()
}
#[inline]
pub fn non_null(self) -> Option<Pointer<U, T>> {
if self.is_null() {
None
} else {
Some(self)
}
}
#[inline]
pub fn to_umem(self) -> umem {
self.inner.to_umem()
}
#[inline]
pub fn address(&self) -> Address {
Address::from(self.inner)
}
}
impl<U: PrimitiveAddress, T: Sized> Pointer<U, T> {
pub fn offset(self, count: imem) -> Self {
let pointee_size = U::from_umem(size_of::<T>() as umem);
assert!(U::null() < pointee_size && pointee_size <= PrimitiveAddress::max());
if count >= 0 {
self.inner
.wrapping_add(U::from_umem(pointee_size.to_umem() * count as umem))
.into()
} else {
self.inner
.wrapping_sub(U::from_umem(pointee_size.to_umem() * (-count) as umem))
.into()
}
}
pub fn offset_from(self, origin: Self) -> imem {
let pointee_size: imem = size_of::<T>().try_into().unwrap();
let offset = self.inner.to_imem().wrapping_sub(origin.inner.to_imem());
offset / pointee_size as imem
}
#[allow(clippy::should_implement_trait)]
pub fn add(self, count: umem) -> Self {
self.offset(count as imem)
}
#[allow(clippy::should_implement_trait)]
pub fn sub(self, count: umem) -> Self {
self.offset((count as imem).wrapping_neg())
}
}
impl<U: PrimitiveAddress, T: Pod + ?Sized> Pointer<U, T> {
pub fn read_into<M: MemoryView>(self, mem: &mut M, out: &mut T) -> PartialResult<()> {
mem.read_ptr_into(self, out)
}
}
impl<U: PrimitiveAddress, T: Pod + Sized> Pointer<U, T> {
pub fn read<M: MemoryView>(self, mem: &mut M) -> PartialResult<T> {
mem.read_ptr(self)
}
pub fn write<M: MemoryView>(self, mem: &mut M, data: &T) -> PartialResult<()> {
mem.write_ptr(self, data)
}
}
impl<U: PrimitiveAddress> Pointer<U, ReprCString> {
pub fn read_string<M: MemoryView>(self, mem: &mut M) -> PartialResult<ReprCString> {
match mem.read_char_string(self.inner.to_umem().into()) {
Ok(s) => Ok(s.into()),
Err(PartialError::Error(e)) => Err(PartialError::Error(e)),
Err(PartialError::PartialVirtualRead(s)) => {
Err(PartialError::PartialVirtualRead(s.into()))
}
Err(PartialError::PartialVirtualWrite(s)) => {
Err(PartialError::PartialVirtualWrite(s.into()))
}
}
}
}
impl<U: PrimitiveAddress, T> Pointer<U, [T]> {
pub fn decay(self) -> Pointer<U, T> {
Pointer {
inner: self.inner,
phantom_data: Pointer::<U, T>::PHANTOM_DATA,
}
}
pub fn at(self, i: umem) -> Pointer<U, T> {
let inner = self
.inner
.wrapping_add(U::from_umem(size_of::<T>() as umem * i));
Pointer {
inner,
phantom_data: Pointer::<U, T>::PHANTOM_DATA,
}
}
}
impl<U: PrimitiveAddress, T: ?Sized> Copy for Pointer<U, T> {}
impl<U: PrimitiveAddress, T: ?Sized> Clone for Pointer<U, T> {
#[inline(always)]
fn clone(&self) -> Pointer<U, T> {
*self
}
}
impl<U: PrimitiveAddress, T: ?Sized> Default for Pointer<U, T> {
#[inline(always)]
fn default() -> Pointer<U, T> {
Pointer::null()
}
}
impl<U: PrimitiveAddress, T: ?Sized> Eq for Pointer<U, T> {}
impl<U: PrimitiveAddress, T: ?Sized> PartialEq for Pointer<U, T> {
#[inline(always)]
fn eq(&self, rhs: &Pointer<U, T>) -> bool {
self.inner == rhs.inner
}
}
impl<U: PrimitiveAddress, T: ?Sized> PartialOrd for Pointer<U, T> {
#[inline(always)]
fn partial_cmp(&self, rhs: &Pointer<U, T>) -> Option<cmp::Ordering> {
self.inner.partial_cmp(&rhs.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized> Ord for Pointer<U, T> {
#[inline(always)]
fn cmp(&self, rhs: &Pointer<U, T>) -> cmp::Ordering {
self.inner.cmp(&rhs.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized> hash::Hash for Pointer<U, T> {
#[inline(always)]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state)
}
}
impl<U: PrimitiveAddress, T: ?Sized> AsRef<U> for Pointer<U, T> {
#[inline(always)]
fn as_ref(&self) -> &U {
&self.inner
}
}
impl<U: PrimitiveAddress, T: ?Sized> AsMut<U> for Pointer<U, T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut U {
&mut self.inner
}
}
impl<U: PrimitiveAddress, T: ?Sized> From<U> for Pointer<U, T> {
#[inline(always)]
fn from(address: U) -> Pointer<U, T> {
Pointer {
inner: address,
phantom_data: PhantomData,
}
}
}
impl<T: ?Sized> From<Address> for Pointer64<T> {
#[inline(always)]
fn from(address: Address) -> Pointer64<T> {
Pointer {
inner: address.to_umem() as u64,
phantom_data: PhantomData,
}
}
}
impl<U: Into<Address>, T: ?Sized> From<Pointer<U, T>> for umem {
#[inline(always)]
fn from(ptr: Pointer<U, T>) -> umem {
let address: Address = ptr.inner.into();
address.to_umem()
}
}
impl<U: PrimitiveAddress, T> ops::Add<umem> for Pointer<U, T> {
type Output = Pointer<U, T>;
#[inline(always)]
fn add(self, other: umem) -> Pointer<U, T> {
let address = self.inner + U::from_umem(size_of::<T>() as umem * other);
Pointer {
inner: address,
phantom_data: self.phantom_data,
}
}
}
impl<U: PrimitiveAddress, T> ops::Sub<umem> for Pointer<U, T> {
type Output = Pointer<U, T>;
#[inline(always)]
fn sub(self, other: umem) -> Pointer<U, T> {
let address = self.inner - U::from_umem(size_of::<T>() as umem * other);
Pointer {
inner: address,
phantom_data: self.phantom_data,
}
}
}
#[cfg(feature = "64_bit_mem")]
impl<U: PrimitiveAddress, T> ops::Add<usize> for Pointer<U, T> {
type Output = Pointer<U, T>;
#[inline(always)]
fn add(self, other: usize) -> Pointer<U, T> {
self + other as umem
}
}
#[cfg(feature = "64_bit_mem")]
impl<U: PrimitiveAddress, T> ops::Sub<usize> for Pointer<U, T> {
type Output = Pointer<U, T>;
#[inline(always)]
fn sub(self, other: usize) -> Pointer<U, T> {
self - other as umem
}
}
impl<U: PrimitiveAddress, T: ?Sized> fmt::Debug for Pointer<U, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized> fmt::UpperHex for Pointer<U, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:X}", self.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized> fmt::LowerHex for Pointer<U, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized> fmt::Display for Pointer<U, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.inner)
}
}
impl<U: PrimitiveAddress, T: ?Sized + 'static> ByteSwap for Pointer<U, T> {
fn byte_swap(&mut self) {
self.inner.byte_swap();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn offset32() {
let ptr8 = Pointer32::<u8>::from(0x1000u32);
assert_eq!(ptr8.offset(3).to_umem(), 0x1003);
assert_eq!(ptr8.offset(-5).to_umem(), 0xFFB);
let ptr16 = Pointer32::<u16>::from(0x1000u32);
assert_eq!(ptr16.offset(3).to_umem(), 0x1006);
assert_eq!(ptr16.offset(-5).to_umem(), 0xFF6);
let ptr32 = Pointer32::<u32>::from(0x1000u32);
assert_eq!(ptr32.offset(3).to_umem(), 0x100C);
assert_eq!(ptr32.offset(-5).to_umem(), 0xFEC);
}
#[test]
fn offset64() {
let ptr8 = Pointer64::<u8>::from(0x1000u64);
assert_eq!(ptr8.offset(3).to_umem(), 0x1003);
assert_eq!(ptr8.offset(-5).to_umem(), 0xFFB);
let ptr16 = Pointer64::<u16>::from(0x1000u64);
assert_eq!(ptr16.offset(3).to_umem(), 0x1006);
assert_eq!(ptr16.offset(-5).to_umem(), 0xFF6);
let ptr32 = Pointer64::<u32>::from(0x1000u64);
assert_eq!(ptr32.offset(3).to_umem(), 0x100C);
assert_eq!(ptr32.offset(-5).to_umem(), 0xFEC);
let ptr64 = Pointer64::<u64>::from(0x1000u64);
assert_eq!(ptr64.offset(3).to_umem(), 0x1018);
assert_eq!(ptr64.offset(-5).to_umem(), 0xFD8);
}
#[test]
fn offset_from() {
let ptr1 = Pointer64::<u16>::from(0x1000u64);
let ptr2 = Pointer64::<u16>::from(0x1008u64);
assert_eq!(ptr2.offset_from(ptr1), 4);
assert_eq!(ptr1.offset_from(ptr2), -4);
}
}