From e1860aee48703424cbfeb3785ae70c1dcc19b3ff Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 27 Feb 2025 16:40:20 +1100 Subject: [PATCH 1/2] Add `no_std` and `no_alloc` support --- Cargo.toml | 28 +++++++++---- src/arc.rs | 82 +++++++++++++++++++++--------------- src/arc_dst.rs | 94 +++++++++++++++++++++++------------------- src/arena.rs | 95 +++++++++++++++++++++++++++--------------- src/boxedset.rs | 20 +++++---- src/container.rs | 54 ++++++++++++++++++++---- src/intern.rs | 105 ++++++++++++++++++++++++++--------------------- src/lib.rs | 7 ++++ src/typearena.rs | 74 ++++++++++++++++++--------------- 9 files changed, 348 insertions(+), 211 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2bb1ec1..a4da7da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,10 @@ categories = ["caching", "memory-management", "concurrency"] keywords = ["hash", "interning", "hashconsing", "caching", "interner"] rust-version = "1.70" +# Ensures dev-dependencies to enable features on standard dependencies unless compiling examples, +# tests, etc. +resolver = "2" + [badges] appveyor = { repository = "droundy/internment" } @@ -31,25 +35,33 @@ once_cell = { version = "1.4", optional = true } tinyset = { version = "0.4.2", optional = true } memorable-wordlist = { version = "0.1.7", optional = true } hashbrown = { version = "0.15.0" } -serde = { version = "1.0", optional = true } -deepsize = { version = "0.2.0", optional = true } +serde = { version = "1.0", optional = true, default-features = false } +deepsize = { version = "0.2.0", optional = true, default-features = false } arc-interner = { version = "0.7", optional = true } append-only-vec = { version = "0.1.2", optional = true } +spin = { version = "0.9.8", default-features = false, optional = true, features = [ + "spin_mutex", +] } +portable-atomic = { version = "1", default-features = false, optional = true } [features] -arc = ["dep:ahash", "dep:dashmap", "dep:once_cell"] +arc = ["std", "dep:ahash", "dep:dashmap", "dep:once_cell"] bench = ["arc", "arena", "_experimental-new-intern", "dep:memorable-wordlist"] -arena = [] -intern = [] -default = ["intern"] -_experimental-new-intern = ["dep:append-only-vec"] +arena = ["alloc"] +intern = ["alloc"] +default = ["intern", "std"] +_experimental-new-intern = ["alloc", "dep:append-only-vec"] +std = ["alloc"] +alloc = ["serde?/alloc"] +tinyset = ["std", "dep:tinyset"] +portable-atomic = ["dep:portable-atomic", "spin?/portable_atomic"] +critical-section = ["portable-atomic?/critical-section"] [dev-dependencies] -quickcheck = "^0.9.2" scaling = "0.1.3" rand = "0.7.2" serde_json = "1.0.87" diff --git a/src/arc.rs b/src/arc.rs index 3f23f25..f96d8e1 100644 --- a/src/arc.rs +++ b/src/arc.rs @@ -1,23 +1,29 @@ #![deny(missing_docs)] use ahash::RandomState; -use std::any::{Any, TypeId}; -use std::fmt::{Debug, Display, Pointer}; -type Container = DashMap, (), RandomState>; -type Untyped = &'static (dyn Any + Send + Sync + 'static); -use std::borrow::Borrow; -use std::convert::AsRef; -use std::ffi::OsStr; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::path::Path; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; - +use alloc::{boxed::Box, string::String}; +use core::{ + any::{Any, TypeId}, + borrow::Borrow, + convert::AsRef, + fmt::{Debug, Display, Pointer}, + hash::{Hash, Hasher}, + ops::Deref, + sync::atomic::{AtomicUsize, Ordering}, +}; use dashmap::{mapref::entry::Entry, DashMap}; +#[cfg(feature = "std")] +use std::{ffi::OsStr, path::Path}; + +#[cfg(test)] +use alloc::{string::ToString, vec}; + #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; +type Container = DashMap, (), RandomState>; +type Untyped = &'static (dyn Any + Send + Sync + 'static); + /// A pointer to a reference-counted interned object. /// /// This type requires feature "arc". The interned object will be held in memory only until its @@ -46,7 +52,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "arc")))] pub struct ArcIntern { - pub(crate) pointer: std::ptr::NonNull>, + pub(crate) pointer: core::ptr::NonNull>, } #[cfg(feature = "deepsize")] @@ -64,10 +70,10 @@ pub fn deep_size_of_arc_interned< T: ?Sized + Eq + Hash + Send + Sync + 'static + deepsize::DeepSizeOf, >() -> usize { let x = ArcIntern::::get_container(); - let pointers = x.capacity() * std::mem::size_of::>(); + let pointers = x.capacity() * core::mem::size_of::>(); let heap_memory = x .iter() - .map(|n| std::mem::size_of::() + n.key().0.data.deep_size_of()) + .map(|n| core::mem::size_of::() + n.key().0.data.deep_size_of()) .sum::(); pointers + heap_memory } @@ -226,7 +232,7 @@ impl ArcIntern { if oldval != 0 { // we can only use this value if the value is not about to be freed return ArcIntern { - pointer: std::ptr::NonNull::from(b.0.borrow()), + pointer: core::ptr::NonNull::from(b.0.borrow()), }; } else { // we have encountered a race condition here. @@ -243,7 +249,7 @@ impl ArcIntern { Entry::Vacant(e) => { // We can insert, all is good let p = ArcIntern { - pointer: std::ptr::NonNull::from(e.key().0.borrow()), + pointer: core::ptr::NonNull::from(e.key().0.borrow()), }; e.insert(()); return p; @@ -257,6 +263,7 @@ impl ArcIntern { } // yield so that the object can finish being freed, // and then we will be able to intern a new copy. + #[cfg(feature = "std")] std::thread::yield_now(); } } @@ -304,7 +311,7 @@ impl Drop for ArcIntern { // happens before decreasing the reference count, which // happens before this fence, which happens before the // deletion of the data. - std::sync::atomic::fence(Ordering::SeqCst); + core::sync::atomic::fence(Ordering::SeqCst); // removed is declared before m, so the mutex guard will be // dropped *before* the removed content is dropped, since it @@ -324,6 +331,7 @@ impl AsRef for ArcIntern { } } +#[cfg_attr(not(feature = "std"), allow(unused_macros))] macro_rules! impl_as_ref { ($from:ty => $to:ty) => { impl AsRef<$to> for ArcIntern<$from> { @@ -336,9 +344,13 @@ macro_rules! impl_as_ref { }; } +#[cfg(feature = "std")] impl_as_ref!(str => OsStr); +#[cfg(feature = "std")] impl_as_ref!(str => Path); +#[cfg(feature = "std")] impl_as_ref!(OsStr => Path); +#[cfg(feature = "std")] impl_as_ref!(Path => OsStr); impl Deref for ArcIntern { @@ -351,14 +363,14 @@ impl Deref for ArcIntern { impl Display for ArcIntern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.deref().fmt(f) } } impl Pointer for ArcIntern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { Pointer::fmt(&self.get_pointer(), f) } } @@ -378,14 +390,14 @@ impl Hash for ArcIntern { impl PartialEq for ArcIntern { #[inline(always)] fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self.get_pointer(), other.get_pointer()) + core::ptr::eq(self.get_pointer(), other.get_pointer()) } } impl Eq for ArcIntern {} impl PartialOrd for ArcIntern { #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { self.as_ref().partial_cmp(other) } #[inline] @@ -407,7 +419,7 @@ impl PartialOrd for ArcIntern< } impl Ord for ArcIntern { #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.as_ref().cmp(other) } } @@ -450,7 +462,11 @@ where #[cfg(test)] mod arc_test { use super::ArcIntern; - use super::{Borrow, Deref}; + use alloc::string::String; + use core::borrow::Borrow; + use core::ops::Deref; + use std::println; + #[test] fn eq_string() { assert_eq!(ArcIntern::new("hello"), ArcIntern::new("hello")); @@ -532,14 +548,14 @@ fn test_arcintern_nested_drop() { } impl Debug for ArcIntern { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.deref().fmt(f) } } #[cfg(test)] #[derive(Eq, PartialEq, Hash)] -pub struct TestStructCount(String, u64, std::sync::Arc); +pub struct TestStructCount(String, u64, alloc::sync::Arc); #[cfg(test)] #[derive(Eq, PartialEq, Hash)] @@ -549,7 +565,7 @@ pub struct TestStruct(String, u64); // multiple threads. #[test] fn multithreading1() { - use std::sync::Arc; + use alloc::sync::Arc; use std::thread; let mut thandles = vec![]; let drop_check = Arc::new(true); @@ -576,12 +592,12 @@ fn multithreading1() { #[test] fn arc_has_niche() { assert_eq!( - std::mem::size_of::>(), - std::mem::size_of::(), + core::mem::size_of::>(), + core::mem::size_of::(), ); assert_eq!( - std::mem::size_of::>>(), - std::mem::size_of::(), + core::mem::size_of::>>(), + core::mem::size_of::(), ); } @@ -617,7 +633,7 @@ fn test_shrink_to_fit() { struct Value(usize); assert_eq!(0, ArcIntern::::get_container().capacity()); { - let v = ArcIntern::new(Value(0)); + let _v = ArcIntern::new(Value(0)); assert!(ArcIntern::::get_container().capacity() >= 1); ArcIntern::::shrink_to_fit(); assert!(ArcIntern::::get_container().capacity() >= 1); diff --git a/src/arc_dst.rs b/src/arc_dst.rs index 143e69a..8504a31 100644 --- a/src/arc_dst.rs +++ b/src/arc_dst.rs @@ -1,33 +1,39 @@ #![deny(missing_docs)] -use std::{ - borrow::{Borrow, Cow}, +use super::arc::{ArcIntern, BoxRefCount, RefCount}; +use alloc::{ + borrow::{Cow, ToOwned}, + boxed::Box, + string::String, + sync::Arc, + vec::Vec, +}; +use core::{ + borrow::Borrow, hash::Hash, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, + sync::atomic::{AtomicUsize, Ordering}, }; - use dashmap::mapref::entry::Entry; + +#[cfg(test)] +use alloc::{string::ToString, vec}; + #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer}; -use super::arc::{ArcIntern, BoxRefCount, RefCount}; - impl RefCount<[T]> { fn from_slice(slice: &[T]) -> Box> { - use std::alloc::Layout; + use core::alloc::Layout; let layout = Layout::new::>() .extend(Layout::array::(slice.len()).unwrap()) .unwrap() .0 .pad_to_align(); // SAFETY: the layout is not zero-sized, it at least has an `AtomicUsize`. - let ptr = unsafe { std::alloc::alloc(layout) }; + let ptr = unsafe { alloc::alloc::alloc(layout) }; // imitate the `std::Arc::new_zeroed_slice` and `std::Arc::try_allocate_for_layout`. let ptr = - std::ptr::slice_from_raw_parts_mut(ptr as *mut T, slice.len()) as *mut RefCount<[T]>; + core::ptr::slice_from_raw_parts_mut(ptr as *mut T, slice.len()) as *mut RefCount<[T]>; unsafe { // SAFETY: the ptr is allocated by the global allocator with proper layout, as per the @@ -35,12 +41,12 @@ impl RefCount<[T]> { // of [`std::boxed`]. let mut this = Box::from_raw(ptr); - std::ptr::write(&mut this.count, AtomicUsize::new(1)); + core::ptr::write(&mut this.count, AtomicUsize::new(1)); // SAFETY: valid for reads, writes, aligned and not overlapped. // and T is Copy, so don't worry about drop. let dst = &mut this.data as *mut [T] as *mut T; - std::ptr::copy_nonoverlapping(slice.as_ptr(), dst, slice.len()); + core::ptr::copy_nonoverlapping(slice.as_ptr(), dst, slice.len()); this } } @@ -61,7 +67,7 @@ impl ArcIntern { /// make new [`ArcIntern`] with copyable initial value, like `&str` or `&[u8]`. fn new_with_copyable_init_val(val: &I, new_fn: NewFn) -> ArcIntern where - I: ?Sized + Hash + std::cmp::Eq, + I: ?Sized + Hash + core::cmp::Eq, BoxRefCount: Borrow, NewFn: Fn(&I) -> Box>, { @@ -78,7 +84,7 @@ impl ArcIntern { if oldval != 0 { // we can only use this value if the value is not about to be freed return ArcIntern { - pointer: std::ptr::NonNull::from(b.0.borrow()), + pointer: core::ptr::NonNull::from(b.0.borrow()), }; } else { // we have encountered a race condition here. @@ -87,12 +93,12 @@ impl ArcIntern { b.0.count.fetch_sub(1, Ordering::SeqCst); } } else { - let b = std::mem::take(&mut converted).unwrap_or_else(|| new_fn(val)); + let b = core::mem::take(&mut converted).unwrap_or_else(|| new_fn(val)); match m.entry(BoxRefCount(b)) { Entry::Vacant(e) => { // We can insert, all is good let p = ArcIntern { - pointer: std::ptr::NonNull::from(e.key().0.borrow()), + pointer: core::ptr::NonNull::from(e.key().0.borrow()), }; e.insert(()); return p; @@ -106,6 +112,7 @@ impl ArcIntern { } // yield so that the object can finish being freed, // and then we will be able to intern a new copy. + #[cfg(feature = "std")] std::thread::yield_now(); } } @@ -130,7 +137,7 @@ macro_rules! impl_from { impl_from! { [] String, ArcIntern } impl_from! { [] Box, ArcIntern } impl_from! { [] Arc, ArcIntern } -impl_from! { [] std::rc::Rc, ArcIntern } +impl_from! { [] alloc::rc::Rc, ArcIntern } impl<'a, B> From> for ArcIntern where B: ToOwned + ?Sized + Send + Sync + Hash + Eq, @@ -158,7 +165,7 @@ where impl_from! { [T: Copy + Send + Sync + Hash + Eq + 'static] Vec, ArcIntern<[T]> } impl_from! { [T: Copy + Send + Sync + Hash + Eq + 'static] Box<[T]>, ArcIntern<[T]> } impl_from! { [T: Copy + Send + Sync + Hash + Eq + 'static] Arc<[T]>, ArcIntern<[T]> } -impl_from! { [T: Copy + Send + Sync + Hash + Eq + 'static] std::rc::Rc<[T]>, ArcIntern<[T]> } +impl_from! { [T: Copy + Send + Sync + Hash + Eq + 'static] alloc::rc::Rc<[T]>, ArcIntern<[T]> } impl Default for ArcIntern { fn default() -> Self { @@ -205,18 +212,18 @@ macro_rules! impl_eq { impl_eq! { [] ArcIntern, str } impl_eq! { [] ArcIntern, &'a str } impl_eq! { [] ArcIntern, String } -impl_eq! { [] ArcIntern, std::borrow::Cow<'a, str> } +impl_eq! { [] ArcIntern, alloc::borrow::Cow<'a, str> } impl_eq! { [] ArcIntern, Box } -impl_eq! { [] ArcIntern, std::rc::Rc } -impl_eq! { [] ArcIntern, std::sync::Arc } +impl_eq! { [] ArcIntern, alloc::rc::Rc } +impl_eq! { [] ArcIntern, alloc::sync::Arc } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, Vec } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, [T] } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, &'a [T] } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, &'a mut [T] } -impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, std::borrow::Cow<'a, [T]> } +impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, alloc::borrow::Cow<'a, [T]> } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, Box<[T]> } -impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, std::rc::Rc<[T]> } -impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, std::sync::Arc<[T]> } +impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, alloc::rc::Rc<[T]> } +impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static] ArcIntern<[T]>, alloc::sync::Arc<[T]> } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static, const N: usize] ArcIntern<[T]>, [T; N] } impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static, const N: usize] ArcIntern<[T]>, &[T; N] } @@ -226,10 +233,10 @@ impl_eq! { [T: Copy + Send + Sync + Hash + Eq + 'static, const N: usize] ArcInte #[cfg(feature = "serde")] struct StrVisitor; #[cfg(feature = "serde")] -impl<'a> serde::de::Visitor<'a> for StrVisitor { +impl serde::de::Visitor<'_> for StrVisitor { type Value = ArcIntern; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { formatter.write_str("a borrowed or owned string") } @@ -249,7 +256,7 @@ impl<'a> serde::de::Visitor<'a> for StrVisitor { } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] -impl<'de: 'a, 'a> Deserialize<'de> for ArcIntern { +impl<'de> Deserialize<'de> for ArcIntern { fn deserialize>(deserializer: D) -> Result { deserializer.deserialize_str(StrVisitor) } @@ -258,10 +265,10 @@ impl<'de: 'a, 'a> Deserialize<'de> for ArcIntern { #[cfg(feature = "serde")] struct BytesVisitor; #[cfg(feature = "serde")] -impl<'a> serde::de::Visitor<'a> for BytesVisitor { +impl serde::de::Visitor<'_> for BytesVisitor { type Value = ArcIntern<[u8]>; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { formatter.write_str("a borrowed or owned byte array") } @@ -272,6 +279,7 @@ impl<'a> serde::de::Visitor<'a> for BytesVisitor { Ok(ArcIntern::from(v)) } + #[cfg(feature = "alloc")] fn visit_byte_buf(self, v: Vec) -> Result where E: serde::de::Error, @@ -282,7 +290,7 @@ impl<'a> serde::de::Visitor<'a> for BytesVisitor { #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] -impl<'de: 'a, 'a> Deserialize<'de> for ArcIntern<[u8]> { +impl<'de> Deserialize<'de> for ArcIntern<[u8]> { fn deserialize>(deserializer: D) -> Result { deserializer.deserialize_bytes(BytesVisitor) } @@ -324,10 +332,10 @@ fn common_equal_comparisons() { let s1: ArcIntern = ArcIntern::from("common_equal_comparisons"); let s2: &str = "common_equal_comparisons"; let s3: String = "common_equal_comparisons".to_string(); - let s4: std::borrow::Cow<'_, str> = "common_equal_comparisons".into(); + let s4: alloc::borrow::Cow<'_, str> = "common_equal_comparisons".into(); let s5: Box = "common_equal_comparisons".into(); - let s6: std::rc::Rc = "common_equal_comparisons".into(); - let s7: std::sync::Arc = "common_equal_comparisons".into(); + let s6: alloc::rc::Rc = "common_equal_comparisons".into(); + let s7: alloc::sync::Arc = "common_equal_comparisons".into(); assert_eq!(s1, s2); assert_eq!(s1, s3); assert_eq!(s1, s4); @@ -341,10 +349,10 @@ fn common_from_conversions() { let s1: ArcIntern = ArcIntern::from("common_from_conversions"); let s2: &str = "common_from_conversions"; let s3: String = "common_from_conversions".to_string(); - let s4: std::borrow::Cow<'_, str> = "common_from_conversions".into(); + let s4: alloc::borrow::Cow<'_, str> = "common_from_conversions".into(); let s5: Box = "common_from_conversions".into(); - let s6: std::rc::Rc = "common_from_conversions".into(); - let s7: std::sync::Arc = "common_from_conversions".into(); + let s6: alloc::rc::Rc = "common_from_conversions".into(); + let s7: alloc::sync::Arc = "common_from_conversions".into(); assert_eq!(ArcIntern::from(s2), s1); assert_eq!(ArcIntern::from(s3), s1); assert_eq!(ArcIntern::from(s4), s1); @@ -395,10 +403,10 @@ fn arc_intern_str() { assert_eq!(y.refcount(), 3); assert_eq!(z.refcount(), 3); - std::mem::drop(x); + core::mem::drop(x); assert_eq!(y.refcount(), 2); assert_eq!(z.refcount(), 2); - std::mem::drop(y); + core::mem::drop(y); assert_eq!(z.refcount(), 1); } @@ -433,8 +441,8 @@ fn dst_memory_aligned() { struct Aligned(u8); // [The size of a value is always a multiple of its alignment](https://doc.rust-lang.org/reference/type-layout.html) - assert_eq!(std::mem::align_of::(), $align); - assert_eq!(std::mem::size_of::(), $align); + assert_eq!(core::mem::align_of::(), $align); + assert_eq!(core::mem::size_of::(), $align); let x: ArcIntern<[Aligned]> = ArcIntern::from(&[Aligned::default(); 10][..]); let ptr = unsafe { &*x.pointer.as_ptr() }; @@ -446,7 +454,7 @@ fn dst_memory_aligned() { { assert_eq!(addr0 % $align, 0) }; for idx in 1..10 { let addr_offset = &ptr.data[idx] as *const _ as usize; - assert_eq!(addr0 + idx * std::mem::size_of::(), addr_offset); + assert_eq!(addr0 + idx * core::mem::size_of::(), addr_offset); } }}; diff --git a/src/arena.rs b/src/arena.rs index 647d8dc..ca5d8c3 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -1,8 +1,18 @@ #![deny(missing_docs)] use crate::boxedset::HashSet; -use std::borrow::Borrow; -use std::hash::{Hash, Hasher}; -use std::sync::Mutex; +use alloc::{boxed::Box, string::String, vec::Vec}; +use core::{ + borrow::Borrow, + hash::{Hash, Hasher}, +}; + +#[cfg(not(any(feature = "std", feature = "spin")))] +compile_error!( + "Require either the `std` or spin `features` to be enabled when using the `arena` feature" +); + +#[cfg(test)] +use std::println; /// A arena for storing interned data /// @@ -39,13 +49,29 @@ use std::sync::Mutex; #[cfg_attr(docsrs, doc(cfg(feature = "arena")))] pub struct Arena { - data: Mutex>>, + #[cfg(feature = "std")] + data: std::sync::Mutex>>, + #[cfg(all(feature = "spin", not(feature = "std")))] + data: spin::mutex::Mutex>>, +} + +impl Arena { + fn get_mut(&self) -> impl core::ops::DerefMut>> + '_ { + #[cfg(feature = "std")] + return self + .data + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + #[cfg(all(feature = "spin", not(feature = "std")))] + return self.data.lock(); + } } #[cfg(feature = "deepsize")] impl deepsize::DeepSizeOf for Arena { fn deep_size_of_children(&self, context: &mut deepsize::Context) -> usize { - let hashset = self.data.lock().unwrap(); + let hashset = self.get_mut(); (*hashset).deep_size_of_children(context) } } @@ -57,26 +83,29 @@ pub struct ArenaIntern<'a, T: ?Sized> { } #[cfg(feature = "deepsize")] -impl<'a, T: ?Sized + deepsize::DeepSizeOf> deepsize::DeepSizeOf for ArenaIntern<'a, T> { +impl deepsize::DeepSizeOf for ArenaIntern<'_, T> { fn deep_size_of_children(&self, _context: &mut deepsize::Context) -> usize { - std::mem::size_of::<&T>() + core::mem::size_of::<&T>() } } -impl<'a, T: ?Sized> Clone for ArenaIntern<'a, T> { +impl Clone for ArenaIntern<'_, T> { #[inline(always)] fn clone(&self) -> Self { *self } } -impl<'a, T: ?Sized> Copy for ArenaIntern<'a, T> {} +impl Copy for ArenaIntern<'_, T> {} impl Arena { /// Allocate a new `Arena` #[inline] pub fn new() -> Self { Arena { - data: Mutex::new(HashSet::new()), + #[cfg(feature = "std")] + data: std::sync::Mutex::new(HashSet::new()), + #[cfg(all(feature = "spin", not(feature = "std")))] + data: spin::mutex::Mutex::new(HashSet::new()), } } } @@ -87,7 +116,7 @@ impl Arena { /// allocate a spot for the value on the heap. Otherwise, it will return a /// pointer to the object previously allocated. pub fn intern(&self, val: T) -> ArenaIntern { - let mut m = self.data.lock().unwrap(); + let mut m = self.get_mut(); if let Some(b) = m.get(&val) { let p = b.as_ref() as *const T; return ArenaIntern { @@ -108,9 +137,9 @@ impl Arena { where T: 'a + Borrow, Box: From<&'b I>, - I: Eq + std::hash::Hash + ?Sized, + I: Eq + core::hash::Hash + ?Sized, { - let mut m = self.data.lock().unwrap(); + let mut m = self.get_mut(); if let Some(b) = m.get(val) { let p = b.as_ref() as *const T; return ArenaIntern { @@ -127,9 +156,9 @@ impl Arena { fn intern_from_owned(&self, val: I) -> ArenaIntern where Box: From, - I: Eq + std::hash::Hash + AsRef, + I: Eq + core::hash::Hash + AsRef, { - let mut m = self.data.lock().unwrap(); + let mut m = self.get_mut(); if let Some(b) = m.get(val.as_ref()) { let p = b.as_ref() as *const T; return ArenaIntern { @@ -173,7 +202,7 @@ impl Arena { self.intern_from_owned(val) } } -impl Arena { +impl Arena { /// Intern a `&CStr` as `ArenaIntern`. /// /// If this value has not previously been interned, then `intern` will @@ -189,7 +218,7 @@ impl Arena { /// assert_eq!(x, y); /// ``` #[inline] - pub fn intern<'a>(&'a self, val: &std::ffi::CStr) -> ArenaIntern<'a, std::ffi::CStr> { + pub fn intern<'a>(&'a self, val: &core::ffi::CStr) -> ArenaIntern<'a, core::ffi::CStr> { self.intern_ref(val) } /// Intern a `CString` as `ArenaIntern`. @@ -207,7 +236,7 @@ impl Arena { /// assert_eq!(x, y); /// ``` #[inline] - pub fn intern_cstring(&self, val: std::ffi::CString) -> ArenaIntern { + pub fn intern_cstring(&self, val: alloc::ffi::CString) -> ArenaIntern { self.intern_from_owned(val) } /// Intern a `Box` as `ArenaIntern`. @@ -225,10 +254,11 @@ impl Arena { /// assert_eq!(x, y); /// ``` #[inline] - pub fn intern_box(&self, val: Box) -> ArenaIntern { + pub fn intern_box(&self, val: Box) -> ArenaIntern { self.intern_from_owned(val) } } +#[cfg(feature = "std")] impl Arena { /// Intern a `&OsStr` as `ArenaIntern`. /// @@ -285,6 +315,7 @@ impl Arena { self.intern_from_owned(val) } } +#[cfg(feature = "std")] impl Arena { /// Intern a `&Path` as `ArenaIntern`. /// @@ -375,9 +406,9 @@ impl Arena { pub fn intern_from<'a, 'b, I>(&'a self, val: &'b I) -> ArenaIntern<'a, T> where T: 'a + Borrow + From<&'b I>, - I: Eq + std::hash::Hash + ?Sized, + I: Eq + core::hash::Hash + ?Sized, { - let mut m = self.data.lock().unwrap(); + let mut m = self.get_mut(); if let Some(b) = m.get(val) { let p = b.as_ref() as *const T; return ArenaIntern { @@ -400,14 +431,14 @@ impl Default for Arena { } } -impl<'a, T: ?Sized> AsRef for ArenaIntern<'a, T> { +impl AsRef for ArenaIntern<'_, T> { #[inline(always)] fn as_ref(&self) -> &T { self.pointer } } -impl<'a, T: ?Sized> std::ops::Deref for ArenaIntern<'a, T> { +impl core::ops::Deref for ArenaIntern<'_, T> { type Target = T; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -486,34 +517,34 @@ impl<'a, T: ?Sized> ArenaIntern<'a, T> { /// be irrelevant, since there is a unique pointer for every /// value, but it *is* observable, since you could compare the /// hash of the pointer with hash of the data itself. -impl<'a, T: ?Sized> Hash for ArenaIntern<'a, T> { +impl Hash for ArenaIntern<'_, T> { #[inline] fn hash(&self, state: &mut H) { self.get_pointer().hash(state); } } -impl<'a, T: ?Sized> PartialEq for ArenaIntern<'a, T> { +impl PartialEq for ArenaIntern<'_, T> { #[inline] fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self.get_pointer(), other.get_pointer()) + core::ptr::eq(self.get_pointer(), other.get_pointer()) } } -impl<'a, T: ?Sized> Eq for ArenaIntern<'a, T> {} +impl Eq for ArenaIntern<'_, T> {} // #[cfg(feature = "arena")] // create_impls_no_new!(ArenaIntern, arenaintern_impl_tests, ['a], [Eq, Hash], [Eq, Hash]); -impl<'a, T: std::fmt::Debug + ?Sized> std::fmt::Debug for ArenaIntern<'a, T> { +impl core::fmt::Debug for ArenaIntern<'_, T> { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.as_ref().fmt(f) } } -impl<'a, T: std::fmt::Display + ?Sized> std::fmt::Display for ArenaIntern<'a, T> { +impl core::fmt::Display for ArenaIntern<'_, T> { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.as_ref().fmt(f) } } @@ -546,7 +577,7 @@ fn has_deref() { let arena = Arena::>::new(); let x = arena.intern(None); let b: &Option = x.as_ref(); - use std::ops::Deref; + use core::ops::Deref; assert_eq!(b, arena.intern(None).deref()); } diff --git a/src/boxedset.rs b/src/boxedset.rs index c5a3b5d..3a0b942 100644 --- a/src/boxedset.rs +++ b/src/boxedset.rs @@ -1,9 +1,13 @@ -use core::ops::Deref; -use hashbrown::HashMap; -use std::{ +use core::{ borrow::Borrow, hash::{BuildHasher, Hash, Hasher}, + ops::Deref, }; +use hashbrown::HashMap; + +#[cfg(all(feature = "deepsize", feature = "alloc"))] +use alloc::boxed::Box; + pub struct HashSet

(HashMap); impl Default for HashSet

{ @@ -22,24 +26,24 @@ impl

HashSet

{ #[cfg(feature = "deepsize")] impl deepsize::DeepSizeOf for HashSet<&'static P> { fn deep_size_of_children(&self, context: &mut deepsize::Context) -> usize { - let pointers = self.0.capacity() * std::mem::size_of::<&'static P>(); + let pointers = self.0.capacity() * core::mem::size_of::<&'static P>(); let heap_memory = self .0 .keys() - .map(|n| (**n).deep_size_of_children(context) + std::mem::size_of::

()) + .map(|n| (**n).deep_size_of_children(context) + core::mem::size_of::

()) .sum::(); pointers + heap_memory } } -#[cfg(feature = "deepsize")] +#[cfg(all(feature = "deepsize", feature = "alloc"))] impl deepsize::DeepSizeOf for HashSet> { fn deep_size_of_children(&self, context: &mut deepsize::Context) -> usize { - let pointers = self.0.capacity() * std::mem::size_of::>(); + let pointers = self.0.capacity() * core::mem::size_of::>(); let heap_memory = self .0 .keys() - .map(|n| (**n).deep_size_of_children(context) + std::mem::size_of_val(&**n)) + .map(|n| (**n).deep_size_of_children(context) + core::mem::size_of_val(&**n)) .sum::(); pointers + heap_memory } diff --git a/src/container.rs b/src/container.rs index 6702a6e..1938b1c 100644 --- a/src/container.rs +++ b/src/container.rs @@ -1,7 +1,14 @@ -use std::any::Any; -use std::any::TypeId; -use std::hash::{Hash, Hasher}; -use std::sync::Mutex; +use alloc::{boxed::Box, vec::Vec}; +use core::{ + any::{Any, TypeId}, + hash::{Hash, Hasher}, + ops::DerefMut, +}; + +#[cfg(not(any(feature = "std", feature = "spin")))] +compile_error!( + "Require either the `std` or spin `features` to be enabled when using the `intern` feature" +); pub struct TypeHolderSend(Vec); @@ -26,16 +33,46 @@ impl TypeHolderSend { } } +struct TypeHolderSendCell { + #[cfg(feature = "std")] + inner: std::sync::Mutex, + #[cfg(all(feature = "spin", not(feature = "std")))] + inner: spin::mutex::Mutex, +} + +impl TypeHolderSendCell { + const fn new() -> Self { + Self { + #[cfg(feature = "std")] + inner: std::sync::Mutex::new(TypeHolderSend::new()), + #[cfg(all(feature = "spin", not(feature = "std")))] + inner: spin::mutex::Mutex::new(TypeHolderSend::new()), + } + } + + fn get_mut(&self) -> impl DerefMut + '_ { + #[cfg(feature = "std")] + return self + .inner + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + #[cfg(all(feature = "spin", not(feature = "std")))] + return self.inner.lock(); + } +} + const INTERN_CONTAINER_COUNT: usize = 32; pub struct Arena { - containers: [Mutex; INTERN_CONTAINER_COUNT], + containers: [TypeHolderSendCell; INTERN_CONTAINER_COUNT], } impl Arena { pub const fn new() -> Self { - const EMPTY: Mutex = Mutex::new(TypeHolderSend::new()); + #[allow(clippy::declare_interior_mutable_const)] + const CONTAINER: TypeHolderSendCell = TypeHolderSendCell::new(); Arena { - containers: [EMPTY; INTERN_CONTAINER_COUNT], + containers: [CONTAINER; INTERN_CONTAINER_COUNT], } } @@ -81,8 +118,7 @@ impl Arena { f( self.containers[hash_of_type::() as usize % INTERN_CONTAINER_COUNT] - .lock() - .unwrap() + .get_mut() .get_type_mut(), ) } diff --git a/src/intern.rs b/src/intern.rs index 5823f17..0fc0e5b 100644 --- a/src/intern.rs +++ b/src/intern.rs @@ -1,18 +1,24 @@ #![deny(missing_docs)] -use super::boxedset; +use super::{boxedset, container}; +use alloc::boxed::Box; use boxedset::HashSet; -use std::borrow::Borrow; -use std::convert::AsRef; -use std::ffi::OsStr; -use std::fmt::{Debug, Display, Pointer}; -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::path::Path; +use core::{ + borrow::Borrow, + convert::AsRef, + fmt::{Debug, Display, Pointer}, + hash::{Hash, Hasher}, + ops::Deref, +}; -use super::container; +#[cfg(feature = "std")] +use std::{ffi::OsStr, path::Path}; + +#[cfg(test)] +use alloc::string::{String, ToString}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; + #[cfg(feature = "tinyset")] use tinyset::Fits64; @@ -82,12 +88,12 @@ pub fn deep_size_of_interned>(), - std::mem::size_of::(), + core::mem::size_of::>(), + core::mem::size_of::(), ); assert_eq!( - std::mem::size_of::>>(), - std::mem::size_of::(), + core::mem::size_of::>>(), + core::mem::size_of::(), ); } @@ -98,7 +104,7 @@ impl Clone for Intern { } } -/// An `Intern` is `Copy`, which is unusal for a pointer. This is safe +/// An `Intern` is `Copy`, which is unusual for a pointer. This is safe /// because we never free the data pointed to by an `Intern`. impl Copy for Intern {} @@ -121,8 +127,9 @@ macro_rules! from_via_box { } }; } -from_via_box!(std::ffi::CStr); +from_via_box!(core::ffi::CStr); from_via_box!(str); +#[cfg(feature = "std")] from_via_box!(std::path::Path); impl From<&[T]> for Intern<[T]> { #[inline] @@ -288,16 +295,16 @@ fn allocate_ptr() -> *mut usize { #[cfg(feature = "tinyset")] fn heap_location() -> u64 { - static HEAP_LOCATION: std::sync::atomic::AtomicPtr = - std::sync::atomic::AtomicPtr::new(0 as *mut usize); - let mut p = HEAP_LOCATION.load(std::sync::atomic::Ordering::Relaxed) as u64; + static HEAP_LOCATION: core::sync::atomic::AtomicPtr = + core::sync::atomic::AtomicPtr::new(0 as *mut usize); + let mut p = HEAP_LOCATION.load(core::sync::atomic::Ordering::Relaxed) as u64; if p == 0 { let ptr = allocate_ptr(); p = match HEAP_LOCATION.compare_exchange( - std::ptr::null_mut(), + core::ptr::null_mut(), ptr, - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, + core::sync::atomic::Ordering::Relaxed, + core::sync::atomic::Ordering::Relaxed, ) { Ok(_) => ptr as u64, Err(ptr) => ptr as u64, // this means another thread allocated this. @@ -307,7 +314,7 @@ fn heap_location() -> u64 { } #[cfg(feature = "tinyset")] const fn sz() -> u64 { - std::mem::align_of::() as u64 + core::mem::align_of::() as u64 } /// The `Fits64` implementation for `Intern` is designed to normally give /// (relatively) small numbers, by XORing with a fixed pointer that is also on @@ -356,6 +363,7 @@ impl AsRef for Intern { } } +#[cfg_attr(not(feature = "std"), allow(unused_macros))] macro_rules! impl_as_ref { ($from:ty => $to:ty) => { impl AsRef<$to> for Intern<$from> { @@ -367,9 +375,13 @@ macro_rules! impl_as_ref { }; } +#[cfg(feature = "std")] impl_as_ref!(str => OsStr); +#[cfg(feature = "std")] impl_as_ref!(str => Path); +#[cfg(feature = "std")] impl_as_ref!(OsStr => Path); +#[cfg(feature = "std")] impl_as_ref!(Path => OsStr); impl Deref for Intern { @@ -382,14 +394,14 @@ impl Deref for Intern { impl Display for Intern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.deref().fmt(f) } } impl Pointer for Intern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { Pointer::fmt(&self.get_pointer(), f) } } @@ -409,14 +421,14 @@ impl Hash for Intern { impl PartialEq for Intern { #[inline] fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self.get_pointer(), other.get_pointer()) + core::ptr::eq(self.get_pointer(), other.get_pointer()) } } impl Eq for Intern {} impl PartialOrd for Intern { #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { self.as_ref().partial_cmp(other) } #[inline] @@ -438,7 +450,7 @@ impl PartialOrd for Intern } impl Ord for Intern { #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.as_ref().cmp(other) } } @@ -467,9 +479,7 @@ impl Default for Intern { #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] -impl<'de, T: Eq + Hash + Send + Sync + ?Sized + 'static + Deserialize<'de>> Deserialize<'de> - for Intern -{ +impl<'de, T: Eq + Hash + Send + Sync + 'static + Deserialize<'de>> Deserialize<'de> for Intern { fn deserialize>(deserializer: D) -> Result { T::deserialize(deserializer).map(|x: T| Self::new(x)) } @@ -478,17 +488,20 @@ impl<'de, T: Eq + Hash + Send + Sync + ?Sized + 'static + Deserialize<'de>> Dese #[cfg(test)] mod intern_tests { use super::Intern; - use super::{Borrow, Deref}; - use std::hash::Hash; + use alloc::{ + string::{String, ToString}, + vec, + }; + use core::{borrow::Borrow, hash::Hash, ops::Deref}; + use std::println; #[cfg(feature = "deepsize")] - use super::INTERN_CONTAINERS; - #[cfg(feature = "deepsize")] - use crate::{boxedset::HashSet, deep_size_of_interned}; - #[cfg(feature = "deepsize")] - use deepsize::{Context, DeepSizeOf}; - #[cfg(feature = "deepsize")] - use std::sync::Arc; + use { + super::INTERN_CONTAINERS, + crate::{boxedset::HashSet, deep_size_of_interned}, + alloc::sync::Arc, + deepsize::{Context, DeepSizeOf}, + }; #[test] fn eq_string() { @@ -592,10 +605,10 @@ mod intern_tests { let _ = Intern::new(string3); // size of set let set_size = - INTERN_CONTAINERS.with(|m: &mut HashSet<&'static String>| std::mem::size_of_val(m)); + INTERN_CONTAINERS.with(|m: &mut HashSet<&'static String>| core::mem::size_of_val(m)); // size of pointers in the set let pointers_in_set_size = INTERN_CONTAINERS.with(|m: &mut HashSet<&'static String>| { - std::mem::size_of::<&'static String>() * m.capacity() + core::mem::size_of::<&'static String>() * m.capacity() }); let interned_size = deep_size_of_interned::(); @@ -613,7 +626,7 @@ mod intern_tests { impl Hash for ArcInside { /// For testing purposes, we only hash the hash field. /// In order to make [`ArcInside`] instances containing the same string have different hash values. - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { self.hash.hash(state); } } @@ -657,7 +670,7 @@ mod intern_tests { pointer: a1.pointer.clone(), }; // size of ArcInside, 16 bytes each - let object_size = std::mem::size_of::() * 3; + let object_size = core::mem::size_of::() * 3; let _ = Intern::new(a1); let _ = Intern::new(a2); @@ -665,10 +678,10 @@ mod intern_tests { // size of set let set_size = - INTERN_CONTAINERS.with(|m: &mut HashSet<&'static ArcInside>| std::mem::size_of_val(m)); + INTERN_CONTAINERS.with(|m: &mut HashSet<&'static ArcInside>| core::mem::size_of_val(m)); // size of pointers in the set let pointers_in_set_size = INTERN_CONTAINERS.with(|m: &mut HashSet<&'static ArcInside>| { - std::mem::size_of::<&'static ArcInside>() * m.capacity() + core::mem::size_of::<&'static ArcInside>() * m.capacity() }); let interned_size = deep_size_of_interned::(); @@ -691,7 +704,7 @@ mod intern_tests { impl Debug for Intern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.as_ref().fmt(f) } } diff --git a/src/lib.rs b/src/lib.rs index e4d3675..0854f6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,13 @@ // Enable the `doc_cfg` feature when the `docsrs` configuration attribute is // defined #![cfg_attr(docsrs, feature(doc_cfg))] +#![no_std] + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "alloc")] +extern crate alloc; mod boxedset; diff --git a/src/typearena.rs b/src/typearena.rs index 347b493..1e857f7 100644 --- a/src/typearena.rs +++ b/src/typearena.rs @@ -1,17 +1,24 @@ #![deny(missing_docs)] -use std::any::Any; -use std::any::TypeId; -use std::ffi::OsStr; -use std::hash::{Hash, Hasher}; -use std::path::Path; - +use alloc::boxed::Box; use append_only_vec::AppendOnlyVec; +use core::{ + any::{Any, TypeId}, + hash::{Hash, Hasher}, +}; +use std::{ffi::OsStr, path::Path, println}; + +#[cfg(test)] +use alloc::{ + string::{String, ToString}, + vec, +}; struct AnySend(Box); const CONTAINER_COUNT: usize = 32; -const EMPTY: AppendOnlyVec = AppendOnlyVec::new(); -static CONTAINERS: [AppendOnlyVec; CONTAINER_COUNT] = [EMPTY; CONTAINER_COUNT]; +#[allow(clippy::declare_interior_mutable_const)] +const CONTAINER: AppendOnlyVec = AppendOnlyVec::new(); +static CONTAINERS: [AppendOnlyVec; CONTAINER_COUNT] = [CONTAINER; CONTAINER_COUNT]; pub fn with_mutex_hashset(f: F) -> R where @@ -78,10 +85,10 @@ where use super::boxedset; use boxedset::HashSet; -use std::borrow::Borrow; -use std::convert::AsRef; -use std::fmt::{Debug, Display, Pointer}; -use std::ops::Deref; +use core::borrow::Borrow; +use core::convert::AsRef; +use core::fmt::{Debug, Display, Pointer}; +use core::ops::Deref; use std::sync::Mutex; #[cfg(feature = "serde")] @@ -141,12 +148,12 @@ pub struct Intern { #[test] fn has_niche() { assert_eq!( - std::mem::size_of::>(), - std::mem::size_of::(), + core::mem::size_of::>(), + core::mem::size_of::(), ); assert_eq!( - std::mem::size_of::>>(), - std::mem::size_of::(), + core::mem::size_of::>>(), + core::mem::size_of::(), ); } @@ -178,7 +185,7 @@ macro_rules! from_via_box { } }; } -from_via_box!(std::ffi::CStr); +from_via_box!(core::ffi::CStr); from_via_box!(str); from_via_box!(std::path::Path); impl From<&[T]> for Intern<[T]> { @@ -310,16 +317,16 @@ fn allocate_ptr() -> *mut usize { #[cfg(feature = "tinyset")] fn heap_location() -> u64 { - static HEAP_LOCATION: std::sync::atomic::AtomicPtr = - std::sync::atomic::AtomicPtr::new(0 as *mut usize); - let mut p = HEAP_LOCATION.load(std::sync::atomic::Ordering::Relaxed) as u64; + static HEAP_LOCATION: core::sync::atomic::AtomicPtr = + core::sync::atomic::AtomicPtr::new(0 as *mut usize); + let mut p = HEAP_LOCATION.load(core::sync::atomic::Ordering::Relaxed) as u64; if p == 0 { let ptr = allocate_ptr(); p = match HEAP_LOCATION.compare_exchange( - std::ptr::null_mut(), + core::ptr::null_mut(), ptr, - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, + core::sync::atomic::Ordering::Relaxed, + core::sync::atomic::Ordering::Relaxed, ) { Ok(_) => ptr as u64, Err(ptr) => { @@ -332,7 +339,7 @@ fn heap_location() -> u64 { } #[cfg(feature = "tinyset")] const fn sz() -> u64 { - std::mem::align_of::() as u64 + core::mem::align_of::() as u64 } /// The `Fits64` implementation for `Intern` is designed to normally give /// (relatively) small numbers, by XORing with a fixed pointer that is also on @@ -402,14 +409,14 @@ impl Deref for Intern { impl Display for Intern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.deref().fmt(f) } } impl Pointer for Intern { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { Pointer::fmt(&self.get_pointer(), f) } } @@ -429,14 +436,14 @@ impl Hash for Intern { impl PartialEq for Intern { #[inline(always)] fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self.get_pointer(), other.get_pointer()) + core::ptr::eq(self.get_pointer(), other.get_pointer()) } } impl Eq for Intern {} impl PartialOrd for Intern { #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { self.as_ref().partial_cmp(other) } #[inline] @@ -458,7 +465,7 @@ impl PartialOrd for Intern } impl Ord for Intern { #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.as_ref().cmp(other) } } @@ -497,7 +504,10 @@ impl<'de, T: Eq + Hash + Send + Sync + 'static + Deserialize<'de>> Deserialize<' #[cfg(test)] mod intern_tests { use super::Intern; - use super::{Borrow, Deref}; + use alloc::string::String; + use core::{borrow::Borrow, ops::Deref}; + use std::println; + #[test] fn eq_string() { assert_eq!(Intern::new("hello"), Intern::new("hello")); @@ -542,7 +552,7 @@ mod intern_tests { } impl Debug for Intern { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { self.as_ref().fmt(f) } } @@ -568,7 +578,7 @@ fn test_intern_num_objects() { #[cfg(test)] #[derive(Eq, PartialEq, Hash)] -pub struct TestStructCount(String, u64, std::sync::Arc); +pub struct TestStructCount(String, u64, alloc::sync::Arc); #[cfg(test)] #[derive(Eq, PartialEq, Hash)] From 332dc26c65980e7b6620d9a5c547febfc974d705 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Sun, 2 Mar 2025 19:06:36 +1100 Subject: [PATCH 2/2] Add `no_std` checks Covers `portable-atomic` too --- .github/workflows/rust.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 06693b9..e7805af 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} + targets: "x86_64-unknown-none, thumbv6m-none-eabi" - run: cargo check --features "arc,tinyset" + - run: cargo check --no-default-features --target x86_64-unknown-none --features serde,arena,spin,intern,alloc,deepsize + - run: cargo check --no-default-features --target thumbv6m-none-eabi --features serde,arena,spin,intern,portable-atomic,critical-section test: name: Test Suite