From 0a1c85f8655a71b995d228ed0c9496f732268c5b Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 25 Jan 2022 21:24:48 +1100 Subject: [PATCH] no_std support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit I’m quite pleased with how this has turned out. Given the stability-despite-instability of hashbrown (that the API surface we’re depending on hasn’t changed since 0.1.1), and the deliberate altered SemVer guarantees for it, it was very tempting to leave the hashbrown range open, `version = ">=0.1.1"` or at least `version = ">=0.1.1, <1"`, but for some reason or other I ended up deciding not to. I’m still of two minds about it, really. --- .travis.yml | 5 ++++- CHANGELOG.md | 5 ++++- Cargo.toml | 14 +++++++++++--- README.md | 20 ++++++++++++++++++++ src/any.rs | 2 ++ src/lib.rs | 11 +++++++++++ src/raw.rs | 13 +++++++++++++ test | 24 ++++++++++++++++++++---- test-oldest-Cargo.lock | 32 ++++++++++++++++++++++++++++++++ 9 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 test-oldest-Cargo.lock diff --git a/.travis.yml b/.travis.yml index d6e378b..b5c4583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,12 @@ language: rust rust: - - 1.34.0 + - 1.36.0 - nightly - stable script: - if [[ "$(rustc --version)" =~ -(dev|nightly) ]]; then cargo bench; fi + - if [[ "$(rustc --version)" =~ 1.36.0 ]]; then cp test-oldest-Cargo.lock Cargo.lock; fi - cargo test - cargo test --release + - cargo test --no-default-features --features hashbrown + - cargo test --release --no-default-features --features hashbrown diff --git a/CHANGELOG.md b/CHANGELOG.md index 6477f99..214ba6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,10 @@ - Relicensed from MIT/Apache-2.0 to BlueOak-1.0.0/MIT/Apache-2.0. -- Increased the minimum supported version of Rust from 1.7.0 to 1.34.0. +- Increased the minimum supported version of Rust from 1.7.0 to 1.36.0. + +- no_std is now possible in the usual way (default Cargo feature 'std'), + depending on alloc and hashbrown. - Removed the `bench` Cargo feature which was mostly to work around historical Cargo limitations, but was solved by moving benchmarks from `src/lib.rs` to diff --git a/Cargo.toml b/Cargo.toml index c1ca92c..011cbe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,18 @@ name = "anymap" version = "0.12.1" authors = ["Chris Morgan "] edition = "2018" -rust-version = "1.34" +rust-version = "1.36" description = "A safe and convenient store for one value of each type" repository = "https://github.com/chris-morgan/anymap" keywords = ["container", "any", "map"] -categories = ["rust-patterns", "data-structures"] +categories = ["rust-patterns", "data-structures", "no-std"] license = "BlueOak-1.0.0 OR MIT OR Apache-2.0" -exclude = ["test", "benches", ".travis.yml", ".gitignore"] +include = ["/README.md", "/COPYING", "/CHANGELOG.md", "/src"] + +[features] +default = ["std"] +std = [] + +[dependencies] +# The hashbrown feature, disabled by default, is exposed under different stability guarantees than the usual SemVer ones: by preference the version range will only be extended, but it may be shrunk in a MINOR release. See README.md. +hashbrown = { version = ">=0.1.1, <0.13", optional = true } diff --git a/README.md b/README.md index ea53945..59b1d96 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,26 @@ The ``AnyMap`` type is a friendly wrapper around a ``HashMap=0.12, <0.14` when they release 0.13.0), but if an incompatible change occurs, I may drop support for older versions of hashbrown with a bump to the *minor* part of the anymap version number (e.g. 1.1.0, 1.2.0). Iff you’re using this feature, this is cause to *consider* using a tilde requirement like `"~1.0"` (or spell it out as `>=1, <1.1`). + ## Unsafe code in this library This library uses a fair bit of unsafe code for several reasons: diff --git a/src/any.rs b/src/any.rs index 437eef2..15b535b 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,5 +1,7 @@ use core::fmt; use core::any::Any; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; #[doc(hidden)] pub trait CloneToAny { diff --git a/src/lib.rs b/src/lib.rs index 966aa5a..6366c70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,20 @@ #![warn(missing_docs, unused_results)] +#![cfg_attr(not(feature = "std"), no_std)] + use core::any::{Any, TypeId}; use core::marker::PhantomData; +#[cfg(not(any(feature = "std", feature = "hashbrown")))] +compile_error!("anymap: you must enable the 'std' feature or the 'hashbrown' feature"); + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; + use raw::RawMap; use any::{UncheckedAnyExt, IntoBox}; pub use any::CloneAny; diff --git a/src/raw.rs b/src/raw.rs index 2b490d8..081edd4 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -4,7 +4,12 @@ use core::any::{Any, TypeId}; use core::borrow::Borrow; +#[cfg(all(feature = "std", not(feature = "hashbrown")))] use std::collections::hash_map::{self, HashMap}; +#[cfg(feature = "hashbrown")] +use hashbrown::hash_map::{self, HashMap}; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; use core::convert::TryInto; use core::hash::Hash; use core::hash::{Hasher, BuildHasherDefault}; @@ -35,6 +40,8 @@ impl Hasher for TypeIdHasher { #[test] fn type_id_hasher() { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; fn verify_hashing_with(type_id: TypeId) { let mut hasher = TypeIdHasher::default(); type_id.hash(&mut hasher); @@ -260,12 +267,18 @@ impl IntoIterator for RawMap { /// A view into a single occupied location in a `RawMap`. pub struct OccupiedEntry<'a, A: ?Sized + UncheckedAnyExt> { + #[cfg(all(feature = "std", not(feature = "hashbrown")))] inner: hash_map::OccupiedEntry<'a, TypeId, Box>, + #[cfg(feature = "hashbrown")] + inner: hash_map::OccupiedEntry<'a, TypeId, Box, BuildHasherDefault>, } /// A view into a single empty location in a `RawMap`. pub struct VacantEntry<'a, A: ?Sized + UncheckedAnyExt> { + #[cfg(all(feature = "std", not(feature = "hashbrown")))] inner: hash_map::VacantEntry<'a, TypeId, Box>, + #[cfg(feature = "hashbrown")] + inner: hash_map::VacantEntry<'a, TypeId, Box, BuildHasherDefault>, } /// A view into a single location in a `RawMap`, which may be vacant or occupied. diff --git a/test b/test index ad55933..f635904 100644 --- a/test +++ b/test @@ -2,9 +2,25 @@ set -e export RUSTFLAGS="-D warnings" export RUSTDOCFLAGS="-D warnings" -cargo +1.34.0 test -cargo +1.34.0 test --release +run_tests() { + for release in "" "--release"; do + cargo $1 test $release --no-default-features --features hashbrown + cargo $1 test $release --features hashbrown + # (2>/dev/null because otherwise you’ll keep seeing errors and double-guessing whether they were supposed to happen or whether the script failed to exit nonzero.) + ! 2>/dev/null cargo $1 test $release --no-default-features || ! echo "'cargo $1 test $release --no-default-features' failed to fail (sorry, its stderr is suppressed, try it manually)" + cargo $1 test $release + done +} + +# We’d like to test with the oldest declared-supported version of *all* our dependencies. +# That means Rust 1.36.0 + hashbrown 0.1.1. +# Hence the different lock file. +# (Also Rust 1.36.0 can’t read the latest lock file format.) +cp test-oldest-Cargo.lock Cargo.lock +run_tests +1.36.0 +rm Cargo.lock +run_tests + cargo clippy -cargo test -cargo test --release +cargo bench cargo doc diff --git a/test-oldest-Cargo.lock b/test-oldest-Cargo.lock new file mode 100644 index 0000000..40aeb75 --- /dev/null +++ b/test-oldest-Cargo.lock @@ -0,0 +1,32 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anymap" +version = "1.0.0-beta.1" +dependencies = [ + "hashbrown 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hashbrown" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +"checksum hashbrown 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dfb69c301cead891d010536c7d1b3affe792f5a2be5d6fbd0fcaf7fa0976962" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -- 2.42.0