summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/impls.rs10
-rw-r--r--src/impls/cstr.rs18
-rw-r--r--src/impls/ptr_traits.rs174
-rw-r--r--src/impls/slice.rs40
-rw-r--r--src/lib.rs155
5 files changed, 397 insertions, 0 deletions
diff --git a/src/impls.rs b/src/impls.rs
new file mode 100644
index 0000000..4690dc9
--- /dev/null
+++ b/src/impls.rs
@@ -0,0 +1,10 @@
+
+/// impls for pointer traits: {Copy,Clone,etc} for {Const,Mut}LtPtr.
+pub mod ptr_traits;
+
+/// impls for CStr.
+#[cfg(feature="std")]
+pub mod cstr;
+
+/// impls for slices.
+pub mod slice;
diff --git a/src/impls/cstr.rs b/src/impls/cstr.rs
new file mode 100644
index 0000000..5467963
--- /dev/null
+++ b/src/impls/cstr.rs
@@ -0,0 +1,18 @@
+
+use std::ffi::CStr;
+use std::os::raw::c_char;
+
+use crate::*;
+
+impl AsLtPtr for CStr {
+    type Target = c_char;
+
+    /// Returns the inner pointer to this C string.
+    ///
+    /// The returned pointer points to a contiguous region of memory terminated
+    /// with a 0 byte to represent the end of the string.
+    #[inline]
+    fn as_lt_ptr(&self) -> ConstLtPtr<'_, c_char> {
+        unsafe { ConstLtPtr::from_raw(self.as_ptr()) }
+    }
+}
diff --git a/src/impls/ptr_traits.rs b/src/impls/ptr_traits.rs
new file mode 100644
index 0000000..768d364
--- /dev/null
+++ b/src/impls/ptr_traits.rs
@@ -0,0 +1,174 @@
+
+//use core::ops::{Deref, DerefMut};
+use core::panic::{RefUnwindSafe, UnwindSafe};
+
+use crate::*;
+
+// maybe we shouldn't have Deref? we want to make it hard to fuck things up.
+
+//impl<'a, T: ?Sized> Deref for ConstLtPtr<'a, T> {
+//    type Target = *const T;
+//    fn deref(&self) -> &*const T {
+//        &self.raw
+//    }
+//}
+//
+//impl<'a, T: ?Sized> DerefMut for ConstLtPtr<'a, T> {
+//    fn deref_mut(&mut self) -> &mut *const T {
+//        &mut self.raw
+//    }
+//}
+//
+//impl<'a, T: ?Sized> Deref for MutLtPtr<'a, T> {
+//    type Target = *mut T;
+//    fn deref(&self) -> &*mut T {
+//        &self.raw
+//    }
+//}
+//
+//impl<'a, T: ?Sized> DerefMut for MutLtPtr<'a, T> {
+//    fn deref_mut(&mut self) -> &mut *mut T {
+//        &mut self.raw
+//    }
+//}
+
+// *mut T are UnwindSafe, so ours should be too.
+impl<'a, T: RefUnwindSafe + ?Sized> UnwindSafe for MutLtPtr<'a, T> {}
+
+impl<'a, T: ?Sized> Copy for ConstLtPtr<'a, T> {}
+impl<'a, T: ?Sized> Clone for ConstLtPtr<'a, T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<'a, T: ?Sized> core::fmt::Debug for ConstLtPtr<'a, T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        core::fmt::Debug::fmt(&self.raw, f)
+    }
+}
+
+impl<'a, T: ?Sized> core::fmt::Pointer for ConstLtPtr<'a, T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        core::fmt::Pointer::fmt(&self.raw, f)
+    }
+}
+
+impl<'a, T: ?Sized> core::hash::Hash for ConstLtPtr<'a, T> {
+    #[inline]
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        core::hash::Hash::hash(&self.raw, state)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::Ord for ConstLtPtr<'a, T> {
+    #[inline]
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        core::cmp::Ord::cmp(&self.raw, &other.raw)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::Eq for ConstLtPtr<'a, T> {}
+impl<'a, T: ?Sized> core::cmp::PartialEq for ConstLtPtr<'a, T> {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        core::cmp::PartialEq::eq(&self.raw, &other.raw)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::PartialOrd for ConstLtPtr<'a, T> {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        core::cmp::PartialOrd::partial_cmp(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn lt(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::lt(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn le(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::le(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn gt(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::gt(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn ge(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::ge(&self.raw, &other.raw)
+    }
+}
+
+impl<'a, T: ?Sized> Copy for MutLtPtr<'a, T> {}
+impl<'a, T: ?Sized> Clone for MutLtPtr<'a, T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<'a, T: ?Sized> core::fmt::Debug for MutLtPtr<'a, T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        core::fmt::Debug::fmt(&self.raw, f)
+    }
+}
+
+impl<'a, T: ?Sized> core::fmt::Pointer for MutLtPtr<'a, T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        core::fmt::Pointer::fmt(&self.raw, f)
+    }
+}
+
+impl<'a, T: ?Sized> core::hash::Hash for MutLtPtr<'a, T> {
+    #[inline]
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        core::hash::Hash::hash(&self.raw, state)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::Ord for MutLtPtr<'a, T> {
+    #[inline]
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        core::cmp::Ord::cmp(&self.raw, &other.raw)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::Eq for MutLtPtr<'a, T> {}
+impl<'a, T: ?Sized> core::cmp::PartialEq for MutLtPtr<'a, T> {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        core::cmp::PartialEq::eq(&self.raw, &other.raw)
+    }
+}
+
+impl<'a, T: ?Sized> core::cmp::PartialOrd for MutLtPtr<'a, T> {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        core::cmp::PartialOrd::partial_cmp(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn lt(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::lt(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn le(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::le(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn gt(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::gt(&self.raw, &other.raw)
+    }
+
+    #[inline]
+    fn ge(&self, other: &Self) -> bool {
+        core::cmp::PartialOrd::ge(&self.raw, &other.raw)
+    }
+}
diff --git a/src/impls/slice.rs b/src/impls/slice.rs
new file mode 100644
index 0000000..ff9b9de
--- /dev/null
+++ b/src/impls/slice.rs
@@ -0,0 +1,40 @@
+
+use crate::*;
+
+pub trait Sealed {}
+impl<T> Sealed for [T] {}
+
+impl<T> AsLtPtr for [T] {
+    type Target = T;
+    fn as_lt_ptr(&self) -> ConstLtPtr<'_, T> {
+        unsafe { ConstLtPtr::from_raw(self.as_ptr()) }
+    }
+}
+
+impl<T> AsMutLtPtr for [T] {
+    fn as_mut_lt_ptr(&mut self) -> MutLtPtr<'_, T> {
+        unsafe { MutLtPtr::from_raw(self.as_mut_ptr()) }
+    }
+}
+
+impl<T> SliceExt<T> for [T] {
+    fn as_lt_ptr_range(&self) -> Range<ConstLtPtr<'_, T>> {
+        let Range { start, end } = self.as_ptr_range();
+        unsafe {
+            Range {
+                start: ConstLtPtr::from_raw(start),
+                end: ConstLtPtr::from_raw(end),
+            }
+        }
+    }
+
+    fn as_mut_lt_ptr_range(&mut self) -> Range<MutLtPtr<'_, T>> {
+        let Range { start, end } = self.as_mut_ptr_range();
+        unsafe {
+            Range {
+                start: MutLtPtr::from_raw(start),
+                end: MutLtPtr::from_raw(end),
+            }
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..0b1837d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,155 @@
+#![cfg_attr(not(feature="std"),no_std)]
+
+//! This crate provides "checked" raw pointers.
+//!
+//! We found ourselves to be experiencing a lot of cognitive overhead when
+//! doing FFI interactions. A non-insignificant portion of them came from raw
+//! pointers, `as_ptr`, `as_mut_ptr`, and the like. This removes that cognitive
+//! overhead.
+
+use core::cell::UnsafeCell;
+use core::marker::PhantomData;
+use core::ops::Range;
+
+/// The actual impls.
+mod impls;
+
+/// `*const T` with a lifetime.
+///
+/// # Examples
+///
+/// Rust doesn't catch incorrect `as_ptr` usage (tho there is a lint for it):
+///
+/// ```no_run
+/// use std::ffi::{CString, CStr};
+/// use std::os::raw::c_char;
+///
+/// extern "C" {
+///     fn my_ffi_function(data: *const c_char);
+/// }
+/// let str = CString::new("world!").unwrap().as_ptr();
+/// //                    oops! CString dropped here! ^
+/// unsafe { my_ffi_function(str) } // crashes at runtime
+/// ```
+///
+/// but we can use a `ConstLtPtr` to make it do so:
+///
+/// ```compile_fail
+/// use std::ffi::{CString, CStr};
+/// use std::os::raw::c_char;
+/// use ltptr::*;
+///
+/// extern "C" {
+///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
+/// }
+/// let str = CString::new("world!").unwrap().as_lt_ptr();
+/// unsafe { my_ffi_function(str) }
+/// // error[E0716]: temporary value dropped while borrowed
+/// ```
+///
+/// which we can then fix:
+///
+/// ```no_run
+/// use std::ffi::{CString, CStr};
+/// use std::os::raw::c_char;
+/// use ltptr::*;
+///
+/// extern "C" {
+///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
+/// }
+/// let str = CString::new("world!").unwrap();
+/// unsafe { my_ffi_function(str.as_lt_ptr()) }
+/// ```
+///
+///
+/// and if we call the wrong thing, it fails to compile:
+///
+/// ```compile_fail
+/// use std::ffi::{CString, CStr};
+/// use std::os::raw::c_char;
+/// use ltptr::*;
+///
+/// extern "C" {
+///     fn my_ffi_function(data: ConstLtPtr<'_, c_char>);
+/// }
+/// let str = CString::new("world!").unwrap();
+/// unsafe { my_ffi_function(str.as_ptr()) } // should be as_lt_ptr!
+/// ```
+#[repr(transparent)]
+pub struct ConstLtPtr<'a, T: ?Sized> {
+  raw: *const T,
+  _lt: PhantomData<&'a T>,
+}
+
+/// `*mut T` with a lifetime.
+#[repr(transparent)]
+pub struct MutLtPtr<'a, T: ?Sized> {
+  raw: *mut T,
+  _lt: PhantomData<&'a UnsafeCell<T>>,
+}
+
+/// Trait for conversion into a [`ConstLtPtr`].
+pub trait AsLtPtr {
+    type Target: ?Sized;
+    /// Returns a pointer as if by `as_ptr` on a type that implements this, but
+    /// with a bound lifetime.
+    fn as_lt_ptr<'a>(&'a self) -> ConstLtPtr<'a, Self::Target>;
+}
+
+/// Trait for conversion into a [`MutLtPtr`].
+pub trait AsMutLtPtr: AsLtPtr {
+    /// Returns a pointer as if by `as_ptr` on a type that implements this, but
+    /// with a bound lifetime.
+    fn as_mut_lt_ptr<'a>(&'a mut self) -> MutLtPtr<'a, Self::Target>;
+}
+
+/// Additional slice methods.
+pub trait SliceExt<T>: impls::slice::Sealed {
+    /// Returns the two raw pointers spanning the slice.
+    ///
+    /// See [`[T]::as_ptr_range`] for extended documentation.
+    ///
+    /// The pointers returned by this function have bound lifetimes.
+    fn as_lt_ptr_range<'a>(&'a self) -> Range<ConstLtPtr<'a, T>>;
+
+    /// Returns the two unsafe mutable pointers spanning the slice.
+    ///
+    /// See [`[T]::as_mut_ptr_range`] for extended documentation.
+    ///
+    /// The pointers returned by this function have bound lifetimes.
+    fn as_mut_lt_ptr_range<'a>(&'a mut self) -> Range<MutLtPtr<'a, T>>;
+}
+
+impl<'a, T: ?Sized> ConstLtPtr<'a, T> {
+    /// Creates a new `ConstLtPtr` from the given raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// This function is unsafe as an advisory: the returned `ConstLtPtr` has
+    /// an arbitrary lifetime `'a`, so using this function directly doesn't
+    /// help reduce cognitive overhead.
+    #[inline]
+    pub const unsafe fn from_raw(raw: *const T) -> Self {
+        Self {
+            raw: raw,
+            _lt: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: ?Sized> MutLtPtr<'a, T> {
+    /// Creates a new `MutLtPtr` from the given raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// This function is unsafe as an advisory: the returned `MutLtPtr` has
+    /// an arbitrary lifetime `'a`, so using this function directly doesn't
+    /// help reduce cognitive overhead.
+    #[inline]
+    pub const unsafe fn from_raw(raw: *mut T) -> Self {
+        Self {
+            raw: raw,
+            _lt: PhantomData,
+        }
+    }
+}