From 453d2ad0f09a6dbda7d514f46af1a6f4b543473b Mon Sep 17 00:00:00 2001 From: LI Qing Date: Tue, 11 Apr 2023 02:47:09 -0400 Subject: [PATCH] Add keyable-arc --- services/libs/keyable-arc/Cargo.toml | 9 + services/libs/keyable-arc/src/lib.rs | 362 +++++++++++++++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 services/libs/keyable-arc/Cargo.toml create mode 100644 services/libs/keyable-arc/src/lib.rs diff --git a/services/libs/keyable-arc/Cargo.toml b/services/libs/keyable-arc/Cargo.toml new file mode 100644 index 000000000..38258725d --- /dev/null +++ b/services/libs/keyable-arc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "keyable-arc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + diff --git a/services/libs/keyable-arc/src/lib.rs b/services/libs/keyable-arc/src/lib.rs new file mode 100644 index 000000000..edee39205 --- /dev/null +++ b/services/libs/keyable-arc/src/lib.rs @@ -0,0 +1,362 @@ +//! Same as the standard `Arc`, except that it can be used as the key type of a hash table. +//! +//! # Motivation +//! +//! A type `K` is _keyable_ if it can be used as the key type for a hash map. Specifically, +//! according to the document of `std::collections::HashMap`, the type `K` must satisfy +//! the following properties. +//! +//! 1. It implements the `Eq` and `Hash` traits. +//! 2. The two values of `k1` and `k2` of type `K` equal to each other, +//! if and only if their hash values equal to each other. +//! 3. The hashes of a value of `k` of type `K` cannot change while it +//! is in a map. +//! +//! Sometimes we want to use `Arc` as the key type for a hash map but cannot do so +//! since `T` does not satisfy the properties above. For example, a lot of types +//! do not or cannot implemennt the `Eq` trait. This is when `KeyableArc` can come +//! to your aid. +//! +//! # Overview +//! +//! For any type `T`, `KeyableArc` satisfies all the properties to be keyable. +//! This can be achieved easily and efficiently as we can simply use the address +//! of the data (of `T` type) of a `KeyableArc` object in the heap to determine the +//! equality and hash of the `KeyableArc` object. As the address won't change for +//! an immutable `KeyableArc` object, the hash and equality also stay the same. +//! +//! This crate is `#[no_std]` compatible, but requires the `alloc` crate. +//! +//! # Usage +//! +//! Here is a basic example to how that `KeyableArc` is keyable even when `T` +//! is not. +//! +//! ```rust +//! use std::collections::HashMap; +//! use std::sync::Arc; +//! use keyable_arc::KeyableArc; +//! +//! struct Dummy; // Does not implement Eq and Hash +//! +//! let map: HashMap, String> = HashMap::new(); +//! ``` +//! +//! `KeyableArc` is a reference counter-based smart pointer, just like `Arc`. +//! So you can use `KeyableArc` the same way you would use `Arc`. +//! +//! ```rust +//! use std::sync::atomic::{AtomicU64, Ordering::Relaxed}; +//! use keyable_arc::KeyableArc; +//! +//! let key_arc0 = KeyableArc::new(AtomicU64::new(0)); +//! let key_arc1 = key_arc0.clone(); +//! assert!(key_arc0.load(Relaxed) == 0 && key_arc1.load(Relaxed) == 0); +//! +//! key_arc0.fetch_add(1, Relaxed); +//! assert!(key_arc0.load(Relaxed) == 1 && key_arc1.load(Relaxed) == 1); +//! ``` +//! +//! # Differences from `Arc` +//! +//! Notice how `KeyableArc` differs from standard smart pointers in determining equality? +//! Two `KeyableArc` objects are considered different even when their data have the same +//! value. +//! +//! ```rust +//! use keyable_arc::KeyableArc; +//! +//! let key_arc0 = KeyableArc::new(0); +//! let key_arc1 = key_arc0.clone(); +//! assert!(key_arc0 == key_arc1); +//! assert!(*key_arc0 == *key_arc1); +//! +//! let key_arc1 = KeyableArc::new(0); +//! assert!(key_arc0 != key_arc1); +//! assert!(*key_arc0 == *key_arc1); +//! ``` +//! +//! `KeyableArc` is simply a wrapper of `Arc. So converting between them +//! through the `From` and `Into` traits is zero cost. +//! +//! ```rust +//! use std::sync::Arc; +//! use keyable_arc::KeyableArc; +//! +//! let key_arc: KeyableArc = Arc::new(0).into(); +//! let arc: Arc = KeyableArc::new(0).into(); +//! ``` +//! +//! # The weak version +//! +//! `KeyableWeak` is the weak version of `KeyableArc`, just like `Weak` is +//! that of `Arc`. And of course, `KeyableWeak` is also _keyable_ for any +//! type `T`. + +// TODO: Add `KeyableBox` or other keyable versions of smart pointers. +// If this is needed in the future, this crate should be renamed to `keyable`. + +// TODO: Add the missing methods offered by `Arc` or `Weak` but not their +// keyable counterparts. + +#![cfg_attr(not(test), no_std)] +#![feature(coerce_unsized)] +#![feature(unsize)] +#![forbid(unsafe_code)] + +extern crate alloc; + +use alloc::sync::{Arc, Weak}; +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::convert::AsRef; +use core::fmt; +use core::hash::{Hash, Hasher}; +use core::marker::Unsize; +use core::ops::{CoerceUnsized, Deref}; + +/// Same as the standard `Arc`, except that it can be used as the key type of a hash table. +#[repr(transparent)] +pub struct KeyableArc(Arc); + +impl KeyableArc { + /// Constructs a new instance of `KeyableArc`. + #[inline] + pub fn new(data: T) -> Self { + Self(Arc::new(data)) + } +} + +impl KeyableArc { + /// Returns a raw pointer to the object `T` pointed to by this `KeyableArc`. + #[inline] + pub fn as_ptr(this: &Self) -> *const T { + Arc::as_ptr(&this.0) + } + + /// Creates a new `KeyableWeak` pointer to this allocation. + pub fn downgrade(this: &Self) -> KeyableWeak { + Arc::downgrade(&this.0).into() + } +} + +impl Deref for KeyableArc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &*self.0 + } +} + +impl AsRef for KeyableArc { + #[inline] + fn as_ref(&self) -> &T { + &**self + } +} + +impl Borrow for KeyableArc { + #[inline] + fn borrow(&self) -> &T { + &**self + } +} + +impl From> for KeyableArc { + #[inline] + fn from(arc: Arc) -> Self { + Self(arc) + } +} + +impl Into> for KeyableArc { + #[inline] + fn into(self) -> Arc { + self.0 + } +} + +impl PartialEq for KeyableArc { + fn eq(&self, other: &Self) -> bool { + Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0) + } +} + +impl Eq for KeyableArc {} + +impl PartialOrd for KeyableArc { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Arc::as_ptr(&self.0).cmp(&Arc::as_ptr(&other.0))) + } +} + +impl Ord for KeyableArc { + fn cmp(&self, other: &Self) -> Ordering { + Arc::as_ptr(&self.0).cmp(&Arc::as_ptr(&other.0)) + } +} + +impl Hash for KeyableArc { + fn hash(&self, s: &mut H) { + Arc::as_ptr(&self.0).hash(s) + } +} + +impl Clone for KeyableArc { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl fmt::Debug for KeyableArc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +//========================================================= +// The weak version +//========================================================= + +/// The weak counterpart of `KeyableArc`, similar to `Weak`. +/// +/// `KeyableWeak` is also _keyable_ for any type `T` just like +/// `KeyableArc`. +#[repr(transparent)] +pub struct KeyableWeak(Weak); + +impl KeyableWeak { + /// Constructs a new `KeyableWeak`, without allocating any memory. + /// Calling `upgrade` on the return value always gives `None`. + #[inline] + pub fn new() -> Self { + Self(Weak::new()) + } + + /// Returns a raw pointer to the object `T` pointed to by this `KeyableWeak`. + /// + /// The pointer is valid only if there are some strong references. + /// The pointer may be dangling, unaligned or even null otherwise. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.0.as_ptr() + } +} + +impl KeyableWeak { + /// Attempts to upgrade the Weak pointer to an Arc, + /// delaying dropping of the inner value if successful. + /// + /// Returns None if the inner value has since been dropped. + #[inline] + pub fn upgrade(&self) -> Option> { + self.0.upgrade().map(|arc| arc.into()) + } + + /// Gets the number of strong pointers pointing to this allocation. + #[inline] + pub fn strong_count(&self) -> usize { + self.0.strong_count() + } + + /// Gets the number of weak pointers pointing to this allocation. + #[inline] + pub fn weak_count(&self) -> usize { + self.0.weak_count() + } +} + +impl PartialEq for KeyableWeak { + fn eq(&self, other: &Self) -> bool { + self.0.as_ptr() == other.0.as_ptr() + } +} + +impl Eq for KeyableWeak {} + +impl PartialOrd for KeyableWeak { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.0.as_ptr().cmp(&other.0.as_ptr())) + } +} + +impl Ord for KeyableWeak { + fn cmp(&self, other: &Self) -> Ordering { + self.0.as_ptr().cmp(&other.0.as_ptr()) + } +} + +impl Hash for KeyableWeak { + fn hash(&self, s: &mut H) { + self.0.as_ptr().hash(s) + } +} + +impl From> for KeyableWeak { + #[inline] + fn from(weak: Weak) -> Self { + Self(weak) + } +} + +impl Into> for KeyableWeak { + #[inline] + fn into(self) -> Weak { + self.0 + } +} + +impl fmt::Debug for KeyableWeak { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(KeyableWeak)") + } +} + +// Enabling type coercing, e.g., converting from `KeyableArc` to `KeyableArc`, +// where `T` implements `S`. +impl, U: ?Sized> CoerceUnsized> for KeyableArc {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn downgrade_and_upgrade() { + let arc = KeyableArc::new(1); + let weak = KeyableArc::downgrade(&arc); + assert!(arc.clone() == weak.upgrade().unwrap()); + assert!(weak == KeyableArc::downgrade(&arc)); + } + + #[test] + fn debug_format() { + println!("{:?}", KeyableArc::new(1u32)); + println!("{:?}", KeyableWeak::::new()); + } + + #[test] + fn use_as_key() { + use std::collections::HashMap; + + let mut map: HashMap, u32> = HashMap::new(); + let key = KeyableArc::new(1); + let val = 1; + map.insert(key.clone(), val); + assert!(map.get(&key) == Some(&val)); + assert!(map.remove(&key) == Some(val)); + assert!(map.keys().count() == 0); + } + + #[test] + fn as_trait_object() { + trait DummyTrait {} + struct DummyStruct; + impl DummyTrait for DummyStruct {} + + let arc_struct = KeyableArc::new(DummyStruct); + let arc_dyn0: KeyableArc = arc_struct.clone(); + let arc_dyn1: KeyableArc = arc_struct.clone(); + assert!(arc_dyn0 == arc_dyn1); + } +}