tagptr-0.2.0/.cargo_vcs_info.json0000644000000001120000000000100123260ustar { "git": { "sha1": "a7e04904e32277eed00fa812613750a6ae891800" } } tagptr-0.2.0/.gitignore000064400000000000000000000000450072674642500131430ustar 00000000000000/.idea /target **/*.rs.bk Cargo.lock tagptr-0.2.0/Cargo.toml0000644000000016410000000000100103340ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "tagptr" version = "0.2.0" authors = ["Oliver Giersch"] exclude = [".github/"] description = "Strongly typed atomic and non-atomic tagged pointers" documentation = "https://docs.rs/tagptr" readme = "README.md" keywords = ["pointer", "tagging", "concurrency"] categories = ["no-std"] license = "MIT/Apache-2.0" repository = "https://github.com/oliver-giersch/tagptr.git" tagptr-0.2.0/Cargo.toml.orig000064400000000000000000000006230072674642500140440ustar 00000000000000[package] name = "tagptr" version = "0.2.0" authors = ["Oliver Giersch"] description = "Strongly typed atomic and non-atomic tagged pointers" license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/oliver-giersch/tagptr.git" documentation = "https://docs.rs/tagptr" keywords = ["pointer", "tagging", "concurrency"] categories = ["no-std"] edition = "2018" exclude = [".github/"]tagptr-0.2.0/LICENSE-APACHE000064400000000000000000000010530072674642500130770ustar 00000000000000Copyright 2021 Oliver Giersch Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tagptr-0.2.0/LICENSE-MIT000064400000000000000000000020570072674642500126140ustar 00000000000000MIT License Copyright (c) 2021 Oliver Giersch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tagptr-0.2.0/README.md000064400000000000000000000023470072674642500124410ustar 00000000000000# tagptr Strongly typed marked pointers for storing bit patterns (_tags_) alongside raw pointers for concurrent programming with atomic operations. [![Build Status](https://github.com/oliver-giersch/tagptr/actions/workflows/rust.yml/badge.svg?branch=master)](https://github.com/oliver-giersch/tagptr/actions/workflows/rust.yml) [![Latest version](https://img.shields.io/crates/v/tagptr.svg)](https://crates.io/crates/tagptr) [![Documentation](https://docs.rs/tagptr/badge.svg)](https://docs.rs/tagptr) [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/oliver-giersch/tagptr) ## Usage Add the following to your `Cargo.toml` ``` [dependencies] tagptr = "0.2.0" ``` ## Motivation Most atomic CPU instructions only work with register-sized memory words (e.g., 32-bit or 64-bit). Many low-level concurrent algorithms thus need to store aditional data (_tags_) in the unused lower bits of pointers to referenced data objects. This crate provides thin and efficient abstractions for working with such pointers. ## License `tagptr` is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. tagptr-0.2.0/rustfmt.toml000064400000000000000000000000750072674642500135570ustar 00000000000000use_field_init_shorthand = true use_small_heuristics = "Max" tagptr-0.2.0/src/imp/atomic.rs000064400000000000000000000376220072674642500143640ustar 00000000000000use core::{ fmt, marker::PhantomData, sync::atomic::{AtomicUsize, Ordering}, }; use crate::{AtomicTagPtr, TagPtr}; /********** impl Send + Sync **********************************************************************/ unsafe impl Send for AtomicTagPtr {} unsafe impl Sync for AtomicTagPtr {} /********** impl inherent *************************************************************************/ impl AtomicTagPtr { doc_comment! { doc_tag_bits!(), pub const TAG_BITS: usize = N; } doc_comment! { doc_tag_mask!(), pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS); } doc_comment! { doc_ptr_mask!(), pub const POINTER_MASK: usize = !Self::TAG_MASK; } doc_comment! { doc_null!(), /// /// # Examples /// /// ``` /// use core::{ptr, sync::atomic::Ordering}; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// /// let ptr = AtomicTagPtr::null(); /// assert_eq!( /// ptr.load(Ordering::Relaxed).decompose(), /// (ptr::null_mut(), 0) /// ); /// ``` pub const fn null() -> Self { Self { inner: AtomicUsize::new(0), _marker: PhantomData } } } doc_comment! { doc_atomic_new!(), #[inline] pub fn new(marked_ptr: TagPtr) -> Self { Self { inner: AtomicUsize::new(marked_ptr.into_usize()), _marker: PhantomData } } } doc_comment! { doc_atomic_into_inner!(), #[inline] pub fn into_inner(self) -> TagPtr { TagPtr::from_usize(self.inner.into_inner()) } } /// Returns a mutable reference to the underlying marked pointer. /// /// This is safe because the mutable reference guarantees no other /// threads are concurrently accessing the atomic pointer. #[inline] pub fn get_mut(&mut self) -> &mut TagPtr { // SAFETY: the mutable self reference ensures the dereferencing is sound unsafe { &mut *(self.inner.get_mut() as *mut usize as *mut _) } } /// Loads the value of the atomic marked pointer. /// /// `load` takes an [`Ordering`] argument which describes the memory /// ordering of this operation. /// Possible values are [`SeqCst`][seq_cst], [`Acquire`][acq] and /// [`Relaxed`][rlx]. /// /// # Panics /// /// Panics if `order` is [`Release`][rel] or [`AcqRel`][acq_rel]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// [acq_rel]: Ordering::AcqRel /// [seq_cst]: Ordering::SeqCst #[inline] pub fn load(&self, order: Ordering) -> TagPtr { TagPtr::from_usize(self.inner.load(order)) } /// Stores a value into the atomic marked pointer. /// /// `store` takes an [`Ordering`] argument which describes the memory /// ordering of this operation. /// Possible values are [`SeqCst`][seq_cst], [`Release`][rel] and /// [`Relaxed`][rlx]. /// /// # Panics /// /// Panics if `order` is [`Acquire`][acq] or [`AcqRel`][acq_rel]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// [acq_rel]: Ordering::AcqRel /// [seq_cst]: Ordering::SeqCst #[inline] pub fn store(&self, ptr: TagPtr, order: Ordering) { self.inner.store(ptr.into_usize(), order) } /// Stores a value into the atomic marked pointer and returns the previous /// value. /// /// `swap` takes an [`Ordering`] argument which describes the memory /// ordering of this operation. /// All ordering modes are possible. /// Note that using [`Acquire`][acq] makes the store part of this operation /// [`Relaxed`][rlx], and using [`Release`][rel] makes the load part /// [`Relaxed`][rlx]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// /// # Examples /// /// ``` /// use core::sync::atomic::Ordering; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// type TagPtr = tagptr::TagPtr; /// /// let ptr = AtomicTagPtr::null(); /// let prev = ptr.swap(TagPtr::new(&mut 1), Ordering::Relaxed); /// /// assert!(prev.is_null()); /// ``` pub fn swap(&self, ptr: TagPtr, order: Ordering) -> TagPtr { TagPtr::from_usize(self.inner.swap(ptr.into_usize(), order)) } /// Stores a value into the pointer if the current value is the same as /// `current`. /// /// The return value is a result indicating whether the new value was /// written and containing the previous value. /// On success this value is guaranteed to be equal to `current`. /// /// `compare_exchange` takes takes two [`Ordering`] arguments to describe /// the memory ordering of this operation. /// The first describes the required ordering if the operation succeeds /// while the second describes the required ordering when the operation /// fails. /// Using [`Acquire`][acq] as success ordering makes store part of this /// operation [`Relaxed`][rlx], and using [`Release`][rel] makes the /// successful load [`Relaxed`][rlx]. /// The failure ordering can only be [`SeqCst`][seq_cst], [`Acquire`][acq] /// or [`Relaxed`][rlx] and must be equivalent or weaker than the success /// ordering. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// [seq_cst]: Ordering::SeqCst #[inline] pub fn compare_exchange( &self, current: TagPtr, new: TagPtr, (success, failure): (Ordering, Ordering), ) -> Result, TagPtr> { self.inner .compare_exchange(current.into_usize(), new.into_usize(), success, failure) .map(|_| current) .map_err(TagPtr::from_usize) } /// Stores a value into the pointer if the current value is the same as /// `current`. /// /// The return value is a result indicating whether the new value was /// written and containing the previous value. /// On success this value is guaranteed to be equal to `current`. /// /// Unlike `compare_exchange`, this function is allowed to spuriously fail, /// even when the comparison succeeds, which can result in more efficient /// code on some platforms. /// The return value is a result indicating whether the new value was /// written and containing the previous value. /// /// `compare_exchange` takes takes two [`Ordering`] arguments to describe /// the memory ordering of this operation. /// The first describes the required ordering if the operation succeeds /// while the second describes the required ordering when the operation /// fails. /// Using [`Acquire`][acq] as success ordering makes store part of this /// operation [`Relaxed`][rlx], and using [`Release`][rel] makes the /// successful load [`Relaxed`][rlx]. /// The failure ordering can only be [`SeqCst`][seq_cst], [`Acquire`][acq] /// or [`Relaxed`][rlx] and must be equivalent or weaker than the success /// ordering. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// [seq_cst]: Ordering::SeqCst #[inline] pub fn compare_exchange_weak( &self, current: TagPtr, new: TagPtr, (success, failure): (Ordering, Ordering), ) -> Result, TagPtr> { self.inner .compare_exchange_weak(current.into_usize(), new.into_usize(), success, failure) .map(|_| current) .map_err(TagPtr::from_usize) } /// Adds `value` to the current tag value, returning the previous marked /// pointer. /// /// This operation directly and unconditionally alters the internal numeric /// representation of the atomic marked pointer. /// Hence there is no way to reliably guarantee the operation only affects /// the tag bits and does not overflow into the pointer bits. /// /// `fetch_add` takes takes an [`Ordering`] argument which describes the /// memory ordering of this operation. /// All ordering modes are possible. /// Note that using [`Acquire`][acq] makes the store part of this operation /// [`Relaxed`][rlx] and using [`Release`][rel] makes the load part /// [`Relaxed`][rlx]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// /// # Examples /// /// ``` /// use core::sync::atomic::Ordering; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = AtomicTagPtr::new(TagPtr::new(reference)); /// /// assert_eq!( /// ptr.fetch_add(1, Ordering::Relaxed).decompose(), /// (reference as *mut _, 0) /// ); /// /// assert_eq!( /// ptr.load(Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b01) /// ); /// ``` #[inline] pub fn fetch_add(&self, value: usize, order: Ordering) -> TagPtr { debug_assert!(value < Self::TAG_MASK, "`value` exceeds tag bits (would overflow)"); TagPtr::from_usize(self.inner.fetch_add(value, order)) } /// Subtracts `value` from the current tag value, returning the previous /// marked pointer. /// /// This operation directly and unconditionally alters the internal numeric /// representation of the atomic marked pointer. /// Hence there is no way to reliably guarantee the operation only affects /// the tag bits and does not overflow into the pointer bits. /// /// `fetch_sub` takes takes an [`Ordering`] argument which describes the /// memory ordering of this operation. /// All ordering modes are possible. /// Note that using [`Acquire`][acq] makes the store part of this operation /// [`Relaxed`][rlx] and using [`Release`][rel] makes the load part /// [`Relaxed`][rlx]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// /// # Examples /// /// ``` /// use core::sync::atomic::Ordering; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = AtomicTagPtr::new(TagPtr::compose(reference, 0b10)); /// /// assert_eq!( /// ptr.fetch_sub(1, Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b10) /// ); /// /// assert_eq!( /// ptr.load(Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b01) /// ); /// ``` #[inline] pub fn fetch_sub(&self, value: usize, order: Ordering) -> TagPtr { debug_assert!(value < Self::TAG_MASK, "`value` exceeds tag bits (would underflow)"); TagPtr::from_usize(self.inner.fetch_sub(value, order)) } /// Performs a bitwise "or" of `value` with the current tag value, returning /// the previous marked pointer. /// /// This operation directly and unconditionally alters the internal numeric /// representation of the atomic marked pointer. /// Hence there is no way to reliably guarantee the operation only affects /// the tag bits and does not overflow into the pointer bits. /// /// `fetch_or` takes takes an [`Ordering`] argument which describes the /// memory ordering of this operation. /// All ordering modes are possible. /// Note that using [`Acquire`][acq] makes the store part of this operation /// [`Relaxed`][rlx] and using [`Release`][rel] makes the load part /// [`Relaxed`][rlx]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// /// # Examples /// /// ``` /// use core::sync::atomic::Ordering; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = AtomicTagPtr::new(TagPtr::compose(reference, 0b10)); /// /// assert_eq!( /// ptr.fetch_or(0b11, Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b10) /// ); /// /// assert_eq!( /// ptr.load(Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b11) /// ); /// ``` #[inline] pub fn fetch_or(&self, value: usize, order: Ordering) -> TagPtr { debug_assert!(value <= Self::TAG_MASK, "`value` exceeds tag bits (would corrupt pointer)"); TagPtr::from_usize(self.inner.fetch_or(Self::TAG_MASK & value, order)) } /// Performs a bitwise "and" of `value` with the current tag value, /// returning the previous marked pointer. /// /// This operation directly and unconditionally alters the internal numeric /// representation of the atomic marked pointer. /// Hence there is no way to reliably guarantee the operation only affects /// the tag bits and does not overflow into the pointer bits. /// /// `fetch_and` takes takes an [`Ordering`] argument which describes the /// memory ordering of this operation. /// All ordering modes are possible. /// Note that using [`Acquire`][acq] makes the store part of this operation /// [`Relaxed`][rlx] and using [`Release`][rel] makes the load part /// [`Relaxed`][rlx]. /// /// [rlx]: Ordering::Relaxed /// [acq]: Ordering::Acquire /// [rel]: Ordering::Release /// /// # Examples /// /// ``` /// use core::sync::atomic::Ordering; /// /// type AtomicTagPtr = tagptr::AtomicTagPtr; /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = AtomicTagPtr::new(TagPtr::compose(reference, 0b10)); /// /// // fetch_x returns previous value /// assert_eq!( /// ptr.fetch_and(0b11, Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b10) /// ); /// /// assert_eq!( /// ptr.load(Ordering::Relaxed).decompose(), /// (reference as *mut _, 0b10) /// ); /// ``` #[inline] pub fn fetch_and(&self, value: usize, order: Ordering) -> TagPtr { debug_assert!(value <= Self::TAG_MASK, "`value` exceeds tag bits (would corrupt pointer)"); TagPtr::from_usize(self.inner.fetch_and(Self::POINTER_MASK | value, order)) } } /********** impl Debug ****************************************************************************/ impl fmt::Debug for AtomicTagPtr { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (ptr, tag) = self.load(Ordering::SeqCst).decompose(); f.debug_struct("AtomicTagPtr").field("ptr", &ptr).field("tag", &tag).finish() } } /********** impl Default **************************************************************************/ impl Default for AtomicTagPtr { impl_default!(); } /********** impl From (*mut T) ********************************************************************/ impl From<*mut T> for AtomicTagPtr { #[inline] fn from(ptr: *mut T) -> Self { Self::new(ptr.into()) } } /********** impl From (TagPtr) ***********************************************************/ impl From> for AtomicTagPtr { #[inline] fn from(ptr: TagPtr) -> Self { Self::new(ptr) } } /********** impl Pointer **************************************************************************/ impl fmt::Pointer for AtomicTagPtr { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&self.load(Ordering::SeqCst), f) } } tagptr-0.2.0/src/imp/non_null.rs000064400000000000000000000311440072674642500147250ustar 00000000000000use core::{ cmp, convert::TryFrom, fmt, hash::{Hash, Hasher}, marker::PhantomData, mem, ptr::NonNull, }; use crate::{Null, TagNonNull, TagPtr}; /********** impl Clone ****************************************************************************/ impl Clone for TagNonNull { impl_clone!(); } /********** impl Copy *****************************************************************************/ impl Copy for TagNonNull {} /********** impl inherent *************************************************************************/ impl TagNonNull { doc_comment! { doc_tag_bits!(), pub const TAG_BITS: usize = N; } doc_comment! { doc_tag_mask!(), pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS); } doc_comment! { doc_ptr_mask!(), pub const POINTER_MASK: usize = !Self::TAG_MASK; } const COMPOSE_ERR_MSG: &'static str = "argument `ptr` is mis-aligned for `N` tag bits and could be parsed as marked `null` \ pointer."; /// Creates a new marked non-null pointer from `marked_ptr` without /// checking if it is `null`. /// /// # Safety /// /// The caller has to ensure that `marked_ptr` is not `null`. /// This includes `null` pointers with non-zero tag values. #[inline] pub const unsafe fn new_unchecked(marked_ptr: TagPtr) -> Self { Self { inner: NonNull::new_unchecked(marked_ptr.inner), _marker: PhantomData } } doc_comment! { doc_from_usize!(), #[inline] pub const unsafe fn from_usize(val: usize) -> Self { Self { inner: NonNull::new_unchecked(val as *mut _), _marker: PhantomData } } } doc_comment! { doc_into_raw!(), #[inline] pub const fn into_raw(self) -> NonNull { self.inner } } doc_comment! { doc_cast!(), pub const fn cast(self) -> TagNonNull { TagNonNull { inner: self.inner.cast(), _marker: PhantomData } } } doc_comment! { doc_into_usize!(), #[inline] pub fn into_usize(self) -> usize { self.inner.as_ptr() as _ } } /// Converts `self` into a (nullable) marked pointer. #[inline] pub const fn into_marked_ptr(self) -> TagPtr { TagPtr::new(self.inner.as_ptr()) } /// Creates a new non-null pointer from `marked_ptr`. /// /// # Errors /// /// Fails if `marked_ptr` is `null`, in which case a [`Null`] instance is /// returned containing argument pointer's tag value. #[inline] pub fn new(marked_ptr: TagPtr) -> Result { Self::try_from(marked_ptr) } /// Creates a new pointer that is dangling but well aligned. #[inline] pub const fn dangling() -> Self { let alignment = mem::align_of::(); let val = if alignment >= Self::TAG_MASK + 1 { alignment } else { Self::TAG_MASK + 1 }; // SAFETY: a type's alignment is never 0, so val is always non-zero unsafe { Self::from_usize(val) } } doc_comment! { doc_compose!(), /// # Panics /// /// Panics if `ptr` is mis-aligned for `N` tag bits and contains only /// zero bits in the upper bits, i.e., it would be parsed as a marked /// `null` pointer. #[inline] pub fn compose(ptr: NonNull, tag: usize) -> Self { Self::try_compose(ptr, tag).expect(Self::COMPOSE_ERR_MSG) } } /// Attempts to compose a new marked pointer from a raw (non-null) `ptr` and /// a `tag` value. /// /// # Errors /// /// Fails if `ptr` is mis-aligned for `N` tag bits and contains only /// zero bits in the upper bits, i.e., it would be parsed as a marked /// `null` pointer. /// In this case a [`Null`] instance is returned containing the argument /// pointer's tag value. #[inline] pub fn try_compose(ptr: NonNull, tag: usize) -> Result { Self::try_compose_inner(ptr.as_ptr(), tag) } /// Composes a new marked pointer from a raw (non-null) `ptr` and a `tag` /// value without checking if `ptr` is valid. /// /// # Safety /// /// The caller has to ensure that `ptr` is non-null even after considering /// its `N` lower bits as tag bits. #[inline] pub unsafe fn compose_unchecked(ptr: NonNull, tag: usize) -> Self { Self::new_unchecked(TagPtr::compose(ptr.as_ptr(), tag)) } doc_comment! { doc_clear_tag!(), #[inline] pub fn clear_tag(self) -> Self { Self { inner: self.decompose_non_null(), _marker: PhantomData } } } doc_comment! { doc_split_tag!(), #[inline] pub fn split_tag(self) -> (Self, usize) { let (inner, tag) = self.decompose(); (Self { inner, _marker: PhantomData }, tag) } } doc_comment! { doc_set_tag!(), #[inline] pub fn set_tag(self, tag: usize) -> Self { let ptr = self.decompose_non_null(); // SAFETY: ptr was decomposed from a valid marked non-nullable pointer unsafe { Self::compose_unchecked(ptr, tag) } } } doc_comment! { doc_update_tag!(), #[inline] pub fn update_tag(self, func: impl FnOnce(usize) -> usize) -> Self { let (ptr, tag) = self.decompose(); // SAFETY: ptr was decomposed from a valid marked non-nullable pointer unsafe { Self::compose_unchecked(ptr, func(tag)) } } } doc_comment! { doc_add_tag!(), /// # Safety /// /// The caller has to ensure that the resulting pointer is not /// `null` (neither marked nor unmarked). #[inline] pub unsafe fn add_tag(self, value: usize) -> Self { Self::from_usize(self.into_usize().wrapping_add(value)) } } doc_comment! { doc_sub_tag!(), /// # Safety /// /// The caller has to ensure that the resulting pointer is not /// `null` (neither marked nor unmarked). #[inline] pub unsafe fn sub_tag(self, value: usize) -> Self { Self::from_usize(self.into_usize().wrapping_sub(value)) } } doc_comment! { doc_decompose!(), #[inline] pub fn decompose(self) -> (NonNull, usize) { (self.decompose_non_null(), self.decompose_tag()) } } doc_comment! { doc_decompose_ptr!(), #[inline] pub fn decompose_ptr(self) -> *mut T { crate::decompose_ptr(self.inner.as_ptr() as usize, Self::TAG_BITS) } } doc_comment! { doc_decompose_non_null!(), #[inline] pub fn decompose_non_null(self) -> NonNull { // SAFETY: every valid TagNonNull is also a valid NonNull unsafe { NonNull::new_unchecked(self.decompose_ptr()) } } } doc_comment! { doc_decompose_tag!(), #[inline] pub fn decompose_tag(self) -> usize { crate::decompose_tag::(self.inner.as_ptr() as usize, Self::TAG_BITS) } } doc_comment! { doc_as_ref!("non-nullable"), #[inline] pub unsafe fn as_ref(&self) -> &T { &*self.decompose_non_null().as_ptr() } } doc_comment! { doc_as_mut!("non-nullable", TagNonNull), #[inline] pub unsafe fn as_mut(&mut self) -> &mut T { &mut *self.decompose_non_null().as_ptr() } } /// Decomposes the marked pointer, returning a reference and the separated /// tag. /// /// # Safety /// /// The same safety caveats as with [`as_ref`][TagNonNull::as_ref] apply. #[inline] pub unsafe fn decompose_ref(&self) -> (&T, usize) { let (ptr, tag) = self.decompose(); (&*ptr.as_ptr(), tag) } /// Decomposes the marked pointer, returning a *mutable* reference and the /// separated tag. /// /// # Safety /// /// The same safety caveats as with [`as_mut`][TagNonNull::as_mut] apply. #[inline] pub unsafe fn decompose_mut(&mut self) -> (&mut T, usize) { let (ptr, tag) = self.decompose(); (&mut *ptr.as_ptr(), tag) } #[inline] fn try_compose_inner(ptr: *mut T, tag: usize) -> Result { match ptr as usize & Self::POINTER_MASK { 0 => Err(Null(ptr as usize)), // SAFETY: the pointer's upper bits are non-zero, _ => Ok(unsafe { Self::new_unchecked(TagPtr::compose(ptr, tag)) }), } } } /********** impl Debug ****************************************************************************/ impl fmt::Debug for TagNonNull { impl_debug!("TagNonNull"); } /********** impl Pointer **************************************************************************/ impl fmt::Pointer for TagNonNull { impl_pointer!(); } /********** impl From (&T) ************************************************************************/ impl From<&T> for TagNonNull { #[inline] fn from(reference: &T) -> Self { Self { inner: NonNull::from(reference), _marker: PhantomData } } } /********** impl From (&mut T) ********************************************************************/ impl From<&mut T> for TagNonNull { #[inline] fn from(reference: &mut T) -> Self { Self { inner: NonNull::from(reference), _marker: PhantomData } } } /********** impl PartialEq ************************************************************************/ impl PartialEq for TagNonNull { impl_partial_eq!(); } /********** impl PartialOrd ***********************************************************************/ impl PartialOrd for TagNonNull { impl_partial_ord!(); } /********** impl Eq *******************************************************************************/ impl Eq for TagNonNull {} /********** impl Ord ******************************************************************************/ impl Ord for TagNonNull { impl_ord!(); } /********** impl Hash *****************************************************************************/ impl Hash for TagNonNull { impl_hash!(); } /********** impl TryFrom (*mut T) *****************************************************************/ impl TryFrom<*mut T> for TagNonNull { type Error = Null; #[inline] fn try_from(ptr: *mut T) -> Result { Self::try_compose_inner(ptr, 0) } } /********** impl TryFrom (*const T) ***************************************************************/ impl TryFrom<*const T> for TagNonNull { type Error = Null; #[inline] fn try_from(ptr: *const T) -> Result { Self::try_from(ptr as *mut _) } } /********** impl TryFrom (TagPtr) **************************************************************/ impl TryFrom> for TagNonNull { type Error = Null; #[inline] fn try_from(ptr: TagPtr) -> Result { Self::try_from(ptr.into_raw()) } } /********** impl TryFrom (NonNull) ****************************************************************/ impl TryFrom> for TagNonNull { type Error = Null; #[inline] fn try_from(ptr: NonNull) -> Result { Self::try_from(ptr.as_ptr()) } } #[cfg(test)] mod tests { use core::ptr::NonNull; use crate::Null; type TagNonNull = crate::TagNonNull; #[test] fn test_dangling() { assert_eq!(TagNonNull::dangling().into_raw(), NonNull::dangling()); #[repr(align(64))] struct Alignment64; assert_eq!(crate::TagNonNull::::dangling().into_usize(), 64); } #[test] fn test_try_compose() { let reference = &1; let ptr = NonNull::from(reference); let res = TagNonNull::try_compose(ptr, 0b11).map(|ptr| ptr.decompose()); assert_eq!(res, Ok((ptr, 0b11))); let dangling = NonNull::dangling(); let res = TagNonNull::try_compose(dangling, 0).map(|ptr| ptr.decompose()); assert_eq!(res, Ok((dangling, 0))); let ptr = NonNull::new(0b11 as *mut i32).unwrap(); let res = TagNonNull::try_compose(ptr, 0b11); assert_eq!(res, Err(Null(0b11))); } } tagptr-0.2.0/src/imp/ptr.rs000064400000000000000000000407120072674642500137070ustar 00000000000000use core::{ cmp, fmt, hash::{Hash, Hasher}, marker::PhantomData, ptr::{self, NonNull}, }; use crate::{TagNonNull, TagPtr}; /********** impl Clone ****************************************************************************/ impl Clone for TagPtr { impl_clone!(); } /********** impl Copy *****************************************************************************/ impl Copy for TagPtr {} /********** impl inherent *************************************************************************/ impl TagPtr { doc_comment! { doc_tag_bits!(), pub const TAG_BITS: usize = N; } doc_comment! { doc_tag_mask!(), pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS); } doc_comment! { doc_ptr_mask!(), pub const POINTER_MASK: usize = !Self::TAG_MASK; } doc_comment! { doc_null!(), /// /// # Examples /// /// ``` /// use core::ptr; /// /// type TagPtr = tagptr::TagPtr; /// /// let ptr = TagPtr::null(); /// assert_eq!(ptr.decompose(), (ptr::null_mut(), 0)); /// ``` #[inline] pub const fn null() -> Self { Self::new(ptr::null_mut()) } } doc_comment! { doc_new!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::new(reference); /// assert_eq!(ptr.decompose(), (reference as *mut _, 0)); /// ``` #[inline] pub const fn new(ptr: *mut T) -> Self { Self { inner: ptr, _marker: PhantomData } } } doc_comment! { doc_from_usize!(), /// /// # Examples /// /// ``` /// use core::ptr; /// /// type TagPtr = tagptr::TagPtr; /// /// let ptr = TagPtr::from_usize(0b11); /// assert_eq!(ptr.decompose(), (ptr::null_mut(), 0b11)); /// ``` #[inline] pub const fn from_usize(val: usize) -> Self { Self::new(val as _) } } doc_comment! { doc_into_raw!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let ptr = TagPtr::from_usize(0b11); /// assert_eq!(ptr.into_raw(), 0b11 as *mut _); /// ``` #[inline] pub const fn into_raw(self) -> *mut T { self.inner } } doc_comment! { doc_cast!(), pub const fn cast(self) -> TagPtr { TagPtr { inner: self.inner.cast(), _marker: PhantomData } } } doc_comment! { doc_into_usize!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let ptr = TagPtr::from_usize(0b11); /// assert_eq!(ptr.into_usize(), 0b11); /// ``` #[inline] pub fn into_usize(self) -> usize { self.inner as usize } } doc_comment! { doc_compose!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let raw = &1 as *const i32 as *mut i32; /// let ptr = TagPtr::compose(raw, 0b11); /// assert_eq!(ptr.decompose(), (raw, 0b11)); /// // excess bits are silently truncated /// let ptr = TagPtr::compose(raw, 0b101); /// assert_eq!(ptr.decompose(), (raw, 0b01)); /// ``` #[inline] pub fn compose(ptr: *mut T, tag: usize) -> Self { Self::new(crate::compose::(ptr, tag)) } } /// Returns `true` if the marked pointer is `null`. /// /// # Examples /// /// ``` /// use core::ptr; /// /// type TagPtr = tagptr::TagPtr; /// /// let ptr = TagPtr::compose(ptr::null_mut(), 0b11); /// assert!(ptr.is_null()); /// ``` #[inline] pub fn is_null(self) -> bool { self.decompose_ptr().is_null() } doc_comment! { doc_clear_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b11); /// /// assert_eq!(ptr.clear_tag().decompose(), (reference as *mut _, 0)); /// ``` #[inline] pub fn clear_tag(self) -> Self { Self::new(self.decompose_ptr()) } } doc_comment! { doc_split_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b11); /// /// assert_eq!(ptr.split_tag(), (TagPtr::new(reference), 0b11)); /// ``` #[inline] pub fn split_tag(self) -> (Self, usize) { let (ptr, tag) = self.decompose(); (Self::new(ptr), tag) } } doc_comment! { doc_set_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b11); /// /// assert_eq!(ptr.set_tag(0b01).decompose(), (reference as *mut _, 0b01)); /// ``` #[inline] pub fn set_tag(self, tag: usize) -> Self { let ptr = self.decompose_ptr(); Self::compose(ptr, tag) } } doc_comment! { doc_update_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b11); /// /// assert_eq!(ptr.update_tag(|tag| tag - 1).decompose(), (reference as *mut _, 0b10)); /// ``` #[inline] pub fn update_tag(self, func: impl FnOnce(usize) -> usize) -> Self { let (ptr, tag) = self.decompose(); Self::compose(ptr, func(tag)) } } doc_comment! { doc_add_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b10); /// /// assert_eq!(ptr.add_tag(1).decompose(), (reference as *mut _, 0b11)); /// ``` #[inline] pub fn add_tag(self, value: usize) -> Self { Self::from_usize(self.into_usize().wrapping_add(value)) } } doc_comment! { doc_sub_tag!(), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &mut 1; /// let ptr = TagPtr::compose(reference, 0b10); /// /// assert_eq!(ptr.sub_tag(1).decompose(), (reference as *mut _, 0b01)); /// ``` #[inline] pub fn sub_tag(self, value: usize) -> Self { Self::from_usize(self.into_usize().wrapping_sub(value)) } } doc_comment! { doc_decompose!(), #[inline] pub fn decompose(self) -> (*mut T, usize) { (self.decompose_ptr(), self.decompose_tag()) } } doc_comment! { doc_decompose_ptr!(), #[inline] pub fn decompose_ptr(self) -> *mut T { crate::decompose_ptr::(self.inner as usize, Self::TAG_BITS) } } doc_comment! { doc_decompose_tag!(), #[inline] pub fn decompose_tag(self) -> usize { crate::decompose_tag::(self.inner as usize, Self::TAG_BITS) } } doc_comment! { doc_as_ref!("nullable"), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &1; /// let ptr = TagPtr::compose(reference as *const _ as *mut _, 0b11); /// /// unsafe { /// assert_eq!(ptr.as_ref(), Some(&1)); /// } /// ``` #[inline] pub unsafe fn as_ref<'a>(self) -> Option<&'a T> { self.decompose_ptr().as_ref() } } doc_comment! { doc_as_mut!("nullable", TagPtr), /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let mut val = 1; /// let ptr = TagPtr::compose(&mut val, 0b11); /// /// unsafe { /// assert_eq!(ptr.as_mut(), Some(&mut 1)); /// } /// ``` #[inline] pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { self.decompose_ptr().as_mut() } } /// Decomposes the marked pointer, returning an optional reference and the /// separated tag. /// /// # Safety /// /// The same safety caveats as with [`as_ref`][TagPtr::as_ref] apply. /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let reference = &1; /// let ptr = TagPtr::compose(reference as *const _ as *mut _, 0b11); /// /// unsafe { /// assert_eq!(ptr.decompose_ref(), (Some(&1), 0b11)); /// } /// ``` #[inline] pub unsafe fn decompose_ref<'a>(self) -> (Option<&'a T>, usize) { (self.as_ref(), self.decompose_tag()) } /// Decomposes the marked pointer, returning an optional *mutable* reference /// and the separated tag. /// /// # Safety /// /// The same safety caveats as with [`as_mut`][TagPtr::as_mut] apply. /// /// # Examples /// /// ``` /// type TagPtr = tagptr::TagPtr; /// /// let mut val = 1; /// let ptr = TagPtr::compose(&mut val, 0b11); /// /// unsafe { /// assert_eq!(ptr.decompose_mut(), (Some(&mut 1), 0b11)); /// } /// ``` #[inline] pub unsafe fn decompose_mut<'a>(self) -> (Option<&'a mut T>, usize) { (self.as_mut(), self.decompose_tag()) } } /********** impl Debug ****************************************************************************/ impl fmt::Debug for TagPtr { impl_debug!("TagPtr"); } /********** impl Default **************************************************************************/ impl Default for TagPtr { impl_default!(); } /********** impl From (*mut T) ********************************************************************/ impl From<*mut T> for TagPtr { #[inline] fn from(ptr: *mut T) -> Self { Self::new(ptr) } } /********** impl From (*const T) ******************************************************************/ impl From<*const T> for TagPtr { #[inline] fn from(ptr: *const T) -> Self { Self::new(ptr as _) } } /********** impl From (&T) ************************************************************************/ impl From<&T> for TagPtr { #[inline] fn from(reference: &T) -> Self { Self::from(reference as *const _) } } /********** impl From ((&T, usize)) ***************************************************************/ impl From<(&T, usize)> for TagPtr { #[inline] fn from((reference, tag): (&T, usize)) -> Self { Self::compose(reference as *const T as *mut T, tag) } } /********** impl From (&mut T) ********************************************************************/ impl From<&mut T> for TagPtr { #[inline] fn from(reference: &mut T) -> Self { Self::from(reference as *const _) } } /********** impl From ((&mut T, usize)) ***********************************************************/ impl From<(&mut T, usize)> for TagPtr { #[inline] fn from((reference, tag): (&mut T, usize)) -> Self { Self::compose(reference, tag) } } /********** impl From (NonNull) *******************************************************************/ impl From> for TagPtr { #[inline] fn from(ptr: NonNull) -> Self { Self::new(ptr.as_ptr()) } } /********** impl From (TagNonNull) *************************************************************/ impl From> for TagPtr { #[inline] fn from(ptr: TagNonNull) -> Self { ptr.into_marked_ptr() } } /********** impl PartialEq ************************************************************************/ impl PartialEq for TagPtr { impl_partial_eq!(); } /********** impl PartialOrd ***********************************************************************/ impl PartialOrd for TagPtr { impl_partial_ord!(); } /********** impl Pointer **************************************************************************/ impl fmt::Pointer for TagPtr { impl_pointer!(); } /********** impl Eq *******************************************************************************/ impl Eq for TagPtr {} /********** impl Ord ******************************************************************************/ impl Ord for TagPtr { impl_ord!(); } /********** impl Hash *****************************************************************************/ impl Hash for TagPtr { impl_hash!(); } #[cfg(test)] mod tests { type TagPtr = crate::TagPtr; #[test] fn test_debug() { let reference = &mut 1; let ptr = TagPtr::compose(reference, 0b11); assert_eq!( std::format!("{:?}", ptr), std::format!("TagPtr {{ ptr: {:0p}, tag: {} }}", reference as *mut _, 0b11) ); } #[test] fn test_cast() { type ErasedPtr = crate::TagPtr<(), 2>; let reference = &mut 1; let ptr = TagPtr::compose(reference, 0b11); let cast: ErasedPtr = ptr.cast().set_tag(0b10); assert_eq!(cast.into_usize(), reference as *mut _ as usize | 0b10); assert_eq!(cast.cast(), TagPtr::compose(reference, 0b10)); } #[test] fn test_from_usize() { let reference = &1; let ptr = TagPtr::from_usize(reference as *const i32 as usize | 0b1); assert_eq!(ptr.decompose(), (reference as *const _ as *mut _, 0b1)); } #[test] fn test_compose() { let reference = &mut 1; let ptr1 = TagPtr::compose(reference, 0b11); let ptr2 = TagPtr::compose(reference, 0b111); // compose silently truncates excess bits, so ptr1 and ptr2 are identical assert_eq!(ptr1, ptr2); assert_eq!(ptr2.decompose(), (reference as *mut _, 0b11)); } #[test] fn test_set_tag() { let reference = &mut 1; let ptr = TagPtr::compose(reference, 0b11); // set_tag must silently truncate excess tag bits assert_eq!(ptr, ptr.set_tag(0b111)); } #[test] fn test_overflow_tag() { let reference = &mut 1; let ptr = TagPtr::compose(reference, 0b11); // add must cause overflow (corrupt the pointer) assert_eq!(ptr.add_tag(1).into_usize(), reference as *mut _ as usize + 0b11 + 1); // update must only overflow the tag bits assert_eq!(ptr.update_tag(|tag| tag + 1).decompose(), (reference as *mut _, 0)); } #[test] fn test_underflow_tag() { let reference = &mut 1; let ptr = TagPtr::new(reference); // sub_tag must underflow the entire pointer assert_eq!(ptr.sub_tag(1).into_usize(), reference as *mut _ as usize - 1); // update_tag must only underflow the tag value assert_eq!( ptr.update_tag(|tag| tag.wrapping_sub(1)).decompose(), (reference as *mut _, 0b11) ); } #[test] fn test_erase() { #[repr(align(64))] struct Aligned64(i32); let reference = &Aligned64(1); let ptr = crate::TagPtr::::from((reference, 55)); let mut erased: crate::TagPtr<(), 6> = ptr.cast(); erased = erased.update_tag(|tag| tag + 3); let ptr: crate::TagPtr = erased.cast(); assert_eq!(ptr.decompose(), (reference as *const _ as *mut _, 58)); } } tagptr-0.2.0/src/lib.rs000064400000000000000000000211310072674642500130550ustar 00000000000000//! Strongly typed pointers with reserved space for storing additional bit //! patterns within the same memory word. //! //! # Motivation //! //! In low-level concurrent programming (synchronization primitives, //! lock-free data structures, etc) it is often required to store additional //! state information (tags) alongside pointers to objects in memory, since //! most atomic CPU instructions operate on pointer-wide memory words. //! The marked pointer types provided by this crate encapsulate the logic and //! pointer arithmetic for composing (creating), decomposing and mutating //! such pointers and tag values. //! //! # Tag Bits and Type Alignment //! //! The possible space for storing tag bits in a pointer is determined by the //! alignment of the pointed-to type, as long as the pointer is well-aligned //! (e.g., not packed). //! For instance, pointers to types with an alignment of 2 (2^1) bytes (e.g., //! `u16`) never use the first of their lower bits (i.e., it is always zero), //! pointers to types with an alignment of 8 (2^3) bytes such as `u64` never //! use their 3 lowest bits and so on. //! Great care must be taken at all times to avoid over- or underflows in the //! usually highly restricted range of valid tags for common tag sizes when //! doing arithmetic operations. //! Any operations resulting in tag values outside of their valid range will //! invariably corrupt the bits representing the pointer and hence invoke //! undefined behavior when dereferencing these pointers. //! //! Constructing a type such as `TagPtr` is hence usually a user error, //! since a pointer to a `u64` has only 3 unused bits. //! The resulting type would consider the first actual bit of the pointer to be //! part of its tag and return a potentially corrupted pointer in methods such //! as [`decompose`][TagPtr::decompose]. //! The [`has_sufficient_alignment`] and [`assert_alignment`] functions can be //! used to explicitly check for or assert this property. //! There is, however, one exception where using an otherwise ill-formed tag //! pointer type is valid: //! After composing a well-formed tag pointer instance (e.g., `TagPtr`) //! it is valid to [`cast`][TagPtr::cast] it to a type with a smaller alignment //! and the same number of tag bits such as `TagPtr<(), 3>` for the purpose of //! type-erasure. //! //! # Example //! //! Storing a boolean status flag alongside the pointer to a mutable `u64`: //! //! ``` //! type TagPtr = tagptr::TagPtr; //! //! let mut val = 0xCAFE; //! let is_ok = true; //! //! let ptr = TagPtr::compose(&mut val, is_ok as usize); //! let (reference, tag) = unsafe { ptr.decompose_mut() }; //! assert_eq!(reference, Some(&mut 0xCAFE)); //! assert_eq!(tag == 1, true); //! ``` #![no_std] #[cfg(test)] extern crate std; #[macro_use] mod macros; mod imp { mod atomic; mod non_null; mod ptr; } use core::{marker::PhantomData, mem, ptr::NonNull, sync::atomic::AtomicUsize}; // ************************************************************************************************* // AtomicTagPtr (impl in "imp/atomic.rs") // ************************************************************************************************* /// A raw pointer type which can be safely shared between threads and which can /// use up to `N` of its lower bits to store additional information (the *tag*). /// /// This type has the same in-memory representation as a `*mut T`. /// It is mostly identical to [`AtomicPtr`][atomic], except that all of its /// methods take or return a [`TagPtr`] instead of `*mut T`. /// See the [crate][crate] level documentation for restrictions on the value of /// `N`. /// /// [atomic]: core::sync::atomic::AtomicPtr #[repr(transparent)] pub struct AtomicTagPtr { inner: AtomicUsize, _marker: PhantomData<*mut T>, } // ************************************************************************************************* // TagPtr (impl in "imp/ptr.rs") // ************************************************************************************************* /// A raw, unsafe pointer type like `*mut T` which can use up to `N` of its /// lower bits to store additional information (the *tag*). /// /// This type has the same in-memory representation as a `*mut T`. /// See the [crate][crate] level documentation for restrictions on the value of /// `N`. #[repr(transparent)] pub struct TagPtr { inner: *mut T, _marker: PhantomData<()>, // the "fake" marker allows to use the same macro for all pointers } // ************************************************************************************************* // TagNonNull (impl in "imp/non_null.rs") // ************************************************************************************************* /// A non-nullable tagged raw pointer type similar to [`NonNull`] which can use /// up to `N` of its lower bits to store additional information (the *tag*). /// /// This type has the same in-memory representation as a `NonNull`. /// See the [crate][crate] level documentation for restrictions on the value of /// `N`. /// /// # Invariants /// /// This type imposes stricter construction requirements than a regular /// [`NonNull`], since it requires the pointer to be non-null even after its `N` /// tag bits are stripped off as well. /// For instance, the value `0x1` can be used to construct a valid (but not /// dereferencable) [`NonNull`] since it is not zero, but it can not be used to /// construct e.g. a valid `TagNonNull`, since its only non-zero bit /// would be considered to represent the tag and the value of the pointer would /// be 0. /// For valid, well-aligned pointers, this is usually not a concern. #[repr(transparent)] pub struct TagNonNull { inner: NonNull, _marker: PhantomData<()>, } // ************************************************************************************************* // Null // ************************************************************************************************* /// A type representing a `null` pointer with potential tag bits. /// /// The contained `usize` is the value of the pointer's tag. #[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)] #[repr(transparent)] pub struct Null(pub usize); /********** impl inherent *************************************************************************/ impl Null { /// Returns the tag value. #[inline] pub fn tag(self) -> usize { self.0 } } /********** public functions **********************************************************************/ /// Returns `true` if the alignment of `T` is large enough so a pointer to an /// instance may store the given number of `tag_bits`. #[inline] pub const fn has_sufficient_alignment(tag_bits: usize) -> bool { lower_bits::() >= tag_bits } /// Asserts that the alignment of `U` is large enough so a pointer to an /// instance may store `N` tag bits. /// /// # Panics /// /// This function panics if the alignment of `U` is insufficient for storing /// `N` tag bits. #[inline] pub fn assert_alignment() { assert!( has_sufficient_alignment::(N), "the respective type has insufficient alignment for storing N tag bits" ); } /********** helper functions **********************************************************************/ /// Composes the given `ptr` with `tag` and returns the composed marked pointer /// as a raw `*mut T`. /// /// # Panics /// /// Panics in *debug builds only* if `ptr` is not well aligned, i.e., if it /// contains any bits in its lower bits reserved for the tag value. #[inline(always)] fn compose(ptr: *mut T, tag: usize) -> *mut T { debug_assert_eq!(ptr as usize & mark_mask(N), 0, "tag bits in raw pointer must be zeroed"); ((ptr as usize) | (mark_mask(N) & tag)) as *mut _ } /// Decomposes the integer representation of a `ptr` for a given number /// of `tag_bits` into only a raw pointer stripped of its tag. #[inline(always)] const fn decompose_ptr(ptr: usize, tag_bits: usize) -> *mut T { (ptr & !mark_mask(tag_bits)) as *mut _ } /// Decomposes the integer representation of a `ptr` for a given number /// of `tag_bits` into only a separated tag value. #[inline(always)] const fn decompose_tag(ptr: usize, tag_bits: usize) -> usize { ptr & mark_mask(tag_bits) } /// Returns the (alignment-dependent) number of unused lower bits in a pointer /// to type `T`. #[inline(always)] const fn lower_bits() -> usize { mem::align_of::().trailing_zeros() as usize } /// Returns the bit-mask for the lower bits containing the tag value. #[inline(always)] const fn mark_mask(tag_bits: usize) -> usize { (1 << tag_bits) - 1 } tagptr-0.2.0/src/macros/doc.rs000064400000000000000000000247020072674642500143470ustar 00000000000000/// All macros for generating documentation. /// A macro for generating arbitrary documented code items macro_rules! doc_comment { ($docs:expr, $($item:tt)*) => { #[doc = $docs] $($item)* }; } /********** macros for generating constants docs **************************************************/ /// A macro for generating the docs for the `TAG_BITS` constant. macro_rules! doc_tag_bits { () => { "The number of available tag bits for this type." }; } /// A macro for generating the docs for the `TAG_MASK` constant. macro_rules! doc_tag_mask { () => { "The bitmask for the lower bits available for storing the tag value." }; } /// A macro for generating the docs for the `PTR_MASK` constants. macro_rules! doc_ptr_mask { () => { "The bitmask for the (higher) bits for storing the pointer itself." }; } /********** macros for generating function docs ***************************************************/ macro_rules! doc_null { () => { "Creates a new `null` pointer." }; } macro_rules! doc_new { () => { "Creates a new unmarked pointer." }; } macro_rules! doc_from_usize { () => { "Creates a new pointer from the numeric (integer) representation of a \ potentially marked pointer." }; } macro_rules! doc_into_raw { () => { "Returns the internal representation of the pointer *as is*, i.e. any \ potential tag value is **not** stripped." }; } macro_rules! doc_into_usize { () => { "Returns the numeric (integer) representation of the pointer with its \ tag value." }; } macro_rules! doc_cast { () => { "Casts to a pointer of another type." }; } macro_rules! doc_compose { () => { "Composes a new marked pointer from a raw `ptr` and a `tag` value.\n\n\ The supplied `ptr` is assumed to be well-aligned (i.e. has no tag bits \ set) and calling this function may lead to unexpected results when \ this is not the case." }; } macro_rules! doc_clear_tag { ("non-null" $example_type_path:path) => { concat!( doc_clear_tag!(), "# Examples\n\n\ ```\nuse core::ptr::NonNull;\n\n\ type TagNonNull = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagNonNull::compose(NonNull::from(reference), 0b11);\n\ assert_eq!(ptr.clear_tag(), TagNonNull::from(reference));\n```" ) }; ($example_type_path:path) => { concat!( doc_clear_tag!(), "# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagPtr = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagPtr::compose(reference, 0b11);\n\ assert_eq!(ptr.clear_tag(), TagPtr::new(reference));\n```" ) }; () => { "Clears the marked pointer's tag value.\n\n" }; } macro_rules! doc_split_tag { ("non-null" $example_type_path:path) => { concat!( doc_split_tag!(), "# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagNonNull = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagNonNull::compose(NonNull::from(reference), 0b11);\n\ assert_eq!(ptr.split_tag(), (TagNonNull::from(reference), 0b11));\n```" ) }; ($example_type_path:path) => { concat!( doc_split_tag!(), "# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagPtr = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagPtr::compose(reference, 0b11);\n\ assert_eq!(ptr.split_tag(), (TagPtr::new(reference), 0b11));\n```" ) }; () => { "Splits the tag value from the marked pointer, returning both the cleared pointer and the \ separated tag value.\n\n" }; } macro_rules! doc_set_tag { ("non-null" $example_type_path:path) => { concat!( doc_set_tag!(), "\n\n# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagNonNull = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagNonNull::compose(NonNull::from(reference), 0b11);\n\ assert_eq!(ptr.set_tag(0b10).decompose(), (NonNull::from(reference), 0b10));\n```" ) }; ($example_type_path:path) => { concat!( doc_set_tag!(), "\n\n# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagPtr = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagPtr::compose(reference, 0b11);\n\ assert_eq!(ptr.set_tag(0b10).decompose(), (reference as *mut _, 0b10));\n```" ) }; () => { "Sets the marked pointer's tag value to `tag` and overwrites any previous value." }; } macro_rules! doc_update_tag { ("non-null" $example_type_path:path) => { concat!( doc_update_tag!(), "\n\n# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagNonNull = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1;\n\ let ptr = TagNonNull::compose(reference, 0b11);\n\ assert_eq!(ptr.update_tag(|tag| tag - 2).decompose(), (NonNull::from(reference), 0b01));\n```" ) }; ($example_type_path:path) => { concat!( doc_update_tag!(), "\n\n# Examples\n\n\ ```\nuse core::ptr;\n\n\ type TagPtr = ", stringify!($example_type_path), ";\n\n\ let reference = &mut 1; let ptr = TagPtr::compose(reference, 0b11);\n\ let ptr = ptr.update_tag(|tag| tag - 1);\n\ assert_eq!(ptr.decompose(), (reference as *mut _, 0b10));\n```" ) }; () => { "Updates the marked pointer's tag value to the result of `func`, which is called with the \ current tag value." }; } macro_rules! doc_add_tag { () => { "Adds `value` to the current tag *without* regard for the previous \ value.\n\n\ This method does not perform any checks so it may silently overflow \ the tag bits, result in a pointer to a different value, a null pointer \ or an unaligned pointer." }; } macro_rules! doc_sub_tag { () => { "Subtracts `value` from the current tag *without* regard for the \ previous value.\n\n\ This method does not perform any checks so it may silently overflow \ the tag bits, result in a pointer to a different value, a null \ pointer or an unaligned pointer." }; } macro_rules! doc_decompose { () => { "Decomposes the marked pointer, returning the raw pointer and the \ separated tag value." }; } macro_rules! doc_decompose_ptr { () => { "Decomposes the marked pointer, returning only the separated raw \ pointer." }; } macro_rules! doc_decompose_non_null { () => { "Decomposes the marked pointer, returning only the separated raw \ [`NonNull`] pointer." }; } macro_rules! doc_decompose_tag { () => { "Decomposes the marked pointer, returning only the separated tag value." }; } macro_rules! doc_as_ref_or_mut { ("safety") => { "When calling this method, you have to ensure that *either* the \ pointer is `null` *or* all of the following is true:\n\n\ - it is properly aligned\n\ - it must point to an initialized instance of T; in particular, \ the pointer must be \"de-referencable\" in the sense defined \ [here].\n\n\ This applies even if the result of this method is unused! (The \ part about being initialized is not yet fully decided, but until \ it is the only safe approach is to ensure that they are indeed \ initialized.)\n\n\ Additionally, the lifetime `'a` returned is arbitrarily chosen and \ does not necessarily reflect the actual lifetime of the data. \ *You* must enforce Rust's aliasing rules. \ In particular, for the duration of this lifetime, the memory this \ pointer points to must not get accessed (read or written) through \ any other pointer.\n\n\ [here]: [std::ptr]" }; ($ret_str:expr) => { concat!( "Decomposes the marked pointer, returning ", $ret_str, " reference and discarding the tag value." ) }; } macro_rules! doc_as_ref { (@inner, $ret_str:expr) => { concat!( doc_as_ref_or_mut!($ret_str), "\n\n# Safety\n\ While this method and its mutable counterpart are useful for \ null-safety, it is important to note that this is still an unsafe \ operation because the returned value could be pointing to invalid \ memory.\n\n", doc_as_ref_or_mut!("safety") ) }; ("nullable") => { doc_as_ref!(@inner, "an optional") }; ("non-nullable") => { doc_as_ref!(@inner, "a") }; } macro_rules! doc_as_mut { (@inner, $self_ident:ident, $ret_str:expr) => { concat!( doc_as_ref_or_mut!($ret_str), "\n\n# Safety\n\ As with [`as_ref`][", stringify!($self_ident), "::as_ref], this is unsafe because it cannot verify the validity \ of the returned pointer, nor can it ensure that the lifetime `'a` \ returned is indeed a valid lifetime for the contained data.\n\n", doc_as_ref_or_mut!("safety") ) }; ("nullable", $self_ident:ident) => { doc_as_mut!(@inner, $self_ident, "an optional *mutable*") }; ("non-nullable", $self_ident:ident) => { doc_as_mut!(@inner, $self_ident, "a *mutable*") }; } macro_rules! doc_atomic_new { () => { "Creates a new atomic marked pointer." }; } macro_rules! doc_atomic_into_inner { () => { "Consumes the atomic marked pointer and returns its contained value.\n\n\ This is safe because passing `self` by value guarantees no other \ threads are concurrently accessing the atomic pointer." }; } tagptr-0.2.0/src/macros.rs000064400000000000000000000027460072674642500136060ustar 00000000000000#[macro_use] mod doc; macro_rules! impl_clone { () => { #[inline] fn clone(&self) -> Self { Self { inner: self.inner, _marker: PhantomData } } }; } macro_rules! impl_debug { ($type_name:expr) => { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (ptr, tag) = self.decompose(); f.debug_struct($type_name).field("ptr", &ptr).field("tag", &tag).finish() } }; } macro_rules! impl_default { () => { #[inline] fn default() -> Self { Self::null() } }; } macro_rules! impl_pointer { () => { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&self.decompose_ptr(), f) } }; } macro_rules! impl_partial_eq { () => { #[inline] fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) } }; } macro_rules! impl_partial_ord { () => { #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.inner.partial_cmp(&other.inner) } }; } macro_rules! impl_ord { () => { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { self.inner.cmp(&other.inner) } }; } macro_rules! impl_hash { () => { #[inline] fn hash(&self, state: &mut H) { self.inner.hash(state) } }; }