mirror of
https://github.com/Noratrieb/stuff.git
synced 2026-01-14 16:35:08 +01:00
start with pointer
This commit is contained in:
parent
062e2f7ee2
commit
6042f4702d
3 changed files with 209 additions and 6 deletions
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sptr = "0.2.3"
|
||||
|
|
|
|||
167
src/lib.rs
167
src/lib.rs
|
|
@ -1,8 +1,163 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
mod strategies;
|
||||
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Not;
|
||||
|
||||
use sptr::Strict;
|
||||
|
||||
pub struct StuffedPtr<T, S>(*mut T, PhantomData<S>)
|
||||
where
|
||||
S: StuffingStrategy;
|
||||
|
||||
impl<T, S> StuffedPtr<T, S>
|
||||
where
|
||||
S: StuffingStrategy,
|
||||
{
|
||||
pub fn new_ptr(ptr: *mut T) -> Self {
|
||||
Self(map_ptr(ptr, S::stuff_ptr), PhantomData)
|
||||
}
|
||||
|
||||
pub fn new_extra(extra: S::Extra) -> Self {
|
||||
// this doesn't have any provenance, which is ok, since it's never a pointer anyways.
|
||||
// if the user calls `set_ptr` it will use the new provenance from that ptr
|
||||
let ptr = std::ptr::null_mut();
|
||||
let ptr = Strict::with_addr(ptr, S::stuff_extra(extra));
|
||||
Self(ptr, PhantomData)
|
||||
}
|
||||
|
||||
pub unsafe fn get_ptr(&self) -> Option<*mut T> {
|
||||
self.is_extra().not().then(|| self.get_ptr_unchecked())
|
||||
}
|
||||
|
||||
pub unsafe fn get_ptr_unchecked(&self) -> *mut T {
|
||||
map_ptr(self.0, S::extract_ptr)
|
||||
}
|
||||
|
||||
pub unsafe fn into_extra_unchecked(self) -> S::Extra {
|
||||
let data = self.addr();
|
||||
S::extract_extra(data)
|
||||
}
|
||||
|
||||
pub unsafe fn get_extra_unchecked(&self) -> S::Extra {
|
||||
let data = self.addr();
|
||||
S::extract_extra(data)
|
||||
}
|
||||
|
||||
pub unsafe fn get_extra(&self) -> Option<S::Extra> {
|
||||
self.is_extra().then(|| self.get_extra_unchecked())
|
||||
}
|
||||
|
||||
fn addr(&self) -> usize {
|
||||
Strict::addr(self.0)
|
||||
}
|
||||
|
||||
fn is_extra(&self) -> bool {
|
||||
S::is_extra(self.addr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Debug for StuffedPtr<T, S>
|
||||
where
|
||||
S: StuffingStrategy,
|
||||
S::Extra: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_extra() {
|
||||
// SAFETY: We checked that self contains the extra
|
||||
// Note: if S::Extra: !Copy, we can't just copy it out and call it a day
|
||||
// For example, if it's a Box, not forgetting it here would lead to a double free
|
||||
// So we just format it and forget it afterwards
|
||||
let extra = unsafe { self.get_extra_unchecked() };
|
||||
f.debug_struct("StuffedPtr::Extra")
|
||||
.field("extra", &extra)
|
||||
.finish()?;
|
||||
std::mem::forget(extra);
|
||||
Ok(())
|
||||
} else {
|
||||
let ptr = map_ptr(self.0, S::extract_ptr);
|
||||
f.debug_struct("StuffedPtr::Ptr")
|
||||
.field("ptr", &ptr)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Drop for StuffedPtr<T, S>
|
||||
where
|
||||
S: StuffingStrategy,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if self.is_extra() {
|
||||
// SAFETY: We move it out here and it's never accessed again.
|
||||
let extra = unsafe { self.get_extra_unchecked() };
|
||||
drop(extra);
|
||||
} else {
|
||||
// dropping a ptr is a no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> From<Box<T>> for StuffedPtr<T, S>
|
||||
where
|
||||
S: StuffingStrategy,
|
||||
{
|
||||
fn from(boxed: Box<T>) -> Self {
|
||||
Self::new_ptr(Box::into_raw(boxed))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait StuffingStrategy {
|
||||
type Extra;
|
||||
|
||||
fn is_extra(data: usize) -> bool;
|
||||
fn stuff_extra(inner: Self::Extra) -> usize;
|
||||
fn extract_extra(data: usize) -> Self::Extra;
|
||||
|
||||
fn stuff_ptr(inner: usize) -> usize {
|
||||
inner
|
||||
}
|
||||
fn extract_ptr(inner: usize) -> usize {
|
||||
inner
|
||||
}
|
||||
}
|
||||
|
||||
fn map_ptr<T>(ptr: *mut T, map: impl FnOnce(usize) -> usize) -> *mut T {
|
||||
let int = Strict::addr(ptr);
|
||||
let result = map(int);
|
||||
Strict::with_addr(ptr, result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::strategies::test_strategies::HasDebug;
|
||||
use crate::StuffedPtr;
|
||||
|
||||
#[test]
|
||||
fn set_get_ptr_no_extra() {
|
||||
unsafe {
|
||||
let boxed = Box::new(1);
|
||||
let stuffed_ptr: StuffedPtr<i32, ()> = boxed.into();
|
||||
let ptr = stuffed_ptr.get_ptr_unchecked();
|
||||
let boxed = Box::from_raw(ptr);
|
||||
assert_eq!(*boxed, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug() {
|
||||
let boxed = Box::new(1);
|
||||
let stuffed_ptr: StuffedPtr<i32, HasDebug> = boxed.into();
|
||||
assert!(format!("{stuffed_ptr:?}").starts_with("StuffedPtr::Ptr {"));
|
||||
|
||||
drop(unsafe { Box::from_raw(stuffed_ptr.get_ptr().unwrap()) });
|
||||
|
||||
let extra = HasDebug;
|
||||
let stuffed_ptr: StuffedPtr<i32, HasDebug> = StuffedPtr::new_extra(extra);
|
||||
println!("{:?} {:X}", stuffed_ptr.0, usize::MAX);
|
||||
assert_eq!(
|
||||
format!("{stuffed_ptr:?}"),
|
||||
"StuffedPtr::Extra { extra: hello! }"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
src/strategies.rs
Normal file
47
src/strategies.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use crate::StuffingStrategy;
|
||||
|
||||
unsafe impl StuffingStrategy for () {
|
||||
type Extra = ();
|
||||
|
||||
fn is_extra(_data: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn stuff_extra(_inner: Self::Extra) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn extract_extra(_data: usize) -> Self::Extra {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_strategies {
|
||||
use crate::StuffingStrategy;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
pub struct HasDebug;
|
||||
|
||||
impl Debug for HasDebug {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("hello!")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl StuffingStrategy for HasDebug {
|
||||
type Extra = Self;
|
||||
|
||||
fn is_extra(data: usize) -> bool {
|
||||
data == usize::MAX
|
||||
}
|
||||
|
||||
fn stuff_extra(_inner: Self::Extra) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn extract_extra(_data: usize) -> Self::Extra {
|
||||
Self
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue