From 9423fb68da97176a4ec977dffa5a734ffea01bc0 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 1 Oct 2019 22:38:52 +0200 Subject: [PATCH 1/7] FEAT: Support building without libstd --- .travis.yml | 9 +++++++++ Cargo.toml | 7 +++++++ build.rs | 6 ++++++ src/lib.rs | 21 ++++++++++++++++++++- src/map.rs | 17 ++++++++++++++++- src/set.rs | 14 +++++++++++++- test-nostd/Cargo.toml | 12 ++++++++++++ test-nostd/src/lib.rs | 23 +++++++++++++++++++++++ 8 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 build.rs create mode 100644 test-nostd/Cargo.toml create mode 100644 test-nostd/src/lib.rs diff --git a/.travis.yml b/.travis.yml index 0067b1cd..a14c967e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,15 @@ matrix: - rust: nightly env: - FEATURES='test_low_transition_point' + - name: "no_std" + 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..fdb35cd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,13 @@ indexmap. keywords = ["hashmap"] categories = ["data-structures"] +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..968d351d --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +extern crate autocfg; + +fn main() { + let ac = autocfg::new(); + ac.emit_sysroot_crate("std"); +} diff --git a/src/lib.rs b/src/lib.rs index cb28caf8..91c2cb4a 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. @@ -21,6 +21,25 @@ //! upgrade policy, where in a later 1.x version, we will raise the minimum //! required Rust version. +#[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; #[cfg(feature = "serde-1")] 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..b5facc89 --- /dev/null +++ b/test-nostd/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-nostd" +version = "0.1.0" +authors = ["bluss"] +publish = false + +[dependencies] +indexmap = { path = ".." } +# no-std compatible hasher +ahash = "0.2" + +[dev-dependencies] diff --git a/test-nostd/src/lib.rs b/test-nostd/src/lib.rs new file mode 100644 index 00000000..3c045b29 --- /dev/null +++ b/test-nostd/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] + +extern crate indexmap; +extern crate ahash; + +use indexmap::IndexMap; +use indexmap::IndexSet; +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"); +} From b1bfb860da9c9d7f83161325df9ea5666ee9e98b Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 1 Oct 2019 23:54:10 +0200 Subject: [PATCH 2/7] FIX: Fix rerun in build script --- build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/build.rs b/build.rs index 968d351d..f8bb38a1 100644 --- a/build.rs +++ b/build.rs @@ -3,4 +3,5 @@ extern crate autocfg; fn main() { let ac = autocfg::new(); ac.emit_sysroot_crate("std"); + autocfg::rerun_path(file!()); } From 37026329988baef71b511a1dd0f492620a871d99 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 2 Oct 2019 10:12:57 +0200 Subject: [PATCH 3/7] TEST: Build no-std test from Rust 1.36 --- .travis.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a14c967e..dcf9acb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,15 @@ matrix: - rust: nightly env: - FEATURES='test_low_transition_point' - - name: "no_std" - rust: stable + - 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 From b96208885521e317334a000f234d065ad4e50854 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 4 Oct 2019 20:32:27 +0200 Subject: [PATCH 4/7] TEST: Use twox-hash for testing nostd build Uses shepmaster's hash crate for testing, it's simple and no-std, tip by cuviper. --- test-nostd/Cargo.toml | 3 ++- test-nostd/src/lib.rs | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test-nostd/Cargo.toml b/test-nostd/Cargo.toml index b5facc89..ba2e7fe5 100644 --- a/test-nostd/Cargo.toml +++ b/test-nostd/Cargo.toml @@ -3,10 +3,11 @@ name = "test-nostd" version = "0.1.0" authors = ["bluss"] publish = false +edition = "2018" [dependencies] indexmap = { path = ".." } # no-std compatible hasher -ahash = "0.2" +twox-hash = { version = "1.5", default-features = false } [dev-dependencies] diff --git a/test-nostd/src/lib.rs b/test-nostd/src/lib.rs index 3c045b29..45de85c6 100644 --- a/test-nostd/src/lib.rs +++ b/test-nostd/src/lib.rs @@ -1,12 +1,11 @@ #![no_std] -extern crate indexmap; -extern crate ahash; - use indexmap::IndexMap; use indexmap::IndexSet; -type Map = IndexMap; -type Set = IndexSet; +use core::hash::BuildHasherDefault; +use twox_hash::XxHash64; +type Map = IndexMap>; +type Set = IndexSet>; use core::iter::FromIterator; From aa0778a59cc9f0e6541c6d704182aedf273a5b32 Mon Sep 17 00:00:00 2001 From: bluss Date: Sat, 12 Oct 2019 22:30:40 +0200 Subject: [PATCH 5/7] FEAT: indexmap!() macros require std (Due to using the ::with_capacity method.) --- src/macros.rs | 2 ++ 1 file changed, 2 insertions(+) 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 /// From 3e8d9b5c80e76117ca58d208a8b6af78e914e728 Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 13 Oct 2019 11:11:13 +0200 Subject: [PATCH 6/7] DOC: Add docs for nostd --- src/lib.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 91c2cb4a..4030a04d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,14 +12,34 @@ //! [`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)] From 6bd5fb67af858a0afed117ac20a20a63b4a3ccba Mon Sep 17 00:00:00 2001 From: bluss Date: Sun, 13 Oct 2019 11:13:25 +0200 Subject: [PATCH 7/7] MAINT: Update crate keywords and categories --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fdb35cd7..487a00e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ 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"