use core::fmt;
use core::marker::PhantomData;
#[doc(alias = "large-projection")]
pub struct RelPtr<T, O = i32> {
offset: O,
_phantom: PhantomData<T>,
}
impl<T, O: Clone> Clone for RelPtr<T, O> {
fn clone(&self) -> Self {
Self {
offset: self.offset.clone(),
_phantom: PhantomData,
}
}
}
impl<T, O: Copy> Copy for RelPtr<T, O> {}
impl<T, O: PartialEq> PartialEq for RelPtr<T, O> {
fn eq(&self, other: &Self) -> bool {
self.offset.eq(&other.offset)
}
}
impl<T, O: Eq> Eq for RelPtr<T, O> {}
impl<T, O: std::hash::Hash> std::hash::Hash for RelPtr<T, O> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.offset.hash(state);
}
}
impl<T, O: bincode_next::enc::Encode> bincode_next::enc::Encode for RelPtr<T, O> {
fn encode<E: bincode_next::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode_next::error::EncodeError> {
self.offset.encode(encoder)
}
}
impl<T, O: bincode_next::de::Decode<C>, C> bincode_next::de::Decode<C> for RelPtr<T, O> {
fn decode<D: bincode_next::de::Decoder<Context = C>>(
decoder: &mut D,
) -> Result<Self, bincode_next::error::DecodeError> {
let offset = O::decode(decoder)?;
Ok(Self {
offset,
_phantom: PhantomData,
})
}
}
impl<'de, T, O: bincode_next::de::BorrowDecode<'de, C>, C> bincode_next::de::BorrowDecode<'de, C>
for RelPtr<T, O>
{
fn borrow_decode<D: bincode_next::de::BorrowDecoder<'de, Context = C>>(
decoder: &mut D,
) -> Result<Self, bincode_next::error::DecodeError> {
let offset = O::borrow_decode(decoder)?;
Ok(Self {
offset,
_phantom: PhantomData,
})
}
}
impl<T, O: fmt::Display> fmt::Debug for RelPtr<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RelPtr(offset: {})", self.offset)
}
}
impl<T> RelPtr<T, i64> {
pub const NULL_OFFSET: i64 = i64::MIN;
#[must_use]
pub const fn null() -> Self {
Self {
offset: Self::NULL_OFFSET,
_phantom: core::marker::PhantomData,
}
}
#[must_use]
pub const fn is_null(self) -> bool {
self.offset == Self::NULL_OFFSET
}
#[must_use]
pub fn from_indices(source: usize, target: usize) -> Self {
Self::from_indices_checked(source, target).unwrap_or_else(Self::null)
}
#[must_use]
pub fn from_indices_checked(source: usize, target: usize) -> Option<Self> {
let diff = (target as i128) - (source as i128);
let offset = i64::try_from(diff).ok()?;
if offset == Self::NULL_OFFSET {
return None;
}
Some(Self {
offset,
_phantom: core::marker::PhantomData,
})
}
#[must_use]
pub fn resolve(self, source: usize) -> Option<usize> {
if self.is_null() {
return None;
}
let target = (source as i128) + i128::from(self.offset);
if target < 0 {
None
} else {
usize::try_from(target).ok()
}
}
}
impl<T> RelPtr<T, i32> {
pub const NULL_OFFSET: i32 = i32::MIN;
#[must_use]
pub const fn null() -> Self {
Self {
offset: Self::NULL_OFFSET,
_phantom: PhantomData,
}
}
#[must_use]
pub const fn is_null(self) -> bool {
self.offset == Self::NULL_OFFSET
}
#[must_use]
pub fn from_indices(source: usize, target: usize) -> Self {
Self::from_indices_checked(source, target).unwrap_or_else(Self::null)
}
#[must_use]
pub fn from_indices_checked(source: usize, target: usize) -> Option<Self> {
let diff = (target as isize) - (source as isize);
let offset = i32::try_from(diff).ok()?;
if offset == Self::NULL_OFFSET {
return None;
}
Some(Self {
offset,
_phantom: PhantomData,
})
}
#[must_use]
pub const fn resolve(self, source: usize) -> Option<usize> {
if self.is_null() {
return None;
}
let target = (source as isize) + (self.offset as isize);
if target < 0 {
None
} else {
Some(target as usize)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_relative_pointer_i32() {
let source = 10;
let target = 15;
let ptr = RelPtr::<u64>::from_indices(source, target);
assert!(!ptr.is_null());
assert_eq!(ptr.resolve(source), Some(target));
let backward_ptr = RelPtr::<u64>::from_indices(target, source);
assert_eq!(backward_ptr.resolve(target), Some(source));
let null_ptr = RelPtr::<u64>::null();
assert!(null_ptr.is_null());
assert_eq!(null_ptr.resolve(source), None);
}
}