diff --git a/.travis.yml b/.travis.yml index 0067b1cd..dcf9acb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,22 @@ matrix: - rust: nightly env: - FEATURES='test_low_transition_point' + - rust: 1.36.0 + env: TARGET=thumbv6m-none-eabi + before_script: + - rustup target add $TARGET + - set -ex + script: + - cargo build -vv --target=$TARGET + - cargo build -v -p test-nostd --target=$TARGET + - rust: stable + env: TARGET=thumbv6m-none-eabi + before_script: + - rustup target add $TARGET + - set -ex + script: + - cargo build -vv --target=$TARGET + - cargo build -v -p test-nostd --target=$TARGET branches: only: - master diff --git a/Cargo.toml b/Cargo.toml index 178747c1..487a00e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,12 +21,16 @@ This crate was initially published under the name ordermap, but it was renamed t indexmap. """ -keywords = ["hashmap"] -categories = ["data-structures"] +keywords = ["hashmap", "no_std"] +categories = ["data-structures", "no-std"] + +build = "build.rs" [lib] bench = false +[build-dependencies] +autocfg = "0.1.6" [dependencies] serde = { version = "1.0", optional = true } rayon = { version = "1.0", optional = true } @@ -56,3 +60,6 @@ tag-name = "{{version}}" [package.metadata.docs.rs] features = ["serde-1", "rayon"] + +[workspace] +members = ["test-nostd"] diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..f8bb38a1 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +extern crate autocfg; + +fn main() { + let ac = autocfg::new(); + ac.emit_sysroot_crate("std"); + autocfg::rerun_path(file!()); +} diff --git a/src/lib.rs b/src/lib.rs index cb28caf8..4030a04d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ - #![deny(unsafe_code)] #![doc(html_root_url = "https://docs.rs/indexmap/1/")] +#![cfg_attr(not(has_std), no_std)] //! [`IndexMap`] is a hash table where the iteration order of the key-value //! pairs is independent of the hash values of the keys. @@ -12,14 +12,53 @@ //! [`IndexSet`]: set/struct.IndexSet.html //! //! +//! //! ## Rust Version //! //! This version of indexmap requires Rust 1.18 or later, or 1.30+ for -//! development builds. +//! development builds, and Rust 1.36+ for using with `alloc` (without `std`), +//! see below. //! //! The indexmap 1.x release series will use a carefully considered version //! upgrade policy, where in a later 1.x version, we will raise the minimum //! required Rust version. +//! +//! ## No Standard Library Targets +//! +//! From Rust 1.36, this crate supports being built without `std`, requiring +//! `alloc` instead. This is enabled automatically when it is detected that +//! `std` is not available. There is no crate feature to enable/disable to +//! trigger this. It can be tested by building for a std-less target. +//! +//! - Creating maps and sets using [`new`][IndexMap::new] and +//! [`with_capacity`][IndexMap::with_capacity] is unavailable without `std`. +//! Use methods [`IndexMap::default`][def], +//! [`with_hasher`][IndexMap::with_hasher], +//! [`with_capacity_and_hasher`][IndexMap::with_capacity_and_hasher] instead. +//! A no-std compatible hasher will be needed as well, for example +//! from the crate `twox-hash`. +//! - Macros [`indexmap!`] and [`indexset!`] are unavailable without `std`. +//! +//! [def]: map/struct.IndexMap.html#impl-Default + +#[cfg(not(has_std))] +#[macro_use(vec)] +extern crate alloc; + +#[cfg(not(has_std))] +pub(crate) mod std { + pub use core::*; + pub mod alloc { + pub use ::alloc::*; + } + pub mod collections { + pub use ::alloc::collections::*; + } + pub use ::alloc::vec as vec; +} + +#[cfg(not(has_std))] +use std::vec::Vec; #[macro_use] mod macros; diff --git a/src/macros.rs b/src/macros.rs index 3b7c83c9..6ab0e1ea 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,5 @@ +#[cfg(has_std)] #[macro_export(local_inner_macros)] /// Create an `IndexMap` from a list of key-value pairs /// @@ -37,6 +38,7 @@ macro_rules! indexmap { }; } +#[cfg(has_std)] #[macro_export(local_inner_macros)] /// Create an `IndexSet` from a list of values /// diff --git a/src/map.rs b/src/map.rs index 13b3635a..dcc4abcc 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,6 +1,11 @@ //! `IndexMap` is a hash table where the iteration order of the key-value //! pairs is independent of the hash values of the keys. +#[cfg(not(has_std))] +use alloc::boxed::Box; +#[cfg(not(has_std))] +use std::vec::Vec; + pub use mutable_keys::MutableKeys; #[cfg(feature = "rayon")] @@ -10,9 +15,11 @@ use std::hash::Hash; use std::hash::BuildHasher; use std::hash::Hasher; use std::iter::FromIterator; -use std::collections::hash_map::RandomState; use std::ops::RangeFull; +#[cfg(has_std)] +use std::collections::hash_map::RandomState; + use std::cmp::{max, Ordering}; use std::fmt; use std::mem::{replace}; @@ -264,10 +271,17 @@ impl ShortHashProxy /// assert_eq!(letters.get(&'y'), None); /// ``` #[derive(Clone)] +#[cfg(has_std)] pub struct IndexMap { core: OrderMapCore, hash_builder: S, } +#[derive(Clone)] +#[cfg(not(has_std))] +pub struct IndexMap { + core: OrderMapCore, + hash_builder: S, +} // core of the map that does not depend on S #[derive(Clone)] @@ -379,6 +393,7 @@ macro_rules! probe_loop { } } +#[cfg(has_std)] impl IndexMap { /// Create a new map. (Does not allocate.) pub fn new() -> Self { diff --git a/src/set.rs b/src/set.rs index c50e5ee2..7c0f02ab 100644 --- a/src/set.rs +++ b/src/set.rs @@ -3,8 +3,13 @@ #[cfg(feature = "rayon")] pub use ::rayon::set as rayon; -use std::cmp::Ordering; +#[cfg(not(has_std))] +use std::vec::Vec; + +#[cfg(has_std)] use std::collections::hash_map::RandomState; + +use std::cmp::Ordering; use std::fmt; use std::iter::{FromIterator, Chain}; use std::hash::{Hash, BuildHasher}; @@ -59,9 +64,15 @@ type Bucket = super::Bucket; /// assert!(!letters.contains(&'y')); /// ``` #[derive(Clone)] +#[cfg(has_std)] pub struct IndexSet { map: IndexMap, } +#[cfg(not(has_std))] +#[derive(Clone)] +pub struct IndexSet { + map: IndexMap, +} impl Entries for IndexSet { type Entry = Bucket; @@ -99,6 +110,7 @@ impl fmt::Debug for IndexSet } } +#[cfg(has_std)] impl IndexSet { /// Create a new set. (Does not allocate.) pub fn new() -> Self { diff --git a/test-nostd/Cargo.toml b/test-nostd/Cargo.toml new file mode 100644 index 00000000..ba2e7fe5 --- /dev/null +++ b/test-nostd/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test-nostd" +version = "0.1.0" +authors = ["bluss"] +publish = false +edition = "2018" + +[dependencies] +indexmap = { path = ".." } +# no-std compatible hasher +twox-hash = { version = "1.5", default-features = false } + +[dev-dependencies] diff --git a/test-nostd/src/lib.rs b/test-nostd/src/lib.rs new file mode 100644 index 00000000..45de85c6 --- /dev/null +++ b/test-nostd/src/lib.rs @@ -0,0 +1,22 @@ +#![no_std] + +use indexmap::IndexMap; +use indexmap::IndexSet; +use core::hash::BuildHasherDefault; +use twox_hash::XxHash64; +type Map = IndexMap>; +type Set = IndexSet>; + +use core::iter::FromIterator; + +pub fn test_compile() { + let mut map = Map::default(); + map.insert(1, 1); + map.insert(2, 4); + for (_, _) in map.iter() { } + + let _map2 = Map::from_iter(Some((1, 1))); + + let mut set = Set::default(); + set.insert("a"); +}