Skip to content

Commit 7f4a073

Browse files
committed
no_std support.
This PR is similar to other no-std PRs, however it takes the approach of using the new [`core::error`] module in Rust 1.81. This means that no-std mode has an MSRV of Rust 1.81, while the existing MSRV of 1.49 is still supported for existing users, as suggested [here]. This PR also preserves semver compatibility, and avoids adding any new dependencies or required features for existing users. And it avoids modifying the tests and benchmark sources, as those don't need to be no-std. And it avoids making any unrelated changes. And, it adds CI coverage and README.md documentation. [here]: #563 (comment) [`core::error`]: https://doc.rust-lang.org/stable/core/error/index.html Fixes #551.
1 parent 8c1fb20 commit 7f4a073

22 files changed

+140
-72
lines changed

.github/workflows/ci.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ jobs:
8888
- name: Test
8989
run: cargo check -p http
9090

91+
no-std:
92+
name: Check no_std
93+
runs-on: ubuntu-latest
94+
steps:
95+
- name: Checkout
96+
uses: actions/checkout@v4
97+
98+
- name: Install Rust
99+
uses: dtolnay/rust-toolchain@stable
100+
101+
- name: Check
102+
run: cargo check --no-default-features --features=no-std
103+
91104
wasm:
92105
name: WASM
93106
#needs: [style]

Cargo.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ exclude = [
3333
]
3434

3535
[features]
36-
default = ["std"]
37-
std = []
36+
default = ["std", "bytes/default", "fnv/default"]
37+
std = ["bytes/std", "fnv/std"]
38+
no-std = ["hashbrown", "ahash"]
3839

3940
[dependencies]
40-
bytes = "1"
41-
fnv = "1.0.5"
41+
bytes = { version = "1", default-features = false }
42+
fnv = { version = "1.0.5", default-features = false }
4243
itoa = "1"
44+
hashbrown = { version = "0.15.2", optional = true }
45+
ahash = { version = "0.8.6", default-features = false, optional = true }
4346

4447
[dev-dependencies]
4548
quickcheck = "1"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ This project follows the [Tokio MSRV][msrv] and is currently set to `1.49`.
6666

6767
[msrv]: https://github.com/tokio-rs/tokio/#supported-rust-versions
6868

69+
# no-std support
70+
71+
For no-std support, disable the default "std" feature and enable the "no-std"
72+
feature. no-std support has an MSRV of Rust 1.81.
73+
6974
# License
7075

7176
Licensed under either of

src/byte_str.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bytes::Bytes;
22

3-
use std::{ops, str};
3+
use alloc::string::String;
4+
use core::{ops, str};
45

56
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
67
pub(crate) struct ByteStr {
@@ -46,7 +47,7 @@ impl ByteStr {
4647
ByteStr { bytes }
4748
}
4849

49-
pub(crate) fn from_utf8(bytes: Bytes) -> Result<ByteStr, std::str::Utf8Error> {
50+
pub(crate) fn from_utf8(bytes: Bytes) -> Result<ByteStr, core::str::Utf8Error> {
5051
str::from_utf8(&bytes)?;
5152
// Invariant: just checked is utf8
5253
Ok(ByteStr { bytes })

src/convert.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
macro_rules! if_downcast_into {
22
($in_ty:ty, $out_ty:ty, $val:ident, $body:expr) => {{
3-
if std::any::TypeId::of::<$in_ty>() == std::any::TypeId::of::<$out_ty>() {
3+
if core::any::TypeId::of::<$in_ty>() == core::any::TypeId::of::<$out_ty>() {
44
// Store the value in an `Option` so we can `take`
55
// it after casting to `&mut dyn Any`.
66
let mut slot = Some($val);
77
// Re-write the `$val` ident with the downcasted value.
8-
let $val = (&mut slot as &mut dyn std::any::Any)
8+
let $val = (&mut slot as &mut dyn core::any::Any)
99
.downcast_mut::<Option<$out_ty>>()
1010
.unwrap()
1111
.take()

src/error.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
#[cfg(not(feature = "std"))]
2+
use core::error;
3+
use core::fmt;
4+
use core::result;
5+
#[cfg(feature = "std")]
16
use std::error;
2-
use std::fmt;
3-
use std::result;
47

58
use crate::header;
69
use crate::header::MaxSizeReached;
@@ -132,8 +135,8 @@ impl From<header::InvalidHeaderValue> for Error {
132135
}
133136
}
134137

135-
impl From<std::convert::Infallible> for Error {
136-
fn from(err: std::convert::Infallible) -> Error {
138+
impl From<core::convert::Infallible> for Error {
139+
fn from(err: core::convert::Infallible) -> Error {
137140
match err {}
138141
}
139142
}

src/extensions.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
use std::any::{Any, TypeId};
1+
use alloc::boxed::Box;
2+
use core::any::{Any, TypeId};
3+
use core::fmt;
4+
use core::hash::{BuildHasherDefault, Hasher};
5+
#[cfg(not(feature = "std"))]
6+
use hashbrown::HashMap;
7+
#[cfg(feature = "std")]
28
use std::collections::HashMap;
3-
use std::fmt;
4-
use std::hash::{BuildHasherDefault, Hasher};
59

610
type AnyMap = HashMap<TypeId, Box<dyn AnyClone + Send + Sync>, BuildHasherDefault<IdHasher>>;
711

src/header/map.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
use std::collections::hash_map::RandomState;
2-
use std::collections::HashMap;
3-
use std::convert::TryFrom;
4-
use std::hash::{BuildHasher, Hash, Hasher};
5-
use std::iter::{FromIterator, FusedIterator};
6-
use std::marker::PhantomData;
7-
use std::{fmt, mem, ops, ptr, vec};
1+
use alloc::boxed::Box;
2+
use alloc::vec;
3+
use alloc::vec::Vec;
4+
use core::convert::TryFrom;
5+
use core::hash::{BuildHasher, Hash, Hasher};
6+
use core::iter::{FromIterator, FusedIterator};
7+
use core::marker::PhantomData;
8+
use core::{fmt, mem, ops, ptr};
9+
#[cfg(feature = "std")]
10+
use std::collections::{hash_map::RandomState, HashMap};
11+
#[cfg(not(feature = "std"))]
12+
use {ahash::RandomState, hashbrown::HashMap};
813

914
use crate::Error;
1015

@@ -116,7 +121,7 @@ pub struct IntoIter<T> {
116121
/// associated value.
117122
#[derive(Debug)]
118123
pub struct Keys<'a, T> {
119-
inner: ::std::slice::Iter<'a, Bucket<T>>,
124+
inner: ::core::slice::Iter<'a, Bucket<T>>,
120125
}
121126

122127
/// `HeaderMap` value iterator.
@@ -209,7 +214,7 @@ pub struct ValueIterMut<'a, T> {
209214
#[derive(Debug)]
210215
pub struct ValueDrain<'a, T> {
211216
first: Option<T>,
212-
next: Option<::std::vec::IntoIter<T>>,
217+
next: Option<::alloc::vec::IntoIter<T>>,
213218
lt: PhantomData<&'a mut HeaderMap<T>>,
214219
}
215220

@@ -3574,7 +3579,10 @@ impl fmt::Display for MaxSizeReached {
35743579
}
35753580
}
35763581

3582+
#[cfg(feature = "std")]
35773583
impl std::error::Error for MaxSizeReached {}
3584+
#[cfg(not(feature = "std"))]
3585+
impl core::error::Error for MaxSizeReached {}
35783586

35793587
// ===== impl Utils =====
35803588

@@ -3736,6 +3744,7 @@ mod into_header_name {
37363744

37373745
mod as_header_name {
37383746
use super::{Entry, HdrName, HeaderMap, HeaderName, InvalidHeaderName, MaxSizeReached};
3747+
use alloc::string::String;
37393748

37403749
/// A marker trait used to identify values that can be used as search keys
37413750
/// to a `HeaderMap`.

src/header/name.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
use crate::byte_str::ByteStr;
22
use bytes::{Bytes, BytesMut};
33

4-
use std::borrow::Borrow;
5-
use std::convert::TryFrom;
4+
use alloc::string::String;
5+
use alloc::vec::Vec;
6+
use core::borrow::Borrow;
7+
use core::convert::TryFrom;
8+
#[cfg(not(feature = "std"))]
9+
use core::error::Error;
10+
use core::fmt;
11+
use core::hash::{Hash, Hasher};
12+
use core::mem::MaybeUninit;
13+
use core::str::FromStr;
14+
#[cfg(feature = "std")]
615
use std::error::Error;
7-
use std::fmt;
8-
use std::hash::{Hash, Hasher};
9-
use std::mem::MaybeUninit;
10-
use std::str::FromStr;
1116

1217
/// Represents an HTTP header field name
1318
///
@@ -89,7 +94,7 @@ macro_rules! standard_headers {
8994
match *self {
9095
// Safety: test_parse_standard_headers ensures these &[u8]s are &str-safe.
9196
$(
92-
StandardHeader::$konst => unsafe { std::str::from_utf8_unchecked( $name_bytes ) },
97+
StandardHeader::$konst => unsafe { core::str::from_utf8_unchecked( $name_bytes ) },
9398
)+
9499
}
95100
}

src/header/value.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use bytes::{Bytes, BytesMut};
22

3-
use std::convert::TryFrom;
3+
use alloc::string::String;
4+
use alloc::vec::Vec;
5+
use core::convert::TryFrom;
6+
#[cfg(not(feature = "std"))]
7+
use core::error::Error;
8+
use core::fmt::Write;
9+
use core::hash::{Hash, Hasher};
10+
use core::str::FromStr;
11+
use core::{cmp, fmt, str};
12+
#[cfg(feature = "std")]
413
use std::error::Error;
5-
use std::fmt::Write;
6-
use std::hash::{Hash, Hasher};
7-
use std::str::FromStr;
8-
use std::{cmp, fmt, str};
914

1015
use crate::header::name::HeaderName;
1116

@@ -234,7 +239,7 @@ impl HeaderValue {
234239
}
235240

236241
fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
237-
HeaderValue::try_from_generic(src, std::convert::identity)
242+
HeaderValue::try_from_generic(src, core::convert::identity)
238243
}
239244

240245
fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(

0 commit comments

Comments
 (0)